powertop.c 21 KB

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