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