~vcs-imports/busybox/trunk

« back to all changes in this revision

Viewing changes to procps/powertop.c

  • Committer: Eric Andersen
  • Date: 1999-11-24 09:04:33 UTC
  • Revision ID: git-v1:b99df0fd65abe3245fa2d04115326100847f865e
First draft

Show diffs side-by-side

added added

removed removed

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