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