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:
parent
373789e567
commit
c3f1fa10d6
@ -19,7 +19,7 @@
|
||||
//config: help
|
||||
//config: Analyze power consumption on Intel-based laptops
|
||||
|
||||
// XXX This should de configurable
|
||||
// XXX This should be configurable
|
||||
#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
|
||||
|
||||
#include "libbb.h"
|
||||
@ -39,7 +39,7 @@
|
||||
|
||||
/* Frequency of the ACPI timer */
|
||||
#define FREQ_ACPI 3579.545
|
||||
#define FREQ_ACPI_1000 3579545
|
||||
#define FREQ_ACPI_1000 3579545
|
||||
|
||||
/* Max filename length of entry in /sys/devices subsystem */
|
||||
#define BIG_SYSNAME_LEN 16
|
||||
@ -62,12 +62,11 @@ struct irqdata {
|
||||
#endif
|
||||
|
||||
struct globals {
|
||||
struct line *lines; /* the most often used member */
|
||||
int lines_cnt;
|
||||
int lines_cumulative_count;
|
||||
int linesize;
|
||||
int maxcstate;
|
||||
unsigned total_cpus;
|
||||
struct line *lines;
|
||||
smallint cant_enable_timer_stats;
|
||||
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
|
||||
# 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 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;
|
||||
for (i = 0; i < G.lines_cnt; i++)
|
||||
free(G.lines[i].string);
|
||||
free(G.lines);
|
||||
G.lines_cnt = 0;
|
||||
G.linesize = 0;
|
||||
G.lines = NULL;
|
||||
if (G.lines) {
|
||||
for (i = 0; i < G.lines_cnt; i++)
|
||||
free(G.lines[i].string);
|
||||
free(G.lines);
|
||||
G.lines_cnt = 0;
|
||||
G.lines = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_lines_cumulative_count(void)
|
||||
@ -220,7 +220,7 @@ static void save_line(const char *string, int count)
|
||||
}
|
||||
|
||||
/* 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].count = count;
|
||||
/*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 */
|
||||
static void process_irq_count_deltas(void)
|
||||
static void process_irq_counts(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[128];
|
||||
@ -399,9 +399,121 @@ static void process_irq_count_deltas(void)
|
||||
fclose(fp);
|
||||
}
|
||||
#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
|
||||
# define process_irq_count_deltas() ((void)0)
|
||||
# define process_irq_counts() ((void)0)
|
||||
#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__
|
||||
/*
|
||||
* Get information about CPU using CPUID opcode.
|
||||
@ -428,7 +540,7 @@ static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void NOINLINE print_intel_cstates(void)
|
||||
static NOINLINE void print_intel_cstates(void)
|
||||
{
|
||||
#ifdef __i386__
|
||||
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
|
||||
* contains a `time' and a `usage' file.
|
||||
* contains a 'time' and a 'usage' file.
|
||||
*/
|
||||
while ((d = readdir(dir)) != NULL) {
|
||||
FILE *fp;
|
||||
@ -525,25 +637,6 @@ static void NOINLINE print_intel_cstates(void)
|
||||
#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)
|
||||
{
|
||||
unsigned lines;
|
||||
@ -556,19 +649,26 @@ static void show_timerstats(void)
|
||||
|
||||
if (!G.cant_enable_timer_stats) {
|
||||
int i, n = 0;
|
||||
char strbuf6[6];
|
||||
|
||||
strbuf6[5] = '\0';
|
||||
puts("\nTop causes for wakeups:");
|
||||
for (i = 0; i < G.lines_cnt; i++) {
|
||||
if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
|
||||
&& n++ < lines
|
||||
) {
|
||||
char c = ' ';
|
||||
/*if (G.lines[i].disk_count)
|
||||
/* NB: upstream powertop prints "(wakeups/sec)",
|
||||
* we print just "(wakeup counts)".
|
||||
*/
|
||||
/*char c = ' ';
|
||||
if (G.lines[i].disk_count)
|
||||
c = 'D';*/
|
||||
printf(" %5.1f%% (%5.1f)%c %s\n",
|
||||
G.lines[i].count * 100.0 / G.lines_cumulative_count,
|
||||
G.lines[i].count * 1.0 / DEFAULT_SLEEP, c,
|
||||
G.lines[i].string);
|
||||
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,
|
||||
strbuf6, /*c,*/
|
||||
G.lines[i].string);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -606,8 +706,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
|
||||
{
|
||||
ullong cur_usage[MAX_CSTATE_COUNT];
|
||||
ullong cur_duration[MAX_CSTATE_COUNT];
|
||||
char cstate_lines[12][64];
|
||||
char buf[128];
|
||||
char cstate_lines[MAX_CSTATE_COUNT + 2][64];
|
||||
#if ENABLE_FEATURE_USE_TERMIOS
|
||||
struct termios new_settings;
|
||||
struct pollfd pfd[1];
|
||||
@ -644,7 +743,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
|
||||
#endif
|
||||
|
||||
/* Collect initial data */
|
||||
process_irq_count_deltas();
|
||||
process_irq_counts();
|
||||
|
||||
/* Read initial usage and 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 */
|
||||
for (;;) {
|
||||
/*double maxsleep = 0.0;*/
|
||||
//double maxsleep = 0.0;
|
||||
ullong totalticks, totalevents;
|
||||
int i;
|
||||
FILE *fp;
|
||||
|
||||
G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
|
||||
#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 */
|
||||
|
||||
clear_lines();
|
||||
process_irq_count_deltas();
|
||||
process_irq_counts();
|
||||
|
||||
/* Clear the stats */
|
||||
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 */
|
||||
print_header();
|
||||
/* Clear the screen */
|
||||
printf("\033[H\033[J");
|
||||
|
||||
/* Clear C-state 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 "
|
||||
"available.>\n");
|
||||
} else {
|
||||
double slept;
|
||||
double percentage;
|
||||
double newticks;
|
||||
|
||||
@ -721,7 +818,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
|
||||
if (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);
|
||||
sprintf(cstate_lines[1], "C0 (cpu running) (%4.1f%%)\n",
|
||||
percentage);
|
||||
@ -729,6 +826,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
|
||||
/* Compute values for individual C-states */
|
||||
for (i = 0; i < MAX_CSTATE_COUNT; i++) {
|
||||
if (cur_usage[i] != 0) {
|
||||
double slept;
|
||||
slept = (cur_duration[i] - G.last_duration[i])
|
||||
/ (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
|
||||
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])
|
||||
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);
|
||||
/*if (maxsleep < slept)
|
||||
maxsleep = slept;*/
|
||||
//if (maxsleep < slept)
|
||||
// maxsleep = slept;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Display C-states */
|
||||
show_cstates(cstate_lines);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
|
||||
if (cstate_lines[i][0])
|
||||
printf("%s", cstate_lines[i]);
|
||||
|
||||
i = process_timer_stats();
|
||||
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
|
||||
if (strstr(buf, "total events")) {
|
||||
int n = bb_strtoull(buf, NULL, 10) / G.total_cpus;
|
||||
|
||||
if (totalevents == 0) {
|
||||
/* No C-state info available, use timerstats */
|
||||
totalevents = n * G.total_cpus + G.total_interrupt;
|
||||
if (n < 0)
|
||||
totalevents += G.interrupt_0 - n;
|
||||
}
|
||||
if (n > 0 && n < G.interrupt_0)
|
||||
save_line("[extra timer interrupt]", G.interrupt_0 - n);
|
||||
if (totalevents == 0) {
|
||||
/* No C-state info available, use timerstats */
|
||||
totalevents = i * G.total_cpus + G.total_interrupt;
|
||||
if (i < 0)
|
||||
totalevents += G.interrupt_0 - i;
|
||||
}
|
||||
#endif
|
||||
if (totalevents != 0)
|
||||
printf("\n\033[1mWakeups-from-idle per second : %4.1f\tinterval:"
|
||||
"%ds\n\033[0m",
|
||||
(double)totalevents / DEFAULT_SLEEP / G.total_cpus, DEFAULT_SLEEP);
|
||||
/* Upstream powertop prints wakeups per sec per CPU,
|
||||
* we print just raw wakeup counts.
|
||||
*/
|
||||
//TODO: show real seconds (think about manual refresh)
|
||||
printf("\nWakeups-from-idle in %u seconds: %llu\n",
|
||||
DEFAULT_SLEEP,
|
||||
totalevents
|
||||
);
|
||||
|
||||
update_lines_cumulative_count();
|
||||
sort_lines();
|
||||
|
Loading…
Reference in New Issue
Block a user