X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=procps%2Fpowertop.c;h=ce85f4191e0f605dfd67e1f00f072956201a4b92;hb=2b1559056cf32c42675ecd937796e1455bcb5c2c;hp=f35aa5c0a6339eb4b7e16d2366625801e868cd11;hpb=b507cc3aceda26eff851230223a62c6fb471573c;p=oweals%2Fbusybox.git diff --git a/procps/powertop.c b/procps/powertop.c index f35aa5c0a..ce85f4191 100644 --- a/procps/powertop.c +++ b/procps/powertop.c @@ -9,7 +9,7 @@ * Licensed under GPLv2, see file LICENSE in this source tree. */ -//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 @@ -19,20 +19,27 @@ //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 @@ -42,14 +49,12 @@ 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]; @@ -57,26 +62,26 @@ struct irqdata { #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; + 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_USE_TERMIOS struct termios init_settings; #endif @@ -95,7 +100,7 @@ static void reset_term(void) static void sig_handler(int signo UNUSED_PARAM) { reset_term(); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } #endif @@ -110,46 +115,42 @@ static int write_str_to_file(const char *fname, const char *str) } /* 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; @@ -161,40 +162,43 @@ static void read_data(ullong *usage, ullong *duration) 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); @@ -203,15 +207,10 @@ static void read_data(ullong *usage, ullong *duration) } /* 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; @@ -219,28 +218,27 @@ static void push_line(const char *string, int 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; @@ -250,7 +248,7 @@ static int percpu_hpet_timer(const char *name) 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) @@ -260,49 +258,46 @@ static int percpu_hpet_timer(const char *name) } 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]; @@ -316,48 +311,33 @@ static void do_proc_irq(void) char irq_desc[sizeof(" : ") + 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; @@ -369,9 +349,9 @@ static void do_proc_irq(void) } /* 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) @@ -380,21 +360,23 @@ static void do_proc_irq(void) } name = p; - strchrnul(name, '\n')[0] = '\0'; + chomp(p); /* Save description of the interrupt */ - if (special) + if (nr >= 20000) sprintf(irq_desc, " : %s", name); else sprintf(irq_desc, " : %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; @@ -402,14 +384,116 @@ static void do_proc_irq(void) 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, " : %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]" : ""; + } + + 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__( @@ -430,9 +514,9 @@ static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, } #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; @@ -457,14 +541,14 @@ static void NOINLINE print_intel_cstates(void) 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; @@ -507,7 +591,7 @@ static void NOINLINE print_intel_cstates(void) 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) @@ -518,35 +602,18 @@ static void NOINLINE print_intel_cstates(void) 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; @@ -556,42 +623,65 @@ static void show_timerstats(int nostats) /* 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) : extra timer interrupt +// 29.0% ( 23.9) : hrtimer_start_range_ns (tick_sched_timer) +// 9.0% ( 7.5) : hrtimer_start (tick_sched_timer) +// 6.5% ( 5.3) : 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) { - ullong cur_usage[8]; - ullong cur_duration[8]; - char cstate_lines[12][64]; - char buf[128]; + 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; struct pollfd pfd[1]; @@ -602,7 +692,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 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 @@ -611,35 +701,27 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 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"); + 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); bb_signals(BB_FATAL_SIGS, sig_handler); tcsetattr_stdin_TCSANOW(&new_settings); #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)); @@ -648,20 +730,15 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) /* 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; + G.cant_enable_timer_stats |= start_timer(); /* 1 on error */ #if !ENABLE_FEATURE_USE_TERMIOS sleep(DEFAULT_SLEEP); #else @@ -675,197 +752,90 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 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(); + /* Clear the screen */ + printf("\033[H\033[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 */ @@ -873,7 +843,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 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));