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