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