#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <curses.h>
#include <signal.h>
#include <fcntl.h>
#include "powertop.h"
#define PT_MIN_COLS 70
#define PT_MIN_ROWS 15
#define PT_COLOR_DEFAULT 1
#define PT_COLOR_HEADER_BAR 2
#define PT_COLOR_ERROR 3
#define PT_COLOR_RED 4
#define PT_COLOR_YELLOW 5
#define PT_COLOR_GREEN 6
#define PT_COLOR_BRIGHT 7
#define PT_COLOR_BLUE 8
#define SINGLE_LINE_SW 1
#define LENGTH_SUGG_SW 2
#define TITLE_LINE 1
#define BLANK_LINE 1
#define NEXT_LINE 1
#define print(win, y, x, fmt, args...) \
if (PT_ON_DUMP) \
(void) printf(fmt, ## args); \
else \
(void) mvwprintw(win, y, x, fmt, ## args);
enum pt_subwindows {
SW_TITLE,
SW_IDLE,
SW_FREQ,
SW_WAKEUPS,
SW_POWER,
SW_EVENTS,
SW_SUGG,
SW_STATUS,
SW_COUNT
};
typedef struct sb_slot {
char *msg;
struct sb_slot *prev;
struct sb_slot *next;
} sb_slot_t;
static WINDOW *sw[SW_COUNT];
static int win_cols, win_rows;
static sb_slot_t *status_bar;
static void
pt_display_cleanup(void)
{
int i;
for (i = 0; i < SW_COUNT; i++) {
if (sw[i] != NULL) {
(void) delwin(sw[i]);
sw[i] = NULL;
}
}
(void) endwin();
(void) fflush(stdout);
(void) putchar('\r');
}
static void
pt_display_get_size(void)
{
getmaxyx(stdscr, win_rows, win_cols);
if (win_rows < PT_MIN_ROWS || win_cols < PT_MIN_COLS) {
pt_display_cleanup();
(void) printf("\n\nPowerTOP cannot run in such a small "
"terminal window. Please resize it.\n\n");
exit(EXIT_FAILURE);
}
}
void
pt_display_resize(void)
{
pt_display_cleanup();
(void) pt_display_init_curses();
pt_display_setup(B_TRUE);
pt_display_title_bar();
pt_display_states();
if (g_features & FEATURE_EVENTS) {
pt_display_wakeups(g_interval_length);
pt_display_events(g_interval_length);
}
pt_battery_print();
pt_sugg_pick();
pt_display_status_bar();
pt_display_update();
g_sig_resize = B_FALSE;
(void) signal(SIGWINCH, pt_sig_handler);
}
void
pt_display_setup(boolean_t resized)
{
int cstate_lines, event_lines, pos_y = 0;
cstate_lines = TITLE_LINE + max((g_max_cstate+2), g_npstates);
sw[SW_TITLE] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
pos_y += NEXT_LINE + BLANK_LINE;
sw[SW_IDLE] = subwin(stdscr, cstate_lines, win_cols/2 + 1, pos_y, 0);
sw[SW_FREQ] = subwin(stdscr, cstate_lines, win_cols/2 - 8, pos_y,
win_cols/2 + 8);
pos_y += cstate_lines + BLANK_LINE;
sw[SW_WAKEUPS] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
pos_y += NEXT_LINE;
sw[SW_POWER] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
pos_y += NEXT_LINE + BLANK_LINE;
event_lines = win_rows - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW -
pos_y;
if (event_lines > 0) {
sw[SW_EVENTS] = subwin(stdscr, event_lines, win_cols, pos_y, 0);
} else {
pt_display_cleanup();
(void) printf("\n\nPowerTOP cannot run in such a small "
"terminal window, please resize it.\n\n");
exit(EXIT_FAILURE);
}
pos_y += event_lines + NEXT_LINE;
sw[SW_SUGG] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
pos_y += BLANK_LINE + NEXT_LINE;
sw[SW_STATUS] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
if (!resized) {
status_bar = NULL;
pt_display_mod_status_bar("Q - Quit");
pt_display_mod_status_bar("R - Refresh");
}
}
void
pt_display_init_curses(void)
{
(void) initscr();
(void) atexit(pt_display_cleanup);
pt_display_get_size();
(void) start_color();
(void) keypad(stdscr, TRUE);
(void) nonl();
(void) cbreak();
(void) noecho();
(void) curs_set(0);
(void) init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
(void) init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE);
(void) init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED);
(void) init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED);
(void) init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW);
(void) init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN);
(void) init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE);
(void) init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK);
}
void
pt_display_update(void)
{
(void) doupdate();
}
void
pt_display_title_bar(void)
{
char title_pad[10];
(void) wattrset(sw[SW_TITLE], COLOR_PAIR(PT_COLOR_HEADER_BAR));
(void) wbkgd(sw[SW_TITLE], COLOR_PAIR(PT_COLOR_HEADER_BAR));
(void) werase(sw[SW_TITLE]);
(void) snprintf(title_pad, 10, "%%%ds",
(win_cols - strlen(TITLE))/2 + strlen(TITLE));
print(sw[SW_TITLE], 0, 0, title_pad, TITLE);
(void) wnoutrefresh(sw[SW_TITLE]);
}
void
pt_display_status_bar(void)
{
sb_slot_t *n = status_bar;
int x = 0;
(void) werase(sw[SW_STATUS]);
while (n && x < win_cols) {
(void) wattron(sw[SW_STATUS], A_REVERSE);
print(sw[SW_STATUS], 0, x, "%s", n->msg);
(void) wattroff(sw[SW_STATUS], A_REVERSE);
x += strlen(n->msg) + 1;
n = n->next;
}
(void) wnoutrefresh(sw[SW_STATUS]);
}
void
pt_display_mod_status_bar(char *msg)
{
sb_slot_t *new, *n;
boolean_t found = B_FALSE, first = B_FALSE;
if (msg == NULL) {
pt_error("can't add an empty status bar item\n");
return;
}
if (status_bar != NULL) {
for (n = status_bar; n != NULL; n = n->next) {
if (strcmp(msg, n->msg) == 0) {
if (n != status_bar)
n->prev->next = n->next;
else
first = B_TRUE;
if (n->next != NULL) {
n->next->prev = n->prev;
if (first)
status_bar = n->next;
} else {
if (first)
status_bar = NULL;
}
free(n);
found = B_TRUE;
}
}
if (found) {
return;
} else {
for (n = status_bar; n->next != NULL; n = n->next)
;
}
}
if ((new = calloc(1, sizeof (sb_slot_t))) == NULL) {
pt_error("failed to allocate a new status bar slot\n");
} else {
new->msg = strdup(msg);
if (status_bar == NULL) {
status_bar = new;
new->prev = NULL;
} else {
new->prev = n;
n->next = new;
}
new->next = NULL;
}
}
void
pt_display_states(void)
{
char c[100];
int i;
double total_pstates = 0.0, avg, res;
uint64_t p0_speed, p1_speed;
print(sw[SW_IDLE], 0, 0, "%s\tAvg\tResidency\n", g_msg_idle_state);
if (g_features & FEATURE_CSTATE) {
res = (((double)g_cstate_info[0].total_time / g_total_c_time))
* 100;
(void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res);
print(sw[SW_IDLE], 1, 0, "%s", c);
for (i = 1; i <= g_max_cstate; i++) {
if (g_cstate_info[i].events > 0)
avg = (((double)g_cstate_info[i].total_time/
MICROSEC)/g_cstate_info[i].events);
else
avg = 0;
res = ((double)g_cstate_info[i].total_time/
g_total_c_time) * 100;
(void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n",
i, (float)avg, (float)res);
print(sw[SW_IDLE], i + 1, 0, "%s", c);
}
}
if (!PT_ON_DUMP)
(void) wnoutrefresh(sw[SW_IDLE]);
print(sw[SW_FREQ], 0, 0, "%s\n", g_msg_freq_state);
if (g_features & FEATURE_PSTATE) {
for (i = 0; i < g_npstates; i++) {
total_pstates +=
(double)(g_pstate_info[i].total_time/
g_ncpus_observed/MICROSEC);
}
for (i = 0; i < g_npstates - 1; i++) {
(void) sprintf(c, "%4lu Mhz\t%.1f%%",
(long)g_pstate_info[i].speed,
100 * (g_pstate_info[i].total_time/
g_ncpus_observed/MICROSEC/total_pstates));
print(sw[SW_FREQ], i+1, 0, "%s\n", c);
}
if (g_turbo_supported) {
int p_diff = 1;
p0_speed = g_pstate_info[g_npstates - 1].speed;
p1_speed = g_pstate_info[g_npstates - 2].speed;
if (p0_speed != p1_speed + 1) {
p1_speed = p0_speed;
p_diff = 0;
}
if (g_turbo_ratio <= 1.0) {
p0_speed = p1_speed + p_diff;
} else {
p0_speed = (uint64_t)(p1_speed *
g_turbo_ratio);
if (p0_speed < (p1_speed + p_diff))
p0_speed = p1_speed + p_diff;
}
g_turbo_ratio = 0.0;
(void) sprintf(c, "%4lu Mhz(turbo)\t%.1f%%",
(long)p0_speed,
100 * (g_pstate_info[i].total_time/
g_ncpus_observed/MICROSEC/total_pstates));
} else {
(void) sprintf(c, "%4lu Mhz\t%.1f%%",
(long)g_pstate_info[i].speed,
100 * (g_pstate_info[i].total_time/
g_ncpus_observed/MICROSEC/total_pstates));
}
print(sw[SW_FREQ], i+1, 0, "%s\n", c);
} else {
if (g_npstates == 1) {
(void) sprintf(c, "%4lu Mhz\t%.1f%%",
(long)g_pstate_info[0].speed, 100.0);
print(sw[SW_FREQ], 1, 0, "%s\n", c);
}
}
if (!PT_ON_DUMP)
(void) wnoutrefresh(sw[SW_FREQ]);
}
void
pt_display_acpi_power(uint32_t flag, double rate, double rem_cap, double cap,
uint32_t state)
{
char buffer[1024];
(void) sprintf(buffer, "no ACPI power usage estimate available");
if (!PT_ON_DUMP)
(void) werase(sw[SW_POWER]);
if (flag) {
char *c;
(void) sprintf(buffer, "Power usage (ACPI estimate): %.3fW",
rate);
(void) strcat(buffer, " ");
c = &buffer[strlen(buffer)];
switch (state) {
case 0:
(void) sprintf(c, "(running on AC power, fully "
"charged)");
break;
case 1:
(void) sprintf(c, "(discharging: %3.1f hours)",
(uint32_t)rem_cap/rate);
break;
case 2:
(void) sprintf(c, "(charging: %3.1f hours)",
(uint32_t)(cap - rem_cap)/rate);
break;
case 4:
(void) sprintf(c, "(##critically low battery power##)");
break;
}
}
print(sw[SW_POWER], 0, 0, "%s\n", buffer);
if (!PT_ON_DUMP)
(void) wnoutrefresh(sw[SW_POWER]);
}
void
pt_display_wakeups(double interval)
{
char c[100];
int i, event_sum = 0;
event_info_t *event = g_event_info;
if (!PT_ON_DUMP) {
(void) werase(sw[SW_WAKEUPS]);
(void) wbkgd(sw[SW_WAKEUPS], COLOR_PAIR(PT_COLOR_RED));
(void) wattron(sw[SW_WAKEUPS], A_BOLD);
}
for (i = 0; i < g_top_events; i++, event++)
event_sum += event->total_count;
if (event_sum > g_total_events)
g_total_events = event_sum;
(void) sprintf(c, "Wakeups-from-idle per second: %4.1f\tinterval: "
"%.1fs", (double)(g_total_events/interval), interval);
print(sw[SW_WAKEUPS], 0, 0, "%s\n", c);
if (!PT_ON_DUMP)
(void) wnoutrefresh(sw[SW_WAKEUPS]);
}
void
pt_display_events(double interval)
{
char c[100];
int i;
double events;
event_info_t *event = g_event_info;
if (!PT_ON_DUMP) {
(void) werase(sw[SW_EVENTS]);
(void) wbkgd(sw[SW_EVENTS], COLOR_PAIR(PT_COLOR_DEFAULT));
(void) wattron(sw[SW_EVENTS], COLOR_PAIR(PT_COLOR_DEFAULT));
}
if (g_top_events > EVENT_NUM_MAX)
g_top_events = EVENT_NUM_MAX;
qsort((void *)g_event_info, g_top_events, sizeof (event_info_t),
pt_event_compare);
if (PT_ON_CPU)
(void) sprintf(c, "Top causes for wakeups on CPU %d:\n",
g_observed_cpu);
else
(void) sprintf(c, "Top causes for wakeups:\n");
print(sw[SW_EVENTS], 0, 0, "%s", c);
for (i = 0; i < g_top_events; i++, event++) {
if (g_total_events > 0 && event->total_count > 0)
events = (double)event->total_count/
(double)g_total_events;
else
continue;
(void) sprintf(c, "%4.1f%% (%5.1f)", 100 * events,
(double)event->total_count/interval);
print(sw[SW_EVENTS], i+1, 0, "%s", c);
print(sw[SW_EVENTS], i+1, 16, "%20s :",
event->offender_name);
print(sw[SW_EVENTS], i+1, 40, "%-64s\n",
event->offense_name);
}
if (!PT_ON_DUMP)
(void) wnoutrefresh(sw[SW_EVENTS]);
}
void
pt_display_suggestions(char *sug)
{
(void) werase(sw[SW_SUGG]);
if (sug != NULL)
print(sw[SW_SUGG], 0, 0, "%s", sug);
(void) wnoutrefresh(sw[SW_SUGG]);
}