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