//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"
/* 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
#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
#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)
}
/* 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;*/
}
/* 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];
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.
}
#endif
-static void NOINLINE print_intel_cstates(void)
+static NOINLINE void print_intel_cstates(void)
{
#ifdef __i386__
int bios_table[8] = { 0 };
/*
* 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;
#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;
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 {
{
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];
#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);
/* 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
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));
}
}
- /* Show title bar */
- print_header();
+ /* Clear the screen */
+ printf("\033[H\033[J");
/* Clear C-state lines */
memset(&cstate_lines, 0, sizeof(cstate_lines));
sprintf(cstate_lines[5], "< Detailed C-state information is not "
"available.>\n");
} else {
- double slept;
double percentage;
double newticks;
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);
/* 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
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();