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