~mmach/netext73/busybox

« back to all changes in this revision

Viewing changes to procps/powertop.c

  • Committer: mmach
  • Date: 2021-04-14 13:54:24 UTC
  • Revision ID: netbit73@gmail.com-20210414135424-8x3fxf716zs4wflb
1

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.6 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(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
/*
 
502
 * Get information about CPU using CPUID opcode.
 
503
 */
 
504
static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
 
505
                                unsigned int *edx)
 
506
{
 
507
        /* EAX value specifies what information to return */
 
508
        __asm__(
 
509
                "       pushl %%ebx\n"     /* Save EBX */
 
510
                "       cpuid\n"
 
511
                "       movl %%ebx, %1\n"  /* Save content of EBX */
 
512
                "       popl %%ebx\n"      /* Restore EBX */
 
513
                : "=a"(*eax), /* Output */
 
514
                  "=r"(*ebx),
 
515
                  "=c"(*ecx),
 
516
                  "=d"(*edx)
 
517
                : "0"(*eax),  /* Input */
 
518
                  "1"(*ebx),
 
519
                  "2"(*ecx),
 
520
                  "3"(*edx)
 
521
                /* No clobbered registers */
 
522
        );
 
523
}
 
524
#endif
 
525
 
 
526
#ifdef __i386__
 
527
static NOINLINE void print_intel_cstates(void)
 
528
{
 
529
        int bios_table[8] = { 0 };
 
530
        int nbios = 0;
 
531
        DIR *cpudir;
 
532
        struct dirent *d;
 
533
        int i;
 
534
        unsigned eax, ebx, ecx, edx;
 
535
 
 
536
        cpudir = opendir("/sys/devices/system/cpu");
 
537
        if (!cpudir)
 
538
                return;
 
539
 
 
540
        /* Loop over cpuN entries */
 
541
        while ((d = readdir(cpudir)) != NULL) {
 
542
                DIR *dir;
 
543
                int len;
 
544
                char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
 
545
 
 
546
                len = strlen(d->d_name);
 
547
                if (len < 3 || len > BIG_SYSNAME_LEN)
 
548
                        continue;
 
549
 
 
550
                if (!isdigit(d->d_name[3]))
 
551
                        continue;
 
552
 
 
553
                len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
 
554
                dir = opendir(fname);
 
555
                if (!dir)
 
556
                        continue;
 
557
 
 
558
                /*
 
559
                 * Every C-state has its own stateN directory, that
 
560
                 * contains a 'time' and a 'usage' file.
 
561
                 */
 
562
                while ((d = readdir(dir)) != NULL) {
 
563
                        FILE *fp;
 
564
                        char buf[64];
 
565
                        int n;
 
566
 
 
567
                        n = strlen(d->d_name);
 
568
                        if (n < 3 || n > BIG_SYSNAME_LEN)
 
569
                                continue;
 
570
 
 
571
                        sprintf(fname + len, "/%s/desc", d->d_name);
 
572
                        fp = fopen_for_read(fname);
 
573
                        if (fp) {
 
574
                                char *p = fgets(buf, sizeof(buf), fp);
 
575
                                fclose(fp);
 
576
                                if (!p)
 
577
                                        break;
 
578
                                p = strstr(p, "MWAIT ");
 
579
                                if (p) {
 
580
                                        int pos;
 
581
                                        p += sizeof("MWAIT ") - 1;
 
582
                                        pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
 
583
                                        if (pos >= ARRAY_SIZE(bios_table))
 
584
                                                continue;
 
585
                                        bios_table[pos]++;
 
586
                                        nbios++;
 
587
                                }
 
588
                        }
 
589
                }
 
590
                closedir(dir);
 
591
        }
 
592
        closedir(cpudir);
 
593
 
 
594
        if (!nbios)
 
595
                return;
 
596
 
 
597
        eax = 5;
 
598
        ebx = ecx = edx = 0;
 
599
        cpuid(&eax, &ebx, &ecx, &edx);
 
600
        if (!edx || !(ecx & 1))
 
601
                return;
 
602
 
 
603
        printf("Your %s the following C-states: ", "CPU supports");
 
604
        i = 0;
 
605
        while (edx) {
 
606
                if (edx & 7)
 
607
                        printf("C%u ", i);
 
608
                edx >>= 4;
 
609
                i++;
 
610
        }
 
611
        bb_putchar('\n');
 
612
 
 
613
        /* Print BIOS C-States */
 
614
        printf("Your %s the following C-states: ", "BIOS reports");
 
615
        for (i = 0; i < ARRAY_SIZE(bios_table); i++)
 
616
                if (bios_table[i])
 
617
                        printf("C%u ", i);
 
618
 
 
619
        bb_putchar('\n');
 
620
}
 
621
#else
 
622
# define print_intel_cstates() ((void)0)
 
623
#endif
 
624
 
 
625
static void show_timerstats(void)
 
