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_BIN, _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                strchrnul(name, '\n')[0] = '\0';
 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        ullong totalticks;
 397        FILE *fp;
 398
 399        buf[0] = '\0';
 400        totalticks = 0;
 401
 402        n = 0;
 403        fp = NULL;
 404        if (!G.cant_enable_timer_stats)
 405                fp = fopen_for_read("/proc/timer_stats");
 406        if (fp) {
 407// Example file contents:
 408// Timer Stats Version: v0.2
 409// Sample period: 1.329 s
 410//    76,     0 swapper          hrtimer_start_range_ns (tick_sched_timer)
 411//    88,     0 swapper          hrtimer_start_range_ns (tick_sched_timer)
 412//    24,  3787 firefox          hrtimer_start_range_ns (hrtimer_wakeup)
 413//   46D,  1136 kondemand/1      do_dbs_timer (delayed_work_timer_fn)
 414// ...
 415//     1,  1656 Xorg             hrtimer_start_range_ns (hrtimer_wakeup)
 416//     1,  2159 udisks-daemon    hrtimer_start_range_ns (hrtimer_wakeup)
 417// 331 total events, 249.059 events/sec
 418                while (fgets(buf, sizeof(buf), fp)) {
 419                        const char *count, *process, *func;
 420                        char *p;
 421                        int idx;
 422                        unsigned cnt;
 423
 424                        count = skip_whitespace(buf);
 425                        p = strchr(count, ',');
 426                        if (!p)
 427                                continue;
 428                        *p++ = '\0';
 429                        cnt = bb_strtou(count, NULL, 10);
 430                        if (strcmp(skip_non_whitespace(count), " total events") == 0) {
 431#if ENABLE_FEATURE_POWERTOP_PROCIRQ
 432                                n = cnt / G.total_cpus;
 433                                if (n > 0 && n < G.interrupt_0) {
 434                                        sprintf(line, "    <interrupt> : %s", "extra timer interrupt");
 435                                        save_line(line, G.interrupt_0 - n);
 436                                }
 437#endif
 438                                break;
 439                        }
 440                        if (strchr(count, 'D'))
 441                                continue; /* deferred */
 442                        p = skip_whitespace(p); /* points to pid now */
 443                        process = NULL;
 444 get_func_name:
 445                        p = strchr(p, ' ');
 446                        if (!p)
 447                                continue;
 448                        *p++ = '\0';
 449                        p = skip_whitespace(p);
 450                        if (process == NULL) {
 451                                process = p;
 452                                goto get_func_name;
 453                        }
 454                        func = p;
 455
 456                        //if (strcmp(process, "swapper") == 0
 457                        // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
 458                        //) {
 459                        //      process = "[kernel scheduler]";
 460                        //      func = "Load balancing tick";
 461                        //}
 462
 463                        if (strncmp(func, "tick_nohz_", 10) == 0)
 464                                continue;
 465                        if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
 466                                continue;
 467                        //if (strcmp(process, "powertop") == 0)
 468                        //      continue;
 469
 470                        idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
 471                        if (idx != -1) {
 472                                process = idx < 2 ? "[kernel module]" : "<kernel core>";
 473                        }
 474
 475                        strchrnul(p, '\n')[0] = '\0';
 476
 477                        // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
 478                        // ^          ^            ^
 479                        // count      process      func
 480
 481                        //if (strchr(process, '['))
 482                                sprintf(line, "%15.15s : %s", process, func);
 483                        //else
 484                        //      sprintf(line, "%s", process);
 485                        save_line(line, cnt);
 486                }
 487                fclose(fp);
 488        }
 489
 490        return n;
 491}
 492
 493#ifdef __i386__
 494/*
 495 * Get information about CPU using CPUID opcode.
 496 */
 497static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
 498                                  unsigned int *edx)
 499{
 500        /* EAX value specifies what information to return */
 501        __asm__(
 502                "       pushl %%ebx\n"     /* Save EBX */
 503                "       cpuid\n"
 504                "       movl %%ebx, %1\n"  /* Save content of EBX */
 505                "       popl %%ebx\n"      /* Restore EBX */
 506                : "=a"(*eax), /* Output */
 507                  "=r"(*ebx),
 508                  "=c"(*ecx),
 509                  "=d"(*edx)
 510                : "0"(*eax),  /* Input */
 511                  "1"(*ebx),
 512                  "2"(*ecx),
 513                  "3"(*edx)
 514                /* No clobbered registers */
 515        );
 516}
 517#endif
 518
 519#ifdef __i386__
 520static NOINLINE void print_intel_cstates(void)
 521{
 522        int bios_table[8] = { 0 };
 523        int nbios = 0;
 524        DIR *cpudir;
 525        struct dirent *d;
 526        int i;
 527        unsigned eax, ebx, ecx, edx;
 528
 529        cpudir = opendir("/sys/devices/system/cpu");
 530        if (!cpudir)
 531                return;
 532
 533        /* Loop over cpuN entries */
 534        while ((d = readdir(cpudir)) != NULL) {
 535                DIR *dir;
 536                int len;
 537                char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
 538
 539                len = strlen(d->d_name);
 540                if (len < 3 || len > BIG_SYSNAME_LEN)
 541                        continue;
 542
 543                if (!isdigit(d->d_name[3]))
 544                        continue;
 545
 546                len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
 547                dir = opendir(fname);
 548                if (!dir)
 549                        continue;
 550
 551                /*
 552                 * Every C-state has its own stateN directory, that
 553                 * contains a 'time' and a 'usage' file.
 554                 */
 555                while ((d = readdir(dir)) != NULL) {
 556                        FILE *fp;
 557                        char buf[64];
 558                        int n;
 559
 560                        n = strlen(d->d_name);
 561                        if (n < 3 || n > BIG_SYSNAME_LEN)
 562                                continue;
 563
 564                        sprintf(fname + len, "/%s/desc", d->d_name);
 565                        fp = fopen_for_read(fname);
 566                        if (fp) {
 567                                char *p = fgets(buf, sizeof(buf), fp);
 568                                fclose(fp);
 569                                if (!p)
 570                                        break;
 571                                p = strstr(p, "MWAIT ");
 572                                if (p) {
 573                                        int pos;
 574                                        p += sizeof("MWAIT ") - 1;
 575                                        pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
 576                                        if (pos >= ARRAY_SIZE(bios_table))
 577                                                continue;
 578                                        bios_table[pos]++;
 579                                        nbios++;
 580                                }
 581                        }
 582                }
 583                closedir(dir);
 584        }
 585        closedir(cpudir);
 586
 587        if (!nbios)
 588                return;
 589
 590        eax = 5;
 591        ebx = ecx = edx = 0;
 592        cpuid(&eax, &ebx, &ecx, &edx);
 593        if (!edx || !(ecx & 1))
 594                return;
 595
 596        printf("Your CPU supports the following C-states: ");
 597        i = 0;
 598        while (edx) {
 599                if (edx & 7)
 600                        printf("C%u ", i);
 601                edx >>= 4;
 602                i++;
 603        }
 604        bb_putchar('\n');
 605
 606        /* Print BIOS C-States */
 607        printf("Your BIOS reports the following C-states: ");
 608        for (i = 0; i < ARRAY_SIZE(bios_table); i++)
 609                if (bios_table[i])
 610                        printf("C%u ", i);
 611
 612        bb_putchar('\n');
 613}
 614#else
 615# define print_intel_cstates() ((void)0)
 616#endif
 617
 618static void show_timerstats(void)
 619{
 620        unsigned lines;
 621
 622        /* Get terminal height */
 623        get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
 624
 625        /* We don't have whole terminal just for timerstats */
 626        lines -= 12;
 627
 628        if (!G.cant_enable_timer_stats) {
 629                int i, n = 0;
 630                char strbuf6[6];
 631
 632                strbuf6[5] = '\0';
 633                puts("\nTop causes for wakeups:");
 634                for (i = 0; i < G.lines_cnt; i++) {
 635                        if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
 636                         && n++ < lines
 637                        ) {
 638                                /* NB: upstream powertop prints "(wakeups/sec)",
 639                                 * we print just "(wakeup counts)".
 640                                 */
 641                                /*char c = ' ';
 642                                if (G.lines[i].disk_count)
 643                                        c = 'D';*/
 644                                smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY");
 645                                printf(/*" %5.1f%% (%s)%c  %s\n"*/
 646                                        " %5.1f%% (%s)   %s\n",
 647                                        G.lines[i].count * 100.0 / G.lines_cumulative_count,
 648                                        strbuf6, /*c,*/
 649                                        G.lines[i].string);
 650                        }
 651                }
 652        } else {
 653                bb_putchar('\n');
 654                bb_error_msg("no stats available; run as root or"
 655                                " enable the cpufreq_stats module");
 656        }
 657}
 658
 659// Example display from powertop version 1.11
 660// Cn                Avg residency       P-states (frequencies)
 661// C0 (cpu running)        ( 0.5%)         2.00 Ghz     0.0%
 662// polling           0.0ms ( 0.0%)         1.67 Ghz     0.0%
 663// C1 mwait          0.0ms ( 0.0%)         1333 Mhz     0.1%
 664// C2 mwait          0.1ms ( 0.1%)         1000 Mhz    99.9%
 665// C3 mwait         12.1ms (99.4%)
 666//
 667// Wakeups-from-idle per second : 93.6     interval: 15.0s
 668// no ACPI power usage estimate available
 669//
 670// Top causes for wakeups:
 671//   32.4% ( 26.7)       <interrupt> : extra timer interrupt
 672//   29.0% ( 23.9)     <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
 673//    9.0% (  7.5)     <kernel core> : hrtimer_start (tick_sched_timer)
 674//    6.5% (  5.3)       <interrupt> : ata_piix
 675//    5.0% (  4.1)             inetd : hrtimer_start_range_ns (hrtimer_wakeup)
 676
 677//usage:#define powertop_trivial_usage
 678//usage:       ""
 679//usage:#define powertop_full_usage "\n\n"
 680//usage:       "Analyze power consumption on Intel-based laptops\n"
 681
 682int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 683int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
 684{
 685        ullong cur_usage[MAX_CSTATE_COUNT];
 686        ullong cur_duration[MAX_CSTATE_COUNT];
 687        char cstate_lines[MAX_CSTATE_COUNT + 2][64];
 688#if ENABLE_FEATURE_USE_TERMIOS
 689        struct termios new_settings;
 690        struct pollfd pfd[1];
 691
 692        pfd[0].fd = 0;
 693        pfd[0].events = POLLIN;
 694#endif
 695
 696        INIT_G();
 697
 698#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
 699        G.percpu_hpet_start = INT_MAX;
 700        G.percpu_hpet_end = INT_MIN;
 701#endif
 702
 703        /* Print warning when we don't have superuser privileges */
 704        if (geteuid() != 0)
 705                bb_error_msg("run as root to collect enough information");
 706
 707        /* Get number of CPUs */
 708        G.total_cpus = get_cpu_count();
 709
 710        printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
 711
 712#if ENABLE_FEATURE_USE_TERMIOS
 713        tcgetattr(0, (void *)&G.init_settings);
 714        memcpy(&new_settings, &G.init_settings, sizeof(new_settings));
 715        /* Turn on unbuffered input, turn off echoing */
 716        new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
 717        /* So we don't forget to reset term settings */
 718        atexit(reset_term);
 719        bb_signals(BB_FATAL_SIGS, sig_handler);
 720        tcsetattr_stdin_TCSANOW(&new_settings);
 721#endif
 722
 723        /* Collect initial data */
 724        process_irq_counts();
 725
 726        /* Read initial usage and duration */
 727        read_cstate_counts(G.start_usage, G.start_duration);
 728
 729        /* Copy them to "last" */
 730        memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
 731        memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
 732
 733        /* Display C-states */
 734        print_intel_cstates();
 735
 736        G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
 737
 738        /* The main loop */
 739        for (;;) {
 740                //double maxsleep = 0.0;
 741                ullong totalticks, totalevents;
 742                int i;
 743
 744                G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
 745#if !ENABLE_FEATURE_USE_TERMIOS
 746                sleep(DEFAULT_SLEEP);
 747#else
 748                if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
 749                        unsigned char c;
 750                        if (safe_read(STDIN_FILENO, &c, 1) != 1)
 751                                break; /* EOF/error */
 752                        if (c == G.init_settings.c_cc[VINTR])
 753                                break; /* ^C */
 754                        if ((c | 0x20) == 'q')
 755                                break;
 756                }
 757#endif
 758                G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
 759
 760                clear_lines();
 761                process_irq_counts();
 762
 763                /* Clear the stats */
 764                memset(cur_duration, 0, sizeof(cur_duration));
 765                memset(cur_usage, 0, sizeof(cur_usage));
 766
 767                /* Read them */
 768                read_cstate_counts(cur_usage, cur_duration);
 769
 770                /* Count totalticks and totalevents */
 771                totalticks = totalevents = 0;
 772                for (i = 0; i < MAX_CSTATE_COUNT; i++) {
 773                        if (cur_usage[i] != 0) {
 774                                totalticks += cur_duration[i] - G.last_duration[i];
 775                                totalevents += cur_usage[i] - G.last_usage[i];
 776                        }
 777                }
 778
 779                /* Clear the screen */
 780                printf("\033[H\033[J");
 781
 782                /* Clear C-state lines */
 783                memset(&cstate_lines, 0, sizeof(cstate_lines));
 784
 785                if (totalevents == 0 && G.maxcstate <= 1) {
 786                        /* This should not happen */
 787                        strcpy(cstate_lines[0], "C-state information is not available\n");
 788                } else {
 789                        double percentage;
 790                        unsigned newticks;
 791
 792                        newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
 793                        /* Handle rounding errors: do not display negative values */
 794                        if ((int)newticks < 0)
 795                                newticks = 0;
 796
 797                        sprintf(cstate_lines[0], "Cn\t\t  Avg residency\n");
 798                        percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
 799                        sprintf(cstate_lines[1], "C0 (cpu running)        (%4.1f%%)\n", percentage);
 800
 801                        /* Compute values for individual C-states */
 802                        for (i = 0; i < MAX_CSTATE_COUNT; i++) {
 803                                if (cur_usage[i] != 0) {
 804                                        double slept;
 805                                        slept = (cur_duration[i] - G.last_duration[i])
 806                                                / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
 807                                        percentage = (cur_duration[i] - G.last_duration[i]) * 100
 808                                                / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
 809                                        sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
 810                                                i + 1, slept, percentage);
 811                                        //if (maxsleep < slept)
 812                                        //      maxsleep = slept;
 813                                }
 814                        }
 815                }
 816
 817                for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
 818                        if (cstate_lines[i][0])
 819                                fputs(cstate_lines[i], stdout);
 820
 821                i = process_timer_stats();
 822#if ENABLE_FEATURE_POWERTOP_PROCIRQ
 823                if (totalevents == 0) {
 824                        /* No C-state info available, use timerstats */
 825                        totalevents = i * G.total_cpus + G.total_interrupt;
 826                        if (i < 0)
 827                                totalevents += G.interrupt_0 - i;
 828                }
 829#endif
 830                /* Upstream powertop prints wakeups per sec per CPU,
 831                 * we print just raw wakeup counts.
 832                 */
 833//TODO: show real seconds (think about manual refresh)
 834                printf("\nWakeups-from-idle in %u seconds: %llu\n",
 835                        DEFAULT_SLEEP,
 836                        totalevents
 837                );
 838
 839                update_lines_cumulative_count();
 840                sort_lines();
 841                show_timerstats();
 842                fflush(stdout);
 843
 844                /* Clear the stats */
 845                memset(cur_duration, 0, sizeof(cur_duration));
 846                memset(cur_usage, 0, sizeof(cur_usage));
 847
 848                /* Get new values */
 849                read_cstate_counts(cur_usage, cur_duration);
 850
 851                /* Save them */
 852                memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
 853                memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
 854        } /* for (;;) */
 855
 856        bb_putchar('\n');
 857
 858        return EXIT_SUCCESS;
 859}
 860