diff options
author | Marek Polacek <mmpolacek@gmail.com> | 2010-10-25 03:44:34 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-10-25 03:44:34 +0200 |
commit | b507cc3aceda26eff851230223a62c6fb471573c (patch) | |
tree | dc07f5c48edac84c1c8a1177453dadae86571a0a | |
parent | ccb070450e79c33fb3f755dbea2fdd979f09d3fb (diff) | |
download | busybox-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.src | 11 | ||||
-rw-r--r-- | procps/powertop.c | 886 | ||||
-rw-r--r-- | util-linux/Config.src | 11 |
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 | ||
46 | config 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 | |||
46 | config FEATURE_EDITING | 57 | config 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 | |||
40 | typedef unsigned long long ullong; | ||
41 | |||
42 | struct line { | ||
43 | char *string; | ||
44 | int count; | ||
45 | int disk_count; | ||
46 | }; | ||
47 | |||
48 | #if ENABLE_FEATURE_POWERTOP_PROCIRQ | ||
49 | #define IRQCOUNT 40 | ||
50 | |||
51 | struct irqdata { | ||
52 | int active; | ||
53 | int number; | ||
54 | ullong count; | ||
55 | char irq_desc[32]; | ||
56 | }; | ||
57 | #endif | ||
58 | |||
59 | struct 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 | ||
90 | static void reset_term(void) | ||
91 | { | ||
92 | tcsetattr_stdin_TCSANOW(&G.init_settings); | ||
93 | } | ||
94 | |||
95 | static void sig_handler(int signo UNUSED_PARAM) | ||
96 | { | ||
97 | reset_term(); | ||
98 | exit(EXIT_FAILURE); | ||
99 | } | ||
100 | #endif | ||
101 | |||
102 | static 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 | |||
116 | static 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 | |||
127 | static 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 | |||
135 | static 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 | |||
143 | static 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 | */ | ||
152 | static 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 */ | ||
206 | static 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 | ||
233 | static 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 | |||
279 | static 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 | */ | ||
305 | static 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 | */ | ||
411 | static 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 | |||
433 | static 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 | |||
530 | static 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 | |||
540 | static 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 | |||
549 | static 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 | |||
588 | int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
589 | int 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 | ||
489 | config 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 | |||
500 | config MOUNT | 489 | config MOUNT |
501 | bool "mount" | 490 | bool "mount" |
502 | default y | 491 | default y |