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