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
pt_sig_handler(int sig)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
main(int argc,char ** argv)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