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@linux.intel.com> 24 * Eric C Saxe <eric.saxe@sun.com> 25 * Aubrey Li <aubrey.li@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 exit(EXIT_USAGE); 155 } 156 157 break; 158 case 't': 159 if (PT_ON_TIME) { 160 pt_usage(); 161 exit(EXIT_USAGE); 162 } 163 164 g_op_mode |= PT_MODE_TIME; 165 interval = interval_usr = (double)strtod(optarg, 166 &endptr); 167 168 if (*endptr != NULL || interval < 1 || 169 interval > INTERVAL_MAX) { 170 pt_usage(); 171 exit(EXIT_USAGE); 172 } 173 174 break; 175 case 'v': 176 if (PT_ON_CPU || PT_ON_VERBOSE) { 177 pt_usage(); 178 exit(EXIT_USAGE); 179 } 180 181 g_op_mode |= PT_MODE_VERBOSE; 182 break; 183 case 'c': 184 if (PT_ON_CPU || PT_ON_VERBOSE) { 185 pt_usage(); 186 exit(EXIT_USAGE); 187 } 188 189 g_op_mode |= PT_MODE_CPU; 190 g_observed_cpu = (uint_t)strtod(optarg, &endptr); 191 192 if (g_observed_cpu >= g_ncpus) { 193 pt_usage(); 194 exit(EXIT_USAGE); 195 } 196 197 g_argc = 1; 198 g_ncpus_observed = 1; 199 200 if ((g_argv = malloc(sizeof (char *))) == NULL) 201 return (EXIT_FAILURE); 202 203 if ((*g_argv = malloc(sizeof (char) * 5)) == NULL) 204 return (EXIT_FAILURE); 205 206 (void) snprintf(*g_argv, 5, "%d\0", g_observed_cpu); 207 break; 208 case 'h': 209 pt_usage(); 210 exit(EXIT_SUCCESS); 211 default: 212 pt_usage(); 213 exit(EXIT_USAGE); 214 } 215 } 216 217 if (optind < argc) { 218 pt_usage(); 219 exit(EXIT_USAGE); 220 } 221 222 (void) printf("%s %s\n\n", TITLE, COPYRIGHT_INTEL); 223 224 (void) printf("Collecting data for %.2f second(s) \n", 225 (float)interval); 226 227 /* Prepare P-state statistics */ 228 if (pt_cpufreq_stat_prepare() == 0) 229 g_features |= FEATURE_PSTATE; 230 231 /* Prepare C-state statistics */ 232 if (pt_cpuidle_stat_prepare() == 0) 233 g_features |= FEATURE_CSTATE; 234 else 235 /* 236 * PowerTop was unable to run a DTrace program, 237 * most likely for lack of permissions. 238 */ 239 exit(EXIT_FAILURE); 240 241 /* Prepare event statistics */ 242 if (pt_events_stat_prepare() != -1) 243 g_features |= FEATURE_EVENTS; 244 245 /* 246 * If the system is running on battery, find out what's 247 * the kstat module for it 248 */ 249 pt_battery_mod_lookup(); 250 251 /* Prepare turbo statistics */ 252 if (pt_turbo_stat_prepare() == 0) 253 g_features |= FEATURE_TURBO; 254 255 /* 256 * Initialize the display. 257 */ 258 if (!PT_ON_DUMP) { 259 pt_display_init_curses(); 260 pt_display_setup(B_FALSE); 261 (void) signal(SIGWINCH, pt_sig_handler); 262 263 pt_display_title_bar(); 264 pt_display_status_bar(); 265 266 g_gui = B_TRUE; 267 pollset.fd = STDIN_FILENO; 268 pollset.events = POLLIN; 269 } 270 271 /* 272 * Installs the initial suggestions, running as root and turning CPU 273 * power management ON. 274 */ 275 if (geteuid() != 0) { 276 pt_sugg_as_root(); 277 } else { 278 root_user = B_TRUE; 279 pt_cpufreq_suggest(); 280 } 281 282 while (true) { 283 key = 0; 284 285 if (g_sig_resize) 286 pt_display_resize(); 287 288 interval_start = gethrtime(); 289 290 if (!PT_ON_DUMP) { 291 if (poll(&pollset, (nfds_t)1, 292 (int)(interval * MILLISEC)) > 0) 293 (void) read(STDIN_FILENO, &key, 1); 294 } else { 295 (void) sleep((int)interval); 296 } 297 298 g_interval_length = (double)(gethrtime() - interval_start) 299 /NANOSEC; 300 301 g_top_events = 0; 302 g_total_events = 0; 303 304 (void) memset(g_event_info, 0, 305 EVENT_NUM_MAX * sizeof (event_info_t)); 306 (void) memset(g_cstate_info, 0, 307 NSTATES * sizeof (state_info_t)); 308 309 /* Collect idle state transition stats */ 310 if (g_features & FEATURE_CSTATE && 311 pt_cpuidle_stat_collect(g_interval_length) < 0) { 312 /* Reinitialize C-state statistics */ 313 if (pt_cpuidle_stat_prepare() != 0) 314 exit(EXIT_FAILURE); 315 316 continue; 317 } 318 319 /* Collect frequency change stats */ 320 if (g_features & FEATURE_PSTATE && 321 pt_cpufreq_stat_collect(g_interval_length) < 0) { 322 /* Reinitialize P-state statistics */ 323 if (pt_cpufreq_stat_prepare() != 0) 324 exit(EXIT_FAILURE); 325 326 continue; 327 } 328 329 /* Collect event statistics */ 330 if (g_features & FEATURE_EVENTS && 331 pt_events_stat_collect() < 0) { 332 /* Reinitialize event statistics */ 333 if (pt_events_stat_prepare() != 0) 334 exit(EXIT_FAILURE); 335 336 continue; 337 } 338 339 /* Collect turbo statistics */ 340 if (g_features & FEATURE_TURBO && 341 pt_turbo_stat_collect() < 0) 342 exit(EXIT_FAILURE); 343 344 /* Show CPU power states */ 345 pt_display_states(); 346 347 /* Show wakeups events affecting PM */ 348 if (g_features & FEATURE_EVENTS) { 349 pt_display_wakeups(g_interval_length); 350 pt_display_events(g_interval_length); 351 } 352 353 pt_battery_print(); 354 355 if (key && !PT_ON_DUMP) { 356 switch (toupper(key)) { 357 case 'Q': 358 exit(EXIT_SUCCESS); 359 break; 360 361 case 'R': 362 interval = 3; 363 break; 364 } 365 366 /* 367 * Check if the user has activated the current 368 * suggestion. 369 */ 370 if (g_curr_sugg != NULL && 371 toupper(key) == g_curr_sugg->key && 372 g_curr_sugg->func) 373 g_curr_sugg->func(); 374 } 375 376 if (dump_count) 377 dump_count--; 378 379 /* Exits if user requested a dump */ 380 if (PT_ON_DUMP && !dump_count) 381 exit(EXIT_SUCCESS); 382 383 /* No key pressed, will suggest something */ 384 if (!key && !dump_count) 385 pt_sugg_pick(); 386 387 /* Refresh display */ 388 if (!PT_ON_DUMP) 389 pt_display_update(); 390 391 if (root_user) 392 pt_cpufreq_suggest(); 393 394 /* 395 * Update the interval based on how long the CPU was in the 396 * longest c-state during the last snapshot. If the user 397 * specified an interval we skip this bit and keep it fixed. 398 */ 399 if (g_features & FEATURE_CSTATE && !PT_ON_TIME && 400 g_longest_cstate > 0 && 401 g_cstate_info[g_longest_cstate].events > 0) { 402 double deep_idle_res = (((double) 403 g_cstate_info[g_longest_cstate].total_time/MICROSEC 404 /g_ncpus)/g_cstate_info[g_longest_cstate].events); 405 406 if (deep_idle_res < INTERVAL_DEFAULT || 407 (g_total_events/interval) < 1) 408 interval = INTERVAL_DEFAULT; 409 else 410 interval = INTERVAL_UPDATE(deep_idle_res); 411 } else { 412 /* 413 * Restore interval after a refresh. 414 */ 415 if (key) 416 interval = interval_usr; 417 } 418 } 419 420 return (EXIT_SUCCESS); 421 }