626
{
 
627
        unsigned lines;
 
628
 
 
629
        /* Get terminal height */
 
630
        get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
 
631
 
 
632
        /* We don't have whole terminal just for timerstats */
 
633
        lines -= 12;
 
634
 
 
635
        if (!G.cant_enable_timer_stats) {
 
636
                int i, n = 0;
 
637
                char strbuf6[6];
 
638
 
 
639
                puts("\nTop causes for wakeups:");
 
640
                for (i = 0; i < G.lines_cnt; i++) {
 
641
                        if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
 
642
                         && n++ < lines
 
643
                        ) {
 
644
                                /* NB: upstream powertop prints "(wakeups/sec)",
 
645
                                 * we print just "(wakeup counts)".
 
646
                                 */
 
647
                                /*char c = ' ';
 
648
                                if (G.lines[i].disk_count)
 
649
                                        c = 'D';*/
 
650
                                smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY")[0] = '\0';
 
651
                                printf(/*" %5.1f%% (%s)%c  %s\n"*/
 
652
                                        " %5.1f%% (%s)   %s\n",
 
653
                                        G.lines[i].count * 100.0 / G.lines_cumulative_count,
 
654
                                        strbuf6, /*c,*/
 
655
                                        G.lines[i].string);
 
656
                        }
 
657
                }
 
658
        } else {
 
659
                bb_putchar('\n');
 
660
                bb_error_msg("no stats available; run as root or"
 
661
                                " enable the timer_stats module");
 
662
        }
 
663
}
 
664
 
 
665
// Example display from powertop version 1.11
 
666
// Cn                Avg residency       P-states (frequencies)
 
667
// C0 (cpu running)        ( 0.5%)         2.00 Ghz     0.0%
 
668
// polling           0.0ms ( 0.0%)         1.67 Ghz     0.0%
 
669
// C1 mwait          0.0ms ( 0.0%)         1333 Mhz     0.1%
 
670
// C2 mwait          0.1ms ( 0.1%)         1000 Mhz    99.9%
 
671
// C3 mwait         12.1ms (99.4%)
 
672
//
 
673
// Wakeups-from-idle per second : 93.6     interval: 15.0s
 
674
// no ACPI power usage estimate available
 
675
//
 
676
// Top causes for wakeups:
 
677
//   32.4% ( 26.7)       <interrupt> : extra timer interrupt
 
678
//   29.0% ( 23.9)     <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
 
679
//    9.0% (  7.5)     <kernel core> : hrtimer_start (tick_sched_timer)
 
680
//    6.5% (  5.3)       <interrupt> : ata_piix
 
681
//    5.0% (  4.1)             inetd : hrtimer_start_range_ns (hrtimer_wakeup)
 
682
 
 
683
//usage:#define powertop_trivial_usage
 
684
//usage:       ""
 
685
//usage:#define powertop_full_usage "\n\n"
 
686
//usage:       "Analyze power consumption on Intel-based laptops"
 
687
 
 
688
int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 
689
int powertop_main(int argc UNUSED_PARAM, char UNUSED_PARAM **argv)
 
