powertop: new applet
[oweals/busybox.git] / procps / powertop.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * A mini 'powertop' utility:
4  *   Analyze power consumption on Intel-based laptops.
5  * Based on powertop 1.11.
6  *
7  * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
8  *
9  * Licensed under GPLv2, see file LICENSE in this source tree.
10  */
11
12 //applet:IF_POWERTOP(APPLET(powertop, _BB_DIR_BIN, _BB_SUID_DROP))
13
14 //kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
15
16 //config:config POWERTOP
17 //config:       bool "powertop"
18 //config:       default y
19 //config:       help
20 //config:         Analyze power consumption on Intel-based laptops
21
22 #include "libbb.h"
23
24 //#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
25 #define debug(fmt, ...) ((void)0)
26
27 // XXX This should not be here
28 #define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
29
30 #define DEFAULT_SLEEP      10
31 #define DEFAULT_SLEEP_STR "10"
32
33 /* Frequency of the ACPI timer */
34 #define FREQ_ACPI          3579.545
35 #define FREQ_ACPI_1000     3579545
36
37 /* Max filename length of entry in /sys/devices subsystem */
38 #define BIG_SYSNAME_LEN    16
39
40 typedef unsigned long long ullong;
41
42 struct line {
43         char *string;
44         int count;
45         int disk_count;
46 };
47
48 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
49 #define IRQCOUNT                40
50
51 struct irqdata {
52         int active;
53         int number;
54         ullong count;
55         char irq_desc[32];
56 };
57 #endif
58
59 struct globals {
60         bool timer_list_read;
61         smallint nostats;
62         int headline;
63         int nlines;
64         int linesize;
65         int maxcstate;
66 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
67         int total_interrupt;
68         int interrupt_0;
69         int percpu_hpet_start;
70         int percpu_hpet_end;
71         struct irqdata interrupts[IRQCOUNT];
72 #endif
73         unsigned total_cpus;
74         ullong start_usage[8];
75         ullong last_usage[8];
76         ullong start_duration[8];
77         ullong last_duration[8];
78         char cstate_names[8][16];
79         struct line *lines;
80 #if ENABLE_FEATURE_USE_TERMIOS
81         struct termios init_settings;
82 #endif
83 };
84 #define G (*ptr_to_globals)
85 #define INIT_G() do { \
86         SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
87 } while (0)
88
89 #if ENABLE_FEATURE_USE_TERMIOS
90 static void reset_term(void)
91 {
92         tcsetattr_stdin_TCSANOW(&G.init_settings);
93 }
94
95 static void sig_handler(int signo UNUSED_PARAM)
96 {
97         reset_term();
98         exit(EXIT_FAILURE);
99 }
100 #endif
101
102 static int write_str_to_file(const char *fname, const char *str)
103 {
104         FILE *fp = fopen_for_write(fname);
105         if (!fp)
106                 return 1;
107         fputs(str, fp);
108         fclose(fp);
109         return 0;
110 }
111
112 /* Make it more readable */
113 #define start_timer()   write_str_to_file("/proc/timer_stats", "1\n")
114 #define stop_timer()    write_str_to_file("/proc/timer_stats", "0\n")
115
116 static void NOINLINE clear_lines(void)
117 {
118         int i;
119
120         for (i = 0; i < G.headline; i++)
121                 free(G.lines[i].string);
122         free(G.lines);
123         G.headline = G.linesize = 0;
124         G.lines = NULL;
125 }
126
127 static void count_lines(void)
128 {
129         int i;
130
131         for (i = 0; i < G.headline; i++)
132                 G.nlines += G.lines[i].count;
133 }
134
135 static int line_compare(const void *p1, const void *p2)
136 {
137         const struct line *a = p1;
138         const struct line *b = p2;
139
140         return (b->count + 50 * b->disk_count) - (a->count + 50 * a->disk_count);
141 }
142
143 static void do_sort(void)
144 {
145         qsort(G.lines, G.headline, sizeof(struct line), line_compare);
146 }
147
148 /*
149  * Save C-state names, usage and duration. Also get maxcstate.
150  * Reads data from /proc.
151  */
152 static void read_data(ullong *usage, ullong *duration)
153 {
154         DIR *dir;
155         struct dirent *d;
156
157         dir = opendir("/proc/acpi/processor");
158         if (!dir)
159                 return;
160
161         while ((d = readdir(dir)) != NULL) {
162                 FILE *fp;
163                 char buf[192];
164                 int level = 0;
165                 int len;
166
167                 len = strlen(d->d_name);
168                 if (len < 3 || len > BIG_SYSNAME_LEN)
169                         continue;
170
171                 sprintf(buf, "/proc/acpi/processor/%s/power", d->d_name);
172                 fp = fopen_for_read(buf);
173                 if (!fp)
174                         continue;
175
176                 while (fgets(buf, sizeof(buf), fp)) {
177                         char *p;
178
179                         /* Get usage */
180                         p = strstr(buf, "age[");
181                         if (!p)
182                                 continue;
183                         p += 4;
184                         usage[level] += bb_strtoull(p, NULL, 10) + 1;
185
186                         /* Get duration */
187                         p = strstr(buf, "ation[");
188                         if (!p)
189                                 continue;
190                         p += 6;
191                         duration[level] += bb_strtoull(p, NULL, 10);
192
193                         /* Increment level */
194                         level++;
195
196                         /* Also update maxcstate */
197                         if (level > G.maxcstate)
198                                 G.maxcstate = level;
199                 }
200                 fclose(fp);
201         }
202         closedir(dir);
203 }
204
205 /* Add line and/or update count */
206 static void push_line(const char *string, int count)
207 {
208         int i;
209
210         if (!string)
211                 return;
212
213         /* Loop through entries */
214         for (i = 0; i < G.headline; i++) {
215                 if (strcmp(string, G.lines[i].string) == 0) {
216                         /* It's already there, only update count */
217                         G.lines[i].count += count;
218                         return;
219                 }
220         }
221
222         G.lines = xrealloc_vector(G.lines, 1, G.headline);
223
224         G.lines[G.headline].string = xstrdup(string);
225         G.lines[G.headline].count = count;
226         G.lines[G.headline].disk_count = 0;
227
228         /* We added a line */
229         G.headline++;
230 }
231
232 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
233 static int percpu_hpet_timer(const char *name)
234 {
235         char *p;
236         long hpet_chan;
237
238         /* This is done once */
239         if (!G.timer_list_read) {
240                 FILE *fp;
241                 char buf[80];
242
243                 G.timer_list_read = true;
244                 fp = fopen_for_read("/proc/timer_list");
245                 if (!fp)
246                         return 0;
247
248                 while (fgets(buf, sizeof(buf), fp)) {
249                         p = strstr(buf, "Clock Event Device: hpet");
250                         if (!p)
251                                 continue;
252                         p += sizeof("Clock Event Device: hpet")-1;
253                         if (!isdigit(p[0]))
254                                 continue;
255                         hpet_chan = xatoi_positive(p);
256                         if (hpet_chan < G.percpu_hpet_start)
257                                 G.percpu_hpet_start = hpet_chan;
258                         if (hpet_chan > G.percpu_hpet_end)
259                                 G.percpu_hpet_end = hpet_chan;
260                 }
261                 fclose(fp);
262         }
263
264         p = strstr(name, "hpet");
265         if (!p)
266                 return 0;
267
268         p += 4;
269         if (!isdigit(p[0]))
270                 return 0;
271
272         hpet_chan = xatoi_positive(p);
273         if (G.percpu_hpet_start <= hpet_chan && hpet_chan <= G.percpu_hpet_end)
274                 return 1;
275
276         return 0;
277 }
278
279 static int update_irq(int irq, ullong count)
280 {
281         int unused = IRQCOUNT;
282         int i;
283
284         for (i = 0; i < IRQCOUNT; i++) {
285                 if (G.interrupts[i].active && G.interrupts[i].number == irq) {
286                         ullong old;
287                         old = G.interrupts[i].count;
288                         G.interrupts[i].count = count;
289                         return count - old;
290                 }
291                 if (!G.interrupts[i].active && unused > i)
292                         unused = i;
293         }
294
295         G.interrupts[unused].active = 1;
296         G.interrupts[unused].count = count;
297         G.interrupts[unused].number = irq;
298
299         return count;
300 }
301
302 /*
303  * Read /proc/interrupts, save IRQ counts and IRQ description.
304  */
305 static void do_proc_irq(void)
306 {
307         FILE *fp;
308         char buf[128];
309
310         /* Reset values */
311         G.interrupt_0 = 0;
312         G.total_interrupt = 0;
313
314         fp = xfopen_for_read("/proc/interrupts");
315         while (fgets(buf, sizeof(buf), fp)) {
316                 char irq_desc[sizeof("   <kernel IPI> : ") + sizeof(buf)];
317                 char *p;
318                 const char *name;
319                 int nr = -1;
320                 ullong count;
321                 ullong delta;
322                 int special;
323
324                 /* Skip header */
325                 p = strchr(buf, ':');
326                 if (!p)
327                         continue;
328                 /*  0:  143646045  153901007   IO-APIC-edge      timer
329                  *   ^
330                  */
331                 /* Deal with non-maskable interrupts -- make up fake numbers */
332                 special = 0;
333                 if (buf[0] != ' ' && !isdigit(buf[0])) {
334                         if (strncmp(buf, "NMI:", 4) == 0)
335                                 nr = 20000;
336                         if (strncmp(buf, "RES:", 4) == 0)
337                                 nr = 20001;
338                         if (strncmp(buf, "CAL:", 4) == 0)
339                                 nr = 20002;
340                         if (strncmp(buf, "TLB:", 4) == 0)
341                                 nr = 20003;
342                         if (strncmp(buf, "TRM:", 4) == 0)
343                                 nr = 20004;
344                         if (strncmp(buf, "THR:", 4) == 0)
345                                 nr = 20005;
346                         if (strncmp(buf, "SPU:", 4) == 0)
347                                 nr = 20006;
348                         special = 1;
349                 } else {
350                         /* bb_strtou don't eat leading spaces, using strtoul */
351                         nr = strtoul(buf, NULL, 10); /* xato*() wouldn't work */
352                 }
353                 if (nr == -1)
354                         continue;
355
356                 p++;
357                 /*  0:  143646045  153901007   IO-APIC-edge      timer
358                  *    ^
359                  */
360                 /* Count sum of the IRQs */
361                 count = 0;
362                 while (1) {
363                         char *tmp;
364                         p = skip_whitespace(p);
365                         if (!isdigit(*p))
366                                 break;
367                         count += bb_strtoull(p, &tmp, 10);
368                         p = tmp;
369                 }
370                 /*   0:  143646045  153901007   IO-APIC-edge      timer
371                  * NMI:          1          2   Non-maskable interrupts
372                  *                              ^
373                  */
374                 if (!special) {
375                         /* Skip to the interrupt name, e.g. 'timer' */
376                         p = strchr(p, ' ');
377                         if (!p)
378                                 continue;
379                         p = skip_whitespace(p);
380                 }
381
382                 name = p;
383                 strchrnul(name, '\n')[0] = '\0';
384                 /* Save description of the interrupt */
385                 if (special)
386                         sprintf(irq_desc, "   <kernel IPI> : %s", name);
387                 else
388                         sprintf(irq_desc, "    <interrupt> : %s", name);
389
390                 delta = update_irq(nr, count);
391
392                 /* Skip per CPU timer interrupts */
393                 if (percpu_hpet_timer(name))
394                         delta = 0;
395                 if (nr > 0 && delta > 0)
396                         push_line(irq_desc, delta);
397                 if (!nr)
398                         G.interrupt_0 = delta;
399                 else
400                         G.total_interrupt += delta;
401         }
402
403         fclose(fp);
404 }
405 #endif /* ENABLE_FEATURE_POWERTOP_PROCIRQ */
406
407 #ifdef __i386__
408 /*
409  * Get information about CPU using CPUID opcode.
410  */
411 static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
412                                   unsigned int *edx)
413 {
414         /* EAX value specifies what information to return */
415         __asm__(
416                 "       pushl %%ebx\n"     /* Save EBX */
417                 "       cpuid\n"
418                 "       movl %%ebx, %1\n"  /* Save content of EBX */
419                 "       popl %%ebx\n"      /* Restore EBX */
420                 : "=a"(*eax), /* Output */
421                   "=r"(*ebx),
422                   "=c"(*ecx),
423                   "=d"(*edx)
424                 : "0"(*eax),  /* Input */
425                   "1"(*ebx),
426                   "2"(*ecx),
427                   "3"(*edx)
428                 /* No clobbered registers */
429         );
430 }
431 #endif
432
433 static void NOINLINE print_intel_cstates(void)
434 {
435 #ifdef __i386__
436         int bios_table[8] = { 0 };
437         int nbios = 0;
438         DIR *cpudir;
439         struct dirent *d;
440         int i;
441         unsigned eax, ebx, ecx, edx;
442
443         cpudir = opendir("/sys/devices/system/cpu");
444         if (!cpudir)
445                 return;
446
447         /* Loop over cpuN entries */
448         while ((d = readdir(cpudir)) != NULL) {
449                 DIR *dir;
450                 int len;
451                 char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
452
453                 len = strlen(d->d_name);
454                 if (len < 3 || len > BIG_SYSNAME_LEN)
455                         continue;
456
457                 if (!isdigit(d->d_name[3]))
458                         continue;
459
460                 len = sprintf(fname, "/sys/devices/system/cpu/%s/cpuidle", d->d_name);
461                 dir = opendir(fname);
462                 if (!dir)
463                         continue;
464
465                 /*
466                  * Every C-state has its own stateN directory, that
467                  * contains a `time' and a `usage' file.
468                  */
469                 while ((d = readdir(dir)) != NULL) {
470                         FILE *fp;
471                         char buf[64];
472                         int n;
473
474                         n = strlen(d->d_name);
475                         if (n < 3 || n > BIG_SYSNAME_LEN)
476                                 continue;
477
478                         sprintf(fname + len, "/%s/desc", d->d_name);
479                         fp = fopen_for_read(fname);
480                         if (fp) {
481                                 char *p = fgets(buf, sizeof(buf), fp);
482                                 fclose(fp);
483                                 if (!p)
484                                         break;
485                                 p = strstr(p, "MWAIT ");
486                                 if (p) {
487                                         int pos;
488                                         p += sizeof("MWAIT ") - 1;
489                                         pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
490                                         if (pos >= ARRAY_SIZE(bios_table))
491                                                 continue;
492                                         bios_table[pos]++;
493                                         nbios++;
494                                 }
495                         }
496                 }
497                 closedir(dir);
498         }
499         closedir(cpudir);
500
501         if (!nbios)
502                 return;
503
504         eax = 5;
505         ebx = ecx = edx = 0;
506         cpuid(&eax, &ebx, &ecx, &edx);
507         if (!edx || !(ecx & 1))
508                 return;
509
510         printf("Your CPU supports the following C-states: ");
511         i = 0;
512         while (edx) {
513                 if (edx & 7)
514                         printf("C%u ", i);
515                 edx >>= 4;
516                 i++;
517         }
518         bb_putchar('\n');
519
520         /* Print BIOS C-States */
521         printf("Your BIOS reports the following C-states: ");
522         for (i = 0; i < 8; i++)
523                 if (bios_table[i])
524                         printf("C%u ", i);
525
526         bb_putchar('\n');
527 #endif
528 }
529
530 static void print_header(void)
531 {
532         printf(
533                 /* Clear the screen */
534                 "\033[H\033[J"
535                 /* Print the header */
536                 "\033[7m%.*s\033[0m", 79, "PowerTOP (C) 2007 Intel Corporation\n"
537         );
538 }
539
540 static void show_cstates(char cstate_lines[][64])
541 {
542         int i;
543
544         for (i = 0; i < 10; i++)
545                 if ((cstate_lines[i][0]))
546                         printf("%s", cstate_lines[i]);
547 }
548
549 static void show_timerstats(int nostats)
550 {
551         unsigned lines;
552
553         /* Get terminal height */
554         get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
555
556         /* We don't have whole terminal just for timerstats */
557         lines -= 12;
558
559         if (!nostats) {
560                 int i, n = 0;
561
562                 puts("\nTop causes for wakeups:");
563                 for (i = 0; i < G.headline; i++) {
564                         if ((G.lines[i].count > 0 || G.lines[i].disk_count > 0)
565                          && n++ < lines
566                         ) {
567                                 char c = ' ';
568                                 if (G.lines[i].disk_count)
569                                         c = 'D';
570                                 printf(" %5.1f%% (%5.1f)%c  %s\n",
571                                                 G.lines[i].count * 100.0 / G.nlines,
572                                                 G.lines[i].count * 1.0 / DEFAULT_SLEEP, c,
573                                                 G.lines[i].string);
574                         }
575                 }
576         } else {
577                 bb_putchar('\n');
578                 bb_error_msg("no stats available; run as root or"
579                                 " enable the cpufreq_stats module");
580         }
581 }
582
583 //usage:#define powertop_trivial_usage
584 //usage:       ""
585 //usage:#define powertop_full_usage "\n\n"
586 //usage:       "Analyze power consumption on Intel-based laptops\n"
587
588 int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
589 int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
590 {
591         ullong cur_usage[8];
592         ullong cur_duration[8];
593         char cstate_lines[12][64];
594         char buf[128];
595 #if ENABLE_FEATURE_USE_TERMIOS
596         struct termios new_settings;
597         struct pollfd pfd[1];
598
599         pfd[0].fd = 0;
600         pfd[0].events = POLLIN;
601 #endif
602
603         INIT_G();
604
605 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
606         G.percpu_hpet_start = INT_MAX;
607         G.percpu_hpet_end = INT_MIN;
608 #endif
609
610         /* Print warning when we don't have superuser privileges */
611         if (geteuid() != 0)
612                 bb_error_msg("run as root to collect enough information");
613
614 #if ENABLE_FEATURE_USE_TERMIOS
615         /* So we don't forget to reset term settings */
616         atexit(reset_term);
617 #endif
618
619         /* Get number of CPUs */
620         G.total_cpus = get_cpu_count();
621
622         printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
623
624 #if ENABLE_FEATURE_USE_TERMIOS
625         tcgetattr(0, (void *)&G.init_settings);
626         memcpy(&new_settings, &G.init_settings, sizeof(new_settings));
627
628         /* Turn on unbuffered input, turn off echoing */
629         new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
630
631         bb_signals(BB_FATAL_SIGS, sig_handler);
632         tcsetattr_stdin_TCSANOW(&new_settings);
633 #endif
634
635 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
636         /* Collect initial data */
637         do_proc_irq();
638         do_proc_irq();
639 #endif
640
641         /* Read initial usage and duration */
642         read_data(&G.start_usage[0], &G.start_duration[0]);
643
644         /* Copy them to "last" */
645         memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
646         memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
647
648         /* Display C-states */
649         print_intel_cstates();
650
651         if (stop_timer())
652                 G.nostats = 1;
653
654         /* The main loop */
655         for (;;) {
656                 double maxsleep = 0.0;
657                 ullong totalticks, totalevents;
658                 int i;
659                 FILE *fp;
660                 double newticks;
661
662                 if (start_timer())
663                         G.nostats = 1;
664
665 #if !ENABLE_FEATURE_USE_TERMIOS
666                 sleep(DEFAULT_SLEEP);
667 #else
668                 if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
669                         unsigned char c;
670                         if (safe_read(STDIN_FILENO, &c, 1) != 1)
671                                 break; /* EOF/error */
672                         if (c == G.init_settings.c_cc[VINTR])
673                                 break; /* ^C */
674                         if ((c | 0x20) == 'q')
675                                 break;
676                 }
677 #endif
678
679                 if (stop_timer())
680                         G.nostats = 1;
681
682                 clear_lines();
683 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
684                 do_proc_irq();
685 #endif
686
687                 /* Clear the stats */
688                 memset(cur_duration, 0, sizeof(cur_duration));
689                 memset(cur_usage, 0, sizeof(cur_usage));
690
691                 /* Read them */
692                 read_data(&cur_usage[0], &cur_duration[0]);
693
694                 totalticks = totalevents = 0;
695
696                 /* Count totalticks and totalevents */
697                 for (i = 0; i < 8; i++) {
698                         if (cur_usage[i]) {
699                                 totalticks += cur_duration[i] - G.last_duration[i];
700                                 totalevents += cur_usage[i] - G.last_usage[i];
701                         }
702                 }
703
704                 /* Show title bar */
705                 print_header();
706
707                 /* Clear C-state lines */
708                 memset(&cstate_lines, 0, sizeof(cstate_lines));
709
710                 if (totalevents == 0 && G.maxcstate <= 1) {
711                         /* This should not happen */
712                         sprintf(cstate_lines[5], "< Detailed C-state information is not "
713                                 "available.>\n");
714                 } else {
715                         double slept;
716                         double percentage;
717
718                         newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
719
720                         /* Handle rounding errors: do not display negative values */
721                         if (newticks < 0)
722                                 newticks = 0;
723
724                         sprintf(cstate_lines[0], "Cn\t          Avg residency\n");
725                         percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
726                         sprintf(cstate_lines[1], "C0 (cpu running)        (%4.1f%%)\n",
727                                 percentage);
728
729                         /* Compute values for individual C-states */
730                         for (i = 0; i < 8; i++) {
731                                 if (cur_usage[i]) {
732                                         slept = (cur_duration[i] - G.last_duration[i])
733                                                 / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
734                                         percentage = (cur_duration[i] - G.last_duration[i]) * 100
735                                                 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
736
737                                         if (!G.cstate_names[i][0])
738                                                 sprintf(G.cstate_names[i], "C%u", i + 1);
739                                         sprintf(cstate_lines[i + 2], "%s\t%5.1fms (%4.1f%%)\n",
740                                                 G.cstate_names[i], slept, percentage);
741                                         if (maxsleep < slept)
742                                                 maxsleep = slept;
743                                 }
744                         }
745                 }
746
747                 /* Display C-states */
748                 show_cstates(cstate_lines);
749
750                 /* Do timer_stats info */
751                 buf[0] = '\0';
752                 totalticks = 0;
753
754                 fp = NULL;
755                 if (!G.nostats)
756                         fp = fopen_for_read("/proc/timer_stats");
757                 if (fp) {
758                         while (fgets(buf, sizeof(buf), fp)) {
759                                 const char *count, *process, *func;
760                                 char line[512];
761                                 int cnt = 0;
762                                 bool defferable = false;
763                                 char *p;
764                                 int j = 0;
765
766 /* Find char ' ', then eat remaining spaces */
767 #define ADVANCE(p) do {           \
768         (p) = strchr((p), ' ');   \
769         if (!(p))                 \
770                 continue;         \
771         *(p) = '\0';              \
772         (p)++;                    \
773         (p) = skip_whitespace(p); \
774 } while (0)
775
776                                 if (strstr(buf, "total events"))
777                                         break;
778
779                                 while (isspace(buf[j]))
780                                         j++;
781
782                                 count = &buf[j];
783                                 p = (char *)count;
784
785                                 /* Skip PID */
786                                 p = strchr(p, ',');
787                                 if (!p)
788                                         continue;
789                                 *p = '\0';
790                                 p++;
791
792                                 p = skip_whitespace(p);
793
794                                 /* Get process */
795                                 ADVANCE(p);
796                                 process = p;
797
798                                 /* Get function */
799                                 ADVANCE(p);
800                                 func = p;
801
802                                 if (strcmp(process, "swapper") == 0
803                                  && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
804                                 ) {
805                                         process = "[kernel scheduler]";
806                                         func = "Load balancing tick";
807                                 }
808
809                                 if (strcmp(process, "insmod") == 0)
810                                         process = "[kernel module]";
811                                 if (strcmp(process, "modprobe") == 0)
812                                         process = "[kernel module]";
813                                 if (strcmp(process, "swapper") == 0)
814                                         process = "[kernel core]";
815
816                                 p = strchr(p, '\n');
817
818                                 if (strncmp(func, "tick_nohz_", 10) == 0)
819                                         continue;
820                                 if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
821                                         continue;
822                                 if (strcmp(process, "powertop") == 0)
823                                         continue;
824
825                                 if (p)
826                                         *p = '\0';
827
828                                 cnt = bb_strtoull(count, &p, 10);
829                                 while (*p != 0) {
830                                         if (*p++ == 'D')
831                                                 defferable = true;
832                                 }
833                                 if (defferable)
834                                         continue;
835
836                                 if (strchr(process, '['))
837                                         sprintf(line, "%s %s", process, func);
838                                 else
839                                         sprintf(line, "%s", process);
840                                 push_line(line, cnt);
841                         }
842                         fclose(fp);
843                 }
844
845 #if ENABLE_FEATURE_POWERTOP_PROCIRQ
846                 if (strstr(buf, "total events")) {
847                         int n = bb_strtoull(buf, NULL, 10) / G.total_cpus;
848
849                         if (totalevents == 0) {
850                                 /* No C-state info available, use timerstats */
851                                 totalevents = n * G.total_cpus + G.total_interrupt;
852                                 if (n < 0)
853                                         totalevents += G.interrupt_0 - n;
854                         }
855                         if (n > 0 && n < G.interrupt_0)
856                                 push_line("[extra timer interrupt]", G.interrupt_0 - n);
857                 }
858 #endif
859                 if (totalevents)
860                         printf("\n\033[1mWakeups-from-idle per second : %4.1f\tinterval:"
861                                 "%ds\n\033[0m",
862                                 (double)totalevents / DEFAULT_SLEEP / G.total_cpus, DEFAULT_SLEEP);
863
864                 count_lines();
865                 do_sort();
866
867                 show_timerstats(G.nostats);
868
869                 fflush(stdout);
870
871                 /* Clear the stats */
872                 memset(cur_duration, 0, sizeof(cur_duration));
873                 memset(cur_usage, 0, sizeof(cur_usage));
874
875                 /* Get new values */
876                 read_data(&cur_usage[0], &cur_duration[0]);
877
878                 /* Save them */
879                 memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
880                 memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
881         } /* for (;;) */
882
883         bb_putchar('\n');
884
885         return EXIT_SUCCESS;
886 }