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 <stdlib.h>
41 #include <string.h>
42 #include <curses.h>
43 #include "powertop.h"
44 
45 static WINDOW 	*title_bar_window;
46 static WINDOW 	*cstate_window;
47 static WINDOW 	*wakeup_window;
48 static WINDOW 	*acpi_power_window;
49 static WINDOW 	*eventstat_window;
50 static WINDOW 	*suggestion_window;
51 static WINDOW 	*status_bar_window;
52 
53 #define	print(win, y, x, fmt, args...)				\
54 	if (PTOP_ON_DUMP)					\
55 		(void) printf(fmt, ## args);			\
56 	else							\
57 		(void) mvwprintw(win, y, x, fmt, ## args);
58 
59 char 		g_status_bar_slots[PTOP_BAR_NSLOTS][PTOP_BAR_LENGTH];
60 char 		g_suggestion_key;
61 
62 static int	maxx, maxy;
63 
64 static void
65 zap_windows(void)
66 {
67 	if (title_bar_window) {
68 		(void) delwin(title_bar_window);
69 		title_bar_window = NULL;
70 	}
71 	if (cstate_window) {
72 		(void) delwin(cstate_window);
73 		cstate_window = NULL;
74 	}
75 	if (wakeup_window) {
76 		(void) delwin(wakeup_window);
77 		wakeup_window = NULL;
78 	}
79 	if (acpi_power_window) {
80 		(void) delwin(acpi_power_window);
81 		acpi_power_window = NULL;
82 	}
83 	if (eventstat_window) {
84 		(void) delwin(eventstat_window);
85 		eventstat_window = NULL;
86 	}
87 	if (suggestion_window) {
88 		(void) delwin(suggestion_window);
89 		suggestion_window = NULL;
90 	}
91 	if (status_bar_window) {
92 		(void) delwin(status_bar_window);
93 		status_bar_window = NULL;
94 	}
95 }
96 
97 void
98 cleanup_curses(void)
99 {
100 	(void) endwin();
101 }
102 
103 /*
104  * This part was re-written to be human readable and easy to modify. Please
105  * try to keep it that way and help us save some time.
106  *
107  * Friendly reminder:
108  * 	subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x)
109  */
110 void
111 setup_windows(void)
112 {
113 	/*
114 	 * These variables are used to properly set the initial y position and
115 	 * number of lines in each subwindow, as the number of supported CPU
116 	 * states affects their placement.
117 	 */
118 	int cstate_lines, event_lines, pos_y;
119 
120 	getmaxyx(stdscr, maxy, maxx);
121 
122 	zap_windows();
123 
124 	cstate_lines 	= TITLE_LINE + max((g_max_cstate+1), g_npstates);
125 
126 	pos_y = 0;
127 	title_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
128 
129 	pos_y += NEXT_LINE + BLANK_LINE;
130 	cstate_window = subwin(stdscr, cstate_lines, maxx, pos_y, 0);
131 
132 	pos_y += cstate_lines + BLANK_LINE;
133 	wakeup_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
134 
135 	pos_y += NEXT_LINE;
136 	acpi_power_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
137 
138 	pos_y += NEXT_LINE + BLANK_LINE;
139 	event_lines = maxy - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW -
140 	    pos_y;
141 	eventstat_window = subwin(stdscr, event_lines, maxx, pos_y, 0);
142 
143 	pos_y += event_lines + NEXT_LINE;
144 	suggestion_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
145 
146 	pos_y += BLANK_LINE + NEXT_LINE;
147 	status_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
148 
149 	(void) strcpy(g_status_bar_slots[0], _(" Q - Quit "));
150 	(void) strcpy(g_status_bar_slots[1], _(" R - Refresh "));
151 
152 	(void) werase(stdscr);
153 	(void) wrefresh(status_bar_window);
154 }
155 
156 void
157 initialize_curses(void)
158 {
159 	(void) initscr();
160 	(void) start_color();
161 
162 	/*
163 	 * Enable keyboard mapping
164 	 */
165 	(void) keypad(stdscr, TRUE);
166 
167 	/*
168 	 * Tell curses not to do NL->CR/NL on output
169 	 */
170 	(void) nonl();
171 
172 	/*
173 	 * Take input chars one at a time, no wait for \n
174 	 */
175 	(void) cbreak();
176 
177 	/*
178 	 * Dont echo input
179 	 */
180 	(void) noecho();
181 
182 	/*
183 	 * Turn off cursor
184 	 */
185 	(void) curs_set(0);
186 
187 	(void) init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
188 	(void) init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE);
189 	(void) init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED);
190 	(void) init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED);
191 	(void) init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW);
192 	(void) init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN);
193 	(void) init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE);
194 	(void) init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK);
195 
196 	(void) atexit(cleanup_curses);
197 }
198 
199 void
200 show_title_bar(void)
201 {
202 	int 	i, x = 0, y = 0;
203 	char	title_pad[10];
204 
205 	(void) wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
206 	(void) wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
207 	(void) werase(title_bar_window);
208 
209 	(void) snprintf(title_pad, 10, "%%%ds",
210 	    (maxx - strlen(TITLE))/2 + strlen(TITLE));
211 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
212 	print(title_bar_window, y, x, title_pad, TITLE);
213 
214 	(void) wrefresh(title_bar_window);
215 	(void) werase(status_bar_window);
216 
217 	for (i = 0; i < PTOP_BAR_NSLOTS; i++) {
218 		if (strlen(g_status_bar_slots[i]) == 0)
219 			continue;
220 		(void) wattron(status_bar_window, A_REVERSE);
221 		print(status_bar_window, y, x, "%s", g_status_bar_slots[i]);
222 		(void) wattroff(status_bar_window, A_REVERSE);
223 		x += strlen(g_status_bar_slots[i]) + 1;
224 	}
225 	(void) wnoutrefresh(status_bar_window);
226 }
227 
228 void
229 show_cstates(void)
230 {
231 	char		c[100];
232 	int		i;
233 	double		total_pstates = 0.0, avg, res;
234 	uint64_t	p0_speed, p1_speed;
235 
236 	if (!PTOP_ON_DUMP) {
237 		(void) werase(cstate_window);
238 		(void) wattrset(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT));
239 		(void) wbkgd(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT));
240 	}
241 
242 	print(cstate_window, 0, 0, "%s\tAvg residency\n", g_msg_idle_state);
243 	res =  (((double)g_cstate_info[0].total_time / g_total_c_time)) * 100;
244 	(void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res);
245 	print(cstate_window, 1, 0, "%s", c);
246 
247 	for (i = 1; i <= g_max_cstate; i++) {
248 		/*
249 		 * In situations where the load is too intensive, the system
250 		 * might not transition at all.
251 		 */
252 		if (g_cstate_info[i].events > 0)
253 			avg = (((double)g_cstate_info[i].total_time/
254 			    g_ncpus_observed)/g_cstate_info[i].events);
255 		else
256 			avg = 0;
257 
258 		res = ((double)g_cstate_info[i].total_time/g_total_c_time)
259 		    * 100;
260 
261 		(void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n", i, (float)avg,
262 		    (float)res);
263 		print(cstate_window, i + 1, 0, "%s", c);
264 	}
265 
266 	print(cstate_window, 0, 48, "%s", g_msg_freq_state);
267 
268 	if (g_npstates < 2) {
269 		(void) sprintf(c, "%4lu Mhz\t%.1f%%",
270 		    (long)g_pstate_info[0].speed, 100.0);
271 		print(cstate_window, 1, 48, "%s\n", c);
272 	} else {
273 		for (i = 0; i < g_npstates; i++) {
274 			total_pstates += (double)(g_pstate_info[i].total_time/
275 			    g_ncpus_observed/1000000);
276 		}
277 
278 		/*
279 		 * display ACPI_PSTATE from P(n) to P(1)
280 		 */
281 		for (i = 0;  i < g_npstates - 1; i++) {
282 			(void) sprintf(c, "%4lu Mhz\t%.1f%%",
283 			    (long)g_pstate_info[i].speed,
284 			    100 * (g_pstate_info[i].total_time/g_ncpus_observed/
285 			    1000000/total_pstates));
286 			print(cstate_window, i+1, 48, "%s\n", c);
287 		}
288 
289 		/*
290 		 * Display ACPI_PSTATE P0 according to if turbo
291 		 * mode is supported
292 		 */
293 		if (g_turbo_supported) {
294 			p1_speed = g_pstate_info[g_npstates - 2].speed;
295 
296 			/*
297 			 * If g_turbo_ratio <= 1.0, it will be ignored.
298 			 * we display P(0) as P(1) + 1.
299 			 */
300 			if (g_turbo_ratio <= 1.0) {
301 				p0_speed = p1_speed + 1;
302 			} else {
303 				/*
304 				 * If g_turbo_ratio > 1.0, that means turbo
305 				 * mode works. So, P(0) = ratio * P(1);
306 				 */
307 				p0_speed = (uint64_t)(p1_speed * g_turbo_ratio);
308 				if (p0_speed < (p1_speed + 1))
309 				p0_speed = p1_speed + 1;
310 			}
311 			/*
312 			 * Reset the ratio for the next round
313 			 */
314 			g_turbo_ratio = 0.0;
315 
316 			/*
317 			 * Setup the string for the display
318 			 */
319 			(void) sprintf(c, "%4lu Mhz(turbo)\t%.1f%%",
320 			    (long)p0_speed,
321 			    100 * (g_pstate_info[i].total_time/
322 			    g_ncpus_observed/1000000/total_pstates));
323 		} else {
324 			(void) sprintf(c, "%4lu Mhz\t%.1f%%",
325 			    (long)g_pstate_info[i].speed,
326 			    100 * (g_pstate_info[i].total_time/
327 			    g_ncpus_observed/1000000/total_pstates));
328 		}
329 		print(cstate_window, i+1, 48, "%s\n", c);
330 	}
331 
332 	if (!PTOP_ON_DUMP)
333 		(void) wnoutrefresh(cstate_window);
334 }
335 
336 void
337 show_acpi_power_line(uint32_t flag, double rate, double rem_cap, double cap,
338     uint32_t state)
339 {
340 	char	buffer[1024];
341 
342 	(void) sprintf(buffer,  _("no ACPI power usage estimate available"));
343 
344 	if (!PTOP_ON_DUMP)
345 		(void) werase(acpi_power_window);
346 	if (flag) {
347 		char *c;
348 		(void) sprintf(buffer, "Power usage (ACPI estimate): %.3fW",
349 		    rate);
350 		(void) strcat(buffer, " ");
351 		c = &buffer[strlen(buffer)];
352 		switch (state) {
353 		case 0:
354 			(void) sprintf(c, "(running on AC power, fully "
355 			    "charged)");
356 			break;
357 		case 1:
358 			(void) sprintf(c, "(discharging: %3.1f hours)",
359 			    (uint32_t)rem_cap/rate);
360 			break;
361 		case 2:
362 			(void) sprintf(c, "(charging: %3.1f hours)",
363 			    (uint32_t)(cap - rem_cap)/rate);
364 			break;
365 		case 4:
366 			(void) sprintf(c, "(##critically low battery power##)");
367 			break;
368 		}
369 
370 	}
371 	print(acpi_power_window, 0, 0, "%s\n", buffer);
372 	if (!PTOP_ON_DUMP)
373 		(void) wnoutrefresh(acpi_power_window);
374 }
375 
376 void
377 show_wakeups(double interval)
378 {
379 	char		c[100];
380 	int		i, event_sum = 0;
381 	event_info_t	*g_p_event = g_event_info;
382 
383 	if (!PTOP_ON_DUMP) {
384 		(void) werase(wakeup_window);
385 		(void) wbkgd(wakeup_window, COLOR_PAIR(PT_COLOR_RED));
386 		(void) wattron(wakeup_window, A_BOLD);
387 	}
388 
389 	/*
390 	 * calculate the actual total event number
391 	 */
392 	for (i = 0; i < g_tog_p_events; i++, g_p_event++)
393 		event_sum += g_p_event->total_count;
394 
395 	/*
396 	 * g_total_events is the sum of the number of Cx->C0 transition,
397 	 * So when the system is very busy, the idle thread will have no
398 	 * chance or very seldom to be scheduled, this could cause >100%
399 	 * event report. Re-assign g_total_events to the actual event
400 	 * number is a way to avoid this issue.
401 	 */
402 	if (event_sum > g_total_events)
403 		g_total_events = event_sum;
404 
405 	(void) sprintf(c, "Wakeups-from-idle per second: %4.1f\tinterval: "
406 	    "%.1fs", (double)(g_total_events/interval), interval);
407 	print(wakeup_window, 0, 0, "%s\n", c);
408 
409 	if (!PTOP_ON_DUMP)
410 		(void) wnoutrefresh(wakeup_window);
411 }
412 
413 void
414 show_eventstats(double interval)
415 {
416 	char		c[100];
417 	int		i;
418 	double		events;
419 	event_info_t	*g_p_event = g_event_info;
420 
421 	if (!PTOP_ON_DUMP) {
422 		(void) werase(eventstat_window);
423 		(void) wattrset(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT));
424 		(void) wbkgd(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT));
425 	}
426 
427 	/*
428 	 * Sort the event report list
429 	 */
430 	if (g_tog_p_events > EVENT_NUM_MAX)
431 		g_tog_p_events = EVENT_NUM_MAX;
432 
433 	qsort((void *)g_event_info, g_tog_p_events, sizeof (event_info_t),
434 	    event_compare);
435 
436 	if (PTOP_ON_CPU)
437 		(void) sprintf(c, "Top causes for wakeups on CPU %d:\n",
438 		    g_observed_cpu);
439 	else
440 		(void) sprintf(c, "Top causes for wakeups:\n");
441 
442 	print(eventstat_window, 0, 0, "%s", c);
443 
444 	for (i = 0; i < g_tog_p_events; i++, g_p_event++) {
445 
446 		if (g_total_events > 0 && g_p_event->total_count > 0)
447 			events = (double)g_p_event->total_count/
448 			    (double)g_total_events;
449 		else
450 			continue;
451 
452 		(void) sprintf(c, "%4.1f%% (%5.1f)", 100 * events,
453 		    (double)g_p_event->total_count/interval);
454 		print(eventstat_window, i+1, 0, "%s", c);
455 		print(eventstat_window, i+1, 16, "%20s :",
456 		    g_p_event->offender_name);
457 		print(eventstat_window, i+1, 40, "%-64s\n",
458 		    g_p_event->offense_name);
459 	}
460 
461 	if (!PTOP_ON_DUMP)
462 		(void) wnoutrefresh(eventstat_window);
463 }
464 
465 void
466 show_suggestion(char *sug)
467 {
468 	(void) werase(suggestion_window);
469 	print(suggestion_window, 0, 0, "%s", sug);
470 	(void) wnoutrefresh(suggestion_window);
471 }
472 
473 void
474 update_windows(void)
475 {
476 	(void) doupdate();
477 }
478