*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config POWERTOP
+//config: bool "powertop (9.1 kb)"
+//config: default y
+//config: help
+//config: Analyze power consumption on Intel-based laptops
+//config:
+//config:config FEATURE_POWERTOP_INTERACTIVE
+//config: bool "Accept keyboard commands"
+//config: default y
+//config: depends on POWERTOP
+//config: help
+//config: Without this, powertop will only refresh display every 10 seconds.
+//config: No keyboard commands will work, only ^C to terminate.
-//applet:IF_POWERTOP(APPLET(powertop, _BB_DIR_BIN, _BB_SUID_DROP))
+//applet:IF_POWERTOP(APPLET(powertop, BB_DIR_USR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
-//config:config POWERTOP
-//config: bool "powertop"
-//config: default y
-//config: help
-//config: Analyze power consumption on Intel-based laptops
+// XXX This should be configurable
+#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
#include "libbb.h"
+
//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
#define debug(fmt, ...) ((void)0)
-// XXX This should not be here
-#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
+
+#define BLOATY_HPET_IRQ_NUM_DETECTION 0
+#define MAX_CSTATE_COUNT 8
+#define IRQCOUNT 40
+
#define DEFAULT_SLEEP 10
#define DEFAULT_SLEEP_STR "10"
/* 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
+#define ESC "\033"
+
typedef unsigned long long ullong;
struct line {
char *string;
int count;
- int disk_count;
+ /*int disk_count;*/
};
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
-#define IRQCOUNT 40
-
struct irqdata {
- int active;
+ smallint active;
int number;
ullong count;
char irq_desc[32];
#endif
struct globals {
- bool timer_list_read;
- smallint nostats;
- int headline;
- int nlines;
- int linesize;
+ struct line *lines; /* the most often used member */
+ int lines_cnt;
+ int lines_cumulative_count;
int maxcstate;
+ unsigned total_cpus;
+ smallint cant_enable_timer_stats;
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
- int total_interrupt;
- int interrupt_0;
+# if BLOATY_HPET_IRQ_NUM_DETECTION
+ smallint scanned_timer_list;
int percpu_hpet_start;
int percpu_hpet_end;
+# endif
+ int interrupt_0;
+ int total_interrupt;
struct irqdata interrupts[IRQCOUNT];
#endif
- unsigned total_cpus;
- ullong start_usage[8];
- ullong last_usage[8];
- ullong start_duration[8];
- ullong last_duration[8];
- char cstate_names[8][16];
- struct line *lines;
-#if ENABLE_FEATURE_USE_TERMIOS
+ ullong start_usage[MAX_CSTATE_COUNT];
+ ullong last_usage[MAX_CSTATE_COUNT];
+ ullong start_duration[MAX_CSTATE_COUNT];
+ ullong last_duration[MAX_CSTATE_COUNT];
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
struct termios init_settings;
#endif
};
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)
-#if ENABLE_FEATURE_USE_TERMIOS
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
static void reset_term(void)
{
tcsetattr_stdin_TCSANOW(&G.init_settings);
static void sig_handler(int signo UNUSED_PARAM)
{
reset_term();
- exit(EXIT_FAILURE);
+ _exit(EXIT_FAILURE);
}
#endif
}
/* Make it more readable */
-#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 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.headline; i++)
- free(G.lines[i].string);
- free(G.lines);
- G.headline = 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 count_lines(void)
+static void update_lines_cumulative_count(void)
{
int i;
-
- for (i = 0; i < G.headline; i++)
- G.nlines += G.lines[i].count;
+ for (i = 0; i < G.lines_cnt; i++)
+ G.lines_cumulative_count += G.lines[i].count;
}
static int line_compare(const void *p1, const void *p2)
{
const struct line *a = p1;
const struct line *b = p2;
-
- return (b->count + 50 * b->disk_count) - (a->count + 50 * a->disk_count);
+ return (b->count /*+ 50 * b->disk_count*/) - (a->count /*+ 50 * a->disk_count*/);
}
-static void do_sort(void)
+static void sort_lines(void)
{
- qsort(G.lines, G.headline, sizeof(struct line), line_compare);
+ qsort(G.lines, G.lines_cnt, sizeof(G.lines[0]), line_compare);
}
-/*
- * Save C-state names, usage and duration. Also get maxcstate.
- * Reads data from /proc.
- */
-static void read_data(ullong *usage, ullong *duration)
+/* Save C-state usage and duration. Also update maxcstate. */
+static void read_cstate_counts(ullong *usage, ullong *duration)
{
DIR *dir;
struct dirent *d;
while ((d = readdir(dir)) != NULL) {
FILE *fp;
char buf[192];
- int level = 0;
+ int level;
int len;
- len = strlen(d->d_name);
+ len = strlen(d->d_name); /* "CPUnn" */
if (len < 3 || len > BIG_SYSNAME_LEN)
continue;
- sprintf(buf, "/proc/acpi/processor/%s/power", d->d_name);
+ sprintf(buf, "%s/%s/power", "/proc/acpi/processor", d->d_name);
fp = fopen_for_read(buf);
if (!fp)
continue;
+// Example file contents:
+// active state: C0
+// max_cstate: C8
+// maximum allowed latency: 2000000000 usec
+// states:
+// C1: type[C1] promotion[--] demotion[--] latency[001] usage[00006173] duration[00000000000000000000]
+// C2: type[C2] promotion[--] demotion[--] latency[001] usage[00085191] duration[00000000000083024907]
+// C3: type[C3] promotion[--] demotion[--] latency[017] usage[01017622] duration[00000000017921327182]
+ level = 0;
while (fgets(buf, sizeof(buf), fp)) {
- char *p;
-
- /* Get usage */
- p = strstr(buf, "age[");
+ char *p = strstr(buf, "age[");
if (!p)
continue;
p += 4;
usage[level] += bb_strtoull(p, NULL, 10) + 1;
-
- /* Get duration */
p = strstr(buf, "ation[");
if (!p)
continue;
p += 6;
duration[level] += bb_strtoull(p, NULL, 10);
- /* Increment level */
+ if (level >= MAX_CSTATE_COUNT-1)
+ break;
level++;
-
- /* Also update maxcstate */
- if (level > G.maxcstate)
+ if (level > G.maxcstate) /* update maxcstate */
G.maxcstate = level;
}
fclose(fp);
}
/* Add line and/or update count */
-static void push_line(const char *string, int count)
+static void save_line(const char *string, int count)
{
int i;
-
- if (!string)
- return;
-
- /* Loop through entries */
- for (i = 0; i < G.headline; i++) {
+ for (i = 0; i < G.lines_cnt; i++) {
if (strcmp(string, G.lines[i].string) == 0) {
/* It's already there, only update count */
G.lines[i].count += count;
}
}
- G.lines = xrealloc_vector(G.lines, 1, G.headline);
-
- G.lines[G.headline].string = xstrdup(string);
- G.lines[G.headline].count = count;
- G.lines[G.headline].disk_count = 0;
-
- /* We added a line */
- G.headline++;
+ /* Add new line */
+ 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;*/
+ G.lines_cnt++;
}
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
-static int percpu_hpet_timer(const char *name)
+static int is_hpet_irq(const char *name)
{
char *p;
+# if BLOATY_HPET_IRQ_NUM_DETECTION
long hpet_chan;
- /* This is done once */
- if (!G.timer_list_read) {
+ /* Learn the range of existing hpet timers. This is done once */
+ if (!G.scanned_timer_list) {
FILE *fp;
char buf[80];
- G.timer_list_read = true;
+ G.scanned_timer_list = true;
fp = fopen_for_read("/proc/timer_list");
if (!fp)
return 0;
if (!p)
continue;
p += sizeof("Clock Event Device: hpet")-1;
- if (!isdigit(p[0]))
+ if (!isdigit(*p))
continue;
hpet_chan = xatoi_positive(p);
if (hpet_chan < G.percpu_hpet_start)
}
fclose(fp);
}
-
+# endif
+//TODO: optimize
p = strstr(name, "hpet");
if (!p)
return 0;
-
p += 4;
- if (!isdigit(p[0]))
+ if (!isdigit(*p))
return 0;
-
+# if BLOATY_HPET_IRQ_NUM_DETECTION
hpet_chan = xatoi_positive(p);
- if (G.percpu_hpet_start <= hpet_chan && hpet_chan <= G.percpu_hpet_end)
- return 1;
-
- return 0;
+ if (hpet_chan < G.percpu_hpet_start || hpet_chan > G.percpu_hpet_end)
+ return 0;
+# endif
+ return 1;
}
-static int update_irq(int irq, ullong count)
+/* Save new IRQ count, return delta from old one */
+static int save_irq_count(int irq, ullong count)
{
int unused = IRQCOUNT;
int i;
-
for (i = 0; i < IRQCOUNT; i++) {
if (G.interrupts[i].active && G.interrupts[i].number == irq) {
- ullong old;
- old = G.interrupts[i].count;
+ ullong old = G.interrupts[i].count;
G.interrupts[i].count = count;
return count - old;
}
if (!G.interrupts[i].active && unused > i)
unused = i;
}
-
- G.interrupts[unused].active = 1;
- G.interrupts[unused].count = count;
- G.interrupts[unused].number = irq;
-
+ if (unused < IRQCOUNT) {
+ G.interrupts[unused].active = 1;
+ G.interrupts[unused].count = count;
+ G.interrupts[unused].number = irq;
+ }
return count;
}
-/*
- * Read /proc/interrupts, save IRQ counts and IRQ description.
- */
-static void do_proc_irq(void)
+/* Read /proc/interrupts, save IRQ counts and IRQ description */
+static void process_irq_counts(void)
{
FILE *fp;
char buf[128];
char irq_desc[sizeof(" <kernel IPI> : ") + sizeof(buf)];
char *p;
const char *name;
- int nr = -1;
+ int nr;
ullong count;
ullong delta;
- int special;
- /* Skip header */
p = strchr(buf, ':');
if (!p)
continue;
/* 0: 143646045 153901007 IO-APIC-edge timer
* ^
*/
+ *p = '\0';
/* Deal with non-maskable interrupts -- make up fake numbers */
- special = 0;
- if (buf[0] != ' ' && !isdigit(buf[0])) {
- if (strncmp(buf, "NMI:", 4) == 0)
- nr = 20000;
- if (strncmp(buf, "RES:", 4) == 0)
- nr = 20001;
- if (strncmp(buf, "CAL:", 4) == 0)
- nr = 20002;
- if (strncmp(buf, "TLB:", 4) == 0)
- nr = 20003;
- if (strncmp(buf, "TRM:", 4) == 0)
- nr = 20004;
- if (strncmp(buf, "THR:", 4) == 0)
- nr = 20005;
- if (strncmp(buf, "SPU:", 4) == 0)
- nr = 20006;
- special = 1;
+ nr = index_in_strings("NMI\0RES\0CAL\0TLB\0TRM\0THR\0SPU\0", buf);
+ if (nr >= 0) {
+ nr += 20000;
} else {
- /* bb_strtou don't eat leading spaces, using strtoul */
- nr = strtoul(buf, NULL, 10); /* xato*() wouldn't work */
+ /* bb_strtou doesn't eat leading spaces, using strtoul */
+ errno = 0;
+ nr = strtoul(buf, NULL, 10);
+ if (errno)
+ continue;
}
- if (nr == -1)
- continue;
-
p++;
/* 0: 143646045 153901007 IO-APIC-edge timer
* ^
*/
- /* Count sum of the IRQs */
+ /* Sum counts for this IRQ */
count = 0;
while (1) {
char *tmp;
}
/* 0: 143646045 153901007 IO-APIC-edge timer
* NMI: 1 2 Non-maskable interrupts
- * ^
+ * ^
*/
- if (!special) {
+ if (nr < 20000) {
/* Skip to the interrupt name, e.g. 'timer' */
p = strchr(p, ' ');
if (!p)
}
name = p;
- strchrnul(name, '\n')[0] = '\0';
+ chomp(p);
/* Save description of the interrupt */
- if (special)
+ if (nr >= 20000)
sprintf(irq_desc, " <kernel IPI> : %s", name);
else
sprintf(irq_desc, " <interrupt> : %s", name);
- delta = update_irq(nr, count);
+ delta = save_irq_count(nr, count);
/* Skip per CPU timer interrupts */
- if (percpu_hpet_timer(name))
- delta = 0;
- if (nr > 0 && delta > 0)
- push_line(irq_desc, delta);
- if (!nr)
+ if (is_hpet_irq(name))
+ continue;
+
+ if (nr != 0 && delta != 0)
+ save_line(irq_desc, delta);
+
+ if (nr == 0)
G.interrupt_0 = delta;
else
G.total_interrupt += delta;
fclose(fp);
}
-#endif /* ENABLE_FEATURE_POWERTOP_PROCIRQ */
+#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
+# define process_irq_counts() ((void)0)
+#endif
+
+static NOINLINE int process_timer_stats(void)
+{
+ char buf[128];
+ char line[15 + 3 + 128];
+ int n;
+ FILE *fp;
+
+ buf[0] = '\0';
+
+ n = 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 idx;
+ unsigned cnt;
+
+ count = skip_whitespace(buf);
+ p = strchr(count, ',');
+ if (!p)
+ continue;
+ *p++ = '\0';
+ cnt = bb_strtou(count, NULL, 10);
+ if (strcmp(skip_non_whitespace(count), " total events") == 0) {
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+ n = cnt / 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
+ break;
+ }
+ if (strchr(count, 'D'))
+ continue; /* deferred */
+ p = skip_whitespace(p); /* points to pid now */
+ process = NULL;
+ get_func_name:
+ p = strchr(p, ' ');
+ if (!p)
+ continue;
+ *p++ = '\0';
+ p = skip_whitespace(p);
+ if (process == NULL) {
+ process = p;
+ goto get_func_name;
+ }
+ 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 (is_prefixed_with(func, "tick_nohz_"))
+ continue;
+ if (is_prefixed_with(func, "tick_setup_sched_timer"))
+ continue;
+ //if (strcmp(process, "powertop") == 0)
+ // continue;
+
+ idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
+ if (idx != -1) {
+ process = idx < 2 ? "[kernel module]" : "<kernel core>";
+ }
+
+ chomp(p);
+
+ // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
+ // ^ ^ ^
+ // count process func
+
+ //if (strchr(process, '['))
+ sprintf(line, "%15.15s : %s", process, func);
+ //else
+ // sprintf(line, "%s", process);
+ save_line(line, cnt);
+ }
+ fclose(fp);
+ }
+
+ return n;
+}
#ifdef __i386__
/*
* Get information about CPU using CPUID opcode.
*/
static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
- unsigned int *edx)
+ unsigned int *edx)
{
/* EAX value specifies what information to return */
__asm__(
}
#endif
-static void NOINLINE print_intel_cstates(void)
-{
#ifdef __i386__
+static NOINLINE void print_intel_cstates(void)
+{
int bios_table[8] = { 0 };
int nbios = 0;
DIR *cpudir;
if (!isdigit(d->d_name[3]))
continue;
- len = sprintf(fname, "/sys/devices/system/cpu/%s/cpuidle", d->d_name);
+ len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
dir = opendir(fname);
if (!dir)
continue;
/*
* 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;
if (!edx || !(ecx & 1))
return;
- printf("Your CPU supports the following C-states: ");
+ printf("Your %s the following C-states: ", "CPU supports");
i = 0;
while (edx) {
if (edx & 7)
bb_putchar('\n');
/* Print BIOS C-States */
- printf("Your BIOS reports the following C-states: ");
- for (i = 0; i < 8; i++)
+ printf("Your %s the following C-states: ", "BIOS reports");
+ for (i = 0; i < ARRAY_SIZE(bios_table); i++)
if (bios_table[i])
printf("C%u ", i);
bb_putchar('\n');
-#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]);
}
+#else
+# define print_intel_cstates() ((void)0)
+#endif
-static void show_timerstats(int nostats)
+static void show_timerstats(void)
{
unsigned lines;
/* We don't have whole terminal just for timerstats */
lines -= 12;
- if (!nostats) {
+ if (!G.cant_enable_timer_stats) {
int i, n = 0;
+ char strbuf6[6];
puts("\nTop causes for wakeups:");
- for (i = 0; i < G.headline; i++) {
- if ((G.lines[i].count > 0 || G.lines[i].disk_count > 0)
+ for (i = 0; i < G.lines_cnt; i++) {
+ if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
&& n++ < lines
) {
- char c = ' ';
+ /* 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.nlines,
- G.lines[i].count * 1.0 / DEFAULT_SLEEP, c,
- G.lines[i].string);
+ c = 'D';*/
+ smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY")[0] = '\0';
+ 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 {
bb_putchar('\n');
bb_error_msg("no stats available; run as root or"
- " enable the cpufreq_stats module");
+ " enable the timer_stats module");
}
}
+// Example display from powertop version 1.11
+// Cn Avg residency P-states (frequencies)
+// C0 (cpu running) ( 0.5%) 2.00 Ghz 0.0%
+// polling 0.0ms ( 0.0%) 1.67 Ghz 0.0%
+// C1 mwait 0.0ms ( 0.0%) 1333 Mhz 0.1%
+// C2 mwait 0.1ms ( 0.1%) 1000 Mhz 99.9%
+// C3 mwait 12.1ms (99.4%)
+//
+// Wakeups-from-idle per second : 93.6 interval: 15.0s
+// no ACPI power usage estimate available
+//
+// Top causes for wakeups:
+// 32.4% ( 26.7) <interrupt> : extra timer interrupt
+// 29.0% ( 23.9) <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
+// 9.0% ( 7.5) <kernel core> : hrtimer_start (tick_sched_timer)
+// 6.5% ( 5.3) <interrupt> : ata_piix
+// 5.0% ( 4.1) inetd : hrtimer_start_range_ns (hrtimer_wakeup)
+
//usage:#define powertop_trivial_usage
//usage: ""
//usage:#define powertop_full_usage "\n\n"
-//usage: "Analyze power consumption on Intel-based laptops\n"
+//usage: "Analyze power consumption on Intel-based laptops"
int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
+int powertop_main(int argc UNUSED_PARAM, char UNUSED_PARAM **argv)
{
- ullong cur_usage[8];
- ullong cur_duration[8];
- char cstate_lines[12][64];
- char buf[128];
-#if ENABLE_FEATURE_USE_TERMIOS
- struct termios new_settings;
+ ullong cur_usage[MAX_CSTATE_COUNT];
+ ullong cur_duration[MAX_CSTATE_COUNT];
+ char cstate_lines[MAX_CSTATE_COUNT + 2][64];
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
struct pollfd pfd[1];
pfd[0].fd = 0;
INIT_G();
-#if ENABLE_FEATURE_POWERTOP_PROCIRQ
+#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
G.percpu_hpet_start = INT_MAX;
G.percpu_hpet_end = INT_MIN;
#endif
if (geteuid() != 0)
bb_error_msg("run as root to collect enough information");
-#if ENABLE_FEATURE_USE_TERMIOS
- /* So we don't forget to reset term settings */
- atexit(reset_term);
-#endif
-
/* Get number of CPUs */
G.total_cpus = get_cpu_count();
- printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
-
-#if ENABLE_FEATURE_USE_TERMIOS
- tcgetattr(0, (void *)&G.init_settings);
- memcpy(&new_settings, &G.init_settings, sizeof(new_settings));
-
- /* Turn on unbuffered input, turn off echoing */
- new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
+ puts("Collecting data for "DEFAULT_SLEEP_STR" seconds");
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
+ /* Turn on unbuffered input; turn off echoing, ^C ^Z etc */
+ set_termios_to_raw(STDIN_FILENO, &G.init_settings, TERMIOS_CLEAR_ISIG);
bb_signals(BB_FATAL_SIGS, sig_handler);
- tcsetattr_stdin_TCSANOW(&new_settings);
+ /* So we don't forget to reset term settings */
+ die_func = reset_term;
#endif
-#if ENABLE_FEATURE_POWERTOP_PROCIRQ
/* Collect initial data */
- do_proc_irq();
- do_proc_irq();
-#endif
+ process_irq_counts();
/* Read initial usage and duration */
- read_data(&G.start_usage[0], &G.start_duration[0]);
+ read_cstate_counts(G.start_usage, G.start_duration);
/* Copy them to "last" */
memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
/* Display C-states */
print_intel_cstates();
- if (stop_timer())
- G.nostats = 1;
+ G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
/* The main loop */
for (;;) {
- double maxsleep = 0.0;
+ //double maxsleep = 0.0;
ullong totalticks, totalevents;
int i;
- FILE *fp;
- double newticks;
- if (start_timer())
- G.nostats = 1;
-
-#if !ENABLE_FEATURE_USE_TERMIOS
+ G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
+#if !ENABLE_FEATURE_POWERTOP_INTERACTIVE
sleep(DEFAULT_SLEEP);
#else
if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
break;
}
#endif
-
- if (stop_timer())
- G.nostats = 1;
+ G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
clear_lines();
-#if ENABLE_FEATURE_POWERTOP_PROCIRQ
- do_proc_irq();
-#endif
+ process_irq_counts();
/* Clear the stats */
memset(cur_duration, 0, sizeof(cur_duration));
memset(cur_usage, 0, sizeof(cur_usage));
/* Read them */
- read_data(&cur_usage[0], &cur_duration[0]);
-
- totalticks = totalevents = 0;
+ read_cstate_counts(cur_usage, cur_duration);
/* Count totalticks and totalevents */
- for (i = 0; i < 8; i++) {
- if (cur_usage[i]) {
+ totalticks = totalevents = 0;
+ for (i = 0; i < MAX_CSTATE_COUNT; i++) {
+ if (cur_usage[i] != 0) {
totalticks += cur_duration[i] - G.last_duration[i];
totalevents += cur_usage[i] - G.last_usage[i];
}
}
- /* Show title bar */
- print_header();
+ /* Home; clear screen */
+ printf(ESC"[H" ESC"[J");
/* Clear C-state lines */
memset(&cstate_lines, 0, sizeof(cstate_lines));
if (totalevents == 0 && G.maxcstate <= 1) {
/* This should not happen */
- sprintf(cstate_lines[5], "< Detailed C-state information is not "
- "available.>\n");
+ strcpy(cstate_lines[0], "C-state information is not available\n");
} else {
- double slept;
double percentage;
+ unsigned newticks;
newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
-
/* Handle rounding errors: do not display negative values */
- if (newticks < 0)
+ if ((int)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);
+ sprintf(cstate_lines[1], "C0 (cpu running) (%4.1f%%)\n", percentage);
/* Compute values for individual C-states */
- for (i = 0; i < 8; i++) {
- if (cur_usage[i]) {
+ 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
/ (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
-
- 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",
- G.cstate_names[i], slept, percentage);
- if (maxsleep < slept)
- maxsleep = slept;
+ sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
+ i + 1, slept, percentage);
+ //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.nostats)
- fp = fopen_for_read("/proc/timer_stats");
- if (fp) {
- while (fgets(buf, sizeof(buf), fp)) {
- const char *count, *process, *func;
- char line[512];
- int cnt = 0;
- bool defferable = false;
- char *p;
- int j = 0;
-
-/* 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)
-
- if (strstr(buf, "total events"))
- break;
-
- while (isspace(buf[j]))
- j++;
-
- count = &buf[j];
- p = (char *)count;
-
- /* Skip PID */
- p = strchr(p, ',');
- if (!p)
- continue;
- *p = '\0';
- p++;
-
- p = skip_whitespace(p);
-
- /* Get process */
- 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]";
-
- p = strchr(p, '\n');
-
- 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 (p)
- *p = '\0';
-
- cnt = bb_strtoull(count, &p, 10);
- while (*p != 0) {
- if (*p++ == 'D')
- defferable = true;
- }
- if (defferable)
- continue;
-
- if (strchr(process, '['))
- sprintf(line, "%s %s", process, func);
- else
- sprintf(line, "%s", process);
- push_line(line, cnt);
- }
- fclose(fp);
- }
+ for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
+ if (cstate_lines[i][0])
+ fputs(cstate_lines[i], stdout);
+ 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)
- push_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)
- printf("\n\033[1mWakeups-from-idle per second : %4.1f\tinterval:"
- "%ds\n\033[0m",
- (double)totalevents / DEFAULT_SLEEP / G.total_cpus, DEFAULT_SLEEP);
-
- count_lines();
- do_sort();
-
- show_timerstats(G.nostats);
-
+ /* 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();
+ show_timerstats();
fflush(stdout);
/* Clear the stats */
memset(cur_usage, 0, sizeof(cur_usage));
/* Get new values */
- read_data(&cur_usage[0], &cur_duration[0]);
+ read_cstate_counts(cur_usage, cur_duration);
/* Save them */
memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
} /* for (;;) */
bb_putchar('\n');
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
+ reset_term();
+#endif
return EXIT_SUCCESS;
}