blob: ebd659bdba29a73fa0a91634153bcc1e9f6157d6 [file] [log] [blame]
Marek Polacekb507cc32010-10-25 03:44:34 +02001/* 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
Denys Vlasenkodac8e792012-06-20 20:24:57 +020012//applet:IF_POWERTOP(APPLET(powertop, BB_DIR_USR_SBIN, BB_SUID_DROP))
Marek Polacekb507cc32010-10-25 03:44:34 +020013
14//kbuild:lib-$(CONFIG_POWERTOP) += powertop.o
15
16//config:config POWERTOP
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020017//config: bool "powertop (9.1 kb)"
Marek Polacekb507cc32010-10-25 03:44:34 +020018//config: default y
19//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020020//config: Analyze power consumption on Intel-based laptops
Denys Vlasenko6c1f3482017-01-11 16:27:12 +010021//config:
22//config:config FEATURE_POWERTOP_INTERACTIVE
23//config: bool "Accept keyboard commands"
24//config: default y
25//config: depends on POWERTOP
26//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020027//config: Without this, powertop will only refresh display every 10 seconds.
28//config: No keyboard commands will work, only ^C to terminate.
Marek Polacekb507cc32010-10-25 03:44:34 +020029
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +020030// XXX This should be configurable
Denys Vlasenko373789e2010-10-26 02:54:13 +020031#define ENABLE_FEATURE_POWERTOP_PROCIRQ 1
32
Marek Polacekb507cc32010-10-25 03:44:34 +020033#include "libbb.h"
34
Denys Vlasenko373789e2010-10-26 02:54:13 +020035
Marek Polacekb507cc32010-10-25 03:44:34 +020036//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
37#define debug(fmt, ...) ((void)0)
38
Denys Vlasenko373789e2010-10-26 02:54:13 +020039
40#define BLOATY_HPET_IRQ_NUM_DETECTION 0
41#define MAX_CSTATE_COUNT 8
42#define IRQCOUNT 40
43
Marek Polacekb507cc32010-10-25 03:44:34 +020044
45#define DEFAULT_SLEEP 10
46#define DEFAULT_SLEEP_STR "10"
47
48/* Frequency of the ACPI timer */
49#define FREQ_ACPI 3579.545
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020050#define FREQ_ACPI_1000 3579545
Marek Polacekb507cc32010-10-25 03:44:34 +020051
52/* Max filename length of entry in /sys/devices subsystem */
53#define BIG_SYSNAME_LEN 16
54
55typedef unsigned long long ullong;
56
57struct line {
58 char *string;
59 int count;
Denys Vlasenko373789e2010-10-26 02:54:13 +020060 /*int disk_count;*/
Marek Polacekb507cc32010-10-25 03:44:34 +020061};
62
63#if ENABLE_FEATURE_POWERTOP_PROCIRQ
Marek Polacekb507cc32010-10-25 03:44:34 +020064struct irqdata {
Denys Vlasenko373789e2010-10-26 02:54:13 +020065 smallint active;
Marek Polacekb507cc32010-10-25 03:44:34 +020066 int number;
67 ullong count;
68 char irq_desc[32];
69};
70#endif
71
72struct globals {
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +020073 struct line *lines; /* the most often used member */
Denys Vlasenko373789e2010-10-26 02:54:13 +020074 int lines_cnt;
75 int lines_cumulative_count;
Marek Polacekb507cc32010-10-25 03:44:34 +020076 int maxcstate;
Denys Vlasenko373789e2010-10-26 02:54:13 +020077 unsigned total_cpus;
Denys Vlasenko373789e2010-10-26 02:54:13 +020078 smallint cant_enable_timer_stats;
Marek Polacekb507cc32010-10-25 03:44:34 +020079#if ENABLE_FEATURE_POWERTOP_PROCIRQ
Denys Vlasenko373789e2010-10-26 02:54:13 +020080# if BLOATY_HPET_IRQ_NUM_DETECTION
81 smallint scanned_timer_list;
Marek Polacekb507cc32010-10-25 03:44:34 +020082 int percpu_hpet_start;
83 int percpu_hpet_end;
Denys Vlasenko373789e2010-10-26 02:54:13 +020084# endif
85 int interrupt_0;
86 int total_interrupt;
Marek Polacekb507cc32010-10-25 03:44:34 +020087 struct irqdata interrupts[IRQCOUNT];
88#endif
Denys Vlasenko373789e2010-10-26 02:54:13 +020089 ullong start_usage[MAX_CSTATE_COUNT];
90 ullong last_usage[MAX_CSTATE_COUNT];
91 ullong start_duration[MAX_CSTATE_COUNT];
92 ullong last_duration[MAX_CSTATE_COUNT];
Denys Vlasenko6c1f3482017-01-11 16:27:12 +010093#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
Marek Polacekb507cc32010-10-25 03:44:34 +020094 struct termios init_settings;
95#endif
96};
97#define G (*ptr_to_globals)
98#define INIT_G() do { \
99 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
100} while (0)
101
Denys Vlasenko6c1f3482017-01-11 16:27:12 +0100102#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
Marek Polacekb507cc32010-10-25 03:44:34 +0200103static void reset_term(void)
104{
105 tcsetattr_stdin_TCSANOW(&G.init_settings);
106}
107
108static void sig_handler(int signo UNUSED_PARAM)
109{
110 reset_term();
Denys Vlasenko373789e2010-10-26 02:54:13 +0200111 _exit(EXIT_FAILURE);
Marek Polacekb507cc32010-10-25 03:44:34 +0200112}
113#endif
114
115static int write_str_to_file(const char *fname, const char *str)
116{
117 FILE *fp = fopen_for_write(fname);
118 if (!fp)
119 return 1;
120 fputs(str, fp);
121 fclose(fp);
122 return 0;
123}
124
125/* Make it more readable */
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200126#define start_timer() write_str_to_file("/proc/timer_stats", "1\n")
127#define stop_timer() write_str_to_file("/proc/timer_stats", "0\n")
Marek Polacekb507cc32010-10-25 03:44:34 +0200128
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200129static NOINLINE void clear_lines(void)
Marek Polacekb507cc32010-10-25 03:44:34 +0200130{
131 int i;
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200132 if (G.lines) {
133 for (i = 0; i < G.lines_cnt; i++)
134 free(G.lines[i].string);
135 free(G.lines);
136 G.lines_cnt = 0;
137 G.lines = NULL;
138 }
Marek Polacekb507cc32010-10-25 03:44:34 +0200139}
140
Denys Vlasenko373789e2010-10-26 02:54:13 +0200141static void update_lines_cumulative_count(void)
Marek Polacekb507cc32010-10-25 03:44:34 +0200142{
143 int i;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200144 for (i = 0; i < G.lines_cnt; i++)
145 G.lines_cumulative_count += G.lines[i].count;
Marek Polacekb507cc32010-10-25 03:44:34 +0200146}
147
148static int line_compare(const void *p1, const void *p2)
149{
150 const struct line *a = p1;
151 const struct line *b = p2;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200152 return (b->count /*+ 50 * b->disk_count*/) - (a->count /*+ 50 * a->disk_count*/);
Marek Polacekb507cc32010-10-25 03:44:34 +0200153}
154
Denys Vlasenko373789e2010-10-26 02:54:13 +0200155static void sort_lines(void)
Marek Polacekb507cc32010-10-25 03:44:34 +0200156{
Denys Vlasenko373789e2010-10-26 02:54:13 +0200157 qsort(G.lines, G.lines_cnt, sizeof(G.lines[0]), line_compare);
Marek Polacekb507cc32010-10-25 03:44:34 +0200158}
159
Denys Vlasenko373789e2010-10-26 02:54:13 +0200160/* Save C-state usage and duration. Also update maxcstate. */
161static void read_cstate_counts(ullong *usage, ullong *duration)
Marek Polacekb507cc32010-10-25 03:44:34 +0200162{
163 DIR *dir;
164 struct dirent *d;
165
166 dir = opendir("/proc/acpi/processor");
167 if (!dir)
168 return;
169
170 while ((d = readdir(dir)) != NULL) {
171 FILE *fp;
172 char buf[192];
Denys Vlasenko373789e2010-10-26 02:54:13 +0200173 int level;
Marek Polacekb507cc32010-10-25 03:44:34 +0200174 int len;
175
Denys Vlasenko373789e2010-10-26 02:54:13 +0200176 len = strlen(d->d_name); /* "CPUnn" */
Marek Polacekb507cc32010-10-25 03:44:34 +0200177 if (len < 3 || len > BIG_SYSNAME_LEN)
178 continue;
179
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100180 sprintf(buf, "%s/%s/power", "/proc/acpi/processor", d->d_name);
Marek Polacekb507cc32010-10-25 03:44:34 +0200181 fp = fopen_for_read(buf);
182 if (!fp)
183 continue;
184
Denys Vlasenko373789e2010-10-26 02:54:13 +0200185// Example file contents:
186// active state: C0
187// max_cstate: C8
188// maximum allowed latency: 2000000000 usec
189// states:
190// C1: type[C1] promotion[--] demotion[--] latency[001] usage[00006173] duration[00000000000000000000]
191// C2: type[C2] promotion[--] demotion[--] latency[001] usage[00085191] duration[00000000000083024907]
192// C3: type[C3] promotion[--] demotion[--] latency[017] usage[01017622] duration[00000000017921327182]
193 level = 0;
Marek Polacekb507cc32010-10-25 03:44:34 +0200194 while (fgets(buf, sizeof(buf), fp)) {
Denys Vlasenko373789e2010-10-26 02:54:13 +0200195 char *p = strstr(buf, "age[");
Marek Polacekb507cc32010-10-25 03:44:34 +0200196 if (!p)
197 continue;
198 p += 4;
199 usage[level] += bb_strtoull(p, NULL, 10) + 1;
Marek Polacekb507cc32010-10-25 03:44:34 +0200200 p = strstr(buf, "ation[");
201 if (!p)
202 continue;
203 p += 6;
204 duration[level] += bb_strtoull(p, NULL, 10);
205
Denys Vlasenko373789e2010-10-26 02:54:13 +0200206 if (level >= MAX_CSTATE_COUNT-1)
207 break;
Marek Polacekb507cc32010-10-25 03:44:34 +0200208 level++;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200209 if (level > G.maxcstate) /* update maxcstate */
Marek Polacekb507cc32010-10-25 03:44:34 +0200210 G.maxcstate = level;
211 }
212 fclose(fp);
213 }
214 closedir(dir);
215}
216
217/* Add line and/or update count */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200218static void save_line(const char *string, int count)
Marek Polacekb507cc32010-10-25 03:44:34 +0200219{
220 int i;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200221 for (i = 0; i < G.lines_cnt; i++) {
Marek Polacekb507cc32010-10-25 03:44:34 +0200222 if (strcmp(string, G.lines[i].string) == 0) {
223 /* It's already there, only update count */
224 G.lines[i].count += count;
225 return;
226 }
227 }
228
Denys Vlasenko373789e2010-10-26 02:54:13 +0200229 /* Add new line */
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200230 G.lines = xrealloc_vector(G.lines, 4, G.lines_cnt);
Denys Vlasenko373789e2010-10-26 02:54:13 +0200231 G.lines[G.lines_cnt].string = xstrdup(string);
232 G.lines[G.lines_cnt].count = count;
233 /*G.lines[G.lines_cnt].disk_count = 0;*/
234 G.lines_cnt++;
Marek Polacekb507cc32010-10-25 03:44:34 +0200235}
236
237#if ENABLE_FEATURE_POWERTOP_PROCIRQ
Denys Vlasenko373789e2010-10-26 02:54:13 +0200238static int is_hpet_irq(const char *name)
Marek Polacekb507cc32010-10-25 03:44:34 +0200239{
240 char *p;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200241# if BLOATY_HPET_IRQ_NUM_DETECTION
Marek Polacekb507cc32010-10-25 03:44:34 +0200242 long hpet_chan;
243
Denys Vlasenko373789e2010-10-26 02:54:13 +0200244 /* Learn the range of existing hpet timers. This is done once */
245 if (!G.scanned_timer_list) {
Marek Polacekb507cc32010-10-25 03:44:34 +0200246 FILE *fp;
247 char buf[80];
248
Denys Vlasenko373789e2010-10-26 02:54:13 +0200249 G.scanned_timer_list = true;
Marek Polacekb507cc32010-10-25 03:44:34 +0200250 fp = fopen_for_read("/proc/timer_list");
251 if (!fp)
252 return 0;
253
254 while (fgets(buf, sizeof(buf), fp)) {
255 p = strstr(buf, "Clock Event Device: hpet");
256 if (!p)
257 continue;
258 p += sizeof("Clock Event Device: hpet")-1;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200259 if (!isdigit(*p))
Marek Polacekb507cc32010-10-25 03:44:34 +0200260 continue;
261 hpet_chan = xatoi_positive(p);
262 if (hpet_chan < G.percpu_hpet_start)
263 G.percpu_hpet_start = hpet_chan;
264 if (hpet_chan > G.percpu_hpet_end)
265 G.percpu_hpet_end = hpet_chan;
266 }
267 fclose(fp);
268 }
Denys Vlasenko373789e2010-10-26 02:54:13 +0200269# endif
270//TODO: optimize
Marek Polacekb507cc32010-10-25 03:44:34 +0200271 p = strstr(name, "hpet");
272 if (!p)
273 return 0;
Marek Polacekb507cc32010-10-25 03:44:34 +0200274 p += 4;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200275 if (!isdigit(*p))
Marek Polacekb507cc32010-10-25 03:44:34 +0200276 return 0;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200277# if BLOATY_HPET_IRQ_NUM_DETECTION
Marek Polacekb507cc32010-10-25 03:44:34 +0200278 hpet_chan = xatoi_positive(p);
Denys Vlasenko373789e2010-10-26 02:54:13 +0200279 if (hpet_chan < G.percpu_hpet_start || hpet_chan > G.percpu_hpet_end)
280 return 0;
281# endif
282 return 1;
Marek Polacekb507cc32010-10-25 03:44:34 +0200283}
284
Denys Vlasenko373789e2010-10-26 02:54:13 +0200285/* Save new IRQ count, return delta from old one */
286static int save_irq_count(int irq, ullong count)
Marek Polacekb507cc32010-10-25 03:44:34 +0200287{
288 int unused = IRQCOUNT;
289 int i;
Marek Polacekb507cc32010-10-25 03:44:34 +0200290 for (i = 0; i < IRQCOUNT; i++) {
291 if (G.interrupts[i].active && G.interrupts[i].number == irq) {
Denys Vlasenko373789e2010-10-26 02:54:13 +0200292 ullong old = G.interrupts[i].count;
Marek Polacekb507cc32010-10-25 03:44:34 +0200293 G.interrupts[i].count = count;
294 return count - old;
295 }
296 if (!G.interrupts[i].active && unused > i)
297 unused = i;
298 }
Denys Vlasenko373789e2010-10-26 02:54:13 +0200299 if (unused < IRQCOUNT) {
300 G.interrupts[unused].active = 1;
301 G.interrupts[unused].count = count;
302 G.interrupts[unused].number = irq;
303 }
Marek Polacekb507cc32010-10-25 03:44:34 +0200304 return count;
305}
306
Denys Vlasenko373789e2010-10-26 02:54:13 +0200307/* Read /proc/interrupts, save IRQ counts and IRQ description */
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200308static void process_irq_counts(void)
Marek Polacekb507cc32010-10-25 03:44:34 +0200309{
310 FILE *fp;
311 char buf[128];
312
313 /* Reset values */
314 G.interrupt_0 = 0;
315 G.total_interrupt = 0;
316
317 fp = xfopen_for_read("/proc/interrupts");
318 while (fgets(buf, sizeof(buf), fp)) {
319 char irq_desc[sizeof(" <kernel IPI> : ") + sizeof(buf)];
320 char *p;
321 const char *name;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200322 int nr;
Marek Polacekb507cc32010-10-25 03:44:34 +0200323 ullong count;
324 ullong delta;
Marek Polacekb507cc32010-10-25 03:44:34 +0200325
Marek Polacekb507cc32010-10-25 03:44:34 +0200326 p = strchr(buf, ':');
327 if (!p)
328 continue;
329 /* 0: 143646045 153901007 IO-APIC-edge timer
330 * ^
331 */
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100332 *p = '\0';
Marek Polacekb507cc32010-10-25 03:44:34 +0200333 /* Deal with non-maskable interrupts -- make up fake numbers */
Denys Vlasenkodcb163a2010-11-04 23:22:40 +0100334 nr = index_in_strings("NMI\0RES\0CAL\0TLB\0TRM\0THR\0SPU\0", buf);
335 if (nr >= 0) {
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100336 nr += 20000;
Marek Polacekb507cc32010-10-25 03:44:34 +0200337 } else {
Denys Vlasenko373789e2010-10-26 02:54:13 +0200338 /* bb_strtou doesn't eat leading spaces, using strtoul */
Denys Vlasenkodcb163a2010-11-04 23:22:40 +0100339 errno = 0;
Denys Vlasenko373789e2010-10-26 02:54:13 +0200340 nr = strtoul(buf, NULL, 10);
Denys Vlasenkodcb163a2010-11-04 23:22:40 +0100341 if (errno)
342 continue;
Marek Polacekb507cc32010-10-25 03:44:34 +0200343 }
Marek Polacekb507cc32010-10-25 03:44:34 +0200344 p++;
345 /* 0: 143646045 153901007 IO-APIC-edge timer
346 * ^
347 */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200348 /* Sum counts for this IRQ */
Marek Polacekb507cc32010-10-25 03:44:34 +0200349 count = 0;
350 while (1) {
351 char *tmp;
352 p = skip_whitespace(p);
353 if (!isdigit(*p))
354 break;
355 count += bb_strtoull(p, &tmp, 10);
356 p = tmp;
357 }
358 /* 0: 143646045 153901007 IO-APIC-edge timer
359 * NMI: 1 2 Non-maskable interrupts
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200360 * ^
Marek Polacekb507cc32010-10-25 03:44:34 +0200361 */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200362 if (nr < 20000) {
Marek Polacekb507cc32010-10-25 03:44:34 +0200363 /* Skip to the interrupt name, e.g. 'timer' */
364 p = strchr(p, ' ');
365 if (!p)
366 continue;
367 p = skip_whitespace(p);
368 }
369
370 name = p;
Ron Yorston8ec1ff32015-03-12 20:18:51 +0100371 chomp(p);
Marek Polacekb507cc32010-10-25 03:44:34 +0200372 /* Save description of the interrupt */
Denys Vlasenkod8b687f2010-10-26 12:42:53 +0200373 if (nr >= 20000)
Marek Polacekb507cc32010-10-25 03:44:34 +0200374 sprintf(irq_desc, " <kernel IPI> : %s", name);
375 else
376 sprintf(irq_desc, " <interrupt> : %s", name);
377
Denys Vlasenko373789e2010-10-26 02:54:13 +0200378 delta = save_irq_count(nr, count);
Marek Polacekb507cc32010-10-25 03:44:34 +0200379
380 /* Skip per CPU timer interrupts */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200381 if (is_hpet_irq(name))
382 continue;
383
384 if (nr != 0 && delta != 0)
385 save_line(irq_desc, delta);
386
387 if (nr == 0)
Marek Polacekb507cc32010-10-25 03:44:34 +0200388 G.interrupt_0 = delta;
389 else
390 G.total_interrupt += delta;
391 }
392
393 fclose(fp);
394}
Denys Vlasenko373789e2010-10-26 02:54:13 +0200395#else /* !ENABLE_FEATURE_POWERTOP_PROCIRQ */
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200396# define process_irq_counts() ((void)0)
Denys Vlasenko373789e2010-10-26 02:54:13 +0200397#endif
Marek Polacekb507cc32010-10-25 03:44:34 +0200398
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200399static NOINLINE int process_timer_stats(void)
400{
401 char buf[128];
402 char line[15 + 3 + 128];
403 int n;
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200404 FILE *fp;
405
406 buf[0] = '\0';
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200407
Maksym Kryzhanovskyy0ebafcc2010-11-06 01:56:19 +0100408 n = 0;
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200409 fp = NULL;
410 if (!G.cant_enable_timer_stats)
411 fp = fopen_for_read("/proc/timer_stats");
412 if (fp) {
413// Example file contents:
414// Timer Stats Version: v0.2
415// Sample period: 1.329 s
416// 76, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
417// 88, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
418// 24, 3787 firefox hrtimer_start_range_ns (hrtimer_wakeup)
419// 46D, 1136 kondemand/1 do_dbs_timer (delayed_work_timer_fn)
420// ...
421// 1, 1656 Xorg hrtimer_start_range_ns (hrtimer_wakeup)
422// 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup)
423// 331 total events, 249.059 events/sec
424 while (fgets(buf, sizeof(buf), fp)) {
425 const char *count, *process, *func;
426 char *p;
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100427 int idx;
Maksym Kryzhanovskyy0ebafcc2010-11-06 01:56:19 +0100428 unsigned cnt;
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200429
430 count = skip_whitespace(buf);
431 p = strchr(count, ',');
432 if (!p)
433 continue;
434 *p++ = '\0';
Maksym Kryzhanovskyy0ebafcc2010-11-06 01:56:19 +0100435 cnt = bb_strtou(count, NULL, 10);
436 if (strcmp(skip_non_whitespace(count), " total events") == 0) {
437#if ENABLE_FEATURE_POWERTOP_PROCIRQ
438 n = cnt / G.total_cpus;
439 if (n > 0 && n < G.interrupt_0) {
440 sprintf(line, " <interrupt> : %s", "extra timer interrupt");
441 save_line(line, G.interrupt_0 - n);
442 }
443#endif
Denys Vlasenkodea28e12010-11-04 23:30:11 +0100444 break;
Maksym Kryzhanovskyy0ebafcc2010-11-06 01:56:19 +0100445 }
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100446 if (strchr(count, 'D'))
447 continue; /* deferred */
448 p = skip_whitespace(p); /* points to pid now */
449 process = NULL;
450 get_func_name:
451 p = strchr(p, ' ');
452 if (!p)
453 continue;
454 *p++ = '\0';
455 p = skip_whitespace(p);
456 if (process == NULL) {
457 process = p;
458 goto get_func_name;
459 }
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200460 func = p;
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100461
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200462 //if (strcmp(process, "swapper") == 0
463 // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0
464 //) {
465 // process = "[kernel scheduler]";
466 // func = "Load balancing tick";
467 //}
468
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100469 if (is_prefixed_with(func, "tick_nohz_"))
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200470 continue;
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100471 if (is_prefixed_with(func, "tick_setup_sched_timer"))
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200472 continue;
473 //if (strcmp(process, "powertop") == 0)
474 // continue;
475
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100476 idx = index_in_strings("insmod\0modprobe\0swapper\0", process);
477 if (idx != -1) {
478 process = idx < 2 ? "[kernel module]" : "<kernel core>";
479 }
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200480
Ron Yorston8ec1ff32015-03-12 20:18:51 +0100481 chomp(p);
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200482
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100483 // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
484 // ^ ^ ^
485 // count process func
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200486
487 //if (strchr(process, '['))
488 sprintf(line, "%15.15s : %s", process, func);
489 //else
490 // sprintf(line, "%s", process);
Maksym Kryzhanovskyy0ebafcc2010-11-06 01:56:19 +0100491 save_line(line, cnt);
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200492 }
493 fclose(fp);
494 }
495
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200496 return n;
497}
498
Marek Polacekb507cc32010-10-25 03:44:34 +0200499#ifdef __i386__
500/*
501 * Get information about CPU using CPUID opcode.
502 */
503static void cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
Denys Vlasenko69675782013-01-14 01:34:48 +0100504 unsigned int *edx)
Marek Polacekb507cc32010-10-25 03:44:34 +0200505{
506 /* EAX value specifies what information to return */
507 __asm__(
508 " pushl %%ebx\n" /* Save EBX */
509 " cpuid\n"
510 " movl %%ebx, %1\n" /* Save content of EBX */
511 " popl %%ebx\n" /* Restore EBX */
512 : "=a"(*eax), /* Output */
513 "=r"(*ebx),
514 "=c"(*ecx),
515 "=d"(*edx)
516 : "0"(*eax), /* Input */
517 "1"(*ebx),
518 "2"(*ecx),
519 "3"(*edx)
520 /* No clobbered registers */
521 );
522}
523#endif
524
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200525#ifdef __i386__
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200526static NOINLINE void print_intel_cstates(void)
Marek Polacekb507cc32010-10-25 03:44:34 +0200527{
Marek Polacekb507cc32010-10-25 03:44:34 +0200528 int bios_table[8] = { 0 };
529 int nbios = 0;
530 DIR *cpudir;
531 struct dirent *d;
532 int i;
533 unsigned eax, ebx, ecx, edx;
534
535 cpudir = opendir("/sys/devices/system/cpu");
536 if (!cpudir)
537 return;
538
539 /* Loop over cpuN entries */
540 while ((d = readdir(cpudir)) != NULL) {
541 DIR *dir;
542 int len;
543 char fname[sizeof("/sys/devices/system/cpu//cpuidle//desc") + 2*BIG_SYSNAME_LEN];
544
545 len = strlen(d->d_name);
546 if (len < 3 || len > BIG_SYSNAME_LEN)
547 continue;
548
549 if (!isdigit(d->d_name[3]))
550 continue;
551
Maksym Kryzhanovskyy6052c2b2010-11-04 08:41:57 +0100552 len = sprintf(fname, "%s/%s/cpuidle", "/sys/devices/system/cpu", d->d_name);
Marek Polacekb507cc32010-10-25 03:44:34 +0200553 dir = opendir(fname);
554 if (!dir)
555 continue;
556
557 /*
558 * Every C-state has its own stateN directory, that
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200559 * contains a 'time' and a 'usage' file.
Marek Polacekb507cc32010-10-25 03:44:34 +0200560 */
561 while ((d = readdir(dir)) != NULL) {
562 FILE *fp;
563 char buf[64];
564 int n;
565
566 n = strlen(d->d_name);
567 if (n < 3 || n > BIG_SYSNAME_LEN)
568 continue;
569
570 sprintf(fname + len, "/%s/desc", d->d_name);
571 fp = fopen_for_read(fname);
572 if (fp) {
573 char *p = fgets(buf, sizeof(buf), fp);
574 fclose(fp);
575 if (!p)
576 break;
577 p = strstr(p, "MWAIT ");
578 if (p) {
579 int pos;
580 p += sizeof("MWAIT ") - 1;
581 pos = (bb_strtoull(p, NULL, 16) >> 4) + 1;
582 if (pos >= ARRAY_SIZE(bios_table))
583 continue;
584 bios_table[pos]++;
585 nbios++;
586 }
587 }
588 }
589 closedir(dir);
590 }
591 closedir(cpudir);
592
593 if (!nbios)
594 return;
595
596 eax = 5;
597 ebx = ecx = edx = 0;
598 cpuid(&eax, &ebx, &ecx, &edx);
599 if (!edx || !(ecx & 1))
600 return;
601
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200602 printf("Your %s the following C-states: ", "CPU supports");
Marek Polacekb507cc32010-10-25 03:44:34 +0200603 i = 0;
604 while (edx) {
605 if (edx & 7)
606 printf("C%u ", i);
607 edx >>= 4;
608 i++;
609 }
610 bb_putchar('\n');
611
612 /* Print BIOS C-States */
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200613 printf("Your %s the following C-states: ", "BIOS reports");
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200614 for (i = 0; i < ARRAY_SIZE(bios_table); i++)
Marek Polacekb507cc32010-10-25 03:44:34 +0200615 if (bios_table[i])
616 printf("C%u ", i);
617
618 bb_putchar('\n');
Marek Polacekb507cc32010-10-25 03:44:34 +0200619}
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200620#else
Denys Vlasenkof29a1c52010-10-29 16:25:18 +0200621# define print_intel_cstates() ((void)0)
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200622#endif
Marek Polacekb507cc32010-10-25 03:44:34 +0200623
Denys Vlasenko373789e2010-10-26 02:54:13 +0200624static void show_timerstats(void)
Marek Polacekb507cc32010-10-25 03:44:34 +0200625{
626 unsigned lines;
627
628 /* Get terminal height */
629 get_terminal_width_height(STDOUT_FILENO, NULL, &lines);
630
631 /* We don't have whole terminal just for timerstats */
632 lines -= 12;
633
Denys Vlasenko373789e2010-10-26 02:54:13 +0200634 if (!G.cant_enable_timer_stats) {
Marek Polacekb507cc32010-10-25 03:44:34 +0200635 int i, n = 0;
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200636 char strbuf6[6];
Marek Polacekb507cc32010-10-25 03:44:34 +0200637
638 puts("\nTop causes for wakeups:");
Denys Vlasenko373789e2010-10-26 02:54:13 +0200639 for (i = 0; i < G.lines_cnt; i++) {
640 if ((G.lines[i].count > 0 /*|| G.lines[i].disk_count > 0*/)
Marek Polacekb507cc32010-10-25 03:44:34 +0200641 && n++ < lines
642 ) {
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200643 /* NB: upstream powertop prints "(wakeups/sec)",
644 * we print just "(wakeup counts)".
645 */
646 /*char c = ' ';
647 if (G.lines[i].disk_count)
Denys Vlasenko373789e2010-10-26 02:54:13 +0200648 c = 'D';*/
Denys Vlasenkoa407cf72013-09-06 12:53:14 +0200649 smart_ulltoa5(G.lines[i].count, strbuf6, " KMGTPEZY")[0] = '\0';
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200650 printf(/*" %5.1f%% (%s)%c %s\n"*/
651 " %5.1f%% (%s) %s\n",
652 G.lines[i].count * 100.0 / G.lines_cumulative_count,
653 strbuf6, /*c,*/
654 G.lines[i].string);
Marek Polacekb507cc32010-10-25 03:44:34 +0200655 }
656 }
657 } else {
658 bb_putchar('\n');
659 bb_error_msg("no stats available; run as root or"
Lauri Hintsalafb499c52013-01-04 10:51:57 +0200660 " enable the timer_stats module");
Marek Polacekb507cc32010-10-25 03:44:34 +0200661 }
662}
663
Denys Vlasenko373789e2010-10-26 02:54:13 +0200664// Example display from powertop version 1.11
665// Cn Avg residency P-states (frequencies)
666// C0 (cpu running) ( 0.5%) 2.00 Ghz 0.0%
667// polling 0.0ms ( 0.0%) 1.67 Ghz 0.0%
668// C1 mwait 0.0ms ( 0.0%) 1333 Mhz 0.1%
669// C2 mwait 0.1ms ( 0.1%) 1000 Mhz 99.9%
670// C3 mwait 12.1ms (99.4%)
671//
672// Wakeups-from-idle per second : 93.6 interval: 15.0s
673// no ACPI power usage estimate available
674//
675// Top causes for wakeups:
Denys Vlasenko4fa07bd2010-10-27 00:04:50 +0200676// 32.4% ( 26.7) <interrupt> : extra timer interrupt
677// 29.0% ( 23.9) <kernel core> : hrtimer_start_range_ns (tick_sched_timer)
Denys Vlasenko373789e2010-10-26 02:54:13 +0200678// 9.0% ( 7.5) <kernel core> : hrtimer_start (tick_sched_timer)
679// 6.5% ( 5.3) <interrupt> : ata_piix
680// 5.0% ( 4.1) inetd : hrtimer_start_range_ns (hrtimer_wakeup)
681
Marek Polacekb507cc32010-10-25 03:44:34 +0200682//usage:#define powertop_trivial_usage
683//usage: ""
684//usage:#define powertop_full_usage "\n\n"
Denys Vlasenko86031a52015-01-24 19:46:45 +0100685//usage: "Analyze power consumption on Intel-based laptops"
Marek Polacekb507cc32010-10-25 03:44:34 +0200686
687int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
688int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
689{
Denys Vlasenko373789e2010-10-26 02:54:13 +0200690 ullong cur_usage[MAX_CSTATE_COUNT];
691 ullong cur_duration[MAX_CSTATE_COUNT];
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200692 char cstate_lines[MAX_CSTATE_COUNT + 2][64];
Denys Vlasenko6c1f3482017-01-11 16:27:12 +0100693#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
Marek Polacekb507cc32010-10-25 03:44:34 +0200694 struct pollfd pfd[1];
695
696 pfd[0].fd = 0;
697 pfd[0].events = POLLIN;
698#endif
699
700 INIT_G();
701
Denys Vlasenko373789e2010-10-26 02:54:13 +0200702#if ENABLE_FEATURE_POWERTOP_PROCIRQ && BLOATY_HPET_IRQ_NUM_DETECTION
Marek Polacekb507cc32010-10-25 03:44:34 +0200703 G.percpu_hpet_start = INT_MAX;
704 G.percpu_hpet_end = INT_MIN;
705#endif
706
707 /* Print warning when we don't have superuser privileges */
708 if (geteuid() != 0)
709 bb_error_msg("run as root to collect enough information");
710
Marek Polacekb507cc32010-10-25 03:44:34 +0200711 /* Get number of CPUs */
712 G.total_cpus = get_cpu_count();
713
Denys Vlasenkod60752f2015-10-07 22:42:45 +0200714 puts("Collecting data for "DEFAULT_SLEEP_STR" seconds");
Marek Polacekb507cc32010-10-25 03:44:34 +0200715
Denys Vlasenko6c1f3482017-01-11 16:27:12 +0100716#if ENABLE_FEATURE_POWERTOP_INTERACTIVE
Denys Vlasenko01ccdd12017-01-11 16:17:59 +0100717 /* Turn on unbuffered input; turn off echoing, ^C ^Z etc */
718 set_termios_to_raw(STDIN_FILENO, &G.init_settings, TERMIOS_CLEAR_ISIG);
719 bb_signals(BB_FATAL_SIGS, sig_handler);
Denys Vlasenko373789e2010-10-26 02:54:13 +0200720 /* So we don't forget to reset term settings */
721 atexit(reset_term);
Marek Polacekb507cc32010-10-25 03:44:34 +0200722#endif
723
Marek Polacekb507cc32010-10-25 03:44:34 +0200724 /* Collect initial data */
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200725 process_irq_counts();
Marek Polacekb507cc32010-10-25 03:44:34 +0200726
727 /* Read initial usage and duration */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200728 read_cstate_counts(G.start_usage, G.start_duration);
Marek Polacekb507cc32010-10-25 03:44:34 +0200729
730 /* Copy them to "last" */
731 memcpy(G.last_usage, G.start_usage, sizeof(G.last_usage));
732 memcpy(G.last_duration, G.start_duration, sizeof(G.last_duration));
733
734 /* Display C-states */
735 print_intel_cstates();
736
Denys Vlasenko373789e2010-10-26 02:54:13 +0200737 G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
Marek Polacekb507cc32010-10-25 03:44:34 +0200738
739 /* The main loop */
740 for (;;) {
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200741 //double maxsleep = 0.0;
Marek Polacekb507cc32010-10-25 03:44:34 +0200742 ullong totalticks, totalevents;
743 int i;
Marek Polacekb507cc32010-10-25 03:44:34 +0200744
Denys Vlasenko373789e2010-10-26 02:54:13 +0200745 G.cant_enable_timer_stats |= start_timer(); /* 1 on error */
Denys Vlasenko6c1f3482017-01-11 16:27:12 +0100746#if !ENABLE_FEATURE_POWERTOP_INTERACTIVE
Marek Polacekb507cc32010-10-25 03:44:34 +0200747 sleep(DEFAULT_SLEEP);
748#else
749 if (safe_poll(pfd, 1, DEFAULT_SLEEP * 1000) > 0) {
750 unsigned char c;
751 if (safe_read(STDIN_FILENO, &c, 1) != 1)
752 break; /* EOF/error */
753 if (c == G.init_settings.c_cc[VINTR])
754 break; /* ^C */
755 if ((c | 0x20) == 'q')
756 break;
757 }
758#endif
Denys Vlasenko373789e2010-10-26 02:54:13 +0200759 G.cant_enable_timer_stats |= stop_timer(); /* 1 on error */
Marek Polacekb507cc32010-10-25 03:44:34 +0200760
761 clear_lines();
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200762 process_irq_counts();
Marek Polacekb507cc32010-10-25 03:44:34 +0200763
764 /* Clear the stats */
765 memset(cur_duration, 0, sizeof(cur_duration));
766 memset(cur_usage, 0, sizeof(cur_usage));
767
768 /* Read them */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200769 read_cstate_counts(cur_usage, cur_duration);
Marek Polacekb507cc32010-10-25 03:44:34 +0200770
771 /* Count totalticks and totalevents */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200772 totalticks = totalevents = 0;
773 for (i = 0; i < MAX_CSTATE_COUNT; i++) {
774 if (cur_usage[i] != 0) {
Marek Polacekb507cc32010-10-25 03:44:34 +0200775 totalticks += cur_duration[i] - G.last_duration[i];
776 totalevents += cur_usage[i] - G.last_usage[i];
777 }
778 }
779
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200780 /* Clear the screen */
781 printf("\033[H\033[J");
Marek Polacekb507cc32010-10-25 03:44:34 +0200782
783 /* Clear C-state lines */
784 memset(&cstate_lines, 0, sizeof(cstate_lines));
785
786 if (totalevents == 0 && G.maxcstate <= 1) {
787 /* This should not happen */
Denys Vlasenkoa43e9692010-10-26 13:03:31 +0200788 strcpy(cstate_lines[0], "C-state information is not available\n");
Marek Polacekb507cc32010-10-25 03:44:34 +0200789 } else {
Marek Polacekb507cc32010-10-25 03:44:34 +0200790 double percentage;
Denys Vlasenkoa43e9692010-10-26 13:03:31 +0200791 unsigned newticks;
Marek Polacekb507cc32010-10-25 03:44:34 +0200792
793 newticks = G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000 - totalticks;
Marek Polacekb507cc32010-10-25 03:44:34 +0200794 /* Handle rounding errors: do not display negative values */
Denys Vlasenkoa43e9692010-10-26 13:03:31 +0200795 if ((int)newticks < 0)
Marek Polacekb507cc32010-10-25 03:44:34 +0200796 newticks = 0;
797
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200798 sprintf(cstate_lines[0], "Cn\t\t Avg residency\n");
Marek Polacekb507cc32010-10-25 03:44:34 +0200799 percentage = newticks * 100.0 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200800 sprintf(cstate_lines[1], "C0 (cpu running) (%4.1f%%)\n", percentage);
Marek Polacekb507cc32010-10-25 03:44:34 +0200801
802 /* Compute values for individual C-states */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200803 for (i = 0; i < MAX_CSTATE_COUNT; i++) {
804 if (cur_usage[i] != 0) {
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200805 double slept;
Marek Polacekb507cc32010-10-25 03:44:34 +0200806 slept = (cur_duration[i] - G.last_duration[i])
807 / (cur_usage[i] - G.last_usage[i] + 0.1) / FREQ_ACPI;
808 percentage = (cur_duration[i] - G.last_duration[i]) * 100
809 / (G.total_cpus * DEFAULT_SLEEP * FREQ_ACPI_1000);
Denys Vlasenkoa43e9692010-10-26 13:03:31 +0200810 sprintf(cstate_lines[i + 2], "C%u\t\t%5.1fms (%4.1f%%)\n",
811 i + 1, slept, percentage);
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200812 //if (maxsleep < slept)
813 // maxsleep = slept;
Marek Polacekb507cc32010-10-25 03:44:34 +0200814 }
815 }
816 }
817
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200818 for (i = 0; i < MAX_CSTATE_COUNT + 2; i++)
819 if (cstate_lines[i][0])
Denys Vlasenkoa43e9692010-10-26 13:03:31 +0200820 fputs(cstate_lines[i], stdout);
Marek Polacekb507cc32010-10-25 03:44:34 +0200821
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200822 i = process_timer_stats();
Marek Polacekb507cc32010-10-25 03:44:34 +0200823#if ENABLE_FEATURE_POWERTOP_PROCIRQ
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200824 if (totalevents == 0) {
825 /* No C-state info available, use timerstats */
826 totalevents = i * G.total_cpus + G.total_interrupt;
827 if (i < 0)
828 totalevents += G.interrupt_0 - i;
Marek Polacekb507cc32010-10-25 03:44:34 +0200829 }
830#endif
Denys Vlasenkoc3f1fa12010-10-26 12:39:36 +0200831 /* Upstream powertop prints wakeups per sec per CPU,
832 * we print just raw wakeup counts.
833 */
834//TODO: show real seconds (think about manual refresh)
835 printf("\nWakeups-from-idle in %u seconds: %llu\n",
836 DEFAULT_SLEEP,
837 totalevents
838 );
Marek Polacekb507cc32010-10-25 03:44:34 +0200839
Denys Vlasenko373789e2010-10-26 02:54:13 +0200840 update_lines_cumulative_count();
841 sort_lines();
842 show_timerstats();
Marek Polacekb507cc32010-10-25 03:44:34 +0200843 fflush(stdout);
844
845 /* Clear the stats */
846 memset(cur_duration, 0, sizeof(cur_duration));
847 memset(cur_usage, 0, sizeof(cur_usage));
848
849 /* Get new values */
Denys Vlasenko373789e2010-10-26 02:54:13 +0200850 read_cstate_counts(cur_usage, cur_duration);
Marek Polacekb507cc32010-10-25 03:44:34 +0200851
852 /* Save them */
853 memcpy(G.last_usage, cur_usage, sizeof(G.last_usage));
854 memcpy(G.last_duration, cur_duration, sizeof(G.last_duration));
855 } /* for (;;) */
856
857 bb_putchar('\n');
858
859 return EXIT_SUCCESS;
860}