*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-
-//applet:IF_POWERTOP(APPLET(powertop, _BB_DIR_BIN, _BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
-
//config:config POWERTOP
-//config: bool "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: Analyze power consumption on Intel-based laptops
+//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_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
// XXX This should be configurable
#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
/* 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 {
ullong last_usage[MAX_CSTATE_COUNT];
ullong start_duration[MAX_CSTATE_COUNT];
ullong last_duration[MAX_CSTATE_COUNT];
- char cstate_names[MAX_CSTATE_COUNT][16];
-#if ENABLE_FEATURE_USE_TERMIOS
+#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);
}
/* 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 NOINLINE void clear_lines(void)
{
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;
/* 0: 143646045 153901007 IO-APIC-edge timer
* ^
*/
+ *p = '\0';
/* Deal with non-maskable interrupts -- make up fake numbers */
- nr = -1;
- if (buf[0] != ' ' && !isdigit(buf[0])) {
-//TODO: optimize
- 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;
+ nr = index_in_strings("NMI\0RES\0CAL\0TLB\0TRM\0THR\0SPU\0", buf);
+ if (nr >= 0) {
+ nr += 20000;
} else {
/* 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
* ^
}
/* 0: 143646045 153901007 IO-APIC-edge timer
* NMI: 1 2 Non-maskable interrupts
- * ^
+ * ^
*/
if (nr < 20000) {
/* Skip to the interrupt name, e.g. 'timer' */
}
name = p;
- strchrnul(name, '\n')[0] = '\0';
+ chomp(p);
/* Save description of the interrupt */
if (nr >= 20000)
sprintf(irq_desc, " <kernel IPI> : %s", name);
char buf[128];
char line[15 + 3 + 128];
int n;
- ullong totalticks;
FILE *fp;
buf[0] = '\0';
- totalticks = 0;
+ n = 0;
fp = NULL;
if (!G.cant_enable_timer_stats)
fp = fopen_for_read("/proc/timer_stats");
while (fgets(buf, sizeof(buf), fp)) {
const char *count, *process, *func;
char *p;
- int cnt;
+ int idx;
+ unsigned cnt;
count = skip_whitespace(buf);
p = strchr(count, ',');
if (!p)
continue;
*p++ = '\0';
- if (strcmp(strchrnul(count, ' '), " total events") == 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;
- 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);
+ }
+ 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;
-#undef ADVANCE
+
//if (strcmp(process, "swapper") == 0
// && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
//) {
// func = "Load balancing tick";
//}
- if (strncmp(func, "tick_nohz_", 10) == 0)
+ if (is_prefixed_with(func, "tick_nohz_"))
continue;
- if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
+ if (is_prefixed_with(func, "tick_setup_sched_timer"))
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>";
+ idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
+ if (idx != -1) {
+ process = idx < 2 ? "[kernel module]" : "<kernel core>";
+ }
- strchrnul(p, '\n')[0] = '\0';
+ chomp(p);
- {
- char *tmp;
- cnt = bb_strtoull(count, &tmp, 10);
- p = tmp;
- }
- while (*p != '\0') {
- if (*p++ == 'D') /* deferred */
- goto skip;
- }
+ // 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);
- 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;
}
* 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
+#ifdef __i386__
static NOINLINE void print_intel_cstates(void)
{
-#ifdef __i386__
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;
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
}
+#else
+# define print_intel_cstates() ((void)0)
+#endif
static void show_timerstats(void)
{
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*/)
/*char c = ' ';
if (G.lines[i].disk_count)
c = 'D';*/
- smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY");
+ 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,
} else {
bb_putchar('\n');
bb_error_msg("no stats available; run as root or"
- " enable the cpufreq_stats module");
+ " enable the timer_stats module");
}
}
// 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)
+// 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[MAX_CSTATE_COUNT];
ullong cur_duration[MAX_CSTATE_COUNT];
char cstate_lines[MAX_CSTATE_COUNT + 2][64];
-#if ENABLE_FEATURE_USE_TERMIOS
- struct termios new_settings;
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
struct pollfd pfd[1];
pfd[0].fd = 0;
/* Get number of CPUs */
G.total_cpus = get_cpu_count();
- printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
+ puts("Collecting data for "DEFAULT_SLEEP_STR" seconds");
-#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);
- /* So we don't forget to reset term settings */
- atexit(reset_term);
+#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
/* Collect initial data */
int i;
G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
-#if !ENABLE_FEATURE_USE_TERMIOS
+#if !ENABLE_FEATURE_POWERTOP_INTERACTIVE
sleep(DEFAULT_SLEEP);
#else
if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
}
}
- /* Clear the screen */
- printf("\033[H\033[J");
+ /* 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 percentage;
- double newticks;
+ 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\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 < MAX_CSTATE_COUNT; 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\t%5.1fms (%4.1f%%)\n",
- G.cstate_names[i], slept, percentage);
+ sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
+ i + 1, slept, percentage);
//if (maxsleep < slept)
// maxsleep = slept;
}
for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
if (cstate_lines[i][0])
- printf("%s", cstate_lines[i]);
+ fputs(cstate_lines[i], stdout);
i = process_timer_stats();
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
} /* for (;;) */
bb_putchar('\n');
+#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
+ reset_term();
+#endif
return EXIT_SUCCESS;
}