aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-10-26 02:54:13 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-10-26 02:54:13 +0200
commit373789e5675ffaeaab183dc3093664c737f2bd36 (patch)
tree019ae794165195585ed5a8430a10e30bb0398d64
parenta2d27a19197cc5ed787dc2439df0cae58053ea2c (diff)
downloadbusybox-w32-373789e5675ffaeaab183dc3093664c737f2bd36.tar.gz
busybox-w32-373789e5675ffaeaab183dc3093664c737f2bd36.tar.bz2
busybox-w32-373789e5675ffaeaab183dc3093664c737f2bd36.zip
powertop: code shrink
function old new delta process_irq_count_deltas - 729 +729 read_cstate_counts - 358 +358 save_line - 150 +150 clear_lines 80 72 -8 line_compare 18 7 -11 .rodata 145736 145699 -37 powertop_main 2527 2341 -186 push_line 193 - -193 read_data 348 - -348 do_proc_irq 890 - -890 ------------------------------------------------------------------------------ (add/remove: 3/3 grow/shrink: 0/4 up/down: 1237/-1673) Total: -436 bytes Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r--procps/powertop.c377
1 files changed, 185 insertions, 192 deletions
diff --git a/procps/powertop.c b/procps/powertop.c
index f35aa5c0a..4b410540e 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -19,20 +19,27 @@
19//config: help 19//config: help
20//config: Analyze power consumption on Intel-based laptops 20//config: Analyze power consumption on Intel-based laptops
21 21
22// XXX This should de configurable
23#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
24
22#include "libbb.h" 25#include "libbb.h"
23 26
27
24//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) 28//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
25#define debug(fmt, ...) ((void)0) 29#define debug(fmt, ...) ((void)0)
26 30
27// XXX This should not be here 31
28#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1 32#define BLOATY_HPET_IRQ_NUM_DETECTION 0
33#define MAX_CSTATE_COUNT 8
34#define IRQCOUNT 40
35
29 36
30#define DEFAULT_SLEEP 10 37#define DEFAULT_SLEEP 10
31#define DEFAULT_SLEEP_STR "10" 38#define DEFAULT_SLEEP_STR "10"
32 39
33/* Frequency of the ACPI timer */ 40/* Frequency of the ACPI timer */
34#define FREQ_ACPI 3579.545 41#define FREQ_ACPI 3579.545
35#define FREQ_ACPI_1000 3579545 42#define FREQ_ACPI_1000 3579545
36 43
37/* Max filename length of entry in /sys/devices subsystem */ 44/* Max filename length of entry in /sys/devices subsystem */
38#define BIG_SYSNAME_LEN 16 45#define BIG_SYSNAME_LEN 16
@@ -42,14 +49,12 @@ typedef unsigned long long ullong;
42struct line { 49struct line {
43 char *string; 50 char *string;
44 int count; 51 int count;
45 int disk_count; 52 /*int disk_count;*/
46}; 53};
47 54
48#if ENABLE_FEATURE_POWERTOP_PROCIRQ 55#if ENABLE_FEATURE_POWERTOP_PROCIRQ
49#define IRQCOUNT 40
50
51struct irqdata { 56struct irqdata {
52 int active; 57 smallint active;
53 int number; 58 int number;
54 ullong count; 59 ullong count;
55 char irq_desc[32]; 60 char irq_desc[32];
@@ -57,26 +62,28 @@ struct irqdata {
57#endif 62#endif
58 63
59struct globals { 64struct globals {
60 bool timer_list_read; 65 int lines_cnt;
61 smallint nostats; 66 int lines_cumulative_count;
62 int headline;
63 int nlines;
64 int linesize; 67 int linesize;
65 int maxcstate; 68 int maxcstate;
69 unsigned total_cpus;
70 struct line *lines;
71 smallint cant_enable_timer_stats;
66#if ENABLE_FEATURE_POWERTOP_PROCIRQ 72#if ENABLE_FEATURE_POWERTOP_PROCIRQ
67 int total_interrupt; 73# if BLOATY_HPET_IRQ_NUM_DETECTION
68 int interrupt_0; 74 smallint scanned_timer_list;
69 int percpu_hpet_start; 75 int percpu_hpet_start;
70 int percpu_hpet_end; 76 int percpu_hpet_end;
77# endif
78 int interrupt_0;
79 int total_interrupt;
71 struct irqdata interrupts[IRQCOUNT]; 80 struct irqdata interrupts[IRQCOUNT];
72#endif 81#endif
73 unsigned total_cpus; 82 ullong start_usage[MAX_CSTATE_COUNT];
74 ullong start_usage[8]; 83 ullong last_usage[MAX_CSTATE_COUNT];
75 ullong last_usage[8]; 84 ullong start_duration[MAX_CSTATE_COUNT];
76 ullong start_duration[8]; 85 ullong last_duration[MAX_CSTATE_COUNT];
77 ullong last_duration[8]; 86 char cstate_names[MAX_CSTATE_COUNT][16];
78 char cstate_names[8][16];
79 struct line *lines;
80#if ENABLE_FEATURE_USE_TERMIOS 87#if ENABLE_FEATURE_USE_TERMIOS
81 struct termios init_settings; 88 struct termios init_settings;
82#endif 89#endif
@@ -95,7 +102,7 @@ static void reset_term(void)
95static void sig_handler(int signo UNUSED_PARAM) 102static void sig_handler(int signo UNUSED_PARAM)
96{ 103{
97 reset_term(); 104 reset_term();
98 exit(EXIT_FAILURE); 105 _exit(EXIT_FAILURE);
99} 106}
100#endif 107#endif
101 108
@@ -116,40 +123,35 @@ static int write_str_to_file(const char *fname, const char *str)
116static void NOINLINE clear_lines(void) 123static void NOINLINE clear_lines(void)
117{ 124{
118 int i; 125 int i;
119 126 for (i = 0; i < G.lines_cnt; i++)
120 for (i = 0; i < G.headline; i++)
121 free(G.lines[i].string); 127 free(G.lines[i].string);
122 free(G.lines); 128 free(G.lines);
123 G.headline = G.linesize = 0; 129 G.lines_cnt = 0;
130 G.linesize = 0;
124 G.lines = NULL; 131 G.lines = NULL;
125} 132}
126 133
127static void count_lines(void) 134static void update_lines_cumulative_count(void)
128{ 135{
129 int i; 136 int i;
130 137 for (i = 0; i < G.lines_cnt; i++)
131 for (i = 0; i < G.headline; i++) 138 G.lines_cumulative_count += G.lines[i].count;
132 G.nlines += G.lines[i].count;
133} 139}
134 140
135static int line_compare(const void *p1, const void *p2) 141static int line_compare(const void *p1, const void *p2)
136{ 142{
137 const struct line *a = p1; 143 const struct line *a = p1;
138 const struct line *b = p2; 144 const struct line *b = p2;
139 145 return (b->count /*+ 50 * b->disk_count*/) - (a->count /*+ 50 * a->disk_count*/);
140 return (b->count + 50 * b->disk_count) - (a->count + 50 * a->disk_count);
141} 146}
142 147
143static void do_sort(void) 148static void sort_lines(void)
144{ 149{
145 qsort(G.lines, G.headline, sizeof(struct line), line_compare); 150 qsort(G.lines, G.lines_cnt, sizeof(G.lines[0]), line_compare);
146} 151}
147 152
148/* 153/* Save C-state usage and duration. Also update maxcstate. */
149 * Save C-state names, usage and duration. Also get maxcstate. 154static void read_cstate_counts(ullong *usage, ullong *duration)
150 * Reads data from /proc.
151 */
152static void read_data(ullong *usage, ullong *duration)
153{ 155{
154 DIR *dir; 156 DIR *dir;
155 struct dirent *d; 157 struct dirent *d;
@@ -161,10 +163,10 @@ static void read_data(ullong *usage, ullong *duration)
161 while ((d = readdir(dir)) != NULL) { 163 while ((d = readdir(dir)) != NULL) {
162 FILE *fp; 164 FILE *fp;
163 char buf[192]; 165 char buf[192];
164 int level = 0; 166 int level;
165 int len; 167 int len;
166 168
167 len = strlen(d->d_name); 169 len = strlen(d->d_name); /* "CPUnn" */
168 if (len < 3 || len > BIG_SYSNAME_LEN) 170 if (len < 3 || len > BIG_SYSNAME_LEN)
169 continue; 171 continue;
170 172
@@ -173,28 +175,31 @@ static void read_data(ullong *usage, ullong *duration)
173 if (!fp) 175 if (!fp)
174 continue; 176 continue;
175 177
178// Example file contents:
179// active state: C0
180// max_cstate: C8
181// maximum allowed latency: 2000000000 usec
182// states:
183// C1: type[C1] promotion[--] demotion[--] latency[001] usage[00006173] duration[00000000000000000000]
184// C2: type[C2] promotion[--] demotion[--] latency[001] usage[00085191] duration[00000000000083024907]
185// C3: type[C3] promotion[--] demotion[--] latency[017] usage[01017622] duration[00000000017921327182]
186 level = 0;
176 while (fgets(buf, sizeof(buf), fp)) { 187 while (fgets(buf, sizeof(buf), fp)) {
177 char *p; 188 char *p = strstr(buf, "age[");
178
179 /* Get usage */
180 p = strstr(buf, "age[");
181 if (!p) 189 if (!p)
182 continue; 190 continue;
183 p += 4; 191 p += 4;
184 usage[level] += bb_strtoull(p, NULL, 10) + 1; 192 usage[level] += bb_strtoull(p, NULL, 10) + 1;
185
186 /* Get duration */
187 p = strstr(buf, "ation["); 193 p = strstr(buf, "ation[");
188 if (!p) 194 if (!p)
189 continue; 195 continue;
190 p += 6; 196 p += 6;
191 duration[level] += bb_strtoull(p, NULL, 10); 197 duration[level] += bb_strtoull(p, NULL, 10);
192 198
193 /* Increment level */ 199 if (level >= MAX_CSTATE_COUNT-1)
200 break;
194 level++; 201 level++;
195 202 if (level > G.maxcstate) /* update maxcstate */
196 /* Also update maxcstate */
197 if (level > G.maxcstate)
198 G.maxcstate = level; 203 G.maxcstate = level;
199 } 204 }
200 fclose(fp); 205 fclose(fp);
@@ -203,15 +208,10 @@ static void read_data(ullong *usage, ullong *duration)
203} 208}
204 209
205/* Add line and/or update count */ 210/* Add line and/or update count */
206static void push_line(const char *string, int count) 211static void save_line(const char *string, int count)
207{ 212{
208 int i; 213 int i;
209 214 for (i = 0; i < G.lines_cnt; i++) {
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) { 215 if (strcmp(string, G.lines[i].string) == 0) {
216 /* It's already there, only update count */ 216 /* It's already there, only update count */
217 G.lines[i].count += count; 217 G.lines[i].count += count;
@@ -219,28 +219,27 @@ static void push_line(const char *string, int count)
219 } 219 }
220 } 220 }
221 221
222 G.lines = xrealloc_vector(G.lines, 1, G.headline); 222 /* Add new line */
223 223 G.lines = xrealloc_vector(G.lines, 1, G.lines_cnt);
224 G.lines[G.headline].string = xstrdup(string); 224 G.lines[G.lines_cnt].string = xstrdup(string);
225 G.lines[G.headline].count = count; 225 G.lines[G.lines_cnt].count = count;
226 G.lines[G.headline].disk_count = 0; 226 /*G.lines[G.lines_cnt].disk_count = 0;*/
227 227 G.lines_cnt++;
228 /* We added a line */
229 G.headline++;
230} 228}
231 229
232#if ENABLE_FEATURE_POWERTOP_PROCIRQ 230#if ENABLE_FEATURE_POWERTOP_PROCIRQ
233static int percpu_hpet_timer(const char *name) 231static int is_hpet_irq(const char *name)
234{ 232{
235 char *p; 233 char *p;
234# if BLOATY_HPET_IRQ_NUM_DETECTION
236 long hpet_chan; 235 long hpet_chan;
237 236
238 /* This is done once */ 237 /* Learn the range of existing hpet timers. This is done once */
239 if (!G.timer_list_read) { 238 if (!G.scanned_timer_list) {
240 FILE *fp; 239 FILE *fp;
241 char buf[80]; 240 char buf[80];
242 241
243 G.timer_list_read = true; 242 G.scanned_timer_list = true;
244 fp = fopen_for_read("/proc/timer_list"); 243 fp = fopen_for_read("/proc/timer_list");
245 if (!fp) 244 if (!fp)
246 return 0; 245 return 0;
@@ -250,7 +249,7 @@ static int percpu_hpet_timer(const char *name)
250 if (!p) 249 if (!p)
251 continue; 250 continue;
252 p += sizeof("Clock Event Device: hpet")-1; 251 p += sizeof("Clock Event Device: hpet")-1;
253 if (!isdigit(p[0])) 252 if (!isdigit(*p))
254 continue; 253 continue;
255 hpet_chan = xatoi_positive(p); 254 hpet_chan = xatoi_positive(p);
256 if (hpet_chan < G.percpu_hpet_start) 255 if (hpet_chan < G.percpu_hpet_start)
@@ -260,49 +259,46 @@ static int percpu_hpet_timer(const char *name)
260 } 259 }
261 fclose(fp); 260 fclose(fp);
262 } 261 }
263 262# endif
263//TODO: optimize
264 p = strstr(name, "hpet"); 264 p = strstr(name, "hpet");
265 if (!p) 265 if (!p)
266 return 0; 266 return 0;
267
268 p += 4; 267 p += 4;
269 if (!isdigit(p[0])) 268 if (!isdigit(*p))
270 return 0; 269 return 0;
271 270# if BLOATY_HPET_IRQ_NUM_DETECTION
272 hpet_chan = xatoi_positive(p); 271 hpet_chan = xatoi_positive(p);
273 if (G.percpu_hpet_start <= hpet_chan && hpet_chan <= G.percpu_hpet_end) 272 if (hpet_chan < G.percpu_hpet_start || hpet_chan > G.percpu_hpet_end)
274 return 1; 273 return 0;
275 274# endif
276 return 0; 275 return 1;
277} 276}
278 277
279static int update_irq(int irq, ullong count) 278/* Save new IRQ count, return delta from old one */
279static int save_irq_count(int irq, ullong count)
280{ 280{
281 int unused = IRQCOUNT; 281 int unused = IRQCOUNT;
282 int i; 282 int i;
283
284 for (i = 0; i < IRQCOUNT; i++) { 283 for (i = 0; i < IRQCOUNT; i++) {
285 if (G.interrupts[i].active && G.interrupts[i].number == irq) { 284 if (G.interrupts[i].active && G.interrupts[i].number == irq) {
286 ullong old; 285 ullong old = G.interrupts[i].count;
287 old = G.interrupts[i].count;
288 G.interrupts[i].count = count; 286 G.interrupts[i].count = count;
289 return count - old; 287 return count - old;
290 } 288 }
291 if (!G.interrupts[i].active && unused > i) 289 if (!G.interrupts[i].active && unused > i)
292 unused = i; 290 unused = i;
293 } 291 }
294 292 if (unused < IRQCOUNT) {
295 G.interrupts[unused].active = 1; 293 G.interrupts[unused].active = 1;
296 G.interrupts[unused].count = count; 294 G.interrupts[unused].count = count;
297 G.interrupts[unused].number = irq; 295 G.interrupts[unused].number = irq;
298 296 }
299 return count; 297 return count;
300} 298}
301 299
302/* 300/* Read /proc/interrupts, save IRQ counts and IRQ description */
303 * Read /proc/interrupts, save IRQ counts and IRQ description. 301static void process_irq_count_deltas(void)
304 */
305static void do_proc_irq(void)
306{ 302{
307 FILE *fp; 303 FILE *fp;
308 char buf[128]; 304 char buf[128];
@@ -316,12 +312,10 @@ static void do_proc_irq(void)
316 char irq_desc[sizeof(" <kernel IPI> : ") + sizeof(buf)]; 312 char irq_desc[sizeof(" <kernel IPI> : ") + sizeof(buf)];
317 char *p; 313 char *p;
318 const char *name; 314 const char *name;
319 int nr = -1; 315 int nr;
320 ullong count; 316 ullong count;
321 ullong delta; 317 ullong delta;
322 int special;
323 318
324 /* Skip header */
325 p = strchr(buf, ':'); 319 p = strchr(buf, ':');
326 if (!p) 320 if (!p)
327 continue; 321 continue;
@@ -329,8 +323,9 @@ static void do_proc_irq(void)
329 * ^ 323 * ^
330 */ 324 */
331 /* Deal with non-maskable interrupts -- make up fake numbers */ 325 /* Deal with non-maskable interrupts -- make up fake numbers */
332 special = 0; 326 nr = -1;
333 if (buf[0] != ' ' && !isdigit(buf[0])) { 327 if (buf[0] != ' ' && !isdigit(buf[0])) {
328//TODO: optimize
334 if (strncmp(buf, "NMI:", 4) == 0) 329 if (strncmp(buf, "NMI:", 4) == 0)
335 nr = 20000; 330 nr = 20000;
336 if (strncmp(buf, "RES:", 4) == 0) 331 if (strncmp(buf, "RES:", 4) == 0)
@@ -345,10 +340,9 @@ static void do_proc_irq(void)
345 nr = 20005; 340 nr = 20005;
346 if (strncmp(buf, "SPU:", 4) == 0) 341 if (strncmp(buf, "SPU:", 4) == 0)
347 nr = 20006; 342 nr = 20006;
348 special = 1;
349 } else { 343 } else {
350 /* bb_strtou don't eat leading spaces, using strtoul */ 344 /* bb_strtou doesn't eat leading spaces, using strtoul */
351 nr = strtoul(buf, NULL, 10); /* xato*() wouldn't work */ 345 nr = strtoul(buf, NULL, 10);
352 } 346 }
353 if (nr == -1) 347 if (nr == -1)
354 continue; 348 continue;
@@ -357,7 +351,7 @@ static void do_proc_irq(void)
357 /* 0: 143646045 153901007 IO-APIC-edge timer 351 /* 0: 143646045 153901007 IO-APIC-edge timer
358 * ^ 352 * ^
359 */ 353 */
360 /* Count sum of the IRQs */ 354 /* Sum counts for this IRQ */
361 count = 0; 355 count = 0;
362 while (1) { 356 while (1) {
363 char *tmp; 357 char *tmp;
@@ -371,7 +365,7 @@ static void do_proc_irq(void)
371 * NMI: 1 2 Non-maskable interrupts 365 * NMI: 1 2 Non-maskable interrupts
372 * ^ 366 * ^
373 */ 367 */
374 if (!special) { 368 if (nr < 20000) {
375 /* Skip to the interrupt name, e.g. 'timer' */ 369 /* Skip to the interrupt name, e.g. 'timer' */
376 p = strchr(p, ' '); 370 p = strchr(p, ' ');
377 if (!p) 371 if (!p)
@@ -382,19 +376,21 @@ static void do_proc_irq(void)
382 name = p; 376 name = p;
383 strchrnul(name, '\n')[0] = '\0'; 377 strchrnul(name, '\n')[0] = '\0';
384 /* Save description of the interrupt */ 378 /* Save description of the interrupt */
385 if (special) 379 if (nr < 20000)
386 sprintf(irq_desc, " <kernel IPI> : %s", name); 380 sprintf(irq_desc, " <kernel IPI> : %s", name);
387 else 381 else
388 sprintf(irq_desc, " <interrupt> : %s", name); 382 sprintf(irq_desc, " <interrupt> : %s", name);
389 383
390 delta = update_irq(nr, count); 384 delta = save_irq_count(nr, count);
391 385
392 /* Skip per CPU timer interrupts */ 386 /* Skip per CPU timer interrupts */
393 if (percpu_hpet_timer(name)) 387 if (is_hpet_irq(name))
394 delta = 0; 388 continue;
395 if (nr > 0 && delta > 0) 389
396 push_line(irq_desc, delta); 390 if (nr != 0 && delta != 0)
397 if (!nr) 391 save_line(irq_desc, delta);
392
393 if (nr == 0)
398 G.interrupt_0 = delta; 394 G.interrupt_0 = delta;
399 else 395 else
400 G.total_interrupt += delta; 396 G.total_interrupt += delta;
@@ -402,7 +398,9 @@ static void do_proc_irq(void)
402 398
403 fclose(fp); 399 fclose(fp);
404} 400}
405#endif /* ENABLE_FEATURE_POWERTOP_PROCIRQ */ 401#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
402# define process_irq_count_deltas() ((void)0)
403#endif
406 404
407#ifdef __i386__ 405#ifdef __i386__
408/* 406/*
@@ -546,7 +544,7 @@ static void show_cstates(char cstate_lines[][64])
546 printf("%s", cstate_lines[i]); 544 printf("%s", cstate_lines[i]);
547} 545}
548 546
549static void show_timerstats(int nostats) 547static void show_timerstats(void)
550{ 548{
551 unsigned lines; 549 unsigned lines;
552 550
@@ -556,19 +554,19 @@ static void show_timerstats(int nostats)
556 /* We don't have whole terminal just for timerstats */ 554 /* We don't have whole terminal just for timerstats */
557 lines -= 12; 555 lines -= 12;
558 556
559 if (!nostats) { 557 if (!G.cant_enable_timer_stats) {
560 int i, n = 0; 558 int i, n = 0;
561 559
562 puts("\nTop causes for wakeups:"); 560 puts("\nTop causes for wakeups:");
563 for (i = 0; i < G.headline; i++) { 561 for (i = 0; i < G.lines_cnt; i++) {
564 if ((G.lines[i].count > 0 || G.lines[i].disk_count > 0) 562 if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
565 && n++ < lines 563 && n++ < lines
566 ) { 564 ) {
567 char c = ' '; 565 char c = ' ';
568 if (G.lines[i].disk_count) 566 /*if (G.lines[i].disk_count)
569 c = 'D'; 567 c = 'D';*/
570 printf(" %5.1f%% (%5.1f)%c %s\n", 568 printf(" %5.1f%% (%5.1f)%c %s\n",
571 G.lines[i].count * 100.0 / G.nlines, 569 G.lines[i].count * 100.0 / G.lines_cumulative_count,
572 G.lines[i].count * 1.0 / DEFAULT_SLEEP, c, 570 G.lines[i].count * 1.0 / DEFAULT_SLEEP, c,
573 G.lines[i].string); 571 G.lines[i].string);
574 } 572 }
@@ -580,6 +578,24 @@ static void show_timerstats(int nostats)
580 } 578 }
581} 579}
582 580
581// Example display from powertop version 1.11
582// Cn Avg residency P-states (frequencies)
583// C0 (cpu running) ( 0.5%) 2.00 Ghz 0.0%
584// polling 0.0ms ( 0.0%) 1.67 Ghz 0.0%
585// C1 mwait 0.0ms ( 0.0%) 1333 Mhz 0.1%
586// C2 mwait 0.1ms ( 0.1%) 1000 Mhz 99.9%
587// C3 mwait 12.1ms (99.4%)
588//
589// Wakeups-from-idle per second : 93.6 interval: 15.0s
590// no ACPI power usage estimate available
591//
592// Top causes for wakeups:
593// 32.4% ( 26.7) <interrupt> : extra timer interrupt
594// 29.0% ( 23.9) <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
595// 9.0% ( 7.5) <kernel core> : hrtimer_start (tick_sched_timer)
596// 6.5% ( 5.3) <interrupt> : ata_piix
597// 5.0% ( 4.1) inetd : hrtimer_start_range_ns (hrtimer_wakeup)
598
583//usage:#define powertop_trivial_usage 599//usage:#define powertop_trivial_usage
584//usage: "" 600//usage: ""
585//usage:#define powertop_full_usage "\n\n" 601//usage:#define powertop_full_usage "\n\n"
@@ -588,8 +604,8 @@ static void show_timerstats(int nostats)
588int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 604int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
589int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv) 605int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
590{ 606{
591 ullong cur_usage[8]; 607 ullong cur_usage[MAX_CSTATE_COUNT];
592 ullong cur_duration[8]; 608 ullong cur_duration[MAX_CSTATE_COUNT];
593 char cstate_lines[12][64]; 609 char cstate_lines[12][64];
594 char buf[128]; 610 char buf[128];
595#if ENABLE_FEATURE_USE_TERMIOS 611#if ENABLE_FEATURE_USE_TERMIOS
@@ -602,7 +618,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
602 618
603 INIT_G(); 619 INIT_G();
604 620
605#if ENABLE_FEATURE_POWERTOP_PROCIRQ 621#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
606 G.percpu_hpet_start = INT_MAX; 622 G.percpu_hpet_start = INT_MAX;
607 G.percpu_hpet_end = INT_MIN; 623 G.percpu_hpet_end = INT_MIN;
608#endif 624#endif
@@ -611,11 +627,6 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
611 if (geteuid() != 0) 627 if (geteuid() != 0)
612 bb_error_msg("run as root to collect enough information"); 628 bb_error_msg("run as root to collect enough information");
613 629
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 */ 630 /* Get number of CPUs */
620 G.total_cpus = get_cpu_count(); 631 G.total_cpus = get_cpu_count();
621 632
@@ -624,22 +635,19 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
624#if ENABLE_FEATURE_USE_TERMIOS 635#if ENABLE_FEATURE_USE_TERMIOS
625 tcgetattr(0, (void *)&G.init_settings); 636 tcgetattr(0, (void *)&G.init_settings);
626 memcpy(&new_settings, &G.init_settings, sizeof(new_settings)); 637 memcpy(&new_settings, &G.init_settings, sizeof(new_settings));
627
628 /* Turn on unbuffered input, turn off echoing */ 638 /* Turn on unbuffered input, turn off echoing */
629 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); 639 new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
630 640 /* So we don't forget to reset term settings */
641 atexit(reset_term);
631 bb_signals(BB_FATAL_SIGS, sig_handler); 642 bb_signals(BB_FATAL_SIGS, sig_handler);
632 tcsetattr_stdin_TCSANOW(&new_settings); 643 tcsetattr_stdin_TCSANOW(&new_settings);
633#endif 644#endif
634 645
635#if ENABLE_FEATURE_POWERTOP_PROCIRQ
636 /* Collect initial data */ 646 /* Collect initial data */
637 do_proc_irq(); 647 process_irq_count_deltas();
638 do_proc_irq();
639#endif
640 648
641 /* Read initial usage and duration */ 649 /* Read initial usage and duration */
642 read_data(&G.start_usage[0], &G.start_duration[0]); 650 read_cstate_counts(G.start_usage, G.start_duration);
643 651
644 /* Copy them to "last" */ 652 /* Copy them to "last" */
645 memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage)); 653 memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
@@ -648,20 +656,16 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
648 /* Display C-states */ 656 /* Display C-states */
649 print_intel_cstates(); 657 print_intel_cstates();
650 658
651 if (stop_timer()) 659 G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
652 G.nostats = 1;
653 660
654 /* The main loop */ 661 /* The main loop */
655 for (;;) { 662 for (;;) {
656 double maxsleep = 0.0; 663 /*double maxsleep = 0.0;*/
657 ullong totalticks, totalevents; 664 ullong totalticks, totalevents;
658 int i; 665 int i;
659 FILE *fp; 666 FILE *fp;
660 double newticks;
661
662 if (start_timer())
663 G.nostats = 1;
664 667
668 G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
665#if !ENABLE_FEATURE_USE_TERMIOS 669#if !ENABLE_FEATURE_USE_TERMIOS
666 sleep(DEFAULT_SLEEP); 670 sleep(DEFAULT_SLEEP);
667#else 671#else
@@ -675,27 +679,22 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
675 break; 679 break;
676 } 680 }
677#endif 681#endif
678 682 G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
679 if (stop_timer())
680 G.nostats = 1;
681 683
682 clear_lines(); 684 clear_lines();
683#if ENABLE_FEATURE_POWERTOP_PROCIRQ 685 process_irq_count_deltas();
684 do_proc_irq();
685#endif
686 686
687 /* Clear the stats */ 687 /* Clear the stats */
688 memset(cur_duration, 0, sizeof(cur_duration)); 688 memset(cur_duration, 0, sizeof(cur_duration));
689 memset(cur_usage, 0, sizeof(cur_usage)); 689 memset(cur_usage, 0, sizeof(cur_usage));
690 690
691 /* Read them */ 691 /* Read them */
692 read_data(&cur_usage[0], &cur_duration[0]); 692 read_cstate_counts(cur_usage, cur_duration);
693
694 totalticks = totalevents = 0;
695 693
696 /* Count totalticks and totalevents */ 694 /* Count totalticks and totalevents */
697 for (i = 0; i < 8; i++) { 695 totalticks = totalevents = 0;
698 if (cur_usage[i]) { 696 for (i = 0; i < MAX_CSTATE_COUNT; i++) {
697 if (cur_usage[i] != 0) {
699 totalticks += cur_duration[i] - G.last_duration[i]; 698 totalticks += cur_duration[i] - G.last_duration[i];
700 totalevents += cur_usage[i] - G.last_usage[i]; 699 totalevents += cur_usage[i] - G.last_usage[i];
701 } 700 }
@@ -714,6 +713,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
714 } else { 713 } else {
715 double slept; 714 double slept;
716 double percentage; 715 double percentage;
716 double newticks;
717 717
718 newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks; 718 newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
719 719
@@ -727,8 +727,8 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
727 percentage); 727 percentage);
728 728
729 /* Compute values for individual C-states */ 729 /* Compute values for individual C-states */
730 for (i = 0; i < 8; i++) { 730 for (i = 0; i < MAX_CSTATE_COUNT; i++) {
731 if (cur_usage[i]) { 731 if (cur_usage[i] != 0) {
732 slept = (cur_duration[i] - G.last_duration[i]) 732 slept = (cur_duration[i] - G.last_duration[i])
733 / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI; 733 / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
734 percentage = (cur_duration[i] - G.last_duration[i]) * 100 734 percentage = (cur_duration[i] - G.last_duration[i]) * 100
@@ -738,8 +738,8 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
738 sprintf(G.cstate_names[i], "C%u", i + 1); 738 sprintf(G.cstate_names[i], "C%u", i + 1);
739 sprintf(cstate_lines[i + 2], "%s\t%5.1fms (%4.1f%%)\n", 739 sprintf(cstate_lines[i + 2], "%s\t%5.1fms (%4.1f%%)\n",
740 G.cstate_names[i], slept, percentage); 740 G.cstate_names[i], slept, percentage);
741 if (maxsleep < slept) 741 /*if (maxsleep < slept)
742 maxsleep = slept; 742 maxsleep = slept;*/
743 } 743 }
744 } 744 }
745 } 745 }
@@ -752,16 +752,34 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
752 totalticks = 0; 752 totalticks = 0;
753 753
754 fp = NULL; 754 fp = NULL;
755 if (!G.nostats) 755 if (!G.cant_enable_timer_stats)
756 fp = fopen_for_read("/proc/timer_stats"); 756 fp = fopen_for_read("/proc/timer_stats");
757 if (fp) { 757 if (fp) {
758// Examlpe file contents:
759// Timer Stats Version: v0.2
760// Sample period: 1.329 s
761// 76, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
762// 88, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
763// 24, 3787 firefox hrtimer_start_range_ns (hrtimer_wakeup)
764// 46D, 1136 kondemand/1 do_dbs_timer (delayed_work_timer_fn)
765// ...
766// 1, 1656 Xorg hrtimer_start_range_ns (hrtimer_wakeup)
767// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup)
768// 331 total events, 249.059 events/sec
758 while (fgets(buf, sizeof(buf), fp)) { 769 while (fgets(buf, sizeof(buf), fp)) {
759 const char *count, *process, *func; 770 const char *count, *process, *func;
771 char *p;
760 char line[512]; 772 char line[512];
761 int cnt = 0; 773 int cnt = 0;
762 bool defferable = false; 774// TODO: optimize
763 char *p; 775 if (strstr(buf, "total events"))
764 int j = 0; 776 break;
777 count = skip_whitespace(buf);
778 p = strchr(count, ',');
779 if (!p)
780 continue;
781 *p++ = '\0';
782 p = skip_whitespace(p); /* points to pid */
765 783
766/* Find char ' ', then eat remaining spaces */ 784/* Find char ' ', then eat remaining spaces */
767#define ADVANCE(p) do { \ 785#define ADVANCE(p) do { \
@@ -772,26 +790,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
772 (p)++; \ 790 (p)++; \
773 (p) = skip_whitespace(p); \ 791 (p) = skip_whitespace(p); \
774} while (0) 792} while (0)
775 793 /* Get process name */
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); 794 ADVANCE(p);
796 process = p; 795 process = p;
797 796
@@ -813,8 +812,6 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
813 if (strcmp(process, "swapper") == 0) 812 if (strcmp(process, "swapper") == 0)
814 process = "[kernel core]"; 813 process = "[kernel core]";
815 814
816 p = strchr(p, '\n');
817
818 if (strncmp(func, "tick_nohz_", 10) == 0) 815 if (strncmp(func, "tick_nohz_", 10) == 0)
819 continue; 816 continue;
820 if (strncmp(func, "tick_setup_sched_timer", 20) == 0) 817 if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
@@ -822,22 +819,20 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
822 if (strcmp(process, "powertop") == 0) 819 if (strcmp(process, "powertop") == 0)
823 continue; 820 continue;
824 821
825 if (p) 822 strchrnul(p, '\n')[0] = '\0';
826 *p = '\0';
827 823
828 cnt = bb_strtoull(count, &p, 10); 824 cnt = bb_strtoull(count, &p, 10);
829 while (*p != 0) { 825 while (*p != '\0') {
830 if (*p++ == 'D') 826 if (*p++ == 'D')
831 defferable = true; 827 goto skip;
832 } 828 }
833 if (defferable)
834 continue;
835 829
836 if (strchr(process, '[')) 830 if (strchr(process, '['))
837 sprintf(line, "%s %s", process, func); 831 sprintf(line, "%s %s", process, func);
838 else 832 else
839 sprintf(line, "%s", process); 833 sprintf(line, "%s", process);
840 push_line(line, cnt); 834 save_line(line, cnt);
835 skip: ;
841 } 836 }
842 fclose(fp); 837 fclose(fp);
843 } 838 }
@@ -853,19 +848,17 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
853 totalevents += G.interrupt_0 - n; 848 totalevents += G.interrupt_0 - n;
854 } 849 }
855 if (n > 0 && n < G.interrupt_0) 850 if (n > 0 && n < G.interrupt_0)
856 push_line("[extra timer interrupt]", G.interrupt_0 - n); 851 save_line("[extra timer interrupt]", G.interrupt_0 - n);
857 } 852 }
858#endif 853#endif
859 if (totalevents) 854 if (totalevents != 0)
860 printf("\n\033[1mWakeups-from-idle per second : %4.1f\tinterval:" 855 printf("\n\033[1mWakeups-from-idle per second : %4.1f\tinterval:"
861 "%ds\n\033[0m", 856 "%ds\n\033[0m",
862 (double)totalevents / DEFAULT_SLEEP / G.total_cpus, DEFAULT_SLEEP); 857 (double)totalevents / DEFAULT_SLEEP / G.total_cpus, DEFAULT_SLEEP);
863 858
864 count_lines(); 859 update_lines_cumulative_count();
865 do_sort(); 860 sort_lines();
866 861 show_timerstats();
867 show_timerstats(G.nostats);
868
869 fflush(stdout); 862 fflush(stdout);
870 863
871 /* Clear the stats */ 864 /* Clear the stats */
@@ -873,7 +866,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
873 memset(cur_usage, 0, sizeof(cur_usage)); 866 memset(cur_usage, 0, sizeof(cur_usage));
874 867
875 /* Get new values */ 868 /* Get new values */
876 read_data(&cur_usage[0], &cur_duration[0]); 869 read_cstate_counts(cur_usage, cur_duration);
877 870
878 /* Save them */ 871 /* Save them */
879 memcpy(G.last_usage, cur_usage, sizeof(G.last_usage)); 872 memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));