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