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