xref: /illumos-gate/usr/src/cmd/powertop/common/powertop.c (revision aedf2b3bb56b025fcaf87b49ec6c8aeea07f16d7)
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 <locale.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 double 			g_ticktime, g_ticktime_usr;
55 double 			g_interval;
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_features;
61 uint_t			g_ncpus;
62 uint_t			g_ncpus_observed;
63 
64 processorid_t 		*g_cpu_table;
65 
66 hrtime_t		g_total_c_time;
67 
68 uchar_t			g_op_mode;
69 boolean_t		g_gui;
70 uint_t			g_observed_cpu;
71 
72 event_info_t    	g_event_info[EVENT_NUM_MAX];
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 
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		user_interval = 0;
91 	int		index2 = 0, c, ret, dump_count = 0;
92 	double		last_time;
93 	char		*endptr;
94 	boolean_t	root_user = B_FALSE;
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, 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_features = 0;
121 	g_ticktime = g_ticktime_usr = INTERVAL_DEFAULT;
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 	g_curr_sugg = NULL;
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 (PT_ON_DUMP)
139 				usage();
140 
141 			g_op_mode |= PT_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 (PT_ON_CPU || PT_ON_VERBOSE)
161 				usage();
162 
163 			g_op_mode |= PT_MODE_VERBOSE;
164 			break;
165 		case 'c':
166 			if (PT_ON_CPU || PT_ON_VERBOSE)
167 				usage();
168 
169 			g_op_mode |= PT_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 	(void) printf("%s   %s\n\n", TITLE, COPYRIGHT_INTEL);
197 
198 	(void) printf(_("Collecting data for %.2f second(s) \n"),
199 	    (float)g_ticktime);
200 
201 	/* Prepare P-state statistics */
202 	if (pt_cpufreq_stat_prepare() == 0)
203 		g_features |= FEATURE_PSTATE;
204 
205 	/* Prepare C-state statistics */
206 	ret = pt_cpuidle_stat_prepare();
207 	if (ret == 0)
208 		g_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 	/*
217 	 * We need to initiate the display to make sure there's enough space
218 	 * in the terminal for all of PowerTOP's subwindows, but after
219 	 * pt_cpufreq_stat_prepare() which finds out how many states the
220 	 * system supports.
221 	 */
222 	if (!PT_ON_DUMP) {
223 		pt_display_init_curses();
224 		pt_display_setup(B_FALSE);
225 		g_gui = B_TRUE;
226 		pt_display_title_bar();
227 		pt_display_status_bar();
228 	}
229 
230 	/* Prepare event statistics */
231 	if (pt_events_stat_prepare() != -1)
232 		g_features |= FEATURE_EVENTS;
233 
234 	/*
235 	 * If the system is running on battery, find out what's
236 	 * the kstat module for it
237 	 */
238 	battery_mod_lookup();
239 
240 	/* Prepare turbo statistics */
241 	if (pt_turbo_stat_prepare() == 0)
242 		g_features |= FEATURE_TURBO;
243 
244 	/*
245 	 * Installs the initial suggestions, running as root and turning CPU
246 	 * power management ON.
247 	 */
248 	if (geteuid() != 0)
249 		pt_sugg_as_root();
250 	else {
251 		root_user = B_TRUE;
252 		pt_cpufreq_suggest();
253 	}
254 
255 	last = gethrtime();
256 
257 	while (true) {
258 		fd_set 	rfds;
259 		struct 	timeval tv;
260 		int 	key;
261 		char 	keychar;
262 
263 		/*
264 		 * Sleep for a while waiting either for input (if we're not
265 		 * in dump mode) or for the timeout to elapse
266 		 */
267 		FD_ZERO(&rfds);
268 		FD_SET(0, &rfds);
269 
270 		tv.tv_sec = (long)g_ticktime;
271 		tv.tv_usec = (long)((g_ticktime - tv.tv_sec) * MICROSEC);
272 
273 		if (!PT_ON_DUMP) {
274 			key = select(1, &rfds, NULL, NULL, &tv);
275 		} else
276 			key = select(1, NULL, NULL, NULL, &tv);
277 
278 		now = gethrtime();
279 
280 		g_interval = (double)(now - last)/NANOSEC;
281 		last = now;
282 
283 		g_top_events 	= 0;
284 		g_total_events 	= 0;
285 
286 		(void) memset(g_event_info, 0,
287 		    EVENT_NUM_MAX * sizeof (event_info_t));
288 		(void) memset(g_cstate_info, 0,
289 		    NSTATES * sizeof (state_info_t));
290 
291 		/* Collect idle state transition stats */
292 		if (g_features & FEATURE_CSTATE &&
293 		    pt_cpuidle_stat_collect(g_interval) < 0) {
294 			/* Reinitialize C-state statistics */
295 			if (pt_cpuidle_stat_prepare() != 0)
296 				exit(EXIT_FAILURE);
297 
298 			continue;
299 		}
300 
301 		/* Collect frequency change stats */
302 		if (g_features & FEATURE_PSTATE &&
303 		    pt_cpufreq_stat_collect(g_interval) < 0) {
304 			/* Reinitialize P-state statistics */
305 			if (pt_cpufreq_stat_prepare() != 0)
306 				exit(EXIT_FAILURE);
307 
308 			continue;
309 		}
310 
311 		/* Collect event statistics */
312 		if (g_features & FEATURE_EVENTS &&
313 		    pt_events_stat_collect() < 0) {
314 			/* Reinitialize event statistics */
315 			if (pt_events_stat_prepare() != 0)
316 				exit(EXIT_FAILURE);
317 
318 			continue;
319 		}
320 
321 		/* Collect turbo statistics */
322 		if (g_features & FEATURE_TURBO &&
323 		    pt_turbo_stat_collect() < 0)
324 			exit(EXIT_FAILURE);
325 
326 		/* Show CPU power states */
327 		pt_display_states();
328 
329 		/* Show wakeups events affecting PM */
330 		if (g_features & FEATURE_EVENTS) {
331 			pt_display_wakeups(g_interval);
332 			pt_display_events(g_interval);
333 		}
334 
335 		pt_battery_print();
336 
337 		if (key && !PT_ON_DUMP) {
338 			keychar = toupper(fgetc(stdin));
339 
340 			switch (keychar) {
341 			case 'Q':
342 				exit(EXIT_SUCCESS);
343 				break;
344 
345 			case 'R':
346 				g_ticktime = 3;
347 				break;
348 			}
349 
350 			/*
351 			 * Check if the user has activated the current
352 			 * suggestion.
353 			 */
354 			if (g_curr_sugg != NULL &&
355 			    keychar == g_curr_sugg->key && g_curr_sugg->func)
356 				g_curr_sugg->func();
357 		}
358 
359 		if (dump_count)
360 			dump_count--;
361 
362 		/* Exits if user requested a dump */
363 		if (PT_ON_DUMP && !dump_count)
364 			exit(EXIT_SUCCESS);
365 
366 		/* No key pressed, will suggest something */
367 		if (!key && !dump_count)
368 			pt_sugg_pick();
369 
370 		/* Refresh display */
371 		if (!PT_ON_DUMP)
372 			pt_display_update();
373 
374 		if (root_user)
375 			pt_cpufreq_suggest();
376 
377 		/*
378 		 * Update the interval based on how long the CPU was in the
379 		 * longest c-state during the last snapshot. If the user
380 		 * specified an interval we skip this bit and keep it fixed.
381 		 */
382 		if (g_features & FEATURE_CSTATE && !user_interval) {
383 			last_time = (((double)
384 			    g_cstate_info[g_longest_cstate].total_time/MICROSEC
385 			    /g_ncpus)/g_cstate_info[g_longest_cstate].events);
386 
387 			if (last_time < INTERVAL_DEFAULT ||
388 			    (g_total_events/g_ticktime) < 1)
389 				g_ticktime = INTERVAL_DEFAULT;
390 			else
391 				g_ticktime = INTERVAL_UPDATE(last_time);
392 		} else {
393 			/*
394 			 * Restore interval after a refresh.
395 			 */
396 			if (key)
397 				g_ticktime = g_ticktime_usr;
398 		}
399 	}
400 
401 	return (EXIT_SUCCESS);
402 }
403