2 * Copyright 2009, Intel Corporation
3 * Copyright 2009, Sun Microsystems, Inc
5 * This file is part of PowerTOP
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.
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
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
23 * Arjan van de Ven <arjan@linux.intel.com>
24 * Eric C Saxe <eric.saxe@sun.com>
25 * Aubrey Li <aubrey.li@intel.com>
29 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
35 * For the avoidance of doubt, except that if any license choice other
36 * than GPL or LGPL is available it will apply instead, Sun elects to
37 * use only the General Public License version 2 (GPLv2) at this time
38 * for any software where a choice of GPL license versions is made
39 * available with the language indicating that GPLv2 or any later
40 * version may be used, or where a choice of which version of the GPL
41 * is applied is otherwise unspecified.
53 * Minimum terminal height and width to run PowerTOP on curses mode.
55 #define PT_MIN_COLS 70
56 #define PT_MIN_ROWS 15
61 #define PT_COLOR_DEFAULT 1
62 #define PT_COLOR_HEADER_BAR 2
63 #define PT_COLOR_ERROR 3
64 #define PT_COLOR_RED 4
65 #define PT_COLOR_YELLOW 5
66 #define PT_COLOR_GREEN 6
67 #define PT_COLOR_BRIGHT 7
68 #define PT_COLOR_BLUE 8
71 * Constants for pt_display_setup()
73 #define SINGLE_LINE_SW 1
74 #define LENGTH_SUGG_SW 2
79 #define print(win, y, x, fmt, args...) \
81 (void) printf(fmt, ## args); \
83 (void) mvwprintw(win, y, x, fmt, ## args);
97 typedef struct sb_slot
{
100 struct sb_slot
*next
;
103 static WINDOW
*sw
[SW_COUNT
];
104 static int win_cols
, win_rows
;
105 static sb_slot_t
*status_bar
;
108 * Delete all subwindows and reset the terminal to a non-visual mode. This
109 * routine is used during resize events and before exiting.
112 pt_display_cleanup(void)
116 for (i
= 0; i
< SW_COUNT
; i
++) {
118 (void) delwin(sw
[i
]);
124 (void) fflush(stdout
);
125 (void) putchar('\r');
129 pt_display_get_size(void)
131 getmaxyx(stdscr
, win_rows
, win_cols
);
133 if (win_rows
< PT_MIN_ROWS
|| win_cols
< PT_MIN_COLS
) {
134 pt_display_cleanup();
135 (void) printf("\n\nPowerTOP cannot run in such a small "
136 "terminal window. Please resize it.\n\n");
142 pt_display_resize(void)
144 pt_display_cleanup();
145 (void) pt_display_init_curses();
146 pt_display_setup(B_TRUE
);
148 pt_display_title_bar();
152 if (g_features
& FEATURE_EVENTS
) {
153 pt_display_wakeups(g_interval_length
);
154 pt_display_events(g_interval_length
);
159 pt_display_status_bar();
163 g_sig_resize
= B_FALSE
;
164 (void) signal(SIGWINCH
, pt_sig_handler
);
168 * This part was re-written to be human readable and easy to modify. Please
169 * try to keep it that way and help us save some time.
172 * subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x)
175 pt_display_setup(boolean_t resized
)
178 * These variables are used to properly set the initial y position and
179 * number of lines in each subwindow, as the number of supported CPU
180 * states affects their placement.
182 int cstate_lines
, event_lines
, pos_y
= 0;
185 * In theory, all systems have at least two idle states. We add two here
186 * since we have to use DTrace to figure out how many this box has.
188 cstate_lines
= TITLE_LINE
+ max((g_max_cstate
+2), g_npstates
);
190 sw
[SW_TITLE
] = subwin(stdscr
, SINGLE_LINE_SW
, win_cols
, pos_y
, 0);
192 pos_y
+= NEXT_LINE
+ BLANK_LINE
;
193 sw
[SW_IDLE
] = subwin(stdscr
, cstate_lines
, win_cols
/2 + 1, pos_y
, 0);
194 sw
[SW_FREQ
] = subwin(stdscr
, cstate_lines
, win_cols
/2 - 8, pos_y
,
197 pos_y
+= cstate_lines
+ BLANK_LINE
;
198 sw
[SW_WAKEUPS
] = subwin(stdscr
, SINGLE_LINE_SW
, win_cols
, pos_y
, 0);
201 sw
[SW_POWER
] = subwin(stdscr
, SINGLE_LINE_SW
, win_cols
, pos_y
, 0);
203 pos_y
+= NEXT_LINE
+ BLANK_LINE
;
204 event_lines
= win_rows
- SINGLE_LINE_SW
- NEXT_LINE
- LENGTH_SUGG_SW
-
207 if (event_lines
> 0) {
208 sw
[SW_EVENTS
] = subwin(stdscr
, event_lines
, win_cols
, pos_y
, 0);
210 pt_display_cleanup();
211 (void) printf("\n\nPowerTOP cannot run in such a small "
212 "terminal window, please resize it.\n\n");
216 pos_y
+= event_lines
+ NEXT_LINE
;
217 sw
[SW_SUGG
] = subwin(stdscr
, SINGLE_LINE_SW
, win_cols
, pos_y
, 0);
219 pos_y
+= BLANK_LINE
+ NEXT_LINE
;
220 sw
[SW_STATUS
] = subwin(stdscr
, SINGLE_LINE_SW
, win_cols
, pos_y
, 0);
225 pt_display_mod_status_bar("Q - Quit");
226 pt_display_mod_status_bar("R - Refresh");
231 * This routine handles all the necessary curses initialization.
234 pt_display_init_curses(void)
238 (void) atexit(pt_display_cleanup
);
240 pt_display_get_size();
242 (void) start_color();
245 * Enable keyboard mapping
247 (void) keypad(stdscr
, TRUE
);
250 * Tell curses not to do NL->CR/NL on output
255 * Take input chars one at a time, no wait for \n
269 (void) init_pair(PT_COLOR_DEFAULT
, COLOR_WHITE
, COLOR_BLACK
);
270 (void) init_pair(PT_COLOR_HEADER_BAR
, COLOR_BLACK
, COLOR_WHITE
);
271 (void) init_pair(PT_COLOR_ERROR
, COLOR_BLACK
, COLOR_RED
);
272 (void) init_pair(PT_COLOR_RED
, COLOR_WHITE
, COLOR_RED
);
273 (void) init_pair(PT_COLOR_YELLOW
, COLOR_WHITE
, COLOR_YELLOW
);
274 (void) init_pair(PT_COLOR_GREEN
, COLOR_WHITE
, COLOR_GREEN
);
275 (void) init_pair(PT_COLOR_BLUE
, COLOR_WHITE
, COLOR_BLUE
);
276 (void) init_pair(PT_COLOR_BRIGHT
, COLOR_WHITE
, COLOR_BLACK
);
280 pt_display_update(void)
286 pt_display_title_bar(void)
290 (void) wattrset(sw
[SW_TITLE
], COLOR_PAIR(PT_COLOR_HEADER_BAR
));
291 (void) wbkgd(sw
[SW_TITLE
], COLOR_PAIR(PT_COLOR_HEADER_BAR
));
292 (void) werase(sw
[SW_TITLE
]);
294 (void) snprintf(title_pad
, 10, "%%%ds",
295 (win_cols
- strlen(TITLE
))/2 + strlen(TITLE
));
297 /* LINTED: E_SEC_PRINTF_VAR_FMT */
298 print(sw
[SW_TITLE
], 0, 0, title_pad
, TITLE
);
300 (void) wnoutrefresh(sw
[SW_TITLE
]);
304 pt_display_status_bar(void)
306 sb_slot_t
*n
= status_bar
;
309 (void) werase(sw
[SW_STATUS
]);
311 while (n
&& x
< win_cols
) {
312 (void) wattron(sw
[SW_STATUS
], A_REVERSE
);
313 print(sw
[SW_STATUS
], 0, x
, "%s", n
->msg
);
314 (void) wattroff(sw
[SW_STATUS
], A_REVERSE
);
315 x
+= strlen(n
->msg
) + 1;
320 (void) wnoutrefresh(sw
[SW_STATUS
]);
324 * Adds or removes items to the status bar automatically.
325 * Only one instance of an item allowed.
328 pt_display_mod_status_bar(char *msg
)
331 boolean_t found
= B_FALSE
, first
= B_FALSE
;
334 pt_error("can't add an empty status bar item\n");
338 if (status_bar
!= NULL
) {
340 * Non-empty status bar. Look for an entry matching this msg.
342 for (n
= status_bar
; n
!= NULL
; n
= n
->next
) {
344 if (strcmp(msg
, n
->msg
) == 0) {
346 n
->prev
->next
= n
->next
;
350 if (n
->next
!= NULL
) {
351 n
->next
->prev
= n
->prev
;
353 status_bar
= n
->next
;
365 * Found and removed at least one occurrance of msg, refresh
366 * the bar and return.
372 * Inserting a new msg, walk to the end of the bar.
374 for (n
= status_bar
; n
->next
!= NULL
; n
= n
->next
)
379 if ((new = calloc(1, sizeof (sb_slot_t
))) == NULL
) {
380 pt_error("failed to allocate a new status bar slot\n");
382 new->msg
= strdup(msg
);
385 * Check if it's the first entry.
387 if (status_bar
== NULL
) {
399 pt_display_states(void)
403 double total_pstates
= 0.0, avg
, res
;
404 uint64_t p0_speed
, p1_speed
;
406 print(sw
[SW_IDLE
], 0, 0, "%s\tAvg\tResidency\n", g_msg_idle_state
);
408 if (g_features
& FEATURE_CSTATE
) {
409 res
= (((double)g_cstate_info
[0].total_time
/ g_total_c_time
))
411 (void) sprintf(c
, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res
);
412 print(sw
[SW_IDLE
], 1, 0, "%s", c
);
414 for (i
= 1; i
<= g_max_cstate
; i
++) {
416 * In situations where the load is too intensive, the
417 * system might not transition at all.
419 if (g_cstate_info
[i
].events
> 0)
420 avg
= (((double)g_cstate_info
[i
].total_time
/
421 MICROSEC
)/g_cstate_info
[i
].events
);
425 res
= ((double)g_cstate_info
[i
].total_time
/
426 g_total_c_time
) * 100;
428 (void) sprintf(c
, "C%d\t\t\t%.1fms\t(%.1f%%)\n",
429 i
, (float)avg
, (float)res
);
430 print(sw
[SW_IDLE
], i
+ 1, 0, "%s", c
);
435 (void) wnoutrefresh(sw
[SW_IDLE
]);
437 print(sw
[SW_FREQ
], 0, 0, "%s\n", g_msg_freq_state
);
439 if (g_features
& FEATURE_PSTATE
) {
440 for (i
= 0; i
< g_npstates
; i
++) {
442 (double)(g_pstate_info
[i
].total_time
/
443 g_ncpus_observed
/MICROSEC
);
447 * display ACPI_PSTATE from P(n) to P(1)
449 for (i
= 0; i
< g_npstates
- 1; i
++) {
450 (void) sprintf(c
, "%4lu Mhz\t%.1f%%",
451 (long)g_pstate_info
[i
].speed
,
452 100 * (g_pstate_info
[i
].total_time
/
453 g_ncpus_observed
/MICROSEC
/total_pstates
));
454 print(sw
[SW_FREQ
], i
+1, 0, "%s\n", c
);
458 * Display ACPI_PSTATE P0 according to if turbo
461 if (g_turbo_supported
) {
463 p0_speed
= g_pstate_info
[g_npstates
- 1].speed
;
464 p1_speed
= g_pstate_info
[g_npstates
- 2].speed
;
467 * AMD systems don't have a visible extra Pstate
468 * indicating turbo mode as Intel does. Use the
469 * actual P0 frequency in that case.
471 if (p0_speed
!= p1_speed
+ 1) {
477 * If g_turbo_ratio <= 1.0, it will be ignored.
478 * we display P(0) as P(1) + p_diff.
480 if (g_turbo_ratio
<= 1.0) {
481 p0_speed
= p1_speed
+ p_diff
;
484 * If g_turbo_ratio > 1.0, that means
485 * turbo mode works. So, P(0) = ratio *
488 p0_speed
= (uint64_t)(p1_speed
*
490 if (p0_speed
< (p1_speed
+ p_diff
))
491 p0_speed
= p1_speed
+ p_diff
;
494 * Reset the ratio for the next round
499 * Setup the string for the display
501 (void) sprintf(c
, "%4lu Mhz(turbo)\t%.1f%%",
503 100 * (g_pstate_info
[i
].total_time
/
504 g_ncpus_observed
/MICROSEC
/total_pstates
));
506 (void) sprintf(c
, "%4lu Mhz\t%.1f%%",
507 (long)g_pstate_info
[i
].speed
,
508 100 * (g_pstate_info
[i
].total_time
/
509 g_ncpus_observed
/MICROSEC
/total_pstates
));
511 print(sw
[SW_FREQ
], i
+1, 0, "%s\n", c
);
513 if (g_npstates
== 1) {
514 (void) sprintf(c
, "%4lu Mhz\t%.1f%%",
515 (long)g_pstate_info
[0].speed
, 100.0);
516 print(sw
[SW_FREQ
], 1, 0, "%s\n", c
);
521 (void) wnoutrefresh(sw
[SW_FREQ
]);
525 pt_display_acpi_power(uint32_t flag
, double rate
, double rem_cap
, double cap
,
530 (void) sprintf(buffer
, "no ACPI power usage estimate available");
533 (void) werase(sw
[SW_POWER
]);
537 (void) sprintf(buffer
, "Power usage (ACPI estimate): %.3fW",
539 (void) strcat(buffer
, " ");
540 c
= &buffer
[strlen(buffer
)];
543 (void) sprintf(c
, "(running on AC power, fully "
547 (void) sprintf(c
, "(discharging: %3.1f hours)",
548 (uint32_t)rem_cap
/rate
);
551 (void) sprintf(c
, "(charging: %3.1f hours)",
552 (uint32_t)(cap
- rem_cap
)/rate
);
555 (void) sprintf(c
, "(##critically low battery power##)");
561 print(sw
[SW_POWER
], 0, 0, "%s\n", buffer
);
563 (void) wnoutrefresh(sw
[SW_POWER
]);
567 pt_display_wakeups(double interval
)
570 int i
, event_sum
= 0;
571 event_info_t
*event
= g_event_info
;
574 (void) werase(sw
[SW_WAKEUPS
]);
575 (void) wbkgd(sw
[SW_WAKEUPS
], COLOR_PAIR(PT_COLOR_RED
));
576 (void) wattron(sw
[SW_WAKEUPS
], A_BOLD
);
580 * calculate the actual total event number
582 for (i
= 0; i
< g_top_events
; i
++, event
++)
583 event_sum
+= event
->total_count
;
586 * g_total_events is the sum of the number of Cx->C0 transition,
587 * So when the system is very busy, the idle thread will have no
588 * chance or very seldom to be scheduled, this could cause >100%
589 * event report. Re-assign g_total_events to the actual event
590 * number is a way to avoid this issue.
592 if (event_sum
> g_total_events
)
593 g_total_events
= event_sum
;
595 (void) sprintf(c
, "Wakeups-from-idle per second: %4.1f\tinterval: "
596 "%.1fs", (double)(g_total_events
/interval
), interval
);
597 print(sw
[SW_WAKEUPS
], 0, 0, "%s\n", c
);
600 (void) wnoutrefresh(sw
[SW_WAKEUPS
]);
604 pt_display_events(double interval
)
609 event_info_t
*event
= g_event_info
;
612 (void) werase(sw
[SW_EVENTS
]);
613 (void) wbkgd(sw
[SW_EVENTS
], COLOR_PAIR(PT_COLOR_DEFAULT
));
614 (void) wattron(sw
[SW_EVENTS
], COLOR_PAIR(PT_COLOR_DEFAULT
));
618 * Sort the event report list
620 if (g_top_events
> EVENT_NUM_MAX
)
621 g_top_events
= EVENT_NUM_MAX
;
623 qsort((void *)g_event_info
, g_top_events
, sizeof (event_info_t
),
627 (void) sprintf(c
, "Top causes for wakeups on CPU %d:\n",
630 (void) sprintf(c
, "Top causes for wakeups:\n");
632 print(sw
[SW_EVENTS
], 0, 0, "%s", c
);
634 for (i
= 0; i
< g_top_events
; i
++, event
++) {
636 if (g_total_events
> 0 && event
->total_count
> 0)
637 events
= (double)event
->total_count
/
638 (double)g_total_events
;
642 (void) sprintf(c
, "%4.1f%% (%5.1f)", 100 * events
,
643 (double)event
->total_count
/interval
);
644 print(sw
[SW_EVENTS
], i
+1, 0, "%s", c
);
645 print(sw
[SW_EVENTS
], i
+1, 16, "%20s :",
646 event
->offender_name
);
647 print(sw
[SW_EVENTS
], i
+1, 40, "%-64s\n",
648 event
->offense_name
);
652 (void) wnoutrefresh(sw
[SW_EVENTS
]);
656 pt_display_suggestions(char *sug
)
658 (void) werase(sw
[SW_SUGG
]);
661 print(sw
[SW_SUGG
], 0, 0, "%s", sug
);
663 (void) wnoutrefresh(sw
[SW_SUGG
]);