1 /*
2 * Copyright 2007, Intel Corporation
3 *
4 * This file is part of PowerTOP
5 *
6 * This program file is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program in a file named COPYING; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 *
21 * Authors:
22 * Arjan van de Ven <arjan@linux.intel.com>
23 */
24
25 #include <getopt.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdint.h>
31 #include <sys/types.h>
32 #include <dirent.h>
33 #include <libintl.h>
34 #include <ctype.h>
35 #include <assert.h>
36 #include <locale.h>
37 #include <time.h>
38 #include <sys/stat.h>
39
40 #include "powertop.h"
41
42 #define VERSION "1.11"
43
44 uint64_t start_usage[8], start_duration[8];
45 uint64_t last_usage[8], last_duration[8];
46 char cnames[8][16];
47
48 double ticktime = 15.0;
49
50 int interrupt_0, total_interrupt;
51
52 int showpids = 0;
53
54 static int maxcstate = 0;
55 int topcstate = 0;
56
57 int dump = 0;
58
59 #define IRQCOUNT 150
60
61 struct irqdata {
62 int active;
63 int number;
64 uint64_t count;
65 char description[256];
66 };
67
68 struct irqdata interrupts[IRQCOUNT];
69
70 #define FREQ_ACPI 3579.545
71 static unsigned long FREQ;
72
73 int nostats;
74
75
76 struct line *lines;
77 int linehead;
78 int linesize;
79 int linectotal;
80
81
82 double last_bat_cap = 0;
83 double prev_bat_cap = 0;
84 time_t last_bat_time = 0;
85 time_t prev_bat_time = 0;
86
87 double displaytime = 0.0;
88
push_line(char * string,int count)89 void push_line(char *string, int count)
90 {
91 int i;
92
93 assert(string != NULL);
94 for (i = 0; i < linehead; i++)
95 if (strcmp(string, lines[i].string) == 0) {
96 lines[i].count += count;
97 return;
98 }
99 if (linehead == linesize)
100 lines = realloc (lines, (linesize ? (linesize *= 2) : (linesize = 64)) * sizeof (struct line));
101 lines[linehead].string = strdup (string);
102 lines[linehead].count = count;
103 lines[linehead].pid[0] = 0;
104 linehead++;
105 }
106
push_line_pid(char * string,int count,char * pid)107 void push_line_pid(char *string, int count, char *pid)
108 {
109 int i;
110 assert(string != NULL);
111 for (i = 0; i < linehead; i++)
112 if (strcmp(string, lines[i].string) == 0) {
113 lines[i].count += count;
114 if (pid && strcmp(lines[i].pid, pid)!=0)
115 lines[i].pid[0] = 0;
116 return;
117 }
118 if (linehead == linesize)
119 lines = realloc (lines, (linesize ? (linesize *= 2) : (linesize = 64)) * sizeof (struct line));
120 lines[linehead].string = strdup (string);
121 lines[linehead].count = count;
122 if (pid)
123 strcpy(lines[linehead].pid, pid);
124 linehead++;
125 }
126
clear_lines(void)127 void clear_lines(void)
128 {
129 int i;
130 for (i = 0; i < linehead; i++)
131 free (lines[i].string);
132 free (lines);
133 linehead = linesize = 0;
134 lines = NULL;
135 }
136
count_lines(void)137 void count_lines(void)
138 {
139 uint64_t q = 0;
140 int i;
141 for (i = 0; i < linehead; i++)
142 q += lines[i].count;
143 linectotal = q;
144 }
145
update_irq(int irq,uint64_t count,char * name)146 int update_irq(int irq, uint64_t count, char *name)
147 {
148 int i;
149 int firstfree = IRQCOUNT;
150
151 if (!name)
152 return 0;
153
154 for (i = 0; i < IRQCOUNT; i++) {
155 if (interrupts[i].active && interrupts[i].number == irq) {
156 uint64_t oldcount;
157 oldcount = interrupts[i].count;
158 interrupts[i].count = count;
159 return count - oldcount;
160 }
161 if (!interrupts[i].active && firstfree > i)
162 firstfree = i;
163 }
164
165 interrupts[firstfree].active = 1;
166 interrupts[firstfree].count = count;
167 interrupts[firstfree].number = irq;
168 strcpy(interrupts[firstfree].description, name);
169 if (strcmp(name,"i8042\n")==0)
170 strcpy(interrupts[firstfree].description, _("PS/2 keyboard/mouse/touchpad"));
171 return count;
172 }
173
do_proc_irq(void)174 static void do_proc_irq(void)
175 {
176 FILE *file;
177 char line[1024];
178 char line2[1024];
179 char *name;
180 uint64_t delta;
181
182 interrupt_0 = 0;
183 total_interrupt = 0;
184
185 file = fopen("/proc/interrupts", "r");
186 if (!file)
187 return;
188 while (!feof(file)) {
189 char *c;
190 int nr = -1;
191 uint64_t count = 0;
192 int special = 0;
193 memset(line, 0, sizeof(line));
194 if (fgets(line, 1024, file) == NULL)
195 break;
196 c = strchr(line, ':');
197 if (!c)
198 continue;
199 /* deal with NMI and the like.. make up fake nrs */
200 if (line[0] != ' ' && (line[0] < '0' || line[0] > '9')) {
201 if (strncmp(line,"NMI:", 4)==0)
202 nr=20000;
203 if (strncmp(line,"RES:", 4)==0)
204 nr=20001;
205 if (strncmp(line,"CAL:", 4)==0)
206 nr=20002;
207 if (strncmp(line,"TLB:", 4)==0)
208 nr=20003;
209 if (strncmp(line,"TRM:", 4)==0)
210 nr=20004;
211 if (strncmp(line,"THR:", 4)==0)
212 nr=20005;
213 if (strncmp(line,"SPU:", 4)==0)
214 nr=20006;
215 special = 1;
216 } else
217 nr = strtoull(line, NULL, 10);
218
219 if (nr==-1)
220 continue;
221 *c = 0;
222 c++;
223 while (c && strlen(c)) {
224 char *newc;
225 count += strtoull(c, &newc, 10);
226 if (newc == c)
227 break;
228 c = newc;
229 }
230 c = strchr(c, ' ');
231 if (!c)
232 continue;
233 while (c && *c == ' ')
234 c++;
235 if (!special) {
236 c = strchr(c, ' ');
237 if (!c)
238 continue;
239 while (c && *c == ' ')
240 c++;
241 }
242 name = c;
243 delta = update_irq(nr, count, name);
244 c = strchr(name, '\n');
245 if (c)
246 *c = 0;
247 if (strcmp(name, "i8042")) {
248 if (special)
249 sprintf(line2, _(" <kernel IPI> : %s"), name);
250 else
251 sprintf(line2, _(" <interrupt> : %s"), name);
252 }
253 else
254 sprintf(line2, _(" <interrupt> : %s"), _("PS/2 keyboard/mouse/touchpad"));
255
256 if (nr > 0 && delta > 0)
257 push_line(line2, delta);
258 if (nr==0)
259 interrupt_0 = delta;
260 else
261 total_interrupt += delta;
262 }
263 fclose(file);
264 }
265
read_data_acpi(uint64_t * usage,uint64_t * duration)266 static void read_data_acpi(uint64_t * usage, uint64_t * duration)
267 {
268 DIR *dir;
269 struct dirent *entry;
270 FILE *file = NULL;
271 char line[4096];
272 char *c;
273 int clevel = 0;
274
275 memset(usage, 0, 64);
276 memset(duration, 0, 64);
277
278 dir = opendir("/proc/acpi/processor");
279 if (!dir)
280 return;
281 while ((entry = readdir(dir))) {
282 if (strlen(entry->d_name) < 3)
283 continue;
284 sprintf(line, "/proc/acpi/processor/%s/power", entry->d_name);
285 file = fopen(line, "r");
286 if (!file)
287 continue;
288
289 clevel = 0;
290
291 while (!feof(file)) {
292 memset(line, 0, 4096);
293 if (fgets(line, 4096, file) == NULL)
294 break;
295 c = strstr(line, "age[");
296 if (!c)
297 continue;
298 c += 4;
299 usage[clevel] += 1+strtoull(c, NULL, 10);
300 c = strstr(line, "ation[");
301 if (!c)
302 continue;
303 c += 6;
304 duration[clevel] += strtoull(c, NULL, 10);
305
306 clevel++;
307 if (clevel > maxcstate)
308 maxcstate = clevel;
309
310 }
311 fclose(file);
312 }
313 closedir(dir);
314 }
315
read_data_cpuidle(uint64_t * usage,uint64_t * duration)316 static void read_data_cpuidle(uint64_t * usage, uint64_t * duration)
317 {
318 DIR *cpudir;
319 DIR *dir;
320 struct dirent *entry;
321 FILE *file = NULL;
322 char line[4096];
323 char filename[128], *f;
324 int len, clevel = 0;
325
326 memset(usage, 0, 64);
327 memset(duration, 0, 64);
328
329 cpudir = opendir("/sys/devices/system/cpu");
330 if (!cpudir)
331 return;
332
333 /* Loop over cpuN entries */
334 while ((entry = readdir(cpudir))) {
335 if (strlen(entry->d_name) < 3)
336 continue;
337
338 if (!isdigit(entry->d_name[3]))
339 continue;
340
341 len = sprintf(filename, "/sys/devices/system/cpu/%s/cpuidle",
342 entry->d_name);
343
344 dir = opendir(filename);
345 if (!dir)
346 return;
347
348 clevel = 0;
349
350 /* For each C-state, there is a stateX directory which
351 * contains a 'usage' and a 'time' (duration) file */
352 while ((entry = readdir(dir))) {
353 if (strlen(entry->d_name) < 3)
354 continue;
355 sprintf(filename + len, "/%s/desc", entry->d_name);
356 file = fopen(filename, "r");
357 if (file) {
358
359 memset(line, 0, 4096);
360 f = fgets(line, 4096, file);
361 fclose(file);
362 if (f == NULL)
363 break;
364
365
366 f = strstr(line, "MWAIT ");
367 if (f) {
368 f += 6;
369 clevel = (strtoull(f, NULL, 16)>>4) + 1;
370 sprintf(cnames[clevel], "C%i mwait", clevel);
371 } else
372 sprintf(cnames[clevel], "C%i\t", clevel);
373
374 f = strstr(line, "POLL IDLE");
375 if (f) {
376 clevel = 0;
377 sprintf(cnames[clevel], "%s\t", _("polling"));
378 }
379
380 f = strstr(line, "ACPI HLT");
381 if (f) {
382 clevel = 1;
383 sprintf(cnames[clevel], "%s\t", "C1 halt");
384 }
385 }
386 sprintf(filename + len, "/%s/usage", entry->d_name);
387 file = fopen(filename, "r");
388 if (!file)
389 continue;
390
391 memset(line, 0, 4096);
392 f = fgets(line, 4096, file);
393 fclose(file);
394 if (f == NULL)
395 break;
396
397 usage[clevel] += 1+strtoull(line, NULL, 10);
398
399 sprintf(filename + len, "/%s/time", entry->d_name);
400 file = fopen(filename, "r");
401 if (!file)
402 continue;
403
404 memset(line, 0, 4096);
405 f = fgets(line, 4096, file);
406 fclose(file);
407 if (f == NULL)
408 break;
409
410 duration[clevel] += 1+strtoull(line, NULL, 10);
411
412 clevel++;
413 if (clevel > maxcstate)
414 maxcstate = clevel;
415
416 }
417 closedir(dir);
418
419 }
420 closedir(cpudir);
421 }
422
read_data(uint64_t * usage,uint64_t * duration)423 static void read_data(uint64_t * usage, uint64_t * duration)
424 {
425 int r;
426 struct stat s;
427
428 /* Then check for CPUidle */
429 r = stat("/sys/devices/system/cpu/cpu0/cpuidle", &s);
430 if (!r) {
431 read_data_cpuidle(usage, duration);
432
433 /* perform residency calculations based on usecs */
434 FREQ = 1000;
435 return;
436 }
437
438 /* First, check for ACPI */
439 r = stat("/proc/acpi/processor", &s);
440 if (!r) {
441 read_data_acpi(usage, duration);
442
443 /* perform residency calculations based on ACPI timer */
444 FREQ = FREQ_ACPI;
445 return;
446 }
447 }
448
stop_timerstats(void)449 void stop_timerstats(void)
450 {
451 FILE *file;
452 file = fopen("/proc/timer_stats", "w");
453 if (!file) {
454 nostats = 1;
455 return;
456 }
457 fprintf(file, "0\n");
458 fclose(file);
459 }
start_timerstats(void)460 void start_timerstats(void)
461 {
462 FILE *file;
463 file = fopen("/proc/timer_stats", "w");
464 if (!file) {
465 nostats = 1;
466 return;
467 }
468 fprintf(file, "1\n");
469 fclose(file);
470 }
471
line_compare(const void * av,const void * bv)472 int line_compare (const void *av, const void *bv)
473 {
474 const struct line *a = av, *b = bv;
475 return b->count - a->count;
476 }
477
sort_lines(void)478 void sort_lines(void)
479 {
480 qsort (lines, linehead, sizeof (struct line), line_compare);
481 }
482
483
484
print_battery_proc_acpi(void)485 int print_battery_proc_acpi(void)
486 {
487 DIR *dir;
488 struct dirent *dirent;
489 FILE *file;
490 double rate = 0;
491 double cap = 0;
492
493 char filename[256];
494
495 dir = opendir("/proc/acpi/battery");
496 if (!dir)
497 return 0;
498
499 while ((dirent = readdir(dir))) {
500 int dontcount = 0;
501 double voltage = 0.0;
502 double amperes_drawn = 0.0;
503 double watts_drawn = 0.0;
504 double amperes_left = 0.0;
505 double watts_left = 0.0;
506 char line[1024];
507
508 if (strlen(dirent->d_name) < 3)
509 continue;
510
511 sprintf(filename, "/proc/acpi/battery/%s/state", dirent->d_name);
512 file = fopen(filename, "r");
513 if (!file)
514 continue;
515 memset(line, 0, 1024);
516 while (fgets(line, 1024, file) != NULL) {
517 char *c;
518 if (strstr(line, "present:") && strstr(line, "no"))
519 break;
520
521 if (strstr(line, "charging state:")
522 && !strstr(line, "discharging"))
523 dontcount = 1;
524 c = strchr(line, ':');
525 if (!c)
526 continue;
527 c++;
528
529 if (strstr(line, "present voltage"))
530 voltage = strtoull(c, NULL, 10) / 1000.0;
531
532 if (strstr(line, "remaining capacity") && strstr(c, "mW"))
533 watts_left = strtoull(c, NULL, 10) / 1000.0;
534
535 if (strstr(line, "remaining capacity") && strstr(c, "mAh"))
536 amperes_left = strtoull(c, NULL, 10) / 1000.0;
537
538 if (strstr(line, "present rate") && strstr(c, "mW"))
539 watts_drawn = strtoull(c, NULL, 10) / 1000.0 ;
540
541 if (strstr(line, "present rate") && strstr(c, "mA"))
542 amperes_drawn = strtoull(c, NULL, 10) / 1000.0;
543
544 }
545 fclose(file);
546
547 if (!dontcount) {
548 rate += watts_drawn + voltage * amperes_drawn;
549 }
550 cap += watts_left + voltage * amperes_left;
551
552
553 }
554 closedir(dir);
555 if (prev_bat_cap - cap < 0.001 && rate < 0.001)
556 last_bat_time = 0;
557 if (!last_bat_time) {
558 last_bat_time = prev_bat_time = time(NULL);
559 last_bat_cap = prev_bat_cap = cap;
560 }
561 if (time(NULL) - last_bat_time >= 400) {
562 prev_bat_cap = last_bat_cap;
563 prev_bat_time = last_bat_time;
564 last_bat_time = time(NULL);
565 last_bat_cap = cap;
566 }
567
568 show_acpi_power_line(rate, cap, prev_bat_cap - cap, time(NULL) - prev_bat_time);
569 return 1;
570 }
571
print_battery_proc_pmu(void)572 int print_battery_proc_pmu(void)
573 {
574 char line[80];
575 int i;
576 int power_present = 0;
577 int num_batteries = 0;
578 /* unsigned rem_time_sec = 0; */
579 unsigned charge_mAh = 0, max_charge_mAh = 0, voltage_mV = 0;
580 int discharge_mA = 0;
581 FILE *fd;
582
583 fd = fopen("/proc/pmu/info", "r");
584 if (fd == NULL)
585 return 0;
586
587 while ( fgets(line, sizeof(line), fd) != NULL )
588 {
589 if (strncmp("AC Power", line, strlen("AC Power")) == 0)
590 sscanf(strchr(line, ':')+2, "%d", &power_present);
591 else if (strncmp("Battery count", line, strlen("Battery count")) == 0)
592 sscanf(strchr(line, ':')+2, "%d", &num_batteries);
593 }
594 fclose(fd);
595
596 for (i = 0; i < num_batteries; ++i)
597 {
598 char file_name[20];
599 int flags = 0;
600 /* int battery_charging, battery_full; */
601 /* unsigned this_rem_time_sec = 0; */
602 unsigned this_charge_mAh = 0, this_max_charge_mAh = 0;
603 unsigned this_voltage_mV = 0, this_discharge_mA = 0;
604
605 snprintf(file_name, sizeof(file_name), "/proc/pmu/battery_%d", i);
606 fd = fopen(file_name, "r");
607 if (fd == NULL)
608 continue;
609
610 while (fgets(line, sizeof(line), fd) != NULL)
611 {
612 if (strncmp("flags", line, strlen("flags")) == 0)
613 sscanf(strchr(line, ':')+2, "%x", &flags);
614 else if (strncmp("charge", line, strlen("charge")) == 0)
615 sscanf(strchr(line, ':')+2, "%d", &this_charge_mAh);
616 else if (strncmp("max_charge", line, strlen("max_charge")) == 0)
617 sscanf(strchr(line, ':')+2, "%d", &this_max_charge_mAh);
618 else if (strncmp("voltage", line, strlen("voltage")) == 0)
619 sscanf(strchr(line, ':')+2, "%d", &this_voltage_mV);
620 else if (strncmp("current", line, strlen("current")) == 0)
621 sscanf(strchr(line, ':')+2, "%d", &this_discharge_mA);
622 /* else if (strncmp("time rem.", line, strlen("time rem.")) == 0) */
623 /* sscanf(strchr(line, ':')+2, "%d", &this_rem_time_sec); */
624 }
625 fclose(fd);
626
627 if ( !(flags & 0x1) )
628 /* battery isn't present */
629 continue;
630
631 /* battery_charging = flags & 0x2; */
632 /* battery_full = !battery_charging && power_present; */
633
634 charge_mAh += this_charge_mAh;
635 max_charge_mAh += this_max_charge_mAh;
636 voltage_mV += this_voltage_mV;
637 discharge_mA += this_discharge_mA;
638 /* rem_time_sec += this_rem_time_sec; */
639 }
640 show_pmu_power_line(voltage_mV, charge_mAh, max_charge_mAh,
641 discharge_mA);
642 return 1;
643 }
644
print_battery_sysfs(void)645 void print_battery_sysfs(void)
646 {
647 DIR *dir;
648 struct dirent *dirent;
649 FILE *file;
650 double rate = 0;
651 double cap = 0;
652
653 char filename[256];
654
655 if (print_battery_proc_acpi())
656 return;
657
658 if (print_battery_proc_pmu())
659 return;
660
661 dir = opendir("/sys/class/power_supply");
662 if (!dir) {
663 return;
664 }
665
666 while ((dirent = readdir(dir))) {
667 int dontcount = 0;
668 double voltage = 0.0;
669 double amperes_drawn = 0.0;
670 double watts_drawn = 0.0;
671 double watts_left = 0.0;
672 char line[1024];
673
674 if (strstr(dirent->d_name, "AC"))
675 continue;
676
677 sprintf(filename, "/sys/class/power_supply/%s/present", dirent->d_name);
678 file = fopen(filename, "r");
679 if (!file)
680 continue;
681 int s;
682 if ((s = getc(file)) != EOF) {
683 if (s == 0)
684 break;
685 }
686 fclose(file);
687
688 sprintf(filename, "/sys/class/power_supply/%s/status", dirent->d_name);
689 file = fopen(filename, "r");
690 if (!file)
691 continue;
692 memset(line, 0, 1024);
693 if (fgets(line, 1024, file) != NULL) {
694 if (!strstr(line, "Discharging"))
695 dontcount = 1;
696 }
697 fclose(file);
698
699 sprintf(filename, "/sys/class/power_supply/%s/voltage_now", dirent->d_name);
700 file = fopen(filename, "r");
701 if (!file)
702 continue;
703 memset(line, 0, 1024);
704 if (fgets(line, 1024, file) != NULL) {
705 voltage = strtoull(line, NULL, 10) / 1000000.0;
706 }
707 fclose(file);
708
709 sprintf(filename, "/sys/class/power_supply/%s/energy_now", dirent->d_name);
710 file = fopen(filename, "r");
711 watts_left = 1;
712 if (!file) {
713 sprintf(filename, "/sys/class/power_supply/%s/charge_now", dirent->d_name);
714 file = fopen(filename, "r");
715 if (!file)
716 continue;
717
718 /* W = A * V */
719 watts_left = voltage;
720 }
721 memset(line, 0, 1024);
722 if (fgets(line, 1024, file) != NULL)
723 watts_left *= strtoull(line, NULL, 10) / 1000000.0;
724 fclose(file);
725
726 sprintf(filename, "/sys/class/power_supply/%s/current_now", dirent->d_name);
727 file = fopen(filename, "r");
728 if (!file)
729 continue;
730 memset(line, 0, 1024);
731 if (fgets(line, 1024, file) != NULL) {
732 amperes_drawn = strtoull(line, NULL, 10) / 1000000.0;
733 }
734 fclose(file);
735
736 if (!dontcount) {
737 rate += watts_drawn + voltage * amperes_drawn;
738 }
739 cap += watts_left;
740
741
742 }
743 closedir(dir);
744 if (prev_bat_cap - cap < 0.001 && rate < 0.001)
745 last_bat_time = 0;
746 if (!last_bat_time) {
747 last_bat_time = prev_bat_time = time(NULL);
748 last_bat_cap = prev_bat_cap = cap;
749 }
750 if (time(NULL) - last_bat_time >= 400) {
751 prev_bat_cap = last_bat_cap;
752 prev_bat_time = last_bat_time;
753 last_bat_time = time(NULL);
754 last_bat_cap = cap;
755 }
756
757 show_acpi_power_line(rate, cap, prev_bat_cap - cap, time(NULL) - prev_bat_time);
758 }
759
760 char cstate_lines[12][200];
761
usage()762 void usage()
763 {
764 printf(_("Usage: powertop [OPTION...]\n"));
765 printf(_(" -d, --dump read wakeups once and print list of top offenders\n"));
766 printf(_(" -t, --time=DOUBLE default time to gather data in seconds\n"));
767 printf(_(" -h, --help Show this help message\n"));
768 printf(_(" -v, --version Show version information and exit\n"));
769 exit(0);
770 }
771
version()772 void version()
773 {
774 printf(_("powertop version %s\n"), VERSION);
775 exit(0);
776 }
777
main(int argc,char ** argv)778 int main(int argc, char **argv)
779 {
780 char line[1024];
781 int ncursesinited=0;
782 FILE *file = NULL;
783 uint64_t cur_usage[8], cur_duration[8];
784 double wakeups_per_second = 0;
785
786 setlocale (LC_ALL, "");
787 bindtextdomain ("powertop", "/usr/share/locale");
788 textdomain ("powertop");
789
790 while (1) {
791 static struct option opts[] = {
792 { "dump", 0, NULL, 'd' },
793 { "time", 1, NULL, 't' },
794 { "help", 0, NULL, 'h' },
795 { "version", 0, NULL, 'v' },
796 { 0, 0, NULL, 0 }
797 };
798 int index2 = 0, c;
799
800 c = getopt_long(argc, argv, "dt:hv", opts, &index2);
801 if (c == -1)
802 break;
803 switch (c) {
804 case 'd':
805 dump = 1;
806 break;
807 case 't':
808 ticktime = strtod(optarg, NULL);
809 break;
810 case 'h':
811 usage();
812 break;
813 case 'v':
814 version();
815 break;
816 default:
817 ;
818 }
819 }
820
821 if (!dump)
822 ticktime = 5.0;
823
824 system("/sbin/modprobe cpufreq_stats &> /dev/null");
825 read_data(&start_usage[0], &start_duration[0]);
826
827
828 memcpy(last_usage, start_usage, sizeof(last_usage));
829 memcpy(last_duration, start_duration, sizeof(last_duration));
830
831 do_proc_irq();
832 do_proc_irq();
833 do_cpufreq_stats();
834 count_usb_urbs();
835 count_usb_urbs();
836
837 memset(cur_usage, 0, sizeof(cur_usage));
838 memset(cur_duration, 0, sizeof(cur_duration));
839 printf("PowerTOP " VERSION " (C) 2007, 2008 Intel Corporation \n\n");
840 if (geteuid() != 0)
841 printf(_("PowerTOP needs to be run as root to collect enough information\n"));
842 printf(_("Collecting data for %i seconds \n"), (int)ticktime);
843 printf("\n\n");
844 print_intel_cstates();
845 stop_timerstats();
846
847 while (1) {
848 double maxsleep = 0.0;
849 int64_t totalticks;
850 int64_t totalevents;
851 fd_set rfds;
852 struct timeval tv;
853 int key;
854
855 int i = 0;
856 double c0 = 0;
857 char *c;
858
859
860 FD_ZERO(&rfds);
861 FD_SET(0, &rfds);
862 tv.tv_sec = ticktime;
863 tv.tv_usec = (ticktime - tv.tv_sec) * 1000000;;
864 do_proc_irq();
865 start_timerstats();
866
867
868 key = select(1, &rfds, NULL, NULL, &tv);
869
870 if (key && tv.tv_sec) ticktime = ticktime - tv.tv_sec - tv.tv_usec/1000000.0;
871
872
873 stop_timerstats();
874 clear_lines();
875 do_proc_irq();
876 read_data(&cur_usage[0], &cur_duration[0]);
877
878 totalticks = 0;
879 totalevents = 0;
880 for (i = 0; i < 8; i++)
881 if (cur_usage[i]) {
882 totalticks += cur_duration[i] - last_duration[i];
883 totalevents += cur_usage[i] - last_usage[i];
884 }
885
886 if (!dump) {
887 if (!ncursesinited) {
888 initialize_curses();
889 ncursesinited++;
890 }
891 setup_windows();
892 show_title_bar();
893 }
894
895 memset(&cstate_lines, 0, sizeof(cstate_lines));
896 topcstate = -4;
897 if (totalevents == 0 && maxcstate <= 1) {
898 sprintf(cstate_lines[5],_("< Detailed C-state information is not available.>\n"));
899 } else {
900 double sleept, percentage;;
901 c0 = sysconf(_SC_NPROCESSORS_ONLN) * ticktime * 1000 * FREQ - totalticks;
902 if (c0 < 0)
903 c0 = 0; /* rounding errors in measurement might make c0 go slightly negative.. this is confusing */
904 sprintf(cstate_lines[0], _("Cn\t Avg residency\n"));
905
906 percentage = c0 * 100.0 / (sysconf(_SC_NPROCESSORS_ONLN) * ticktime * 1000 * FREQ);
907 sprintf(cstate_lines[1], _("C0 (cpu running) (%4.1f%%)\n"), percentage);
908 if (percentage > 50)
909 topcstate = 0;
910 for (i = 0; i < 8; i++)
911 if (cur_usage[i]) {
912 sleept = (cur_duration[i] - last_duration[i]) / (cur_usage[i] - last_usage[i]
913 + 0.1) / FREQ;
914 percentage = (cur_duration[i] -
915 last_duration[i]) * 100 /
916 (sysconf(_SC_NPROCESSORS_ONLN) * ticktime * 1000 * FREQ);
917
918 if (cnames[i][0]==0)
919 sprintf(cnames[i],"C%i",i+1);
920 sprintf
921 (cstate_lines[2+i], _("%s\t%5.1fms (%4.1f%%)\n"),
922 cnames[i], sleept, percentage);
923 if (maxsleep < sleept)
924 maxsleep = sleept;
925 if (percentage > 50)
926 topcstate = i+1;
927
928 }
929 }
930 do_cpufreq_stats();
931 show_cstates();
932 /* now the timer_stats info */
933 memset(line, 0, sizeof(line));
934 totalticks = 0;
935 file = NULL;
936 if (!nostats)
937 file = fopen("/proc/timer_stats", "r");
938 while (file && !feof(file)) {
939 char *count, *pid, *process, *func;
940 char line2[1024];
941 int cnt;
942 int deferrable = 0;
943 memset(line, 0, 1024);
944 if (fgets(line, 1024, file) == NULL)
945 break;
946 if (strstr(line, "total events"))
947 break;
948 c = count = &line[0];
949 c = strchr(c, ',');
950 if (!c)
951 continue;
952 *c = 0;
953 c++;
954 while (*c != 0 && *c == ' ')
955 c++;
956 pid = c;
957 c = strchr(c, ' ');
958 if (!c)
959 continue;
960 *c = 0;
961 c++;
962 while (*c != 0 && *c == ' ')
963 c++;
964 process = c;
965 c = strchr(c, ' ');
966 if (!c)
967 continue;
968 *c = 0;
969 c++;
970 while (*c != 0 && *c == ' ')
971 c++;
972 func = c;
973 if (strcmp(process, "insmod") == 0)
974 process = _("<kernel module>");
975 if (strcmp(process, "modprobe") == 0)
976 process = _("<kernel module>");
977 if (strcmp(process, "swapper") == 0)
978 process = _("<kernel core>");
979 c = strchr(c, '\n');
980 if (strncmp(func, "tick_nohz_", 10) == 0)
981 continue;
982 if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
983 continue;
984 if (strcmp(process, "powertop") == 0)
985 continue;
986 if (c)
987 *c = 0;
988 cnt = strtoull(count, &c, 10);
989 while (*c != 0) {
990 if (*c++ == 'D')
991 deferrable = 1;
992 }
993 if (deferrable)
994 continue;
995 sprintf(line2, "%15s : %s", process, func);
996 push_line_pid(line2, cnt, pid);
997 }
998 if (file)
999 pclose(file);
1000
1001 if (strstr(line, "total events")) {
1002 int d;
1003 d = strtoull(line, NULL, 10) / sysconf(_SC_NPROCESSORS_ONLN);
1004 if (totalevents == 0) { /* No c-state info available, use timerstats instead */
1005 totalevents = d * sysconf(_SC_NPROCESSORS_ONLN) + total_interrupt;
1006 if (d < interrupt_0)
1007 totalevents += interrupt_0 - d;
1008 }
1009 if (d>0 && d < interrupt_0)
1010 push_line(_(" <interrupt> : extra timer interrupt"), interrupt_0 - d);
1011 }
1012
1013
1014 if (totalevents && ticktime) {
1015 wakeups_per_second = totalevents * 1.0 / ticktime / sysconf(_SC_NPROCESSORS_ONLN);
1016 show_wakeups(wakeups_per_second, ticktime, c0 * 100.0 / (sysconf(_SC_NPROCESSORS_ONLN) * ticktime * 1000 * FREQ) );
1017 }
1018 count_usb_urbs();
1019 print_battery_sysfs();
1020 count_lines();
1021 sort_lines();
1022
1023 displaytime = displaytime - ticktime;
1024
1025 show_timerstats(nostats, ticktime);
1026
1027 if (maxsleep < 5.0)
1028 ticktime = 10;
1029 else if (maxsleep < 30.0)
1030 ticktime = 15;
1031 else if (maxsleep < 100.0)
1032 ticktime = 20;
1033 else if (maxsleep < 400.0)
1034 ticktime = 30;
1035 else
1036 ticktime = 45;
1037
1038 if (key) {
1039 char keychar;
1040 int keystroke = fgetc(stdin);
1041 if (keystroke == EOF)
1042 exit(EXIT_SUCCESS);
1043
1044 keychar = toupper(keystroke);
1045 if (keychar == 'Q')
1046 exit(EXIT_SUCCESS);
1047 if (keychar == 'R')
1048 ticktime = 3;
1049 if (keychar == suggestion_key && suggestion_activate) {
1050 suggestion_activate();
1051 ticktime = 2;
1052 displaytime = -1.0;
1053 } else
1054 if (keychar == 'P')
1055 showpids = !showpids;
1056 }
1057
1058 if (wakeups_per_second < 0)
1059 ticktime = 2;
1060
1061 reset_suggestions();
1062
1063 suggest_kernel_config("CONFIG_USB_SUSPEND", 1,
1064 _("Suggestion: Enable the CONFIG_USB_SUSPEND kernel configuration option.\nThis option will automatically disable UHCI USB when not in use, and may\nsave approximately 1 Watt of power."), 20);
1065 suggest_kernel_config("CONFIG_CPU_FREQ_GOV_ONDEMAND", 1,
1066 _("Suggestion: Enable the CONFIG_CPU_FREQ_GOV_ONDEMAND kernel configuration option.\n"
1067 "The 'ondemand' CPU speed governor will minimize the CPU power usage while\n" "giving you performance when it is needed."), 5);
1068 suggest_kernel_config("CONFIG_NO_HZ", 1, _("Suggestion: Enable the CONFIG_NO_HZ kernel configuration option.\nThis option is required to get any kind of longer sleep times in the CPU."), 50);
1069 suggest_kernel_config("CONFIG_ACPI_BATTERY", 1, _("Suggestion: Enable the CONFIG_ACPI_BATTERY kernel configuration option.\n "
1070 "This option is required to get power estimages from PowerTOP"), 5);
1071 suggest_kernel_config("CONFIG_HPET_TIMER", 1,
1072 _("Suggestion: Enable the CONFIG_HPET_TIMER kernel configuration option.\n"
1073 "Without HPET support the kernel needs to wake up every 20 milliseconds for \n" "some housekeeping tasks."), 10);
1074 if (!access("/sys/module/snd_ac97_codec", F_OK) &&
1075 access("/sys/module/snd_ac97_codec/parameters/power_save", F_OK))
1076 suggest_kernel_config("CONFIG_SND_AC97_POWER_SAVE", 1,
1077 _("Suggestion: Enable the CONFIG_SND_AC97_POWER_SAVE kernel configuration option.\n"
1078 "This option will automatically power down your sound codec when not in use,\n"
1079 "and can save approximately half a Watt of power."), 20);
1080 suggest_kernel_config("CONFIG_IRQBALANCE", 0,
1081 _("Suggestion: Disable the CONFIG_IRQBALANCE kernel configuration option.\n" "The in-kernel irq balancer is obsolete and wakes the CPU up far more than needed."), 3);
1082 suggest_kernel_config("CONFIG_CPU_FREQ_STAT", 1,
1083 _("Suggestion: Enable the CONFIG_CPU_FREQ_STAT kernel configuration option.\n"
1084 "This option allows PowerTOP to show P-state percentages \n" "P-states correspond to CPU frequencies."), 2);
1085 suggest_kernel_config("CONFIG_INOTIFY", 1,
1086 _("Suggestion: Enable the CONFIG_INOTIFY kernel configuration option.\n"
1087 "This option allows programs to wait for changes in files and directories\n"
1088 "instead of having to poll for these changes"), 5);
1089
1090
1091 /* suggest to stop beagle if it shows up in the top 20 and wakes up more than 10 times in the measurement */
1092 suggest_process_death("beagled : schedule_timeout", "beagled", lines, min(linehead,20), 10.0,
1093 _("Suggestion: Disable or remove 'beagle' from your system. \n"
1094 "Beagle is the program that indexes for easy desktop search, however it's \n"
1095 "not very efficient and costs a significant amount of battery life."), 30);
1096 suggest_process_death("beagled : futex_wait (hrtimer_wakeup)", "beagled", lines, min(linehead,20), 10.0,
1097 _("Suggestion: Disable or remove 'beagle' from your system. \n"
1098 "Beagle is the program that indexes for easy desktop search, however it's \n"
1099 "not very efficient and costs a significant amount of battery life."), 30);
1100
1101 /* suggest to stop gnome-power-manager *only* if it shows up in the top 10 and wakes up more than 10 times in the measurement */
1102 /* note to distribution makers: There is no need to patch this out! */
1103 /* If you ship a recent enough g-p-m, the warning will not be there, */
1104 /* and if you ship a really old one the warning is really justified. */
1105 suggest_process_death("gnome-power-man : schedule_timeout (process_timeout)", "gnome-power-manager", lines, min(linehead,10), 10.0,
1106 _("Suggestion: Disable or remove 'gnome-power-manager' from your system. \n"
1107 "Older versions of gnome-power-manager wake up far more often than \n"
1108 "needed costing you some power."), 5);
1109
1110 /* suggest to stop pcscd if it shows up in the top 50 and wakes up at all*/
1111 suggest_process_death("pcscd : ", "pcscd", lines, min(linehead,50), 1.0,
1112 _("Suggestion: Disable or remove 'pcscd' from your system. \n"
1113 "pcscd tends to keep the USB subsystem out of power save mode\n"
1114 "and your processor out of deeper powersave states."), 30);
1115
1116
1117 /* suggest to stop hal polilng if it shows up in the top 50 and wakes up too much*/
1118 suggest_process_death("hald-addon-stor : ", "hald-addon-storage", lines, min(linehead,50), 2.0,
1119 _( "Suggestion: Disable 'hal' from polling your cdrom with: \n"
1120 "hal-disable-polling --device /dev/cdrom 'hal' is the component that auto-opens a\n"
1121 "window if you plug in a CD but disables SATA power saving from kicking in."), 30);
1122
1123 /* suggest to kill sealert; it wakes up 10 times/second on a default F7 install*/
1124 suggest_process_death("/usr/bin/sealer : schedule_timeout (process_timeout)", "-/usr/bin/sealert", lines, min(linehead,20), 20.0,
1125 _("Disable the SE-Alert software by removing the 'setroubleshoot-server' rpm\n"
1126 "SE-Alert alerts you about SELinux policy violations, but also\n"
1127 "has a bug that wakes it up 10 times per second."), 20);
1128
1129
1130 suggest_bluetooth_off();
1131 suggest_nmi_watchdog();
1132 suggest_laptop_mode();
1133 if (maxsleep > 15.0)
1134 suggest_hpet();
1135 suggest_ac97_powersave();
1136 suggest_wireless_powersave();
1137 suggest_ondemand_governor();
1138 suggest_noatime();
1139 suggest_sata_alpm();
1140 suggest_powersched();
1141 suggest_xrandr_TV_off();
1142 suggest_WOL_off();
1143 suggest_writeback_time();
1144 suggest_usb_autosuspend();
1145 usb_activity_hint();
1146
1147 if (dump) {
1148 print_all_suggestions();
1149 display_usb_activity();
1150 exit(EXIT_SUCCESS);
1151 }
1152
1153 if (!key)
1154 pick_suggestion();
1155 show_title_bar();
1156
1157 fflush(stdout);
1158 if (!key && ticktime >= 4.8) { /* quiet down the effects of any IO to xterms */
1159 FD_ZERO(&rfds);
1160 FD_SET(0, &rfds);
1161 tv.tv_sec = 3;
1162 tv.tv_usec = 0;
1163 key = select(1, &rfds, NULL, NULL, &tv);
1164 }
1165
1166 read_data(&cur_usage[0], &cur_duration[0]);
1167 memcpy(last_usage, cur_usage, sizeof(last_usage));
1168 memcpy(last_duration, cur_duration, sizeof(last_duration));
1169
1170
1171
1172 }
1173
1174 return 0;
1175 }
1176