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 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