690
{
 
691
        ullong cur_usage[MAX_CSTATE_COUNT];
 
692
        ullong cur_duration[MAX_CSTATE_COUNT];
 
693
        char cstate_lines[MAX_CSTATE_COUNT + 2][64];
 
694
#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
 
695
        struct pollfd pfd[1];
 
696
 
 
697
        pfd[0].fd = 0;
 
698
        pfd[0].events = POLLIN;
 
699
#endif
 
700
 
 
701
        INIT_G();
 
702
 
 
703
#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
 
704
        G.percpu_hpet_start = INT_MAX;
 
705
        G.percpu_hpet_end = INT_MIN;
 
706
#endif
 
707
 
 
708
        /* Print warning when we don't have superuser privileges */
 
709
        if (geteuid() != 0)
 
710
                bb_error_msg("run as root to collect enough information");
 
711
 
 
712
        /* Get number of CPUs */
 
713
        G.total_cpus = get_cpu_count();
 
714
 
 
715
        puts("Collecting data for "DEFAULT_SLEEP_STR" seconds");
 
716
 
 
717
#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
 
718
        /* Turn on unbuffered input; turn off echoing, ^C ^Z etc */
 
719
        set_termios_to_raw(STDIN_FILENO, &G.init_settings, TERMIOS_CLEAR_ISIG);
 
720
        bb_signals(BB_FATAL_SIGS, sig_handler);
 
721
        /* So we don't forget to reset term settings */
 
722
        die_func = reset_term;
 
723
#endif
 
724
 
 
725
        /* Collect initial data */
 
726
        process_irq_counts();
 
727
 
 
728
        /* Read initial usage and duration */
 
729
        read_cstate_counts(G.start_usage, G.start_duration);
 
730
 
 
731
        /* Copy them to "last" */
 
732
        memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
 
733
        memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
 
734
 
 
735
        /* Display C-states */
 
736
        print_intel_cstates();
 
737
 
 
738
        G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
 
739
 
 
740
        /* The main loop */
 
741
        for (;;) {
 
742
                //double maxsleep = 0.0;
 
743
                ullong totalticks, totalevents;
 
744
                int i;
 
745
 
 
746
                G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
 
747
#if !ENABLE_FEATURE_POWERTOP_INTERACTIVE
 
748
                sleep(DEFAULT_SLEEP);
 
749
#else
 
750
                if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
 
751
                        unsigned char c;
 
752
                        if (safe_read(STDIN_FILENO, &c, 1) != 1)
 
753
                                break; /* EOF/error */
 
754
                        if (c == G.init_settings.c_cc[VINTR])
 
755
                                break; /* ^C */
 
756
                        if ((c | 0x20) == 'q')
 
757
                                break;
 
758
                }
 
759
#endif
 
760
                G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
 
761
 
 
762
                clear_lines();
 
763
                process_irq_counts();
 
764
 
 
765
                /* Clear the stats */
 
766
                memset(cur_duration, 0, sizeof(cur_duration));
 
767
                memset(cur_usage, 0, sizeof(cur_usage));
 
768
 
 
769
                /* Read them */
 
770
                read_cstate_counts(cur_usage, cur_duration);
 
771
 
 
772
                /* Count totalticks and totalevents */
 
773
                totalticks = totalevents = 0;
 
774
                for (i = 0; i < MAX_CSTATE_COUNT; i++) {
 
775
                        if (cur_usage[i] != 0) {
 
776
                                totalticks += cur_duration[i] - G.last_duration[i];
 
777
                                totalevents += cur_usage[i] - G.last_usage[i];
 
778
                        }
 
779
                }
 
780
 
 
781
                /* Home; clear screen */
 
782
                printf(ESC"[H" ESC"[J");
 
783
 
 
784
                /* Clear C-state lines */
 
785
                memset(&cstate_lines, 0, sizeof(cstate_lines));
 
786
 
 
787
                if (totalevents == 0 && G.maxcstate <= 1) {
 
788
                        /* This should not happen */
 
789
                        strcpy(cstate_lines[0], "C-state information is not available\n");
 
790
                } else {
 
791
                        double percentage;
 
792
                        unsigned newticks;
 
793
 
 
794
                        newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
 
795
                        /* Handle rounding errors: do not display negative values */
 
796
                        if ((int)newticks < 0)
 
797
                                newticks = 0;
 
798
 
 
799
                        sprintf(cstate_lines[0], "Cn\t\t  Avg residency\n");
 
800
                        percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
 
801
                        sprintf(cstate_lines[1], "C0 (cpu running)        (%4.1f%%)\n", percentage);
 
802
 
 
803
                        /* Compute values for individual C-states */
 
804
                        for (i = 0; i < MAX_CSTATE_COUNT; i++) {
 
805
                                if (cur_usage[i] != 0) {
 
806
                                        double slept;
 
807
                                        slept = (cur_duration[i] - G.last_duration[i])
 
808
                                                / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
 
809
                                        percentage = (cur_duration[i] - G.last_duration[i]) * 100
 
810
                                                / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
 
811
                                        sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
 
812
                                                i + 1, slept, percentage);
 
813
                                        //if (maxsleep < slept)
 
814
                                        //      maxsleep = slept;
 
815
                                }
 
816
                        }
 
817
                }
 
818
 
 
819
                for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
 
820
                        if (cstate_lines[i][0])
 
821
                                fputs(cstate_lines[i], stdout);
 
822
 
 
823
                i = process_timer_stats();
 
824
#if ENABLE_FEATURE_POWERTOP_PROCIRQ
 
825
                if (totalevents == 0) {
 
826
                        /* No C-state info available, use timerstats */
 
827
                        totalevents = i * G.total_cpus + G.total_interrupt;
 
828
                        if (i < 0)
 
829
                                totalevents += G.interrupt_0 - i;
 
830
                }
 
831
#endif
 
832
                /* Upstream powertop prints wakeups per sec per CPU,
 
833
                 * we print just raw wakeup counts.
 
834
                 */
 
835
//TODO: show real seconds (think about manual refresh)
 
836
                printf("\nWakeups-from-idle in %u seconds: %llu\n",
 
837
                        DEFAULT_SLEEP,
 
838
                        totalevents
 
839
                );
 
840
 
 
841
                update_lines_cumulative_count();
 
842
                sort_lines();
 
843
                show_timerstats();
 
844
                fflush(stdout);
 
845
 
 
846
                /* Clear the stats */
 
847
                memset(cur_duration, 0, sizeof(cur_duration));
 
848
                memset(cur_usage, 0, sizeof(cur_usage));
 
849
 
 
850
                /* Get new values */
 
851
                read_cstate_counts(cur_usage, cur_duration);
 
852
 
 
853
                /* Save them */
 
854
                memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
 
855
                memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
 
856
        } /* for (;;) */
 
857
 
 
858
        bb_putchar('\n');
 
859
#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
 
860
        reset_term();
 
861
#endif
 
862
 
 
863
        return EXIT_SUCCESS;
 
864
}