busybox/procps/powertop.c
<<
>>
Prefs
   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
  56typedef unsigned long long ullong;
  57
  58struct line {
  59        char *string;
  60        int count;
  61        /*int disk_count;*/
  62};
  63
  64#if ENABLE_FEATURE_POWERTOP_PROCIRQ
  65struct irqdata {
  66        smallint active;
  67        int number;
  68        ullong count;
  69        char irq_desc[32];
  70};
  71#endif
  72
  73struct 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
 104static void reset_term(void)
 105{
 106        tcsetattr_stdin_TCSANOW(&G.init_settings);
 107}
 108
 109static void sig_handler(int signo UNUSED_PARAM)
 110{
 111        reset_term();
 112        _exit(EXIT_FAILURE);
 113}
 114#endif
 115
 116static 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
 130static 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
 142static 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
 149static 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
 156static 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. */
 162static 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 */
 219static 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
 239static 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 */
 287static 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 */
 309static 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
 400static 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 */
 504static 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__
 527static 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
 625static 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
 688int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 689int 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}
 865