powertop: fixes to output format and code shrink

function                                             old     new   delta
process_timer_stats                                    -     631    +631
clear_lines                                           72      74      +2
process_irq_counts                                   729     726      -3
.rodata                                           145699  145530    -169
powertop_main                                       2341    1510    -831
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 1/2 up/down: 1359/-1729)       Total: -370 bytes

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
Denys Vlasenko 2010-10-26 12:39:36 +02:00
parent 373789e567
commit c3f1fa10d6

View File

@ -19,7 +19,7 @@
//config: help //config: help
//config: Analyze power consumption on Intel-based laptops //config: Analyze power consumption on Intel-based laptops
// XXX This should de configurable // XXX This should be configurable
#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1 #define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
#include "libbb.h" #include "libbb.h"
@ -62,12 +62,11 @@ struct irqdata {
#endif #endif
struct globals { struct globals {
struct line *lines; /* the most often used member */
int lines_cnt; int lines_cnt;
int lines_cumulative_count; int lines_cumulative_count;
int linesize;
int maxcstate; int maxcstate;
unsigned total_cpus; unsigned total_cpus;
struct line *lines;
smallint cant_enable_timer_stats; smallint cant_enable_timer_stats;
#if ENABLE_FEATURE_POWERTOP_PROCIRQ #if ENABLE_FEATURE_POWERTOP_PROCIRQ
# if BLOATY_HPET_IRQ_NUM_DETECTION # if BLOATY_HPET_IRQ_NUM_DETECTION
@ -120,15 +119,16 @@ static int write_str_to_file(const char *fname, const char *str)
#define start_timer() write_str_to_file("/proc/timer_stats", "1\n") #define start_timer() write_str_to_file("/proc/timer_stats", "1\n")
#define stop_timer() write_str_to_file("/proc/timer_stats", "0\n") #define stop_timer() write_str_to_file("/proc/timer_stats", "0\n")
static void NOINLINE clear_lines(void) static NOINLINE void clear_lines(void)
{ {
int i; int i;
if (G.lines) {
for (i = 0; i < G.lines_cnt; i++) for (i = 0; i < G.lines_cnt; i++)
free(G.lines[i].string); free(G.lines[i].string);
free(G.lines); free(G.lines);
G.lines_cnt = 0; G.lines_cnt = 0;
G.linesize = 0;
G.lines = NULL; G.lines = NULL;
}
} }
static void update_lines_cumulative_count(void) static void update_lines_cumulative_count(void)
@ -220,7 +220,7 @@ static void save_line(const char *string, int count)
} }
/* Add new line */ /* Add new line */
G.lines = xrealloc_vector(G.lines, 1, G.lines_cnt); G.lines = xrealloc_vector(G.lines, 4, G.lines_cnt);
G.lines[G.lines_cnt].string = xstrdup(string); G.lines[G.lines_cnt].string = xstrdup(string);
G.lines[G.lines_cnt].count = count; G.lines[G.lines_cnt].count = count;
/*G.lines[G.lines_cnt].disk_count = 0;*/ /*G.lines[G.lines_cnt].disk_count = 0;*/
@ -298,7 +298,7 @@ static int save_irq_count(int irq, ullong count)
} }
/* Read /proc/interrupts, save IRQ counts and IRQ description */ /* Read /proc/interrupts, save IRQ counts and IRQ description */
static void process_irq_count_deltas(void) static void process_irq_counts(void)
{ {
FILE *fp; FILE *fp;
char buf[128]; char buf[128];
@ -399,9 +399,121 @@ static void process_irq_count_deltas(void)
fclose(fp); fclose(fp);
} }
#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */ #else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
# define process_irq_count_deltas() ((void)0) # define process_irq_counts() ((void)0)
#endif #endif
static NOINLINE int process_timer_stats(void)
{
char buf[128];
char line[15 + 3 + 128];
int n;
ullong totalticks;
FILE *fp;
buf[0] = '\0';
totalticks = 0;
fp = NULL;
if (!G.cant_enable_timer_stats)
fp = fopen_for_read("/proc/timer_stats");
if (fp) {
// Example file contents:
// Timer Stats Version: v0.2
// Sample period: 1.329 s
// 76, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
// 88, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
// 24, 3787 firefox hrtimer_start_range_ns (hrtimer_wakeup)
// 46D, 1136 kondemand/1 do_dbs_timer (delayed_work_timer_fn)
// ...
// 1, 1656 Xorg hrtimer_start_range_ns (hrtimer_wakeup)
// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup)
// 331 total events, 249.059 events/sec
while (fgets(buf, sizeof(buf), fp)) {
const char *count, *process, *func;
char *p;
int cnt;
count = skip_whitespace(buf);
p = strchr(count, ',');
if (!p)
continue;
*p++ = '\0';
if (strcmp(strchrnul(count, ' '), " total events") == 0)
break;
p = skip_whitespace(p); /* points to pid */
/* Find char ' ', then eat remaining spaces */
#define ADVANCE(p) do { \
(p) = strchr((p), ' '); \
if (!(p)) \
continue; \
*(p) = '\0'; \
(p)++; \
(p) = skip_whitespace(p); \
} while (0)
/* Get process name */
ADVANCE(p);
process = p;
/* Get function */
ADVANCE(p);
func = p;
#undef ADVANCE
//if (strcmp(process, "swapper") == 0
// && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
//) {
// process = "[kernel scheduler]";
// func = "Load balancing tick";
//}
if (strncmp(func, "tick_nohz_", 10) == 0)
continue;
if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
continue;
//if (strcmp(process, "powertop") == 0)
// continue;
if (strcmp(process, "insmod") == 0)
process = "[kernel module]";
if (strcmp(process, "modprobe") == 0)
process = "[kernel module]";
if (strcmp(process, "swapper") == 0)
process = "<kernel core>";
strchrnul(p, '\n')[0] = '\0';
{
char *tmp;
cnt = bb_strtoull(count, &tmp, 10);
p = tmp;
}
while (*p != '\0') {
if (*p++ == 'D') /* deferred */
goto skip;
}
//if (strchr(process, '['))
sprintf(line, "%15.15s : %s", process, func);
//else
// sprintf(line, "%s", process);
save_line(line, cnt);
skip: ;
}
fclose(fp);
}
n = 0;
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
if (strstr(buf, "total events")) {
n = bb_strtoull(buf, NULL, 10) / G.total_cpus;
if (n > 0 && n < G.interrupt_0) {
sprintf(line, " <interrupt> : %s", "extra timer interrupt");
save_line(line, G.interrupt_0 - n);
}
}
#endif
return n;
}
#ifdef __i386__ #ifdef __i386__
/* /*
* Get information about CPU using CPUID opcode. * Get information about CPU using CPUID opcode.
@ -428,7 +540,7 @@ static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
} }
#endif #endif
static void NOINLINE print_intel_cstates(void) static NOINLINE void print_intel_cstates(void)
{ {
#ifdef __i386__ #ifdef __i386__
int bios_table[8] = { 0 }; int bios_table[8] = { 0 };
@ -462,7 +574,7 @@ static void NOINLINE print_intel_cstates(void)
/* /*
* Every C-state has its own stateN directory, that * Every C-state has its own stateN directory, that
* contains a `time' and a `usage' file. * contains a 'time' and a 'usage' file.
*/ */
while ((d = readdir(dir)) != NULL) { while ((d = readdir(dir)) != NULL) {
FILE *fp; FILE *fp;
@ -525,25 +637,6 @@ static void NOINLINE print_intel_cstates(void)
#endif #endif
} }
static void print_header(void)
{
printf(
/* Clear the screen */
"\033[H\033[J"
/* Print the header */
"\033[7m%.*s\033[0m", 79, "PowerTOP (C) 2007 Intel Corporation\n"
);
}
static void show_cstates(char cstate_lines[][64])
{
int i;
for (i = 0; i < 10; i++)
if ((cstate_lines[i][0]))
printf("%s", cstate_lines[i]);
}
static void show_timerstats(void) static void show_timerstats(void)
{ {
unsigned lines; unsigned lines;
@ -556,18 +649,25 @@ static void show_timerstats(void)
if (!G.cant_enable_timer_stats) { if (!G.cant_enable_timer_stats) {
int i, n = 0; int i, n = 0;
char strbuf6[6];
strbuf6[5] = '\0';
puts("\nTop causes for wakeups:"); puts("\nTop causes for wakeups:");
for (i = 0; i < G.lines_cnt; i++) { for (i = 0; i < G.lines_cnt; i++) {
if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/) if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
&& n++ < lines && n++ < lines
) { ) {
char c = ' '; /* NB: upstream powertop prints "(wakeups/sec)",
/*if (G.lines[i].disk_count) * we print just "(wakeup counts)".
*/
/*char c = ' ';
if (G.lines[i].disk_count)
c = 'D';*/ c = 'D';*/
printf(" %5.1f%% (%5.1f)%c %s\n", smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY");
printf(/*" %5.1f%% (%s)%c %s\n"*/
" %5.1f%% (%s) %s\n",
G.lines[i].count * 100.0 / G.lines_cumulative_count, G.lines[i].count * 100.0 / G.lines_cumulative_count,
G.lines[i].count * 1.0 / DEFAULT_SLEEP, c, strbuf6, /*c,*/
G.lines[i].string); G.lines[i].string);
} }
} }
@ -606,8 +706,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
{ {
ullong cur_usage[MAX_CSTATE_COUNT]; ullong cur_usage[MAX_CSTATE_COUNT];
ullong cur_duration[MAX_CSTATE_COUNT]; ullong cur_duration[MAX_CSTATE_COUNT];
char cstate_lines[12][64]; char cstate_lines[MAX_CSTATE_COUNT + 2][64];
char buf[128];
#if ENABLE_FEATURE_USE_TERMIOS #if ENABLE_FEATURE_USE_TERMIOS
struct termios new_settings; struct termios new_settings;
struct pollfd pfd[1]; struct pollfd pfd[1];
@ -644,7 +743,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
#endif #endif
/* Collect initial data */ /* Collect initial data */
process_irq_count_deltas(); process_irq_counts();
/* Read initial usage and duration */ /* Read initial usage and duration */
read_cstate_counts(G.start_usage, G.start_duration); read_cstate_counts(G.start_usage, G.start_duration);
@ -660,10 +759,9 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
/* The main loop */ /* The main loop */
for (;;) { for (;;) {
/*double maxsleep = 0.0;*/ //double maxsleep = 0.0;
ullong totalticks, totalevents; ullong totalticks, totalevents;
int i; int i;
FILE *fp;
G.cant_enable_timer_stats |= start_timer(); /* 1 on error */ G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
#if !ENABLE_FEATURE_USE_TERMIOS #if !ENABLE_FEATURE_USE_TERMIOS
@ -682,7 +780,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */ G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
clear_lines(); clear_lines();
process_irq_count_deltas(); process_irq_counts();
/* Clear the stats */ /* Clear the stats */
memset(cur_duration, 0, sizeof(cur_duration)); memset(cur_duration, 0, sizeof(cur_duration));
@ -700,8 +798,8 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
} }
} }
/* Show title bar */ /* Clear the screen */
print_header(); printf("\033[H\033[J");
/* Clear C-state lines */ /* Clear C-state lines */
memset(&cstate_lines, 0, sizeof(cstate_lines)); memset(&cstate_lines, 0, sizeof(cstate_lines));
@ -711,7 +809,6 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
sprintf(cstate_lines[5], "< Detailed C-state information is not " sprintf(cstate_lines[5], "< Detailed C-state information is not "
"available.>\n"); "available.>\n");
} else { } else {
double slept;
double percentage; double percentage;
double newticks; double newticks;
@ -721,7 +818,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
if (newticks < 0) if (newticks < 0)
newticks = 0; newticks = 0;
sprintf(cstate_lines[0], "Cn\t Avg residency\n"); sprintf(cstate_lines[0], "Cn\t\t Avg residency\n");
percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000); percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
sprintf(cstate_lines[1], "C0 (cpu running) (%4.1f%%)\n", sprintf(cstate_lines[1], "C0 (cpu running) (%4.1f%%)\n",
percentage); percentage);
@ -729,6 +826,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
/* Compute values for individual C-states */ /* Compute values for individual C-states */
for (i = 0; i < MAX_CSTATE_COUNT; i++) { for (i = 0; i < MAX_CSTATE_COUNT; i++) {
if (cur_usage[i] != 0) { if (cur_usage[i] != 0) {
double slept;
slept = (cur_duration[i] - G.last_duration[i]) slept = (cur_duration[i] - G.last_duration[i])
/ (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI; / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
percentage = (cur_duration[i] - G.last_duration[i]) * 100 percentage = (cur_duration[i] - G.last_duration[i]) * 100
@ -736,125 +834,35 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
if (!G.cstate_names[i][0]) if (!G.cstate_names[i][0])
sprintf(G.cstate_names[i], "C%u", i + 1); sprintf(G.cstate_names[i], "C%u", i + 1);
sprintf(cstate_lines[i + 2], "%s\t%5.1fms (%4.1f%%)\n", sprintf(cstate_lines[i + 2], "%s\t\t%5.1fms (%4.1f%%)\n",
G.cstate_names[i], slept, percentage); G.cstate_names[i], slept, percentage);
/*if (maxsleep < slept) //if (maxsleep < slept)
maxsleep = slept;*/ // maxsleep = slept;
} }
} }
} }
/* Display C-states */ for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
show_cstates(cstate_lines); if (cstate_lines[i][0])
printf("%s", cstate_lines[i]);
/* Do timer_stats info */
buf[0] = '\0';
totalticks = 0;
fp = NULL;
if (!G.cant_enable_timer_stats)
fp = fopen_for_read("/proc/timer_stats");
if (fp) {
// Examlpe file contents:
// Timer Stats Version: v0.2
// Sample period: 1.329 s
// 76, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
// 88, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
// 24, 3787 firefox hrtimer_start_range_ns (hrtimer_wakeup)
// 46D, 1136 kondemand/1 do_dbs_timer (delayed_work_timer_fn)
// ...
// 1, 1656 Xorg hrtimer_start_range_ns (hrtimer_wakeup)
// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup)
// 331 total events, 249.059 events/sec
while (fgets(buf, sizeof(buf), fp)) {
const char *count, *process, *func;
char *p;
char line[512];
int cnt = 0;
// TODO: optimize
if (strstr(buf, "total events"))
break;
count = skip_whitespace(buf);
p = strchr(count, ',');
if (!p)
continue;
*p++ = '\0';
p = skip_whitespace(p); /* points to pid */
/* Find char ' ', then eat remaining spaces */
#define ADVANCE(p) do { \
(p) = strchr((p), ' '); \
if (!(p)) \
continue; \
*(p) = '\0'; \
(p)++; \
(p) = skip_whitespace(p); \
} while (0)
/* Get process name */
ADVANCE(p);
process = p;
/* Get function */
ADVANCE(p);
func = p;
if (strcmp(process, "swapper") == 0
&& strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
) {
process = "[kernel scheduler]";
func = "Load balancing tick";
}
if (strcmp(process, "insmod") == 0)
process = "[kernel module]";
if (strcmp(process, "modprobe") == 0)
process = "[kernel module]";
if (strcmp(process, "swapper") == 0)
process = "[kernel core]";
if (strncmp(func, "tick_nohz_", 10) == 0)
continue;
if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
continue;
if (strcmp(process, "powertop") == 0)
continue;
strchrnul(p, '\n')[0] = '\0';
cnt = bb_strtoull(count, &p, 10);
while (*p != '\0') {
if (*p++ == 'D')
goto skip;
}
if (strchr(process, '['))
sprintf(line, "%s %s", process, func);
else
sprintf(line, "%s", process);
save_line(line, cnt);
skip: ;
}
fclose(fp);
}
i = process_timer_stats();
#if ENABLE_FEATURE_POWERTOP_PROCIRQ #if ENABLE_FEATURE_POWERTOP_PROCIRQ
if (strstr(buf, "total events")) {
int n = bb_strtoull(buf, NULL, 10) / G.total_cpus;
if (totalevents == 0) { if (totalevents == 0) {
/* No C-state info available, use timerstats */ /* No C-state info available, use timerstats */
totalevents = n * G.total_cpus + G.total_interrupt; totalevents = i * G.total_cpus + G.total_interrupt;
if (n < 0) if (i < 0)
totalevents += G.interrupt_0 - n; totalevents += G.interrupt_0 - i;
}
if (n > 0 && n < G.interrupt_0)
save_line("[extra timer interrupt]", G.interrupt_0 - n);
} }
#endif #endif
if (totalevents != 0) /* Upstream powertop prints wakeups per sec per CPU,
printf("\n\033[1mWakeups-from-idle per second : %4.1f\tinterval:" * we print just raw wakeup counts.
"%ds\n\033[0m", */
(double)totalevents / DEFAULT_SLEEP / G.total_cpus, DEFAULT_SLEEP); //TODO: show real seconds (think about manual refresh)
printf("\nWakeups-from-idle in %u seconds: %llu\n",
DEFAULT_SLEEP,
totalevents
);
update_lines_cumulative_count(); update_lines_cumulative_count();
sort_lines(); sort_lines();