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