# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/powertop/Makefile # PROG = powertop include ../Makefile.cmd SUBDIRS += $(MACH64) all : TARGET = all install : TARGET = install clean : TARGET = clean clobber : TARGET = clobber .KEEP_STATE: all clean clobber install: $(SUBDIRS) install: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: include ../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG = powertop COMMON_OBJS = $(PROG).o \ display.o \ battery.o \ cpufreq.o \ cpuidle.o \ events.o \ util.o \ suggestions.o \ turbo.o SRCS = $(COMMON_OBJS:%.o=../common/%.c) include ../../Makefile.cmd .KEEP_STATE: CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) SMOFF += free LDLIBS += -lcurses -ldtrace -lkstat FILEMODE = 0555 CLEANFILES += $(COMMON_OBJS) all: $(PROG) clean: $(RM) $(CLEANFILES) lint: lint_SRCS include ../../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.com include ../../Makefile.cmd.64 MACH_OBJS = pt_amd64.o SRCS += $(MACH_OBJS:%.o=%.c) .KEEP_STATE: CLEANFILES += $(MACH_OBJS) all: $(PROG) $(PROG): $(MACH_OBJS) $(COMMON_OBJS) $(LINK.c) -o $@ $(MACH_OBJS) $(COMMON_OBJS) $(LDLIBS) $(POST_PROCESS) %.o: ../common/%.c $(COMPILE.c) -o $@ $< install: all $(ROOTPROG) /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ /* * DTrace scripts for observing interrupts, callouts and cyclic events * that cause CPU activity. Such activity prevents the processor from * entering lower power states and reducing power consumption. * * g_dtp_events is the default script */ const char *g_dtp_events = "interrupt-complete" "/arg0 != NULL && arg3 !=0/" "{" " this->devi = (struct dev_info *)arg0;" " @interrupts[stringof(`devnamesp[this->devi->devi_major].dn_name)," " this->devi->devi_instance] = count();" "}" "" "sdt:::callout-start" "/(caddr_t)((callout_t *)arg0)->c_func == (caddr_t)&`setrun/" "{" " this->thr = (kthread_t *)(((callout_t *)arg0)->c_arg);" " @events_u[stringof(this->thr->t_procp->p_user.u_comm)] = count();" "}" "" "sdt:::callout-start" "/(caddr_t)((callout_t *)arg0)->c_func != (caddr_t)&`setrun/" "{" " @events_k[(caddr_t)((callout_t *)arg0)->c_func] = count();" "}" "" "sdt:::cyclic-start" "/(caddr_t)((cyclic_t *)arg0)->cy_handler == (caddr_t)&`clock/" "{" " @events_k[(caddr_t)((cyclic_t *)arg0)->cy_handler] = count();" "}" "" "fbt::xc_common:entry" "{" " self->xc_func = arg0;" "}" "" "sysinfo:::xcalls" "/pid != $pid/" "{" " @events_x[execname, self->xc_func] = sum(arg0);" "}" "" "fbt::xc_common:return" "/self->xc_func/" "{" " self->xc_func = 0;" "}"; /* * g_dtp_events_v is enabled through the -v option, it includes cyclic events * in the report, allowing a complete view of system activity */ const char *g_dtp_events_v = "interrupt-complete" "/arg0 != NULL && arg3 !=0/" "{" " this->devi = (struct dev_info *)arg0;" " @interrupts[stringof(`devnamesp[this->devi->devi_major].dn_name)," " this->devi->devi_instance] = count();" "}" "" "sdt:::callout-start" "/(caddr_t)((callout_t *)arg0)->c_func == (caddr_t)&`setrun/" "{" " this->thr = (kthread_t *)(((callout_t *)arg0)->c_arg);" " @events_u[stringof(this->thr->t_procp->p_user.u_comm)] = count();" "}" "" "sdt:::callout-start" "/(caddr_t)((callout_t *)arg0)->c_func != (caddr_t)&`setrun/" "{" " @events_k[(caddr_t)((callout_t *)arg0)->c_func] = count();" "}" "" "sdt:::cyclic-start" "/(caddr_t)((cyclic_t *)arg0)->cy_handler != (caddr_t)&`dtrace_state_deadman &&" " (caddr_t)((cyclic_t *)arg0)->cy_handler != (caddr_t)&`dtrace_state_clean/" "{" " @events_k[(caddr_t)((cyclic_t *)arg0)->cy_handler] = count();" "}" "" "fbt::xc_common:entry" "{" " self->xc_func = arg0;" "}" "" "sysinfo:::xcalls" "/pid != $pid/" "{" " @events_x[execname, self->xc_func] = sum(arg0);" "}" "" "fbt::xc_common:return" "/self->xc_func/" "{" " self->xc_func = 0;" "}"; /* * This script is selected through the -c option, it takes the CPU id as * argument and observes activity generated by that CPU */ const char *g_dtp_events_c = "interrupt-complete" "/cpu == $0 &&" " arg0 != NULL && arg3 != 0/" "{" " this->devi = (struct dev_info *)arg0;" " @interrupts[stringof(`devnamesp[this->devi->devi_major].dn_name)," " this->devi->devi_instance] = count();" "}" "" "sdt:::callout-start" "/cpu == $0 &&" " (caddr_t)((callout_t *)arg0)->c_func == (caddr_t)&`setrun/" "{" " this->thr = (kthread_t *)(((callout_t *)arg0)->c_arg);" " @events_u[stringof(this->thr->t_procp->p_user.u_comm)] = count();" "}" "" "sdt:::callout-start" "/cpu == $0 &&" " (caddr_t)((callout_t *)arg0)->c_func != (caddr_t)&`setrun/" "{" " @events_k[(caddr_t)((callout_t *)arg0)->c_func] = count();" "}" "" "sdt:::cyclic-start" "/cpu == $0 &&" " (caddr_t)((cyclic_t *)arg0)->cy_handler == (caddr_t)&`clock/" "{" " @events_k[(caddr_t)((cyclic_t *)arg0)->cy_handler] = count();" "}" "" "fbt::xc_common:entry" "/cpu == $0/" "{" " self->xc_func = arg0;" "}" "" "sysinfo:::xcalls" "/pid != $pid &&" " cpu == $0/" "{" " @events_x[execname, self->xc_func] = count();" "}" "" "fbt::xc_common:return" "/cpu == $0 &&" " self->xc_func/" "{" " self->xc_func = 0;" "}" "" "fbt::xc_common:entry" "/cpu != $0/" "{" " self->xc_func = arg0;" " self->xc_cpu = cpu;" "}" "" "fbt::send_dirint:entry" "/pid != $pid &&" " self->xc_func &&" " arg0 == $0/" "{" " @events_xc[execname, self->xc_func, self->xc_cpu] = count();" " self->xc_func = 0;" " self->xc_cpu = 0;" "}"; /* * amd64 platform specific display messages */ const char *g_msg_idle_state = "C-states (idle power)"; const char *g_msg_freq_state = "P-states (frequencies)"; const char *g_msg_freq_enable = "P - Enable P-states"; GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. POWERTOP /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include "powertop.h" #define mW2W(value) ((value) / 1000) typedef struct battery_state { uint32_t exist; uint32_t power_unit; uint32_t bst_state; double present_rate; double remain_cap; double last_cap; } battery_state_t; static char *kstat_batt_mod[3] = {NULL, "battery", "acpi_drv"}; static uint_t kstat_batt_idx; static battery_state_t battery_state; static int pt_battery_stat_snapshot(void); /* * Checks if the kstat module for battery information is present and * whether it's called 'battery' or 'acpi_drv' */ void pt_battery_mod_lookup(void) { kstat_ctl_t *kc = kstat_open(); if (kstat_lookup(kc, kstat_batt_mod[1], 0, NULL)) kstat_batt_idx = 1; else if (kstat_lookup(kc, kstat_batt_mod[2], 0, NULL)) kstat_batt_idx = 2; else kstat_batt_idx = 0; (void) kstat_close(kc); } void pt_battery_print(void) { int err; (void) memset(&battery_state, 0, sizeof (battery_state_t)); /* * The return value of pt_battery_stat_snapshot() can be used for * debug or to show/hide the acpi power line. We currently don't * make the distinction of a system that runs only on AC and one * that runs on battery but has no kstat battery info. * * We still display the estimate power usage for systems * running on AC with a fully charged battery because some * batteries may still consume power. * * If pt_battery_mod_lookup() didn't find a kstat battery module, don't * bother trying to take the snapshot */ if (kstat_batt_idx > 0) { if ((err = pt_battery_stat_snapshot()) < 0) pt_error("battery kstat not found (%d)\n", err); } pt_display_acpi_power(battery_state.exist, battery_state.present_rate, battery_state.remain_cap, battery_state.last_cap, battery_state.bst_state); } static int pt_battery_stat_snapshot(void) { kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *knp; kc = kstat_open(); /* * power unit: * 0 - Capacity information is reported in [mWh] and * charge/discharge rate information in [mW] * 1 - Capacity information is reported in [mAh] and * charge/discharge rate information in [mA]. */ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0, "battery BIF0"); if (ksp == NULL) { (void) kstat_close(kc); return (-1); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "bif_unit"); if (knp == NULL) { (void) kstat_close(kc); return (-1); } battery_state.power_unit = knp->value.ui32; /* * Present rate: * the power or current being supplied or accepted * through the battery's terminal */ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0, "battery BST0"); if (ksp == NULL) { (void) kstat_close(kc); return (-1); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "bst_rate"); if (knp == NULL) { (void) kstat_close(kc); return (-1); } if (knp->value.ui32 == 0xFFFFFFFF) battery_state.present_rate = 0; else { battery_state.exist = 1; battery_state.present_rate = mW2W((double)(knp->value.ui32)); } /* * Last Full charge capacity: * Predicted battery capacity when fully charged. */ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0, "battery BIF0"); if (ksp == NULL) { (void) kstat_close(kc); return (-1); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "bif_last_cap"); if (knp == NULL) { (void) kstat_close(kc); return (-1); } battery_state.last_cap = mW2W((double)(knp->value.ui32)); /* * Remaining capacity: * the estimated remaining battery capacity */ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0, "battery BST0"); if (ksp == NULL) { (void) kstat_close(kc); return (-1); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "bst_rem_cap"); if (knp == NULL) { (void) kstat_close(kc); return (-1); } battery_state.remain_cap = mW2W((double)(knp->value.ui32)); /* * Battery State: * Bit0 - 1 : discharging * Bit1 - 1 : charging * Bit2 - 1 : critical energy state */ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0, "battery BST0"); if (ksp == NULL) { (void) kstat_close(kc); return (-1); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "bst_state"); if (knp == NULL) { (void) kstat_close(kc); return (-1); } battery_state.bst_state = knp->value.ui32; (void) kstat_close(kc); return (0); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include #include #include "powertop.h" #define HZ2MHZ(speed) ((speed) / MICROSEC) #define DTP_ARG_COUNT 2 #define DTP_ARG_LENGTH 5 static uint64_t max_cpufreq = 0; static dtrace_hdl_t *dtp; static char **dtp_argv; /* * Enabling PM through /etc/power.conf * See pt_cpufreq_suggest() */ static char default_conf[] = "/etc/power.conf"; static char default_pmconf[] = "/usr/sbin/pmconfig"; static char cpupm_enable[] = "echo cpupm enable >> /etc/power.conf"; static char cpupm_treshold[] = "echo cpu-threshold 1s >> /etc/power.conf"; /* * Buffer containing DTrace program to track CPU frequency transitions */ static const char *dtp_cpufreq = "hrtime_t last[$0];" "" "BEGIN" "{" " begin = timestamp;" "}" "" ":::cpu-change-speed" "/last[(processorid_t)arg0] != 0/" "{" " this->cpu = (processorid_t)arg0;" " this->oldspeed = (uint64_t)arg1;" " @times[this->cpu, this->oldspeed] = sum(timestamp - last[this->cpu]);" " last[this->cpu] = timestamp;" "}" ":::cpu-change-speed" "/last[(processorid_t)arg0] == 0/" "{" " this->cpu = (processorid_t)arg0;" " this->oldspeed = (uint64_t)arg1;" " @times[this->cpu, this->oldspeed] = sum(timestamp - begin);" " last[this->cpu] = timestamp;" "}"; /* * Same as above, but only for a specific CPU */ static const char *dtp_cpufreq_c = "hrtime_t last;" "" "BEGIN" "{" " begin = timestamp;" "}" "" ":::cpu-change-speed" "/(processorid_t)arg0 == $1 &&" " last != 0/" "{" " this->cpu = (processorid_t)arg0;" " this->oldspeed = (uint64_t)arg1;" " @times[this->cpu, this->oldspeed] = sum(timestamp - last);" " last = timestamp;" "}" ":::cpu-change-speed" "/(processorid_t)arg0 == $1 &&" " last == 0/" "{" " this->cpu = (processorid_t)arg0;" " this->oldspeed = (uint64_t)arg1;" " @times[this->cpu, this->oldspeed] = sum(timestamp - begin);" " last = timestamp;" "}"; static int pt_cpufreq_setup(void); static int pt_cpufreq_snapshot(void); static int pt_cpufreq_dtrace_walk(const dtrace_aggdata_t *, void *); static void pt_cpufreq_stat_account(double, uint_t); static int pt_cpufreq_snapshot_cpu(kstat_ctl_t *, uint_t); static int pt_cpufreq_check_pm(void); static void pt_cpufreq_enable(void); static int pt_cpufreq_setup(void) { if ((dtp_argv = malloc(sizeof (char *) * DTP_ARG_COUNT)) == NULL) return (1); if ((dtp_argv[0] = malloc(sizeof (char) * DTP_ARG_LENGTH)) == NULL) { free(dtp_argv); return (1); } (void) snprintf(dtp_argv[0], 5, "%d\0", g_ncpus_observed); if (PT_ON_CPU) { if ((dtp_argv[1] = malloc(sizeof (char) * DTP_ARG_LENGTH)) == NULL) { free(dtp_argv[0]); free(dtp_argv); return (1); } (void) snprintf(dtp_argv[1], 5, "%d\0", g_observed_cpu); } return (0); } /* * Perform setup necessary to enumerate and track CPU speed changes */ int pt_cpufreq_stat_prepare(void) { dtrace_prog_t *prog; dtrace_proginfo_t info; dtrace_optval_t statustime; kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *knp; freq_state_info_t *state; char *s, *token, *prog_ptr; int err; if ((err = pt_cpufreq_setup()) != 0) { pt_error("failed to setup %s report (couldn't allocate " "memory)\n", g_msg_freq_state); return (errno); } state = g_pstate_info; if ((g_cpu_power_states = calloc((size_t)g_ncpus, sizeof (cpu_power_info_t))) == NULL) return (-1); /* * Enumerate the CPU frequencies */ if ((kc = kstat_open()) == NULL) return (errno); ksp = kstat_lookup(kc, "cpu_info", g_cpu_table[g_observed_cpu], NULL); if (ksp == NULL) { err = errno; (void) kstat_close(kc); return (err); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "supported_frequencies_Hz"); s = knp->value.str.addr.ptr; g_npstates = 0; for (token = strtok(s, ":"), s = NULL; token != NULL && g_npstates < NSTATES; token = strtok(NULL, ":")) { state->speed = HZ2MHZ(atoll(token)); if (state->speed > max_cpufreq) max_cpufreq = state->speed; state->total_time = (uint64_t)0; g_npstates++; state++; } if (token != NULL) pt_error("CPU exceeds the supported number of %s\n", g_msg_freq_state); (void) kstat_close(kc); /* * Return if speed transition is not supported */ if (g_npstates < 2) return (-1); /* * Setup DTrace to look for CPU frequency changes */ if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { pt_error("cannot open dtrace library for the %s report: %s\n", g_msg_freq_state, dtrace_errmsg(NULL, err)); return (-2); } /* * Execute different scripts (defined above) depending on * user specified options. Default mode uses dtp_cpufreq. */ if (PT_ON_CPU) prog_ptr = (char *)dtp_cpufreq_c; else prog_ptr = (char *)dtp_cpufreq; if ((prog = dtrace_program_strcompile(dtp, prog_ptr, DTRACE_PROBESPEC_NAME, 0, (1 + g_argc), dtp_argv)) == NULL) { pt_error("failed to compile %s program\n", g_msg_freq_state); return (dtrace_errno(dtp)); } if (dtrace_program_exec(dtp, prog, &info) == -1) { pt_error("failed to enable %s probes\n", g_msg_freq_state); return (dtrace_errno(dtp)); } if (dtrace_setopt(dtp, "aggsize", "128k") == -1) pt_error("failed to set %s 'aggsize'\n", g_msg_freq_state); if (dtrace_setopt(dtp, "aggrate", "0") == -1) pt_error("failed to set %s 'aggrate'\n", g_msg_freq_state); if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) pt_error("failed to set %s 'aggpercpu'\n", g_msg_freq_state); if (dtrace_go(dtp) != 0) { pt_error("failed to start %s observation\n", g_msg_freq_state); return (dtrace_errno(dtp)); } if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) { pt_error("failed to get %s 'statusrate'\n", g_msg_freq_state); return (dtrace_errno(dtp)); } return (0); } /* * The DTrace probes have already been enabled, and are tracking * CPU speed transitions. Take a snapshot of the aggregations, and * look for any CPUs that have made a speed transition over the last * sampling interval. Note that the aggregations may be empty if no * speed transitions took place over the last interval. In that case, * notate that we have already accounted for the time, so that when * we do encounter a speed transition in a future sampling interval * we can subtract that time back out. */ int pt_cpufreq_stat_collect(double interval) { int i, ret; /* * Zero out the interval time reported by DTrace for * this interval */ for (i = 0; i < g_npstates; i++) g_pstate_info[i].total_time = 0; for (i = 0; i < g_ncpus; i++) g_cpu_power_states[i].dtrace_time = 0; if (dtrace_status(dtp) == -1) return (-1); if (dtrace_aggregate_snap(dtp) != 0) pt_error("failed to collect data for %s\n", g_msg_freq_state); if (dtrace_aggregate_walk_keyvarsorted(dtp, pt_cpufreq_dtrace_walk, NULL) != 0) pt_error("failed to sort data for %s\n", g_msg_freq_state); dtrace_aggregate_clear(dtp); if ((ret = pt_cpufreq_snapshot()) != 0) { pt_error("failed to snapshot %s state\n", g_msg_freq_state); return (ret); } switch (g_op_mode) { case PT_MODE_CPU: pt_cpufreq_stat_account(interval, g_observed_cpu); break; case PT_MODE_DEFAULT: default: for (i = 0; i < g_ncpus_observed; i++) pt_cpufreq_stat_account(interval, i); break; } return (0); } static void pt_cpufreq_stat_account(double interval, uint_t cpu) { cpu_power_info_t *cpu_pow; uint64_t speed; hrtime_t duration; int i; cpu_pow = &g_cpu_power_states[cpu]; speed = cpu_pow->current_pstate; duration = (hrtime_t)(interval * NANOSEC) - cpu_pow->dtrace_time; /* * 'duration' may be a negative value when we're using or forcing a * small interval, and the amount of time already accounted ends up * being larger than the the former. */ if (duration < 0) return; for (i = 0; i < g_npstates; i++) { if (g_pstate_info[i].speed == speed) { g_pstate_info[i].total_time += duration; cpu_pow->time_accounted += duration; cpu_pow->speed_accounted = speed; } } } /* * Take a snapshot of each CPU's speed by looking through the cpu_info kstats. */ static int pt_cpufreq_snapshot(void) { kstat_ctl_t *kc; int ret; uint_t i; if ((kc = kstat_open()) == NULL) return (errno); switch (g_op_mode) { case PT_MODE_CPU: ret = pt_cpufreq_snapshot_cpu(kc, g_observed_cpu); break; case PT_MODE_DEFAULT: default: for (i = 0; i < g_ncpus_observed; i++) if ((ret = pt_cpufreq_snapshot_cpu(kc, i)) != 0) break; break; } if (kstat_close(kc) != 0) pt_error("couldn't close %s kstat\n", g_msg_freq_state); return (ret); } static int pt_cpufreq_snapshot_cpu(kstat_ctl_t *kc, uint_t cpu) { kstat_t *ksp; kstat_named_t *knp; ksp = kstat_lookup(kc, "cpu_info", g_cpu_table[cpu], NULL); if (ksp == NULL) { pt_error("couldn't find 'cpu_info' kstat for CPU %d\n while " "taking a snapshot of %s\n", cpu, g_msg_freq_state); return (1); } if (kstat_read(kc, ksp, NULL) == -1) { pt_error("couldn't read 'cpu_info' kstat for CPU %d\n while " "taking a snapshot of %s\n", cpu, g_msg_freq_state); return (2); } knp = kstat_data_lookup(ksp, "current_clock_Hz"); if (knp == NULL) { pt_error("couldn't find 'current_clock_Hz' kstat for CPU %d " "while taking a snapshot of %s\n", cpu, g_msg_freq_state); return (3); } g_cpu_power_states[cpu].current_pstate = HZ2MHZ(knp->value.ui64); return (0); } /* * DTrace aggregation walker that sorts through a snapshot of the * aggregation data collected during firings of the cpu-change-speed * probe. */ /*ARGSUSED*/ static int pt_cpufreq_dtrace_walk(const dtrace_aggdata_t *data, void *arg) { dtrace_aggdesc_t *aggdesc = data->dtada_desc; dtrace_recdesc_t *cpu_rec, *speed_rec; cpu_power_info_t *cp; int32_t cpu; uint64_t speed; hrtime_t res; int i; if (strcmp(aggdesc->dtagd_name, "times") == 0) { cpu_rec = &aggdesc->dtagd_rec[1]; speed_rec = &aggdesc->dtagd_rec[2]; /* LINTED - alignment */ cpu = *(int32_t *)(data->dtada_data + cpu_rec->dtrd_offset); /* LINTED - alignment */ res = *((hrtime_t *)(data->dtada_percpu[cpu])); /* LINTED - alignment */ speed = *(uint64_t *)(data->dtada_data + speed_rec->dtrd_offset); if (speed == 0) speed = max_cpufreq; else speed = HZ2MHZ(speed); /* * We have an aggregation record for "cpu" being at "speed" * for an interval of "n" nanoseconds. The reported interval * may exceed the powertop sampling interval, since we only * notice during potentially infrequent firings of the * "speed change" DTrace probe. In this case powertop would * have already accounted for the portions of the interval * that happened during prior powertop samplings, so subtract * out time already accounted. */ cp = &g_cpu_power_states[cpu]; for (i = 0; i < g_npstates; i++) { if (g_pstate_info[i].speed == speed) { if (cp->time_accounted > 0 && cp->speed_accounted == speed) { if (res > cp->time_accounted) { res -= cp->time_accounted; cp->time_accounted = 0; cp->speed_accounted = 0; } else { return (DTRACE_AGGWALK_NEXT); } } g_pstate_info[i].total_time += res; cp->dtrace_time += res; } } } return (DTRACE_AGGWALK_NEXT); } /* * Checks if PM is enabled in /etc/power.conf, enabling if not */ void pt_cpufreq_suggest(void) { int ret = pt_cpufreq_check_pm(); switch (ret) { case 0: pt_sugg_add("Suggestion: enable CPU power management by " "pressing the P key", 40, 'P', (char *)g_msg_freq_enable, pt_cpufreq_enable); break; } } /* * Checks /etc/power.conf and returns: * * 0 if CPUPM is not enabled * 1 if there's nothing for us to do because: * (a) the system does not support frequency scaling * (b) there's no power.conf. * 2 if CPUPM is enabled * 3 if the system is running in poll-mode, as opposed to event-mode * * Notice the ordering of the return values, they will be picked up and * switched upon ascendingly. */ static int pt_cpufreq_check_pm(void) { char line[1024]; FILE *file; int ret = 0; if (g_npstates < 2 || (file = fopen(default_conf, "r")) == NULL) return (1); (void) memset(line, 0, 1024); while (fgets(line, 1024, file)) { if (strstr(line, "cpupm")) { if (strstr(line, "enable")) { (void) fclose(file); return (2); } } if (strstr(line, "poll")) ret = 3; } (void) fclose(file); return (ret); } /* * Used as a suggestion, sets PM in /etc/power.conf and * a 1sec threshold, then calls /usr/sbin/pmconfig */ static void pt_cpufreq_enable(void) { (void) system(cpupm_enable); (void) system(cpupm_treshold); (void) system(default_pmconf); if (pt_sugg_remove(pt_cpufreq_enable) == 0) pt_error("failed to remove a %s suggestion\n", g_msg_freq_state); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include "powertop.h" #define S2NS(x) ((x) * (NANOSEC)) static dtrace_hdl_t *dtp; /* * Buffer containing DTrace program to track CPU idle state transitions */ static const char *dtp_cpuidle = ":::idle-state-transition" "/arg0 != 0/" "{" " self->start = timestamp;" " self->state = arg0;" "}" "" ":::idle-state-transition" "/arg0 == 0 && self->start/" "{" " @number[self->state] = count();" " @times[self->state] = sum(timestamp - self->start);" " self->start = 0;" " self->state = 0;" "}"; /* * Same as above but only for a specific CPU */ static const char *dtp_cpuidle_c = ":::idle-state-transition" "/cpu == $0 &&" " arg0 != 0/" "{" " self->start = timestamp;" " self->state = arg0;" "}" "" ":::idle-state-transition" "/cpu == $0 &&" " arg0 == 0 && self->start/" "{" " @number[self->state] = count();" " @times[self->state] = sum(timestamp - self->start);" " self->start = 0;" " self->state = 0;" "}"; static int pt_cpuidle_dtrace_walk(const dtrace_aggdata_t *, void *); /* * Perform setup necessary to track CPU idle state transitions */ int pt_cpuidle_stat_prepare(void) { dtrace_prog_t *prog; dtrace_proginfo_t info; dtrace_optval_t statustime; int err; char *prog_ptr; if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { pt_error("cannot open dtrace library for the %s report: %s\n", g_msg_idle_state, dtrace_errmsg(NULL, err)); return (-1); } /* * Execute different scripts (defined above) depending on * user specified options. */ if (PT_ON_CPU) prog_ptr = (char *)dtp_cpuidle_c; else prog_ptr = (char *)dtp_cpuidle; if ((prog = dtrace_program_strcompile(dtp, prog_ptr, DTRACE_PROBESPEC_NAME, 0, g_argc, g_argv)) == NULL) { pt_error("failed to compile %s program\n", g_msg_idle_state); return (dtrace_errno(dtp)); } if (dtrace_program_exec(dtp, prog, &info) == -1) { pt_error("failed to enable %s probes\n", g_msg_idle_state); return (dtrace_errno(dtp)); } if (dtrace_setopt(dtp, "aggsize", "128k") == -1) pt_error("failed to set %s 'aggsize'\n", g_msg_idle_state); if (dtrace_setopt(dtp, "aggrate", "0") == -1) pt_error("failed to set %s 'aggrate'\n", g_msg_idle_state); if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) pt_error("failed to set %s 'aggpercpu'\n", g_msg_idle_state); if (dtrace_go(dtp) != 0) { pt_error("failed to start %s observation\n", g_msg_idle_state); return (dtrace_errno(dtp)); } if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) { pt_error("failed to get %s 'statusrate'\n", g_msg_idle_state); return (dtrace_errno(dtp)); } return (0); } /* * The DTrace probes have been enabled, and are tracking CPU idle state * transitions. Take a snapshot of the aggregations, and invoke the aggregation * walker to process any records. The walker does most of the accounting work * chalking up time spent into the g_cstate_info structure. */ int pt_cpuidle_stat_collect(double interval) { int i; hrtime_t t = 0; /* * Assume that all the time spent in this interval will * be the default "0" state. The DTrace walker will reallocate * time out of the default bucket as it processes aggregation * records for time spent in other states. */ g_cstate_info[0].total_time = (uint64_t)S2NS(interval * g_ncpus_observed); if (dtrace_status(dtp) == -1) return (-1); if (dtrace_aggregate_snap(dtp) != 0) pt_error("failed to collect data for %s\n", g_msg_idle_state); if (dtrace_aggregate_walk_keyvarsorted(dtp, pt_cpuidle_dtrace_walk, NULL) != 0) pt_error("failed to sort %s data\n", g_msg_idle_state); dtrace_aggregate_clear(dtp); /* * Populate g_cstate_info with the correct amount of time spent * in each C state and update the number of C states in g_max_cstate */ g_total_c_time = 0; for (i = 0; i < NSTATES; i++) { if (g_cstate_info[i].total_time > 0) { g_total_c_time += g_cstate_info[i].total_time; if (i > g_max_cstate) g_max_cstate = i; if (g_cstate_info[i].last_time > t) { t = g_cstate_info[i].last_time; g_longest_cstate = i; } } } return (0); } /* * DTrace aggregation walker that sorts through a snapshot of data records * collected during firings of the idle-state-transition probe. * * XXX A way of querying the current idle state for a CPU is needed in addition * to logic similar to that in cpufreq.c */ /*ARGSUSED*/ static int pt_cpuidle_dtrace_walk(const dtrace_aggdata_t *data, void *arg) { dtrace_aggdesc_t *aggdesc = data->dtada_desc; dtrace_recdesc_t *rec; uint64_t n = 0, state; int i; rec = &aggdesc->dtagd_rec[1]; switch (g_bit_depth) { case 32: /* LINTED - alignment */ state = *(uint32_t *)(data->dtada_data + rec->dtrd_offset); break; case 64: /* LINTED - alignment */ state = *(uint64_t *)(data->dtada_data + rec->dtrd_offset); break; } if (strcmp(aggdesc->dtagd_name, "number") == 0) { for (i = 0; i < g_ncpus; i++) { /* LINTED - alignment */ n += *((uint64_t *)(data->dtada_percpu[i])); } g_total_events += n; g_cstate_info[state].events += n; } else if (strcmp(aggdesc->dtagd_name, "times") == 0) { for (i = 0; i < g_ncpus; i++) { /* LINTED - alignment */ n += *((uint64_t *)(data->dtada_percpu[i])); } g_cstate_info[state].last_time = n; g_cstate_info[state].total_time += n; if (g_cstate_info[0].total_time >= n) g_cstate_info[0].total_time -= n; else g_cstate_info[0].total_time = 0; } return (DTRACE_AGGWALK_NEXT); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include #include #include #include "powertop.h" /* * Minimum terminal height and width to run PowerTOP on curses mode. */ #define PT_MIN_COLS 70 #define PT_MIN_ROWS 15 /* * Display colors */ #define PT_COLOR_DEFAULT 1 #define PT_COLOR_HEADER_BAR 2 #define PT_COLOR_ERROR 3 #define PT_COLOR_RED 4 #define PT_COLOR_YELLOW 5 #define PT_COLOR_GREEN 6 #define PT_COLOR_BRIGHT 7 #define PT_COLOR_BLUE 8 /* * Constants for pt_display_setup() */ #define SINGLE_LINE_SW 1 #define LENGTH_SUGG_SW 2 #define TITLE_LINE 1 #define BLANK_LINE 1 #define NEXT_LINE 1 #define print(win, y, x, fmt, args...) \ if (PT_ON_DUMP) \ (void) printf(fmt, ## args); \ else \ (void) mvwprintw(win, y, x, fmt, ## args); enum pt_subwindows { SW_TITLE, SW_IDLE, SW_FREQ, SW_WAKEUPS, SW_POWER, SW_EVENTS, SW_SUGG, SW_STATUS, SW_COUNT }; typedef struct sb_slot { char *msg; struct sb_slot *prev; struct sb_slot *next; } sb_slot_t; static WINDOW *sw[SW_COUNT]; static int win_cols, win_rows; static sb_slot_t *status_bar; /* * Delete all subwindows and reset the terminal to a non-visual mode. This * routine is used during resize events and before exiting. */ static void pt_display_cleanup(void) { int i; for (i = 0; i < SW_COUNT; i++) { if (sw[i] != NULL) { (void) delwin(sw[i]); sw[i] = NULL; } } (void) endwin(); (void) fflush(stdout); (void) putchar('\r'); } static void pt_display_get_size(void) { getmaxyx(stdscr, win_rows, win_cols); if (win_rows < PT_MIN_ROWS || win_cols < PT_MIN_COLS) { pt_display_cleanup(); (void) printf("\n\nPowerTOP cannot run in such a small " "terminal window. Please resize it.\n\n"); exit(EXIT_FAILURE); } } void pt_display_resize(void) { pt_display_cleanup(); (void) pt_display_init_curses(); pt_display_setup(B_TRUE); pt_display_title_bar(); pt_display_states(); if (g_features & FEATURE_EVENTS) { pt_display_wakeups(g_interval_length); pt_display_events(g_interval_length); } pt_battery_print(); pt_sugg_pick(); pt_display_status_bar(); pt_display_update(); g_sig_resize = B_FALSE; (void) signal(SIGWINCH, pt_sig_handler); } /* * This part was re-written to be human readable and easy to modify. Please * try to keep it that way and help us save some time. * * Friendly reminder: * subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x) */ void pt_display_setup(boolean_t resized) { /* * These variables are used to properly set the initial y position and * number of lines in each subwindow, as the number of supported CPU * states affects their placement. */ int cstate_lines, event_lines, pos_y = 0; /* * In theory, all systems have at least two idle states. We add two here * since we have to use DTrace to figure out how many this box has. */ cstate_lines = TITLE_LINE + max((g_max_cstate+2), g_npstates); sw[SW_TITLE] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0); pos_y += NEXT_LINE + BLANK_LINE; sw[SW_IDLE] = subwin(stdscr, cstate_lines, win_cols/2 + 1, pos_y, 0); sw[SW_FREQ] = subwin(stdscr, cstate_lines, win_cols/2 - 8, pos_y, win_cols/2 + 8); pos_y += cstate_lines + BLANK_LINE; sw[SW_WAKEUPS] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0); pos_y += NEXT_LINE; sw[SW_POWER] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0); pos_y += NEXT_LINE + BLANK_LINE; event_lines = win_rows - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW - pos_y; if (event_lines > 0) { sw[SW_EVENTS] = subwin(stdscr, event_lines, win_cols, pos_y, 0); } else { pt_display_cleanup(); (void) printf("\n\nPowerTOP cannot run in such a small " "terminal window, please resize it.\n\n"); exit(EXIT_FAILURE); } pos_y += event_lines + NEXT_LINE; sw[SW_SUGG] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0); pos_y += BLANK_LINE + NEXT_LINE; sw[SW_STATUS] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0); if (!resized) { status_bar = NULL; pt_display_mod_status_bar("Q - Quit"); pt_display_mod_status_bar("R - Refresh"); } } /* * This routine handles all the necessary curses initialization. */ void pt_display_init_curses(void) { (void) initscr(); (void) atexit(pt_display_cleanup); pt_display_get_size(); (void) start_color(); /* * Enable keyboard mapping */ (void) keypad(stdscr, TRUE); /* * Tell curses not to do NL->CR/NL on output */ (void) nonl(); /* * Take input chars one at a time, no wait for \n */ (void) cbreak(); /* * Dont echo input */ (void) noecho(); /* * Turn off cursor */ (void) curs_set(0); (void) init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); (void) init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE); (void) init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED); (void) init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED); (void) init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW); (void) init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN); (void) init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE); (void) init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK); } void pt_display_update(void) { (void) doupdate(); } void pt_display_title_bar(void) { char title_pad[10]; (void) wattrset(sw[SW_TITLE], COLOR_PAIR(PT_COLOR_HEADER_BAR)); (void) wbkgd(sw[SW_TITLE], COLOR_PAIR(PT_COLOR_HEADER_BAR)); (void) werase(sw[SW_TITLE]); (void) snprintf(title_pad, 10, "%%%ds", (win_cols - strlen(TITLE))/2 + strlen(TITLE)); /* LINTED: E_SEC_PRINTF_VAR_FMT */ print(sw[SW_TITLE], 0, 0, title_pad, TITLE); (void) wnoutrefresh(sw[SW_TITLE]); } void pt_display_status_bar(void) { sb_slot_t *n = status_bar; int x = 0; (void) werase(sw[SW_STATUS]); while (n && x < win_cols) { (void) wattron(sw[SW_STATUS], A_REVERSE); print(sw[SW_STATUS], 0, x, "%s", n->msg); (void) wattroff(sw[SW_STATUS], A_REVERSE); x += strlen(n->msg) + 1; n = n->next; } (void) wnoutrefresh(sw[SW_STATUS]); } /* * Adds or removes items to the status bar automatically. * Only one instance of an item allowed. */ void pt_display_mod_status_bar(char *msg) { sb_slot_t *new, *n; boolean_t found = B_FALSE, first = B_FALSE; if (msg == NULL) { pt_error("can't add an empty status bar item\n"); return; } if (status_bar != NULL) { /* * Non-empty status bar. Look for an entry matching this msg. */ for (n = status_bar; n != NULL; n = n->next) { if (strcmp(msg, n->msg) == 0) { if (n != status_bar) n->prev->next = n->next; else first = B_TRUE; if (n->next != NULL) { n->next->prev = n->prev; if (first) status_bar = n->next; } else { if (first) status_bar = NULL; } free(n); found = B_TRUE; } } /* * Found and removed at least one occurrance of msg, refresh * the bar and return. */ if (found) { return; } else { /* * Inserting a new msg, walk to the end of the bar. */ for (n = status_bar; n->next != NULL; n = n->next) ; } } if ((new = calloc(1, sizeof (sb_slot_t))) == NULL) { pt_error("failed to allocate a new status bar slot\n"); } else { new->msg = strdup(msg); /* * Check if it's the first entry. */ if (status_bar == NULL) { status_bar = new; new->prev = NULL; } else { new->prev = n; n->next = new; } new->next = NULL; } } void pt_display_states(void) { char c[100]; int i; double total_pstates = 0.0, avg, res; uint64_t p0_speed, p1_speed; print(sw[SW_IDLE], 0, 0, "%s\tAvg\tResidency\n", g_msg_idle_state); if (g_features & FEATURE_CSTATE) { res = (((double)g_cstate_info[0].total_time / g_total_c_time)) * 100; (void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res); print(sw[SW_IDLE], 1, 0, "%s", c); for (i = 1; i <= g_max_cstate; i++) { /* * In situations where the load is too intensive, the * system might not transition at all. */ if (g_cstate_info[i].events > 0) avg = (((double)g_cstate_info[i].total_time/ MICROSEC)/g_cstate_info[i].events); else avg = 0; res = ((double)g_cstate_info[i].total_time/ g_total_c_time) * 100; (void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n", i, (float)avg, (float)res); print(sw[SW_IDLE], i + 1, 0, "%s", c); } } if (!PT_ON_DUMP) (void) wnoutrefresh(sw[SW_IDLE]); print(sw[SW_FREQ], 0, 0, "%s\n", g_msg_freq_state); if (g_features & FEATURE_PSTATE) { for (i = 0; i < g_npstates; i++) { total_pstates += (double)(g_pstate_info[i].total_time/ g_ncpus_observed/MICROSEC); } /* * display ACPI_PSTATE from P(n) to P(1) */ for (i = 0; i < g_npstates - 1; i++) { (void) sprintf(c, "%4lu Mhz\t%.1f%%", (long)g_pstate_info[i].speed, 100 * (g_pstate_info[i].total_time/ g_ncpus_observed/MICROSEC/total_pstates)); print(sw[SW_FREQ], i+1, 0, "%s\n", c); } /* * Display ACPI_PSTATE P0 according to if turbo * mode is supported */ if (g_turbo_supported) { int p_diff = 1; p0_speed = g_pstate_info[g_npstates - 1].speed; p1_speed = g_pstate_info[g_npstates - 2].speed; /* * AMD systems don't have a visible extra Pstate * indicating turbo mode as Intel does. Use the * actual P0 frequency in that case. */ if (p0_speed != p1_speed + 1) { p1_speed = p0_speed; p_diff = 0; } /* * If g_turbo_ratio <= 1.0, it will be ignored. * we display P(0) as P(1) + p_diff. */ if (g_turbo_ratio <= 1.0) { p0_speed = p1_speed + p_diff; } else { /* * If g_turbo_ratio > 1.0, that means * turbo mode works. So, P(0) = ratio * * P(1); */ p0_speed = (uint64_t)(p1_speed * g_turbo_ratio); if (p0_speed < (p1_speed + p_diff)) p0_speed = p1_speed + p_diff; } /* * Reset the ratio for the next round */ g_turbo_ratio = 0.0; /* * Setup the string for the display */ (void) sprintf(c, "%4lu Mhz(turbo)\t%.1f%%", (long)p0_speed, 100 * (g_pstate_info[i].total_time/ g_ncpus_observed/MICROSEC/total_pstates)); } else { (void) sprintf(c, "%4lu Mhz\t%.1f%%", (long)g_pstate_info[i].speed, 100 * (g_pstate_info[i].total_time/ g_ncpus_observed/MICROSEC/total_pstates)); } print(sw[SW_FREQ], i+1, 0, "%s\n", c); } else { if (g_npstates == 1) { (void) sprintf(c, "%4lu Mhz\t%.1f%%", (long)g_pstate_info[0].speed, 100.0); print(sw[SW_FREQ], 1, 0, "%s\n", c); } } if (!PT_ON_DUMP) (void) wnoutrefresh(sw[SW_FREQ]); } void pt_display_acpi_power(uint32_t flag, double rate, double rem_cap, double cap, uint32_t state) { char buffer[1024]; (void) sprintf(buffer, "no ACPI power usage estimate available"); if (!PT_ON_DUMP) (void) werase(sw[SW_POWER]); if (flag) { char *c; (void) sprintf(buffer, "Power usage (ACPI estimate): %.3fW", rate); (void) strcat(buffer, " "); c = &buffer[strlen(buffer)]; switch (state) { case 0: (void) sprintf(c, "(running on AC power, fully " "charged)"); break; case 1: (void) sprintf(c, "(discharging: %3.1f hours)", (uint32_t)rem_cap/rate); break; case 2: (void) sprintf(c, "(charging: %3.1f hours)", (uint32_t)(cap - rem_cap)/rate); break; case 4: (void) sprintf(c, "(##critically low battery power##)"); break; } } print(sw[SW_POWER], 0, 0, "%s\n", buffer); if (!PT_ON_DUMP) (void) wnoutrefresh(sw[SW_POWER]); } void pt_display_wakeups(double interval) { char c[100]; int i, event_sum = 0; event_info_t *event = g_event_info; if (!PT_ON_DUMP) { (void) werase(sw[SW_WAKEUPS]); (void) wbkgd(sw[SW_WAKEUPS], COLOR_PAIR(PT_COLOR_RED)); (void) wattron(sw[SW_WAKEUPS], A_BOLD); } /* * calculate the actual total event number */ for (i = 0; i < g_top_events; i++, event++) event_sum += event->total_count; /* * g_total_events is the sum of the number of Cx->C0 transition, * So when the system is very busy, the idle thread will have no * chance or very seldom to be scheduled, this could cause >100% * event report. Re-assign g_total_events to the actual event * number is a way to avoid this issue. */ if (event_sum > g_total_events) g_total_events = event_sum; (void) sprintf(c, "Wakeups-from-idle per second: %4.1f\tinterval: " "%.1fs", (double)(g_total_events/interval), interval); print(sw[SW_WAKEUPS], 0, 0, "%s\n", c); if (!PT_ON_DUMP) (void) wnoutrefresh(sw[SW_WAKEUPS]); } void pt_display_events(double interval) { char c[100]; int i; double events; event_info_t *event = g_event_info; if (!PT_ON_DUMP) { (void) werase(sw[SW_EVENTS]); (void) wbkgd(sw[SW_EVENTS], COLOR_PAIR(PT_COLOR_DEFAULT)); (void) wattron(sw[SW_EVENTS], COLOR_PAIR(PT_COLOR_DEFAULT)); } /* * Sort the event report list */ if (g_top_events > EVENT_NUM_MAX) g_top_events = EVENT_NUM_MAX; qsort((void *)g_event_info, g_top_events, sizeof (event_info_t), pt_event_compare); if (PT_ON_CPU) (void) sprintf(c, "Top causes for wakeups on CPU %d:\n", g_observed_cpu); else (void) sprintf(c, "Top causes for wakeups:\n"); print(sw[SW_EVENTS], 0, 0, "%s", c); for (i = 0; i < g_top_events; i++, event++) { if (g_total_events > 0 && event->total_count > 0) events = (double)event->total_count/ (double)g_total_events; else continue; (void) sprintf(c, "%4.1f%% (%5.1f)", 100 * events, (double)event->total_count/interval); print(sw[SW_EVENTS], i+1, 0, "%s", c); print(sw[SW_EVENTS], i+1, 16, "%20s :", event->offender_name); print(sw[SW_EVENTS], i+1, 40, "%-64s\n", event->offense_name); } if (!PT_ON_DUMP) (void) wnoutrefresh(sw[SW_EVENTS]); } void pt_display_suggestions(char *sug) { (void) werase(sw[SW_SUGG]); if (sug != NULL) print(sw[SW_SUGG], 0, 0, "%s", sug); (void) wnoutrefresh(sw[SW_SUGG]); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include "powertop.h" static dtrace_hdl_t *dtp; static event_info_t *event; /*ARGSUSED*/ static int pt_events_walk(const dtrace_aggdata_t *data, void *arg) { dtrace_aggdesc_t *aggdesc = data->dtada_desc; dtrace_recdesc_t *rec1, *rec2, *rec3; dtrace_syminfo_t dts; GElf_Sym sym; uint64_t offender_addr; uint64_t n = 0; int32_t *instance, *offender_cpu; int i; char *offense_name; if (g_top_events >= EVENT_NUM_MAX) return (0); rec1 = &aggdesc->dtagd_rec[1]; rec2 = &aggdesc->dtagd_rec[2]; /* * Report interrupts */ if (strcmp(aggdesc->dtagd_name, "interrupts") == 0) { offense_name = data->dtada_data + rec1->dtrd_offset; /* LINTED - alignment */ instance = (int32_t *)(data->dtada_data + rec2->dtrd_offset); (void) snprintf((char *)(event->offender_name), EVENT_NAME_MAX, "%s", ""); (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, "%s#%d", offense_name, *instance); /* * Report kernel events */ } else if (strcmp(aggdesc->dtagd_name, "events_k") == 0) { (void) snprintf((char *)(event->offender_name), EVENT_NAME_MAX, "%s", ""); /* * Casting offender_addr to the wrong type will cause * dtrace_lookup_by_addr to return 0 and the report * to show an address instead of a name. */ switch (g_bit_depth) { case 32: /* LINTED - alignment */ offender_addr = *(uint32_t *)(data->dtada_data + rec1->dtrd_offset); break; case 64: /* LINTED - alignment */ offender_addr = *(uint64_t *)(data->dtada_data + rec1->dtrd_offset); break; } /* * We have the address of the kernel callout. * Try to resolve it into a meaningful symbol */ if (offender_addr != 0 && dtrace_lookup_by_addr(dtp, offender_addr, &sym, &dts) == 0) { (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, "%s`%s", dts.dts_object, dts.dts_name); } else { (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, "0x%llx", offender_addr); } /* * Report user events */ } else if (strcmp(aggdesc->dtagd_name, "events_u") == 0) { offense_name = data->dtada_data + rec1->dtrd_offset; (void) snprintf((char *)(event->offender_name), EVENT_NAME_MAX, "%s", offense_name); (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, ""); /* * Report cross calls */ } else if (strcmp(aggdesc->dtagd_name, "events_x") == 0) { offense_name = data->dtada_data + rec1->dtrd_offset; (void) snprintf((char *)(event->offender_name), EVENT_NAME_MAX, "%s", offense_name); switch (g_bit_depth) { case 32: /* LINTED - alignment */ offender_addr = *(uint32_t *)(data->dtada_data + rec2->dtrd_offset); break; case 64: /* LINTED - alignment */ offender_addr = *(uint64_t *)(data->dtada_data + rec2->dtrd_offset); break; } /* * Try to resolve the address of the cross call function. */ if (offender_addr != 0 && dtrace_lookup_by_addr(dtp, offender_addr, &sym, &dts) == 0) { (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, " %s`%s", dts.dts_object, dts.dts_name); } else { (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, ""); } /* * Report cross calls from other CPUs than the one we're observing * with the -C option */ } else if (strcmp(aggdesc->dtagd_name, "events_xc") == 0) { rec3 = &aggdesc->dtagd_rec[3]; offense_name = data->dtada_data + rec1->dtrd_offset; (void) snprintf((char *)(event->offender_name), EVENT_NAME_MAX, "%s", offense_name); switch (g_bit_depth) { case 32: /* LINTED - alignment */ offender_addr = *(uint32_t *)(data->dtada_data + rec2->dtrd_offset); break; case 64: /* LINTED - alignment */ offender_addr = *(uint64_t *)(data->dtada_data + rec2->dtrd_offset); break; } /* LINTED - alignment */ offender_cpu = (int32_t *)(data->dtada_data + rec3->dtrd_offset); /* * Try to resolve the address of the cross call function. */ if (offender_addr != 0 && dtrace_lookup_by_addr(dtp, offender_addr, &sym, &dts) == 0) { (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, " %s`%s (CPU %d)", dts.dts_object, dts.dts_name, *offender_cpu); } else { (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, " (CPU %d)", *offender_cpu); } /* * Report unknown events */ } else { (void) snprintf((char *)(event->offender_name), EVENT_NAME_MAX, "%s", ""); (void) snprintf((char *)(event->offense_name), EVENT_NAME_MAX, "%s", ""); } for (i = 0; i < g_ncpus; i++) { /* LINTED - alignment */ n += *((uint64_t *)(data->dtada_percpu[i])); } event->total_count = n; event++; g_top_events++; return (DTRACE_AGGWALK_NEXT); } int pt_events_stat_prepare(void) { dtrace_prog_t *prog; dtrace_proginfo_t info; dtrace_optval_t statustime; int err; char *prog_ptr; event = g_event_info; if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { pt_error("cannot open dtrace library for the event report: " "%s\n", dtrace_errmsg(NULL, err)); return (-1); } /* * Execute different scripts (defined in the platform specific file) * depending on user specified options. */ if (PT_ON_VERBOSE) { prog_ptr = (char *)g_dtp_events_v; } else { if (PT_ON_CPU) prog_ptr = (char *)g_dtp_events_c; else prog_ptr = (char *)g_dtp_events; } if ((prog = dtrace_program_strcompile(dtp, prog_ptr, DTRACE_PROBESPEC_NAME, 0, g_argc, g_argv)) == NULL) { pt_error("failed to compile the event report program\n"); return (dtrace_errno(dtp)); } if (dtrace_program_exec(dtp, prog, &info) == -1) { pt_error("failed to enable probes for the event report\n"); return (dtrace_errno(dtp)); } if (dtrace_setopt(dtp, "aggsize", "128k") == -1) { pt_error("failed to set 'aggsize' for the event report\n"); return (dtrace_errno(dtp)); } if (dtrace_setopt(dtp, "aggrate", "0") == -1) { pt_error("failed to set 'aggrate' for the event report\n"); return (dtrace_errno(dtp)); } if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) { pt_error("failed to set 'aggpercpu' for the event report\n"); return (dtrace_errno(dtp)); } if (dtrace_go(dtp) != 0) { pt_error("failed to start the event report observation\n"); return (dtrace_errno(dtp)); } if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) { pt_error("failed to get 'statusrate' for the event report\n"); return (dtrace_errno(dtp)); } return (0); } int pt_events_stat_collect(void) { g_top_events = 0; event = g_event_info; if (dtrace_status(dtp) == -1) return (-1); if (dtrace_aggregate_snap(dtp) != 0) pt_error("failed to collect data for the event report\n"); if (dtrace_aggregate_walk_keyvarsorted(dtp, pt_events_walk, NULL) != 0) pt_error("failed to sort data for the event report\n"); dtrace_aggregate_clear(dtp); return (0); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include #include #include #include #include #include "powertop.h" /* * Global variables, see powertop.h for comments and extern declarations. * These are ordered by type, grouped by usage. */ int g_bit_depth; int g_total_events, g_top_events; int g_npstates, g_max_cstate, g_longest_cstate; uint_t g_features; uint_t g_ncpus; uint_t g_ncpus_observed; processorid_t *g_cpu_table; double g_interval_length; hrtime_t g_total_c_time; uchar_t g_op_mode; boolean_t g_gui; uint_t g_observed_cpu; event_info_t g_event_info[EVENT_NUM_MAX]; state_info_t g_cstate_info[NSTATES]; freq_state_info_t g_pstate_info[NSTATES]; cpu_power_info_t *g_cpu_power_states; boolean_t g_sig_resize; uint_t g_argc; char **g_argv; static const int true = 1; void pt_sig_handler(int sig) { switch (sig) { case SIGWINCH: g_sig_resize = B_TRUE; break; } } int main(int argc, char **argv) { double interval, interval_usr; hrtime_t interval_start; int index2 = 0, c, dump_count = 0; char *endptr, key; boolean_t root_user = B_FALSE; struct pollfd pollset; static struct option opts[] = { { "dump", 1, NULL, 'd' }, { "time", 1, NULL, 't' }, { "help", 0, NULL, 'h' }, { "cpu", 1, NULL, 'c' }, { "verbose", 0, NULL, 'v' }, { 0, 0, NULL, 0 } }; pt_set_progname(argv[0]); /* * Enumerate the system's CPUs, populate cpu_table, g_ncpus */ if ((g_ncpus = g_ncpus_observed = pt_enumerate_cpus()) == 0) exit(EXIT_FAILURE); if ((g_bit_depth = pt_get_bit_depth()) < 0) exit(EXIT_FAILURE); g_features = 0; interval = interval_usr = INTERVAL_DEFAULT; g_op_mode = PT_MODE_DEFAULT; g_max_cstate = 0; g_argv = NULL; g_argc = 0; g_observed_cpu = 0; g_turbo_supported = B_FALSE; g_sig_resize = B_FALSE; g_curr_sugg = NULL; while ((c = getopt_long(argc, argv, "d:t:hvc:", opts, &index2)) != EOF) { if (c == -1) break; switch (c) { case 'd': if (PT_ON_DUMP) { pt_usage(); exit(EXIT_USAGE); } g_op_mode |= PT_MODE_DUMP; g_gui = B_FALSE; dump_count = (int)strtod(optarg, &endptr); if (dump_count <= 0 || *endptr != '\0') { pt_usage(); exit(EXIT_USAGE); } break; case 't': if (PT_ON_TIME) { pt_usage(); exit(EXIT_USAGE); } g_op_mode |= PT_MODE_TIME; interval = interval_usr = (double)strtod(optarg, &endptr); if (*endptr != '\0' || interval < 1 || interval > INTERVAL_MAX) { pt_usage(); exit(EXIT_USAGE); } break; case 'v': if (PT_ON_CPU || PT_ON_VERBOSE) { pt_usage(); exit(EXIT_USAGE); } g_op_mode |= PT_MODE_VERBOSE; break; case 'c': if (PT_ON_CPU || PT_ON_VERBOSE) { pt_usage(); exit(EXIT_USAGE); } g_op_mode |= PT_MODE_CPU; g_observed_cpu = (uint_t)strtod(optarg, &endptr); if (g_observed_cpu >= g_ncpus) { pt_usage(); exit(EXIT_USAGE); } g_argc = 1; g_ncpus_observed = 1; if ((g_argv = malloc(sizeof (char *))) == NULL) return (EXIT_FAILURE); if ((*g_argv = malloc(sizeof (char) * 5)) == NULL) return (EXIT_FAILURE); (void) snprintf(*g_argv, 5, "%d\0", g_observed_cpu); break; case 'h': pt_usage(); exit(EXIT_SUCCESS); default: pt_usage(); exit(EXIT_USAGE); } } if (optind < argc) { pt_usage(); exit(EXIT_USAGE); } (void) printf("%s %s\n\n", TITLE, COPYRIGHT_INTEL); (void) printf("Collecting data for %.2f second(s) \n", (float)interval); /* Prepare P-state statistics */ if (pt_cpufreq_stat_prepare() == 0) g_features |= FEATURE_PSTATE; /* Prepare C-state statistics */ if (pt_cpuidle_stat_prepare() == 0) g_features |= FEATURE_CSTATE; else /* * PowerTop was unable to run a DTrace program, * most likely for lack of permissions. */ exit(EXIT_FAILURE); /* Prepare event statistics */ if (pt_events_stat_prepare() != -1) g_features |= FEATURE_EVENTS; /* * If the system is running on battery, find out what's * the kstat module for it */ pt_battery_mod_lookup(); /* Prepare turbo statistics */ if (pt_turbo_stat_prepare() == 0) g_features |= FEATURE_TURBO; /* * Initialize the display. */ if (!PT_ON_DUMP) { pt_display_init_curses(); pt_display_setup(B_FALSE); (void) signal(SIGWINCH, pt_sig_handler); pt_display_title_bar(); pt_display_status_bar(); g_gui = B_TRUE; pollset.fd = STDIN_FILENO; pollset.events = POLLIN; } /* * Installs the initial suggestions, running as root and turning CPU * power management ON. */ if (geteuid() != 0) { pt_sugg_as_root(); } else { root_user = B_TRUE; pt_cpufreq_suggest(); } while (true) { key = 0; if (g_sig_resize) pt_display_resize(); interval_start = gethrtime(); if (!PT_ON_DUMP) { if (poll(&pollset, (nfds_t)1, (int)(interval * MILLISEC)) > 0) (void) read(STDIN_FILENO, &key, 1); } else { (void) sleep((int)interval); } g_interval_length = (double)(gethrtime() - interval_start) /NANOSEC; g_top_events = 0; g_total_events = 0; (void) memset(g_event_info, 0, EVENT_NUM_MAX * sizeof (event_info_t)); (void) memset(g_cstate_info, 0, NSTATES * sizeof (state_info_t)); /* Collect idle state transition stats */ if (g_features & FEATURE_CSTATE && pt_cpuidle_stat_collect(g_interval_length) < 0) { /* Reinitialize C-state statistics */ if (pt_cpuidle_stat_prepare() != 0) exit(EXIT_FAILURE); continue; } /* Collect frequency change stats */ if (g_features & FEATURE_PSTATE && pt_cpufreq_stat_collect(g_interval_length) < 0) { /* Reinitialize P-state statistics */ if (pt_cpufreq_stat_prepare() != 0) exit(EXIT_FAILURE); continue; } /* Collect event statistics */ if (g_features & FEATURE_EVENTS && pt_events_stat_collect() < 0) { /* Reinitialize event statistics */ if (pt_events_stat_prepare() != 0) exit(EXIT_FAILURE); continue; } /* Collect turbo statistics */ if (g_features & FEATURE_TURBO && pt_turbo_stat_collect() < 0) exit(EXIT_FAILURE); /* Show CPU power states */ pt_display_states(); /* Show wakeups events affecting PM */ if (g_features & FEATURE_EVENTS) { pt_display_wakeups(g_interval_length); pt_display_events(g_interval_length); } pt_battery_print(); if (key && !PT_ON_DUMP) { switch (toupper(key)) { case 'Q': exit(EXIT_SUCCESS); break; case 'R': interval = 3; break; } /* * Check if the user has activated the current * suggestion. */ if (g_curr_sugg != NULL && toupper(key) == g_curr_sugg->key && g_curr_sugg->func) g_curr_sugg->func(); } if (dump_count) dump_count--; /* Exits if user requested a dump */ if (PT_ON_DUMP && !dump_count) exit(EXIT_SUCCESS); /* No key pressed, will suggest something */ if (!key && !dump_count) pt_sugg_pick(); /* Refresh display */ if (!PT_ON_DUMP) pt_display_update(); if (root_user) pt_cpufreq_suggest(); /* * Update the interval based on how long the CPU was in the * longest c-state during the last snapshot. If the user * specified an interval we skip this bit and keep it fixed. */ if (g_features & FEATURE_CSTATE && !PT_ON_TIME && g_longest_cstate > 0 && g_cstate_info[g_longest_cstate].events > 0) { double deep_idle_res = (((double) g_cstate_info[g_longest_cstate].total_time/MICROSEC /g_ncpus)/g_cstate_info[g_longest_cstate].events); if (deep_idle_res < INTERVAL_DEFAULT || (g_total_events/interval) < 1) interval = INTERVAL_DEFAULT; else interval = INTERVAL_UPDATE(deep_idle_res); } else { /* * Restore interval after a refresh. */ if (key) interval = interval_usr; } } return (EXIT_SUCCESS); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #ifndef __INCLUDE_GUARD_POWERTOP_H_ #define __INCLUDE_GUARD_POWERTOP_H_ #include #include #define max(A, B) (((A) < (B)) ? (B) : (A)) #define TITLE "OpenSolaris PowerTOP version 1.2" #define COPYRIGHT_INTEL "(C) 2009 Intel Corporation" /* * Exit values. stdlib.h defines EXIT_SUCCESS as 0 and * EXIT_FAILURE as 1 */ #define EXIT_USAGE 2 /* * PowerTOP Features * These may not be available everywhere */ #define FEATURE_CSTATE 0x01 #define FEATURE_PSTATE 0x02 #define FEATURE_EVENTS 0x04 #define FEATURE_TURBO 0x08 #define BIT_DEPTH_BUF 10 #define INTERVAL_DEFAULT 5.0 #define INTERVAL_MAX 30.0 #define INTERVAL_UPDATE(l) \ ((l/INTERVAL_DEFAULT) * INTERVAL_DEFAULT + INTERVAL_DEFAULT) #define STATE_NAME_MAX 16 #define EVENT_NAME_MAX 64 #define EVENT_NUM_MAX 100 #define NSTATES 32 /* * Available op modes. The PT_ON_* macros allow for a simple way of checking * under which mode PowerTOP is operating. */ #define PT_MODE_DEFAULT 0x01 #define PT_MODE_DUMP 0x02 #define PT_MODE_VERBOSE 0x04 #define PT_MODE_CPU 0x08 #define PT_MODE_TIME 0x10 #define PT_ON_DEFAULT (g_op_mode & PT_MODE_DEFAULT) #define PT_ON_DUMP (g_op_mode & PT_MODE_DUMP) #define PT_ON_VERBOSE (g_op_mode & PT_MODE_VERBOSE) #define PT_ON_CPU (g_op_mode & PT_MODE_CPU) #define PT_ON_TIME (g_op_mode & PT_MODE_TIME) /* * Structures and typedefs */ struct line { char *string; int count; }; typedef struct event_info { char offender_name[EVENT_NAME_MAX]; char offense_name[EVENT_NAME_MAX]; uint64_t total_count; } event_info_t; /* * P/C state information */ typedef struct state_info { char name[STATE_NAME_MAX]; hrtime_t total_time; hrtime_t last_time; uint64_t events; } state_info_t; typedef struct freq_state_info { uint64_t speed; hrtime_t total_time; } freq_state_info_t; typedef struct cpu_power_info { uint64_t current_pstate; uint64_t speed_accounted; hrtime_t time_accounted; hrtime_t dtrace_time; } cpu_power_info_t; /* * Turbo mode information */ typedef struct turbo_info { uint64_t t_mcnt; uint64_t t_acnt; } turbo_info_t; /* * Suggestions */ typedef void (sugg_func_t)(void); typedef struct suggestion { char *text; char key; char *sb_msg; int weight; int slice; sugg_func_t *func; struct suggestion *prev; struct suggestion *next; } sugg_t; extern int g_bit_depth; /* * Event accounting */ extern int g_total_events; extern int g_top_events; /* * Command line arguments */ extern uchar_t g_op_mode; extern uint_t g_observed_cpu; extern boolean_t g_gui; /* * Event info array */ extern event_info_t g_event_info[EVENT_NUM_MAX]; /* * Lookup table, sequential CPU id to Solaris CPU id */ extern processorid_t *g_cpu_table; /* * Number of idle/frequency states */ extern int g_npstates; extern int g_max_cstate; extern int g_longest_cstate; /* * Total time, used to display different idle states */ extern hrtime_t g_total_c_time; /* * Current interval length */ extern double g_interval_length; /* * P/C state info arrays */ extern state_info_t g_cstate_info[NSTATES]; extern freq_state_info_t g_pstate_info[NSTATES]; extern uint_t g_features; extern uint_t g_ncpus; extern uint_t g_ncpus_observed; extern cpu_power_info_t *g_cpu_power_states; /* * Turbo mode related information */ extern boolean_t g_turbo_supported; extern double g_turbo_ratio; extern sugg_t *g_curr_sugg; /* * DTrace scripts for the events report */ extern const char *g_dtp_events; extern const char *g_dtp_events_v; extern const char *g_dtp_events_c; /* * Arguments for dtrace_program_strcompile(). Contents vary according to * the specified operation mode. */ extern uint_t g_argc; extern char **g_argv; /* * Platform specific messages */ extern const char *g_msg_idle_state; extern const char *g_msg_freq_state; extern const char *g_msg_freq_enable; /* * Flags for signal handling */ extern boolean_t g_sig_resize; extern void pt_sig_handler(int); /* * Suggestions related */ extern void pt_cpufreq_suggest(void); extern void pt_sugg_as_root(void); /* * See util.c */ extern void pt_error(char *, ...); extern void pt_set_progname(char *); extern uint_t pt_enumerate_cpus(void); extern void pt_usage(void); extern int pt_get_bit_depth(void); extern void pt_battery_mod_lookup(void); extern int pt_event_compare(const void *, const void *); /* * Display/curses related */ extern void pt_display_setup(boolean_t); extern void pt_display_init_curses(void); extern void pt_display_update(void); extern void pt_display_title_bar(void); extern void pt_display_status_bar(void); extern void pt_display_mod_status_bar(char *); extern void pt_display_states(void); extern void pt_display_acpi_power(uint32_t, double, double, double, uint32_t); extern void pt_display_wakeups(double); extern void pt_display_events(double); extern void pt_display_suggestions(char *); extern void pt_display_resize(void); /* * Suggestions */ extern void pt_sugg_add(char *, int, char, char *, sugg_func_t *); extern int pt_sugg_remove(sugg_func_t *); extern void pt_sugg_pick(void); /* * Battery */ extern void pt_battery_print(void); /* * DTrace stats */ extern int pt_cpufreq_stat_prepare(void); extern int pt_cpufreq_stat_collect(double); extern int pt_cpuidle_stat_prepare(void); extern int pt_cpuidle_stat_collect(double); extern int pt_events_stat_prepare(void); extern int pt_events_stat_collect(void); /* * Turbo mode related routines */ extern int pt_turbo_stat_prepare(void); extern int pt_turbo_stat_collect(void); #endif /* __INCLUDE_GUARD_POWERTOP_H_ */ /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include #include "powertop.h" /* * Default number of intervals we display a suggestion before moving * to the next. */ #define PT_SUGG_DEF_SLICE 3 /* * Global pointer to the current suggestion. */ sugg_t *g_curr_sugg; /* * Head of the list of suggestions. */ static sugg_t *sugg; /* * Add a new suggestion. Only one suggestion per text allowed. */ void pt_sugg_add(char *text, int weight, char key, char *sb_msg, sugg_func_t *func) { sugg_t *new, *n, *pos = NULL; /* * Text is a required field for suggestions */ if (text == NULL) return; if (sugg == NULL) { /* * Creating first element */ if ((new = calloc(1, sizeof (sugg_t))) == NULL) return; if (sb_msg != NULL) new->sb_msg = strdup(sb_msg); if (text != NULL) new->text = strdup(text); new->weight = weight; new->key = key; new->func = func; new->slice = 0; sugg = new; new->prev = NULL; new->next = NULL; } else { for (n = sugg; n != NULL; n = n->next) { if (strcmp(n->text, text) == 0) return; if (weight > n->weight && pos == NULL) pos = n; } /* * Create a new element */ if ((new = calloc(1, sizeof (sugg_t))) == NULL) return; if (sb_msg != NULL) new->sb_msg = strdup(sb_msg); new->text = strdup(text); new->weight = weight; new->key = key; new->func = func; new->slice = 0; if (pos == NULL) { /* * Ordering placed the new element at the end */ for (n = sugg; n->next != NULL; n = n->next) ; n->next = new; new->prev = n; new->next = NULL; } else { if (pos == sugg) { /* * Ordering placed the new element at the start */ new->next = sugg; new->prev = sugg; sugg->prev = new; sugg = new; } else { /* * Ordering placed the new element somewhere in * the middle */ new->next = pos; new->prev = pos->prev; pos->prev->next = new; pos->prev = new; } } } } /* * Removes a suggestion, returning 0 if not found and 1 if so. */ int pt_sugg_remove(sugg_func_t *func) { sugg_t *n; int ret = 0; for (n = sugg; n != NULL; n = n->next) { if (n->func == func) { /* Removing the first element */ if (n == sugg) { if (sugg->next == NULL) { /* Removing the only element */ sugg = NULL; } else { sugg = n->next; sugg->prev = NULL; } } else { if (n->next == NULL) { /* Removing the last element */ n->prev->next = NULL; } else { /* Removing an intermediate element */ n->prev->next = n->next; n->next->prev = n->prev; } } /* * If this suggestions is currently being suggested, * remove it and update the screen. */ if (n == g_curr_sugg) { if (n->sb_msg != NULL) { pt_display_mod_status_bar(n->sb_msg); pt_display_status_bar(); } if (n->text != NULL) pt_display_suggestions(NULL); } free(n); ret = 1; } } return (ret); } /* * Chose a suggestion to display. The list of suggestions is ordered by weight, * so we only worry about fariness here. Each suggestion, starting with the * first (the 'heaviest') is displayed during PT_SUGG_DEF_SLICE intervals. */ void pt_sugg_pick(void) { sugg_t *n; if (sugg == NULL) { g_curr_sugg = NULL; return; } search: for (n = sugg; n != NULL; n = n->next) { if (n->slice++ < PT_SUGG_DEF_SLICE) { /* * Don't need to re-suggest the current suggestion. */ if (g_curr_sugg == n && !g_sig_resize) return; /* * Remove the current suggestion from screen. */ if (g_curr_sugg != NULL) { if (g_curr_sugg->sb_msg != NULL) { pt_display_mod_status_bar( g_curr_sugg->sb_msg); pt_display_status_bar(); } if (g_curr_sugg->text != NULL) pt_display_suggestions(NULL); } if (n->sb_msg != NULL) { pt_display_mod_status_bar(n->sb_msg); pt_display_status_bar(); } pt_display_suggestions(n->text); g_curr_sugg = n; return; } } /* * All suggestions have run out of slice quotas, so we restart. */ for (n = sugg; n != NULL; n = n->next) n->slice = 0; goto search; } void pt_sugg_as_root(void) { pt_sugg_add("Suggestion: run as root to get suggestions" " for reducing system power consumption", 40, 0, NULL, NULL); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include #include #include "powertop.h" /* * Global turbo related variables definitions */ boolean_t g_turbo_supported; double g_turbo_ratio; /* * The variables to store kstat snapshot */ static turbo_info_t *cpu_turbo_info = NULL; static turbo_info_t *t_new = NULL; /* * Perform setup necessary to enumerate and track CPU turbo information */ static int pt_turbo_init(void) { kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *knp; /* * check if the CPU turbo is supported */ if ((kc = kstat_open()) == NULL) { g_turbo_supported = B_FALSE; return (errno); } ksp = kstat_lookup(kc, "turbo", 0, NULL); if (ksp == NULL) { g_turbo_supported = B_FALSE; (void) kstat_close(kc); return (-1); } (void) kstat_read(kc, ksp, NULL); knp = kstat_data_lookup(ksp, "turbo_supported"); if (knp == NULL) { pt_error("couldn't find 'turbo_supported' kstat\n"); g_turbo_supported = B_FALSE; (void) kstat_close(kc); return (-2); } /* * Initialize turbo information structure if turbo mode is supported */ if (knp->value.ui32) { g_turbo_supported = B_TRUE; cpu_turbo_info = calloc((size_t)g_ncpus, sizeof (turbo_info_t)); t_new = calloc((size_t)g_ncpus, sizeof (turbo_info_t)); } (void) kstat_close(kc); return (0); } /* * Take a snapshot of each CPU's turbo information * by looking through the turbo kstats. */ static int pt_turbo_snapshot(turbo_info_t *turbo_snapshot) { kstat_ctl_t *kc; kstat_t *ksp; kstat_named_t *knp; int cpu; turbo_info_t *turbo_info; if ((kc = kstat_open()) == NULL) return (errno); for (cpu = 0; cpu < g_ncpus; cpu++) { turbo_info = &turbo_snapshot[cpu]; ksp = kstat_lookup(kc, "turbo", g_cpu_table[cpu], NULL); if (ksp == NULL) { pt_error("couldn't find 'turbo' kstat for CPU %d\n", cpu); (void) kstat_close(kc); return (-1); } if (kstat_read(kc, ksp, NULL) == -1) { pt_error("couldn't read 'turbo' kstat for CPU %d\n", cpu); (void) kstat_close(kc); return (-2); } knp = kstat_data_lookup(ksp, "turbo_mcnt"); if (knp == NULL) { pt_error("couldn't find 'turbo_mcnt' kstat for CPU " "%d\n", cpu); (void) kstat_close(kc); return (-3); } /* * snapshot IA32_MPERF_MSR */ turbo_info->t_mcnt = knp->value.ui64; knp = kstat_data_lookup(ksp, "turbo_acnt"); if (knp == NULL) { pt_error("couldn't find 'turbo_acnt' kstat for CPU " "%d\n", cpu); (void) kstat_close(kc); return (-4); } /* * snapshot IA32_APERF_MSR */ turbo_info->t_acnt = knp->value.ui64; } if (kstat_close(kc) != 0) pt_error("couldn't close 'turbo' kstat\n"); return (0); } /* * Turbo support checking and information initialization */ int pt_turbo_stat_prepare(void) { int ret = 0; if ((ret = pt_turbo_init()) != 0) return (ret); if ((ret = pt_turbo_snapshot(cpu_turbo_info)) != 0) pt_error("failed to snapshot 'turbo' kstat\n"); return (ret); } /* * When doing the statistics collection, we compare two kstat snapshot * and get a delta. the final ratio of performance boost will be worked * out according to the kstat delta */ int pt_turbo_stat_collect(void) { int cpu; uint64_t delta_mcnt, delta_acnt; double ratio; int ret; /* * Take a snapshot of turbo information to setup turbo_info_t * structure */ if ((ret = pt_turbo_snapshot(t_new)) != 0) { pt_error("failed to snapshot 'turbo' kstat\n"); return (ret); } /* * Calculate the kstat delta and work out the performance boost ratio */ for (cpu = 0; cpu < g_ncpus; cpu++) { delta_mcnt = t_new[cpu].t_mcnt - cpu_turbo_info[cpu].t_mcnt; delta_acnt = t_new[cpu].t_acnt - cpu_turbo_info[cpu].t_acnt; if ((delta_mcnt > delta_acnt) || (delta_mcnt == 0)) ratio = 1.0; else ratio = (double)delta_acnt / (double)delta_mcnt; g_turbo_ratio += ratio; } g_turbo_ratio = g_turbo_ratio / (double)g_ncpus; /* * Update the structure of the kstat for the next time calculation */ (void) memcpy(cpu_turbo_info, t_new, g_ncpus * (sizeof (turbo_info_t))); return (0); } /* * Copyright 2009, Intel Corporation * Copyright 2009, Sun Microsystems, Inc * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven * Eric C Saxe * Aubrey Li */ /* * GPL Disclaimer * * For the avoidance of doubt, except that if any license choice other * than GPL or LGPL is available it will apply instead, Sun elects to * use only the General Public License version 2 (GPLv2) at this time * for any software where a choice of GPL license versions is made * available with the language indicating that GPLv2 or any later * version may be used, or where a choice of which version of the GPL * is applied is otherwise unspecified. */ #include #include #include #include #include #include #include #include #include "powertop.h" static char PROG_FMT[] = "%s: "; static char ERR_FMT[] = ": %s"; static char *progname; void pt_set_progname(char *name) { progname = basename(name); } /*PRINTFLIKE1*/ void pt_error(char *format, ...) { int err = errno; va_list alist; if (g_gui) return; if (progname != NULL) (void) fprintf(stderr, PROG_FMT, progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, ERR_FMT, strerror(err)); } /* * Returns the number of online CPUs. */ uint_t pt_enumerate_cpus(void) { int cpuid; int max, cpus_conf; uint_t ncpus = 0; max = sysconf(_SC_CPUID_MAX); cpus_conf = sysconf(_SC_NPROCESSORS_CONF); /* Fall back to one CPU if any of the sysconf calls above failed */ if (max == -1 || cpus_conf == -1) { max = cpus_conf = 1; } if ((g_cpu_table = malloc(cpus_conf * sizeof (processorid_t))) == NULL) return (0); for (cpuid = 0; cpuid < max; cpuid++) { if (p_online(cpuid, P_STATUS) != -1) { g_cpu_table[ncpus] = cpuid; ncpus++; } } return (ncpus); } void pt_usage(void) { (void) fprintf(stderr, "%s %s\n\n", TITLE, COPYRIGHT_INTEL); (void) fprintf(stderr, "usage: powertop [option]\n"); (void) fprintf(stderr, " -d, --dump [count] Read wakeups count " "times and print list of top offenders\n"); (void) fprintf(stderr, " -t, --time [interval] Default time to gather " "data in seconds [1-30s]\n"); (void) fprintf(stderr, " -v, --verbose Verbose mode, reports " "kernel cyclic activity\n"); (void) fprintf(stderr, " -c, --cpu [CPU] Only observe a specific" " CPU\n"); (void) fprintf(stderr, " -h, --help Show this help " "message\n"); } int pt_get_bit_depth(void) { /* * This little routine was derived from isainfo.c to look up * the system's bit depth. It feeds a 10 byte long buffer to * sysinfo (we only need the first word, sysinfo truncates and * \0 terminates the rest) from which we figure out which isa * we're running on. */ char buf[BIT_DEPTH_BUF]; if (sysinfo(SI_ARCHITECTURE_64, buf, BIT_DEPTH_BUF) == -1) if (sysinfo(SI_ARCHITECTURE_32, buf, BIT_DEPTH_BUF) == -1) return (-2); if (strcmp(buf, "sparc") == 0 || strcmp(buf, "i386") == 0) return (32); if (strcmp(buf, "sparcv9") == 0 || strcmp(buf, "amd64") == 0) return (64); return (-3); } /* * Simple integer comparison routine for the event report qsort(3C). */ int pt_event_compare(const void *p1, const void *p2) { event_info_t i = *((event_info_t *)p1); event_info_t j = *((event_info_t *)p2); if (i.total_count > j.total_count) return (-1); if (i.total_count < j.total_count) return (1); return (0); }