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