busybox/procps/powertop.c

862 lines
21 KiB
C
Raw Permalink Normal View History

/* vi: set sw=4 ts=4: */
/*
* A mini 'powertop' utility:
* Analyze power consumption on Intel-based laptops.
* Based on powertop 1.11.
*
* Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
//config:config POWERTOP
//config: bool "powertop (9.6 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_USR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
// 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)
#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
/* 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;*/
};
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
struct irqdata {
smallint active;
int number;
ullong count;
char irq_desc[32];
};
#endif
struct globals {
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
# 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
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
};
#define G (*ptr_to_globals)
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)
#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);
}
#endif
static int write_str_to_file(const char *fname, const char *str)
{
FILE *fp = fopen_for_write(fname);
if (!fp)
return 1;
fputs(str, fp);
fclose(fp);
return 0;
}
/* 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")
static NOINLINE void clear_lines(void)
{
int i;
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)
{
int i;
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*/);
}
static void sort_lines(void)
{
qsort(G.lines, G.lines_cnt, sizeof(G.lines[0]), line_compare);
}
/* Save C-state usage and duration. Also update maxcstate. */
static void read_cstate_counts(ullong *usage, ullong *duration)
{
DIR *dir;
struct dirent *d;
dir = opendir("/proc/acpi/processor");
if (!dir)
return;
while ((d = readdir(dir)) != NULL) {
FILE *fp;
char buf[192];
int level;
int len;
len = strlen(d->d_name); /* "CPUnn" */
if (len < 3 || len > BIG_SYSNAME_LEN)
continue;
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 = strstr(buf, "age[");
if (!p)
continue;
p += 4;
usage[level] += bb_strtoull(p, NULL, 10) + 1;
p = strstr(buf, "ation[");
if (!p)
continue;
p += 6;
duration[level] += bb_strtoull(p, NULL, 10);
if (level >= MAX_CSTATE_COUNT-1)
break;
level++;
if (level > G.maxcstate) /* update maxcstate */
G.maxcstate = level;
}
fclose(fp);
}
closedir(dir);
}
/* Add line and/or update count */
static void save_line(const char *string, int count)
{
int 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;
return;
}
}
/* 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 is_hpet_irq(const char *name)
{
char *p;
# if BLOATY_HPET_IRQ_NUM_DETECTION
long hpet_chan;
/* Learn the range of existing hpet timers. This is done once */
if (!G.scanned_timer_list) {
FILE *fp;
char buf[80];
G.scanned_timer_list = true;
fp = fopen_for_read("/proc/timer_list");
if (!fp)
return 0;
while (fgets(buf, sizeof(buf), fp)) {
p = strstr(buf, "Clock Event Device: hpet");
if (!p)
continue;
p += sizeof("Clock Event Device: hpet")-1;
if (!isdigit(*p))
continue;
hpet_chan = xatoi_positive(p);
if (hpet_chan < G.percpu_hpet_start)
G.percpu_hpet_start = hpet_chan;
if (hpet_chan > G.percpu_hpet_end)
G.percpu_hpet_end = hpet_chan;
}
fclose(fp);
}
# endif
//TODO: optimize
p = strstr(name, "hpet");
if (!p)
return 0;
p += 4;
if (!isdigit(*p))
return 0;
# if BLOATY_HPET_IRQ_NUM_DETECTION
hpet_chan = xatoi_positive(p);
if (hpet_chan < G.percpu_hpet_start || hpet_chan > G.percpu_hpet_end)
return 0;
# endif
return 1;
}
/* 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 = G.interrupts[i].count;
G.interrupts[i].count = count;
return count - old;
}
if (!G.interrupts[i].active && unused > i)
unused = i;
}
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 process_irq_counts(void)
{
FILE *fp;
char buf[128];
/* Reset values */
G.interrupt_0 = 0;
G.total_interrupt = 0;
fp = xfopen_for_read("/proc/interrupts");
while (fgets(buf, sizeof(buf), fp)) {
char irq_desc[sizeof(" <kernel IPI> : ") + sizeof(buf)];
char *p;
const char *name;
int nr;
ullong count;
ullong delta;
p = strchr(buf, ':');
if (!p)
continue;
/* 0: 143646045 153901007 IO-APIC-edge timer
* ^
*/
*p = '\0';
/* Deal with non-maskable interrupts -- make up fake numbers */
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;
}
p++;
/* 0: 143646045 153901007 IO-APIC-edge timer
* ^
*/
/* Sum counts for this IRQ */
count = 0;
while (1) {
char *tmp;
p = skip_whitespace(p);
if (!isdigit(*p))
break;
count += bb_strtoull(p, &tmp, 10);
p = tmp;
}
/* 0: 143646045 153901007 IO-APIC-edge timer
* NMI: 1 2 Non-maskable interrupts
* ^
*/
if (nr < 20000) {
/* Skip to the interrupt name, e.g. 'timer' */
p = strchr(p, ' ');
if (!p)
continue;
p = skip_whitespace(p);
}
name = p;
chomp(p);
/* Save description of the interrupt */
if (nr >= 20000)
sprintf(irq_desc, " <kernel IPI> : %s", name);
else
sprintf(irq_desc, " <interrupt> : %s", name);
delta = save_irq_count(nr, count);
/* Skip per CPU timer interrupts */
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);
}
#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";
//}
libbb: introduce and use is_prefixed_with() function old new delta is_prefixed_with - 18 +18 complete_username 78 77 -1 man_main 737 735 -2 fsck_device 429 427 -2 unpack_ar_archive 80 76 -4 strip_unsafe_prefix 105 101 -4 singlemount 1054 1050 -4 rtc_adjtime_is_utc 90 86 -4 resolve_mount_spec 88 84 -4 parse_one_line 1029 1025 -4 parse_conf 1460 1456 -4 may_wakeup 83 79 -4 loadkmap_main 219 215 -4 get_irqs_from_stat 103 99 -4 get_header_cpio 913 909 -4 findfs_main 79 75 -4 fbsplash_main 1230 1226 -4 load_crontab 776 771 -5 expand_vars_to_list 1151 1146 -5 date_main 881 876 -5 skip_dev_pfx 30 24 -6 make_device 2199 2193 -6 complete_cmd_dir_file 773 767 -6 run_applet_and_exit 715 708 -7 uudecode_main 321 313 -8 pwdx_main 197 189 -8 execute 568 560 -8 i2cdetect_main 1186 1176 -10 procps_scan 1242 1230 -12 procps_read_smaps 1017 1005 -12 process_module 746 734 -12 patch_main 1903 1891 -12 nfsmount 3572 3560 -12 stack_machine 126 112 -14 process_timer_stats 449 435 -14 match_fstype 111 97 -14 do_ipaddr 1344 1330 -14 open_list_and_close 359 343 -16 get_header_tar 1795 1779 -16 prepend_new_eth_table 340 323 -17 fsck_main 1811 1794 -17 find_iface_state 56 38 -18 dnsd_main 1321 1303 -18 base_device 179 158 -21 find_keyword 104 82 -22 handle_incoming_and_exit 2785 2762 -23 parse_and_put_prompt 774 746 -28 modinfo 347 317 -30 find_action 204 171 -33 update_passwd 1470 1436 -34 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/49 up/down: 18/-540) Total: -522 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2015-03-12 22:18:34 +05:30
if (is_prefixed_with(func, "tick_nohz_"))
continue;
libbb: introduce and use is_prefixed_with() function old new delta is_prefixed_with - 18 +18 complete_username 78 77 -1 man_main 737 735 -2 fsck_device 429 427 -2 unpack_ar_archive 80 76 -4 strip_unsafe_prefix 105 101 -4 singlemount 1054 1050 -4 rtc_adjtime_is_utc 90 86 -4 resolve_mount_spec 88 84 -4 parse_one_line 1029 1025 -4 parse_conf 1460 1456 -4 may_wakeup 83 79 -4 loadkmap_main 219 215 -4 get_irqs_from_stat 103 99 -4 get_header_cpio 913 909 -4 findfs_main 79 75 -4 fbsplash_main 1230 1226 -4 load_crontab 776 771 -5 expand_vars_to_list 1151 1146 -5 date_main 881 876 -5 skip_dev_pfx 30 24 -6 make_device 2199 2193 -6 complete_cmd_dir_file 773 767 -6 run_applet_and_exit 715 708 -7 uudecode_main 321 313 -8 pwdx_main 197 189 -8 execute 568 560 -8 i2cdetect_main 1186 1176 -10 procps_scan 1242 1230 -12 procps_read_smaps 1017 1005 -12 process_module 746 734 -12 patch_main 1903 1891 -12 nfsmount 3572 3560 -12 stack_machine 126 112 -14 process_timer_stats 449 435 -14 match_fstype 111 97 -14 do_ipaddr 1344 1330 -14 open_list_and_close 359 343 -16 get_header_tar 1795 1779 -16 prepend_new_eth_table 340 323 -17 fsck_main 1811 1794 -17 find_iface_state 56 38 -18 dnsd_main 1321 1303 -18 base_device 179 158 -21 find_keyword 104 82 -22 handle_incoming_and_exit 2785 2762 -23 parse_and_put_prompt 774 746 -28 modinfo 347 317 -30 find_action 204 171 -33 update_passwd 1470 1436 -34 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 0/49 up/down: 18/-540) Total: -522 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2015-03-12 22:18:34 +05:30
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)
{
/* EAX value specifies what information to return */
asm (
" cpuid\n"
: "=a"(*eax), /* Output */
"=b"(*ebx),
"=c"(*ecx),
"=d"(*edx)
: "0"(*eax), /* Input */
"1"(*ebx),
"2"(*ecx),
"3"(*edx)
/* No clobbered registers */
);
}
#endif
#ifdef __i386__
static NOINLINE void print_intel_cstates(void)
{
int bios_table[8] = { 0 };
int nbios = 0;
DIR *cpudir;
struct dirent *d;
int i;
unsigned eax, ebx, ecx, edx;
cpudir = opendir("/sys/devices/system/cpu");
if (!cpudir)
return;
/* Loop over cpuN entries */
while ((d = readdir(cpudir)) != NULL) {
DIR *dir;
int len;
char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
len = strlen(d->d_name);
if (len < 3 || len > BIG_SYSNAME_LEN)
continue;
if (!isdigit(d->d_name[3]))
continue;
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.
*/
while ((d = readdir(dir)) != NULL) {
FILE *fp;
char buf[64];
int n;
n = strlen(d->d_name);
if (n < 3 || n > BIG_SYSNAME_LEN)
continue;
sprintf(fname + len, "/%s/desc", d->d_name);
fp = fopen_for_read(fname);
if (fp) {
char *p = fgets(buf, sizeof(buf), fp);
fclose(fp);
if (!p)
break;
p = strstr(p, "MWAIT ");
if (p) {
int pos;
p += sizeof("MWAIT ") - 1;
pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
if (pos >= ARRAY_SIZE(bios_table))
continue;
bios_table[pos]++;
nbios++;
}
}
}
closedir(dir);
}
closedir(cpudir);
if (!nbios)
return;
eax = 5;
ebx = ecx = edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
if (!edx || !(ecx & 1))
return;
printf("Your %s the following C-states: ", "CPU supports");
i = 0;
while (edx) {
if (edx & 7)
printf("C%u ", i);
edx >>= 4;
i++;
}
bb_putchar('\n');
/* Print BIOS C-States */
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');
}
#else
# define print_intel_cstates() ((void)0)
#endif
static void show_timerstats(void)
{
unsigned lines;
/* Get terminal height */
get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
/* We don't have whole terminal just for timerstats */
lines -= 12;
if (!G.cant_enable_timer_stats) {
int i, n = 0;
char strbuf6[6];
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
) {
/* NB: upstream powertop prints "(wakeups/sec)",
* we print just "(wakeup counts)".
*/
/*char c = ' ';
if (G.lines[i].disk_count)
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');
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg("no stats available; run as root or"
" 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"
int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
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_POWERTOP_INTERACTIVE
struct pollfd pfd[1];
pfd[0].fd = 0;
pfd[0].events = POLLIN;
#endif
INIT_G();
#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
G.percpu_hpet_start = INT_MAX;
G.percpu_hpet_end = INT_MIN;
#endif
/* Print warning when we don't have superuser privileges */
if (geteuid() != 0)
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg("run as root to collect enough information");
/* Get number of CPUs */
G.total_cpus = get_cpu_count();
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);
/* So we don't forget to reset term settings */
die_func = reset_term;
#endif
/* Collect initial data */
process_irq_counts();
/* Read initial usage and duration */
read_cstate_counts(G.start_usage, G.start_duration);
/* Copy them to "last" */
memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
/* Display C-states */
print_intel_cstates();
G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
/* The main loop */
for (;;) {
//double maxsleep = 0.0;
ullong totalticks, totalevents;
int i;
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) {
unsigned char c;
if (safe_read(STDIN_FILENO, &c, 1) != 1)
break; /* EOF/error */
if (c == G.init_settings.c_cc[VINTR])
break; /* ^C */
if ((c | 0x20) == 'q')
break;
}
#endif
G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
clear_lines();
process_irq_counts();
/* Clear the stats */
memset(cur_duration, 0, sizeof(cur_duration));
memset(cur_usage, 0, sizeof(cur_usage));
/* Read them */
read_cstate_counts(cur_usage, cur_duration);
/* Count totalticks and totalevents */
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];
}
}
/* 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 */
strcpy(cstate_lines[0], "C-state information is not available\n");
} else {
double percentage;
unsigned newticks;
newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
/* Handle rounding errors: do not display negative values */
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);
/* 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
/ (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
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])
fputs_stdout(cstate_lines[i]);
i = process_timer_stats();
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
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
/* 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_duration, 0, sizeof(cur_duration));
memset(cur_usage, 0, sizeof(cur_usage));
/* Get new values */
read_cstate_counts(cur_usage, cur_duration);
/* Save them */
memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
} /* for (;;) */
bb_putchar('\n');
#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
reset_term();
#endif
return EXIT_SUCCESS;
}