X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=procps%2Fpowertop.c;h=ce85f4191e0f605dfd67e1f00f072956201a4b92;hb=2b1559056cf32c42675ecd937796e1455bcb5c2c;hp=4b410540e60abbec27fc5659b9f60c2c8fe2d111;hpb=373789e5675ffaeaab183dc3093664c737f2bd36;p=oweals%2Fbusybox.git diff --git a/procps/powertop.c b/procps/powertop.c index 4b410540e..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,7 +19,7 @@ //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" @@ -62,12 +62,11 @@ struct irqdata { #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 @@ -83,7 +82,6 @@ struct globals { 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 struct termios init_settings; #endif @@ -117,18 +115,19 @@ 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.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) @@ -170,7 +169,7 @@ static void read_cstate_counts(ullong *usage, ullong *duration) 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; @@ -220,7 +219,7 @@ static void save_line(const char *string, int count) } /* 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;*/ @@ -298,7 +297,7 @@ static int save_irq_count(int irq, ullong count) } /* 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]; @@ -322,31 +321,18 @@ static void process_irq_count_deltas(void) /* 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 * ^ @@ -363,7 +349,7 @@ static void process_irq_count_deltas(void) } /* 0: 143646045 153901007 IO-APIC-edge timer * NMI: 1 2 Non-maskable interrupts - * ^ + * ^ */ if (nr < 20000) { /* Skip to the interrupt name, e.g. 'timer' */ @@ -374,9 +360,9 @@ static void process_irq_count_deltas(void) } name = p; - strchrnul(name, '\n')[0] = '\0'; + chomp(p); /* Save description of the interrupt */ - if (nr < 20000) + if (nr >= 20000) sprintf(irq_desc, " : %s", name); else sprintf(irq_desc, " : %s", name); @@ -399,15 +385,115 @@ static void process_irq_count_deltas(void) 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; + 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__( @@ -428,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; @@ -455,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; @@ -505,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) @@ -516,33 +602,16 @@ 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(void) { @@ -556,25 +625,31 @@ static void show_timerstats(void) 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 ) { - 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")[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"); } } @@ -590,8 +665,8 @@ static void show_timerstats(void) // 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) +// 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) @@ -599,15 +674,14 @@ static void show_timerstats(void) //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[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]; @@ -630,7 +704,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) /* 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); @@ -644,7 +718,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) #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); @@ -660,10 +734,9 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) /* 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 @@ -682,7 +755,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 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)); @@ -700,161 +773,65 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) } } - /* 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; - 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 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 < 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.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]) + 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) - 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();