Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright 2009, Intel Corporation
      3  * Copyright 2009, Sun Microsystems, Inc
      4  *
      5  * This file is part of PowerTOP
      6  *
      7  * This program file is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the
      9  * Free Software Foundation; version 2 of the License.
     10  *
     11  * This program is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program in a file named COPYING; if not, write to the
     18  * Free Software Foundation, Inc.,
     19  * 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301 USA
     21  *
     22  * Authors:
     23  *	Arjan van de Ven <arjan (at) linux.intel.com>
     24  *	Eric C Saxe <eric.saxe (at) sun.com>
     25  *	Aubrey Li <aubrey.li (at) intel.com>
     26  */
     27 
     28 /*
     29  * GPL Disclaimer
     30  *
     31  * For the avoidance of doubt, except that if any license choice other
     32  * than GPL or LGPL is available it will apply instead, Sun elects to
     33  * use only the General Public License version 2 (GPLv2) at this time
     34  * for any software where a choice of GPL license versions is made
     35  * available with the language indicating that GPLv2 or any later
     36  * version may be used, or where a choice of which version of the GPL
     37  * is applied is otherwise unspecified.
     38  */
     39 
     40 #include <getopt.h>
     41 #include <unistd.h>
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <signal.h>
     45 #include <string.h>
     46 #include <ctype.h>
     47 #include <poll.h>
     48 #include "powertop.h"
     49 
     50 /*
     51  * Global variables, see powertop.h for comments and extern declarations.
     52  * These are ordered by type, grouped by usage.
     53  */
     54 int			g_bit_depth;
     55 int 			g_total_events, g_top_events;
     56 int			g_npstates, g_max_cstate, g_longest_cstate;
     57 uint_t			g_features;
     58 uint_t			g_ncpus;
     59 uint_t			g_ncpus_observed;
     60 
     61 processorid_t 		*g_cpu_table;
     62 
     63 double			g_interval_length;
     64 hrtime_t		g_total_c_time;
     65 
     66 uchar_t			g_op_mode;
     67 boolean_t		g_gui;
     68 uint_t			g_observed_cpu;
     69 
     70 event_info_t    	g_event_info[EVENT_NUM_MAX];
     71 state_info_t		g_cstate_info[NSTATES];
     72 freq_state_info_t	g_pstate_info[NSTATES];
     73 cpu_power_info_t	*g_cpu_power_states;
     74 
     75 boolean_t		g_turbo_supported;
     76 boolean_t		g_sig_resize;
     77 
     78 uint_t			g_argc;
     79 char			**g_argv;
     80 
     81 char			*optarg;
     82 
     83 static const int	true = 1;
     84 
     85 void
     86 pt_sig_handler(int sig)
     87 {
     88 	switch (sig) {
     89 	case SIGWINCH:
     90 		g_sig_resize = B_TRUE;
     91 		break;
     92 	}
     93 }
     94 
     95 int
     96 main(int argc, char **argv)
     97 {
     98 	double		interval, interval_usr;
     99 	hrtime_t 	interval_start;
    100 	int		index2 = 0, c, dump_count = 0;
    101 	char		*endptr, key;
    102 	boolean_t	root_user = B_FALSE;
    103 	struct pollfd	pollset;
    104 
    105 	static struct option opts[] = {
    106 		{ "dump", 1, NULL, 'd' },
    107 		{ "time", 1, NULL, 't' },
    108 		{ "help", 0, NULL, 'h' },
    109 		{ "cpu", 1, NULL, 'c' },
    110 		{ "verbose", 0, NULL, 'v' },
    111 		{ 0, 0, NULL, 0 }
    112 	};
    113 
    114 	pt_set_progname(argv[0]);
    115 
    116 	/*
    117 	 * Enumerate the system's CPUs, populate cpu_table, g_ncpus
    118 	 */
    119 	if ((g_ncpus = g_ncpus_observed = pt_enumerate_cpus()) == 0)
    120 		exit(EXIT_FAILURE);
    121 
    122 	if ((g_bit_depth = pt_get_bit_depth()) < 0)
    123 		exit(EXIT_FAILURE);
    124 
    125 	g_features = 0;
    126 	interval = interval_usr = INTERVAL_DEFAULT;
    127 	g_op_mode = PT_MODE_DEFAULT;
    128 	g_max_cstate = 0;
    129 	g_argv = NULL;
    130 	g_argc = 0;
    131 	g_observed_cpu = 0;
    132 	g_turbo_supported = B_FALSE;
    133 	g_sig_resize = B_FALSE;
    134 	g_curr_sugg = NULL;
    135 
    136 	while ((c = getopt_long(argc, argv, "d:t:hvc:", opts, &index2))
    137 	    != EOF) {
    138 		if (c == -1)
    139 			break;
    140 
    141 		switch (c) {
    142 		case 'd':
    143 			if (PT_ON_DUMP) {
    144 				pt_usage();
    145 				exit(EXIT_USAGE);
    146 			}
    147 
    148 			g_op_mode |= PT_MODE_DUMP;
    149 			g_gui = B_FALSE;
    150 			dump_count = (int)strtod(optarg, &endptr);
    151 
    152 			if (dump_count <= 0 || *endptr != NULL)
    153 				pt_usage();
    154 			break;
    155 		case 't':
    156 			if (PT_ON_TIME) {
    157 				pt_usage();
    158 				exit(EXIT_USAGE);
    159 			}
    160 
    161 			g_op_mode |= PT_MODE_TIME;
    162 			interval = interval_usr = (double)strtod(optarg,
    163 			    &endptr);
    164 
    165 			if (*endptr != NULL || interval < 1 ||
    166 			    interval > INTERVAL_MAX) {
    167 				pt_usage();
    168 				exit(EXIT_USAGE);
    169 			}
    170 
    171 			break;
    172 		case 'v':
    173 			if (PT_ON_CPU || PT_ON_VERBOSE) {
    174 				pt_usage();
    175 				exit(EXIT_USAGE);
    176 			}
    177 
    178 			g_op_mode |= PT_MODE_VERBOSE;
    179 			break;
    180 		case 'c':
    181 			if (PT_ON_CPU || PT_ON_VERBOSE) {
    182 				pt_usage();
    183 				exit(EXIT_USAGE);
    184 			}
    185 
    186 			g_op_mode |= PT_MODE_CPU;
    187 			g_observed_cpu = (uint_t)strtod(optarg, &endptr);
    188 
    189 			if (g_observed_cpu >= g_ncpus) {
    190 				pt_usage();
    191 				exit(EXIT_USAGE);
    192 			}
    193 
    194 			g_argc = 1;
    195 			g_ncpus_observed = 1;
    196 
    197 			if ((g_argv = malloc(sizeof (char *))) == NULL)
    198 				return (EXIT_FAILURE);
    199 
    200 			if ((*g_argv = malloc(sizeof (char) * 5)) == NULL)
    201 				return (EXIT_FAILURE);
    202 
    203 			(void) snprintf(*g_argv, 5, "%d\0", g_observed_cpu);
    204 			break;
    205 		case 'h':
    206 			pt_usage();
    207 			exit(EXIT_SUCCESS);
    208 		default:
    209 			pt_usage();
    210 			exit(EXIT_USAGE);
    211 		}
    212 	}
    213 
    214 	if (optind < argc)
    215 		pt_usage();
    216 
    217 	(void) printf("%s   %s\n\n", TITLE, COPYRIGHT_INTEL);
    218 
    219 	(void) printf("Collecting data for %.2f second(s) \n",
    220 	    (float)interval);
    221 
    222 	/* Prepare P-state statistics */
    223 	if (pt_cpufreq_stat_prepare() == 0)
    224 		g_features |= FEATURE_PSTATE;
    225 
    226 	/* Prepare C-state statistics */
    227 	if (pt_cpuidle_stat_prepare() == 0)
    228 		g_features |= FEATURE_CSTATE;
    229 	else
    230 		/*
    231 		 * PowerTop was unable to run a DTrace program,
    232 		 * most likely for lack of permissions.
    233 		 */
    234 		exit(EXIT_FAILURE);
    235 
    236 	/* Prepare event statistics */
    237 	if (pt_events_stat_prepare() != -1)
    238 		g_features |= FEATURE_EVENTS;
    239 
    240 	/*
    241 	 * If the system is running on battery, find out what's
    242 	 * the kstat module for it
    243 	 */
    244 	pt_battery_mod_lookup();
    245 
    246 	/* Prepare turbo statistics */
    247 	if (pt_turbo_stat_prepare() == 0)
    248 		g_features |= FEATURE_TURBO;
    249 
    250 	/*
    251 	 * Initialize the display.
    252 	 */
    253 	if (!PT_ON_DUMP) {
    254 		pt_display_init_curses();
    255 		pt_display_setup(B_FALSE);
    256 		(void) signal(SIGWINCH, pt_sig_handler);
    257 
    258 		pt_display_title_bar();
    259 		pt_display_status_bar();
    260 
    261 		g_gui = B_TRUE;
    262 		pollset.fd = STDIN_FILENO;
    263 		pollset.events = POLLIN;
    264 	}
    265 
    266 	/*
    267 	 * Installs the initial suggestions, running as root and turning CPU
    268 	 * power management ON.
    269 	 */
    270 	if (geteuid() != 0) {
    271 		pt_sugg_as_root();
    272 	} else {
    273 		root_user = B_TRUE;
    274 		pt_cpufreq_suggest();
    275 	}
    276 
    277 	while (true) {
    278 		key = 0;
    279 
    280 		if (g_sig_resize)
    281 			pt_display_resize();
    282 
    283 		interval_start = gethrtime();
    284 
    285 		if (!PT_ON_DUMP) {
    286 			if (poll(&pollset, (nfds_t)1,
    287 			    (int)(interval * MILLISEC)) > 0)
    288 				(void) read(STDIN_FILENO, &key, 1);
    289 		} else {
    290 			(void) sleep((int)interval);
    291 		}
    292 
    293 		g_interval_length = (double)(gethrtime() - interval_start)
    294 		    /NANOSEC;
    295 
    296 		g_top_events = 0;
    297 		g_total_events = 0;
    298 
    299 		(void) memset(g_event_info, 0,
    300 		    EVENT_NUM_MAX * sizeof (event_info_t));
    301 		(void) memset(g_cstate_info, 0,
    302 		    NSTATES * sizeof (state_info_t));
    303 
    304 		/* Collect idle state transition stats */
    305 		if (g_features & FEATURE_CSTATE &&
    306 		    pt_cpuidle_stat_collect(g_interval_length) < 0) {
    307 			/* Reinitialize C-state statistics */
    308 			if (pt_cpuidle_stat_prepare() != 0)
    309 				exit(EXIT_FAILURE);
    310 
    311 			continue;
    312 		}
    313 
    314 		/* Collect frequency change stats */
    315 		if (g_features & FEATURE_PSTATE &&
    316 		    pt_cpufreq_stat_collect(g_interval_length) < 0) {
    317 			/* Reinitialize P-state statistics */
    318 			if (pt_cpufreq_stat_prepare() != 0)
    319 				exit(EXIT_FAILURE);
    320 
    321 			continue;
    322 		}
    323 
    324 		/* Collect event statistics */
    325 		if (g_features & FEATURE_EVENTS &&
    326 		    pt_events_stat_collect() < 0) {
    327 			/* Reinitialize event statistics */
    328 			if (pt_events_stat_prepare() != 0)
    329 				exit(EXIT_FAILURE);
    330 
    331 			continue;
    332 		}
    333 
    334 		/* Collect turbo statistics */
    335 		if (g_features & FEATURE_TURBO &&
    336 		    pt_turbo_stat_collect() < 0)
    337 			exit(EXIT_FAILURE);
    338 
    339 		/* Show CPU power states */
    340 		pt_display_states();
    341 
    342 		/* Show wakeups events affecting PM */
    343 		if (g_features & FEATURE_EVENTS) {
    344 			pt_display_wakeups(g_interval_length);
    345 			pt_display_events(g_interval_length);
    346 		}
    347 
    348 		pt_battery_print();
    349 
    350 		if (key && !PT_ON_DUMP) {
    351 			switch (toupper(key)) {
    352 			case 'Q':
    353 				exit(EXIT_SUCCESS);
    354 				break;
    355 
    356 			case 'R':
    357 				interval = 3;
    358 				break;
    359 			}
    360 
    361 			/*
    362 			 * Check if the user has activated the current
    363 			 * suggestion.
    364 			 */
    365 			if (g_curr_sugg != NULL &&
    366 			    toupper(key) == g_curr_sugg->key &&
    367 			    g_curr_sugg->func)
    368 				g_curr_sugg->func();
    369 		}
    370 
    371 		if (dump_count)
    372 			dump_count--;
    373 
    374 		/* Exits if user requested a dump */
    375 		if (PT_ON_DUMP && !dump_count)
    376 			exit(EXIT_SUCCESS);
    377 
    378 		/* No key pressed, will suggest something */
    379 		if (!key && !dump_count)
    380 			pt_sugg_pick();
    381 
    382 		/* Refresh display */
    383 		if (!PT_ON_DUMP)
    384 			pt_display_update();
    385 
    386 		if (root_user)
    387 			pt_cpufreq_suggest();
    388 
    389 		/*
    390 		 * Update the interval based on how long the CPU was in the
    391 		 * longest c-state during the last snapshot. If the user
    392 		 * specified an interval we skip this bit and keep it fixed.
    393 		 */
    394 		if (g_features & FEATURE_CSTATE && !PT_ON_TIME &&
    395 		    g_longest_cstate > 0) {
    396 			double deep_idle_res = (((double)
    397 			    g_cstate_info[g_longest_cstate].total_time/MICROSEC
    398 			    /g_ncpus)/g_cstate_info[g_longest_cstate].events);
    399 
    400 			if (deep_idle_res < INTERVAL_DEFAULT ||
    401 			    (g_total_events/interval) < 1)
    402 				interval = INTERVAL_DEFAULT;
    403 			else
    404 				interval = INTERVAL_UPDATE(deep_idle_res);
    405 		} else {
    406 			/*
    407 			 * Restore interval after a refresh.
    408 			 */
    409 			if (key)
    410 				interval = interval_usr;
    411 		}
    412 	}
    413 
    414 	return (EXIT_SUCCESS);
    415 }
    416