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