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 <string.h> 45 #include <ctype.h> 46 #include <locale.h> 47 #include "powertop.h" 48 49 /* 50 * Global variables, see powertop.h for comments and extern declarations. 51 * These are ordered by type, grouped by usage. 52 */ 53 double g_ticktime, g_ticktime_usr; 54 double g_interval; 55 double g_displaytime; 56 57 int g_bit_depth; 58 int g_total_events, g_top_events; 59 int g_npstates, g_max_cstate, g_longest_cstate; 60 uint_t g_ncpus; 61 uint_t g_ncpus_observed; 62 63 processorid_t *g_cpu_table; 64 65 hrtime_t g_total_c_time; 66 67 uchar_t g_op_mode; 68 boolean_t g_gui; 69 uint_t g_observed_cpu; 70 71 event_info_t g_event_info[EVENT_NUM_MAX]; 72 state_info_t g_cstate_info[NSTATES]; 73 freq_state_info_t g_pstate_info[NSTATES]; 74 cpu_power_info_t *g_cpu_power_states; 75 suggestion_func *g_suggestion_activate; 76 77 boolean_t g_turbo_supported; 78 79 uint_t g_argc; 80 char **g_argv; 81 82 char *optarg; 83 84 static const int true = 1; 85 86 int 87 main(int argc, char **argv) 88 { 89 hrtime_t last, now; 90 uint_t features = 0, user_interval = 0; 91 int ncursesinited = 0, index2 = 0, c, ret, dump_count = 0; 92 double last_time; 93 char *endptr; 94 95 static struct option opts[] = { 96 { "dump", 1, NULL, 'd' }, 97 { "time", 1, NULL, 't' }, 98 { "help", 0, NULL, 'h' }, 99 { "cpu", 1, NULL, 'c' }, 100 { "verbose", 0, NULL, 'v' }, 101 { 0, 0, NULL, 0 } 102 }; 103 104 (void) setlocale(LC_ALL, ""); 105 (void) bindtextdomain("powertop", "/usr/share/locale"); 106 (void) textdomain("powertop"); 107 108 pt_set_progname(argv[0]); 109 110 /* 111 * Enumerate the system's CPUs 112 * Populate cpu_table, g_ncpus 113 */ 114 if ((g_ncpus = g_ncpus_observed = enumerate_cpus()) == 0) 115 exit(EXIT_FAILURE); 116 117 if ((g_bit_depth = get_bit_depth()) < 0) 118 exit(EXIT_FAILURE); 119 120 g_ticktime = g_ticktime_usr = INTERVAL_DEFAULT; 121 g_displaytime = 0.0; 122 g_op_mode = PT_MODE_DEFAULT; 123 g_gui = B_FALSE; 124 g_max_cstate = 0; 125 g_argv = NULL; 126 g_argc = 0; 127 g_observed_cpu = 0; 128 g_turbo_supported = B_FALSE; 129 130 while ((c = getopt_long(argc, argv, "d:t:h:vc:", opts, &index2)) 131 != EOF) { 132 if (c == -1) 133 break; 134 135 switch (c) { 136 case 'd': 137 if (PT_ON_DUMP) 138 usage(); 139 140 g_op_mode |= PT_MODE_DUMP; 141 dump_count = (int)strtod(optarg, &endptr); 142 143 if (dump_count <= 0 || *endptr != NULL) 144 usage(); 145 break; 146 case 't': 147 if (user_interval) 148 usage(); 149 150 user_interval = 1; 151 g_ticktime = g_ticktime_usr = (double)strtod(optarg, 152 &endptr); 153 154 if (*endptr != NULL || g_ticktime < 1 || 155 g_ticktime > INTERVAL_MAX) 156 usage(); 157 break; 158 case 'v': 159 if (PT_ON_CPU || PT_ON_VERBOSE) 160 usage(); 161 162 g_op_mode |= PT_MODE_VERBOSE; 163 break; 164 case 'c': 165 if (PT_ON_CPU || PT_ON_VERBOSE) 166 usage(); 167 168 g_op_mode |= PT_MODE_CPU; 169 g_observed_cpu = (uint_t)strtod(optarg, &endptr); 170 171 if (g_observed_cpu >= g_ncpus) 172 usage(); 173 174 g_argc = 1; 175 g_ncpus_observed = 1; 176 177 if ((g_argv = malloc(sizeof (char *))) == NULL) 178 return (EXIT_FAILURE); 179 180 if ((*g_argv = malloc(sizeof (char) * 5)) == NULL) 181 return (EXIT_FAILURE); 182 183 (void) snprintf(*g_argv, 5, "%d\0", g_observed_cpu); 184 break; 185 case 'h': 186 default: 187 usage(); 188 return (EXIT_USAGE); 189 } 190 } 191 192 if (optind < argc) { 193 usage(); 194 } 195 196 (void) printf("%s %s\n\n", TITLE, COPYRIGHT_INTEL); 197 198 /* 199 * If the system is running on battery, find out what's 200 * the kstat module for it 201 */ 202 battery_mod_lookup(); 203 204 /* Prepare C-state statistics */ 205 ret = pt_cpuidle_stat_prepare(); 206 if (ret == 0) 207 features |= FEATURE_CSTATE; 208 else 209 /* 210 * PowerTop was unable to run a DTrace program, 211 * most likely for lack of permissions. 212 */ 213 exit(EXIT_FAILURE); 214 215 /* Prepare P-state statistics */ 216 if (pt_cpufreq_stat_prepare() == 0) 217 features |= FEATURE_PSTATE; 218 219 /* Prepare event statistics */ 220 if (pt_events_stat_prepare() != -1) 221 features |= FEATURE_EVENTS; 222 223 /* Prepare turbo statistics */ 224 if (pt_turbo_stat_prepare() == 0) { 225 features |= FEATURE_TURBO; 226 } 227 228 (void) printf(_("Collecting data for %.2f second(s) \n"), 229 (float)g_ticktime); 230 231 if (!PT_ON_DUMP) 232 g_gui = B_TRUE; 233 234 last = gethrtime(); 235 236 while (true) { 237 fd_set rfds; 238 struct timeval tv; 239 int key, reinit = 0; 240 char keychar; 241 242 /* 243 * Sleep for a while waiting either for input (if we're not 244 * in dump mode) or for the timeout to elapse 245 */ 246 FD_ZERO(&rfds); 247 FD_SET(0, &rfds); 248 249 tv.tv_sec = (long)g_ticktime; 250 tv.tv_usec = (long)((g_ticktime - tv.tv_sec) * MICROSEC); 251 252 if (!PT_ON_DUMP) 253 key = select(1, &rfds, NULL, NULL, &tv); 254 else 255 key = select(1, NULL, NULL, NULL, &tv); 256 257 now = gethrtime(); 258 259 g_interval = (double)(now - last)/NANOSEC; 260 last = now; 261 262 g_top_events = 0; 263 g_total_events = 0; 264 265 (void) memset(g_event_info, 0, 266 EVENT_NUM_MAX * sizeof (event_info_t)); 267 (void) memset(g_cstate_info, 0, 268 NSTATES * sizeof (state_info_t)); 269 270 /* Collect idle state transition stats */ 271 if (features & FEATURE_CSTATE && 272 pt_cpuidle_stat_collect(g_interval) < 0) { 273 /* Reinitialize C-state statistics */ 274 if (pt_cpuidle_stat_prepare() != 0) 275 exit(EXIT_FAILURE); 276 277 reinit = 1; 278 } 279 280 /* Collect frequency change stats */ 281 if (features & FEATURE_PSTATE && 282 pt_cpufreq_stat_collect(g_interval) < 0) { 283 /* Reinitialize P-state statistics */ 284 if (pt_cpufreq_stat_prepare() != 0) 285 exit(EXIT_FAILURE); 286 287 reinit = 1; 288 } 289 290 /* Collect event statistics */ 291 if (features & FEATURE_EVENTS && 292 pt_events_stat_collect() < 0) { 293 /* Reinitialize event statistics */ 294 if (pt_events_stat_prepare() != 0) 295 exit(EXIT_FAILURE); 296 297 reinit = 1; 298 } 299 300 if (reinit) 301 continue; 302 303 /* Collect turbo statistics */ 304 if (features & FEATURE_TURBO && 305 pt_turbo_stat_collect() < 0) { 306 exit(EXIT_FAILURE); 307 } 308 309 /* 310 * Initialize curses if we're not dumping and 311 * haven't already done it 312 */ 313 if (!PT_ON_DUMP) { 314 if (!ncursesinited) { 315 initialize_curses(); 316 ncursesinited++; 317 } 318 setup_windows(); 319 show_title_bar(); 320 } 321 322 /* Show CPU power states */ 323 if (features & FEATURE_CSTATE) 324 show_cstates(); 325 326 /* Show wakeups events affecting PM */ 327 if (features & FEATURE_EVENTS) { 328 show_wakeups(g_interval); 329 show_eventstats(g_interval); 330 } 331 332 print_battery(); 333 334 g_displaytime = g_displaytime - g_ticktime; 335 336 if (key && !PT_ON_DUMP) { 337 keychar = toupper(fgetc(stdin)); 338 339 switch (keychar) { 340 case 'Q': 341 cleanup_curses(); 342 exit(EXIT_SUCCESS); 343 break; 344 case 'R': 345 g_ticktime = 3; 346 break; 347 } 348 if (keychar == g_suggestion_key && 349 g_suggestion_activate) { 350 g_suggestion_activate(); 351 g_displaytime = -1.0; 352 } 353 } 354 reset_suggestions(); 355 356 /* suggests PM */ 357 if (geteuid() == 0) { 358 suggest_p_state(); 359 } else { 360 suggest_as_root(); 361 } 362 363 if (dump_count) 364 dump_count--; 365 366 /* Exits if user requested a dump */ 367 if (PT_ON_DUMP && !dump_count) { 368 print_all_suggestions(); 369 exit(EXIT_SUCCESS); 370 } 371 372 /* No key pressed, will suggest something */ 373 if (!key && !dump_count) 374 pick_suggestion(); 375 376 /* Refresh display */ 377 if (!PT_ON_DUMP) { 378 show_title_bar(); 379 update_windows(); 380 } 381 382 /* 383 * Update the interval based on how long the CPU was in the 384 * longest c-state during the last snapshot. If the user 385 * specified an interval we skip this bit and keep it fixed. 386 */ 387 last_time = (((double)g_cstate_info[g_longest_cstate].total_time 388 /MICROSEC/g_ncpus)/g_cstate_info[g_longest_cstate].events); 389 390 if (!user_interval) 391 if (last_time < INTERVAL_DEFAULT || 392 (g_total_events/g_ticktime) < 1) 393 g_ticktime = INTERVAL_DEFAULT; 394 else 395 g_ticktime = INTERVAL_UPDATE(last_time); 396 397 /* 398 * Restore user specified interval after a refresh 399 */ 400 if (keychar == 'R' && user_interval) 401 g_ticktime = g_ticktime_usr; 402 } 403 return (EXIT_SUCCESS); 404 } 405 406 void 407 suggest_as_root(void) 408 { 409 add_suggestion("Suggestion: run as root to get suggestions" 410 " for reducing system power consumption", 40, NULL, NULL, 411 NULL); 412 } 413