xref: /illumos-gate/usr/src/cmd/powertop/common/powertop.c (revision dd72704bd9e794056c558153663c739e2012d721)
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
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
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