source: MondoRescue/branches/3.3/mindi-busybox/procps/powertop.c@ 3782

Last change on this file since 3782 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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