/* * Copyright 2007, Intel Corporation * * This file is part of PowerTOP * * This program file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Arjan van de Ven */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "powertop.h" #define VERSION "1.11-1" uint64_t start_usage[8], start_duration[8]; uint64_t last_usage[8], last_duration[8]; char cnames[8][16]; double ticktime = 15.0; int interrupt_0, total_interrupt; int showpids = 0; static int maxcstate = 0; int topcstate = 0; int dump = 0; int reset_pm_stats = 0; static int cpu_count = 0; #define IRQCOUNT 150 struct irqdata { int active; int number; uint64_t count; char description[256]; }; struct irqdata interrupts[IRQCOUNT]; #define FREQ_ACPI 3579.545 static unsigned long FREQ; int nostats; struct line *lines; int linehead; int linesize; int linectotal; double last_bat_cap = 0; double prev_bat_cap = 0; time_t last_bat_time = 0; time_t prev_bat_time = 0; double displaytime = 0.0; void push_line(char *string, int count) { int i; assert(string != NULL); for (i = 0; i < linehead; i++) if (strcmp(string, lines[i].string) == 0) { lines[i].count += count; return; } if (linehead == linesize) lines = realloc (lines, (linesize ? (linesize *= 2) : (linesize = 64)) * sizeof (struct line)); lines[linehead].string = strdup (string); lines[linehead].count = count; lines[linehead].pid[0] = 0; linehead++; } void push_line_pid(char *string, int count, char *pid) { int i; assert(string != NULL); for (i = 0; i < linehead; i++) if (strcmp(string, lines[i].string) == 0) { lines[i].count += count; if (pid && strcmp(lines[i].pid, pid)!=0) lines[i].pid[0] = 0; return; } if (linehead == linesize) lines = realloc (lines, (linesize ? (linesize *= 2) : (linesize = 64)) * sizeof (struct line)); lines[linehead].string = strdup (string); lines[linehead].count = count; if (pid) strcpy(lines[linehead].pid, pid); linehead++; } void clear_lines(void) { int i; for (i = 0; i < linehead; i++) free (lines[i].string); free (lines); linehead = linesize = 0; lines = NULL; } void count_lines(void) { uint64_t q = 0; int i; for (i = 0; i < linehead; i++) q += lines[i].count; linectotal = q; } int update_irq(int irq, uint64_t count, char *name) { int i; int firstfree = IRQCOUNT; if (!name) return 0; for (i = 0; i < IRQCOUNT; i++) { if (interrupts[i].active && interrupts[i].number == irq) { uint64_t oldcount; oldcount = interrupts[i].count; interrupts[i].count = count; return count - oldcount; } if (!interrupts[i].active && firstfree > i) firstfree = i; } interrupts[firstfree].active = 1; interrupts[firstfree].count = count; interrupts[firstfree].number = irq; strcpy(interrupts[firstfree].description, name); if (strcmp(name,"i8042\n")==0) strcpy(interrupts[firstfree].description, _("PS/2 keyboard/mouse/touchpad")); return count; } static void do_proc_irq(void) { FILE *file; char line[1024]; char line2[1024]; char *name; uint64_t delta; interrupt_0 = 0; total_interrupt = 0; file = fopen("/proc/interrupts", "r"); if (!file) return; while (!feof(file)) { char *c; int nr = -1; uint64_t count = 0; int special = 0; memset(line, 0, sizeof(line)); if (fgets(line, 1024, file) == NULL) break; c = strchr(line, ':'); if (!c) continue; /* deal with NMI and the like.. make up fake nrs */ if (line[0] != ' ' && (line[0] < '0' || line[0] > '9')) { if (strncmp(line,"NMI:", 4)==0) nr=20000; if (strncmp(line,"RES:", 4)==0) nr=20001; if (strncmp(line,"CAL:", 4)==0) nr=20002; if (strncmp(line,"TLB:", 4)==0) nr=20003; if (strncmp(line,"TRM:", 4)==0) nr=20004; if (strncmp(line,"THR:", 4)==0) nr=20005; if (strncmp(line,"SPU:", 4)==0) nr=20006; special = 1; } else nr = strtoull(line, NULL, 10); if (nr==-1) continue; *c = 0; c++; while (c && strlen(c)) { char *newc; count += strtoull(c, &newc, 10); if (newc == c) break; c = newc; } c = strchr(c, ' '); if (!c) continue; while (c && *c == ' ') c++; if (!special) { c = strchr(c, ' '); if (!c) continue; while (c && *c == ' ') c++; } name = c; c = strchr(name, '\n'); if (c) *c = 0; delta = update_irq(nr, count, name); if (strcmp(name, "i8042")) { if (special) sprintf(line2, _(" : %s"), name); else sprintf(line2, _(" : %s"), name); } else sprintf(line2, _(" : %s"), _("PS/2 keyboard/mouse/touchpad")); //don't special-case interrupt 0 as it's not the system timer on Android #ifdef PLATFORM_NO_INT0 if (delta > 0) push_line(line2, delta); total_interrupt += delta; #else if (nr > 0 && delta > 0) push_line(line2, delta); if (nr==0) interrupt_0 = delta; else total_interrupt += delta; #endif } fclose(file); } static void read_data_acpi(uint64_t * usage, uint64_t * duration) { DIR *dir; struct dirent *entry; FILE *file = NULL; char line[4096]; char *c; int clevel = 0; memset(usage, 0, 64); memset(duration, 0, 64); dir = opendir("/proc/acpi/processor"); if (!dir) return; while ((entry = readdir(dir))) { if (strlen(entry->d_name) < 3) continue; sprintf(line, "/proc/acpi/processor/%s/power", entry->d_name); file = fopen(line, "r"); if (!file) continue; clevel = 0; while (!feof(file)) { memset(line, 0, 4096); if (fgets(line, 4096, file) == NULL) break; c = strstr(line, "age["); if (!c) continue; c += 4; usage[clevel] += 1+strtoull(c, NULL, 10); c = strstr(line, "ation["); if (!c) continue; c += 6; duration[clevel] += strtoull(c, NULL, 10); clevel++; if (clevel > maxcstate) maxcstate = clevel; } fclose(file); } closedir(dir); } static void read_data_cpuidle(uint64_t * usage, uint64_t * duration) { DIR *cpudir; DIR *dir; struct dirent *entry; FILE *file = NULL; char line[4096]; char filename[128], *f; int len, clevel = 0; int numcpus = 0; memset(usage, 0, 64); memset(duration, 0, 64); cpudir = opendir("/sys/devices/system/cpu"); if (!cpudir) return; /* Loop over cpuN entries */ while ((entry = readdir(cpudir))) { if (strlen(entry->d_name) < 3) continue; if (!isdigit(entry->d_name[3])) continue; len = snprintf(filename, 128, "/sys/devices/system/cpu/%s/online", entry->d_name); file = fopen(filename, "r"); if (file) { f = fgets(line, 4096, file); fclose(file); if (f != NULL) { if (*f == '1') numcpus++; else continue; } } len = snprintf(filename, 128, "/sys/devices/system/cpu/%s/cpuidle", entry->d_name); dir = opendir(filename); if (!dir) return; clevel = 0; /* For each C-state, there is a stateX directory which * contains a 'usage' and a 'time' (duration) file */ while ((entry = readdir(dir))) { if (strlen(entry->d_name) < 3) continue; sprintf(filename + len, "/%s/desc", entry->d_name); file = fopen(filename, "r"); if (file) { memset(line, 0, 4096); f = fgets(line, 4096, file); fclose(file); if (f == NULL) break; f = strstr(line, "MWAIT "); if (f) { f += 6; clevel = (strtoull(f, NULL, 16)>>4) + 1; sprintf(cnames[clevel], "C%i mwait", clevel); } else sprintf(cnames[clevel], "C%i\t", clevel); f = strstr(line, "POLL IDLE"); if (f) { clevel = 0; sprintf(cnames[clevel], "%s\t", _("polling")); } f = strstr(line, "ACPI HLT"); if (f) { clevel = 1; sprintf(cnames[clevel], "%s\t", "C1 halt"); } } sprintf(filename + len, "/%s/usage", entry->d_name); file = fopen(filename, "r"); if (!file) continue; memset(line, 0, 4096); f = fgets(line, 4096, file); fclose(file); if (f == NULL) break; usage[clevel] += 1+strtoull(line, NULL, 10); sprintf(filename + len, "/%s/time", entry->d_name); file = fopen(filename, "r"); if (!file) continue; memset(line, 0, 4096); f = fgets(line, 4096, file); fclose(file); if (f == NULL) break; duration[clevel] += 1+strtoull(line, NULL, 10); clevel++; if (clevel > maxcstate) maxcstate = clevel; } closedir(dir); } closedir(cpudir); if (numcpus) cpu_count = numcpus; } static void read_data(uint64_t * usage, uint64_t * duration) { int r; struct stat s; /* Then check for CPUidle */ r = stat("/sys/devices/system/cpu/cpu0/cpuidle", &s); if (!r) { read_data_cpuidle(usage, duration); /* perform residency calculations based on usecs */ FREQ = 1000; return; } /* First, check for ACPI */ r = stat("/proc/acpi/processor", &s); if (!r) { read_data_acpi(usage, duration); /* perform residency calculations based on ACPI timer */ FREQ = FREQ_ACPI; return; } } void stop_timerstats(void) { FILE *file; file = fopen("/proc/timer_stats", "w"); if (!file) { nostats = 1; return; } fprintf(file, "0\n"); fclose(file); } void start_timerstats(void) { FILE *file; file = fopen("/proc/timer_stats", "w"); if (!file) { nostats = 1; return; } fprintf(file, "1\n"); fclose(file); } void reset_msm_pm_stats(void) { FILE *file; file = fopen("/proc/msm_pm_stats", "w"); if (!file) return; fprintf(file, "reset\n"); fclose(file); } int line_compare (const void *av, const void *bv) { const struct line *a = av, *b = bv; return b->count - a->count; } void sort_lines(void) { qsort (lines, linehead, sizeof (struct line), line_compare); } int print_battery_proc_acpi(void) { DIR *dir; struct dirent *dirent; FILE *file; double rate = 0; double cap = 0; char filename[256]; dir = opendir("/proc/acpi/battery"); if (!dir) return 0; while ((dirent = readdir(dir))) { int dontcount = 0; double voltage = 0.0; double amperes_drawn = 0.0; double watts_drawn = 0.0; double amperes_left = 0.0; double watts_left = 0.0; char line[1024]; if (strlen(dirent->d_name) < 3) continue; sprintf(filename, "/proc/acpi/battery/%s/state", dirent->d_name); file = fopen(filename, "r"); if (!file) continue; memset(line, 0, 1024); while (fgets(line, 1024, file) != NULL) { char *c; if (strstr(line, "present:") && strstr(line, "no")) break; if (strstr(line, "charging state:") && !strstr(line, "discharging")) dontcount = 1; c = strchr(line, ':'); if (!c) continue; c++; if (strstr(line, "present voltage")) voltage = strtoull(c, NULL, 10) / 1000.0; if (strstr(line, "remaining capacity") && strstr(c, "mW")) watts_left = strtoull(c, NULL, 10) / 1000.0; if (strstr(line, "remaining capacity") && strstr(c, "mAh")) amperes_left = strtoull(c, NULL, 10) / 1000.0; if (strstr(line, "present rate") && strstr(c, "mW")) watts_drawn = strtoull(c, NULL, 10) / 1000.0 ; if (strstr(line, "present rate") && strstr(c, "mA")) amperes_drawn = strtoull(c, NULL, 10) / 1000.0; } fclose(file); if (!dontcount) { rate += watts_drawn + voltage * amperes_drawn; } cap += watts_left + voltage * amperes_left; } closedir(dir); if (prev_bat_cap - cap < 0.001 && rate < 0.001) last_bat_time = 0; if (!last_bat_time) { last_bat_time = prev_bat_time = time(NULL); last_bat_cap = prev_bat_cap = cap; } if (time(NULL) - last_bat_time >= 400) { prev_bat_cap = last_bat_cap; prev_bat_time = last_bat_time; last_bat_time = time(NULL); last_bat_cap = cap; } show_acpi_power_line(rate, cap, prev_bat_cap - cap, time(NULL) - prev_bat_time); return 1; } int print_battery_proc_pmu(void) { char line[80]; int i; int power_present = 0; int num_batteries = 0; /* unsigned rem_time_sec = 0; */ unsigned charge_mAh = 0, max_charge_mAh = 0, voltage_mV = 0; int discharge_mA = 0; FILE *fd; fd = fopen("/proc/pmu/info", "r"); if (fd == NULL) return 0; while ( fgets(line, sizeof(line), fd) != NULL ) { if (strncmp("AC Power", line, strlen("AC Power")) == 0) sscanf(strchr(line, ':')+2, "%d", &power_present); else if (strncmp("Battery count", line, strlen("Battery count")) == 0) sscanf(strchr(line, ':')+2, "%d", &num_batteries); } fclose(fd); for (i = 0; i < num_batteries; ++i) { char file_name[20]; int flags = 0; /* int battery_charging, battery_full; */ /* unsigned this_rem_time_sec = 0; */ unsigned this_charge_mAh = 0, this_max_charge_mAh = 0; unsigned this_voltage_mV = 0, this_discharge_mA = 0; snprintf(file_name, sizeof(file_name), "/proc/pmu/battery_%d", i); fd = fopen(file_name, "r"); if (fd == NULL) continue; while (fgets(line, sizeof(line), fd) != NULL) { if (strncmp("flags", line, strlen("flags")) == 0) sscanf(strchr(line, ':')+2, "%x", &flags); else if (strncmp("charge", line, strlen("charge")) == 0) sscanf(strchr(line, ':')+2, "%d", &this_charge_mAh); else if (strncmp("max_charge", line, strlen("max_charge")) == 0) sscanf(strchr(line, ':')+2, "%d", &this_max_charge_mAh); else if (strncmp("voltage", line, strlen("voltage")) == 0) sscanf(strchr(line, ':')+2, "%d", &this_voltage_mV); else if (strncmp("current", line, strlen("current")) == 0) sscanf(strchr(line, ':')+2, "%d", &this_discharge_mA); /* else if (strncmp("time rem.", line, strlen("time rem.")) == 0) */ /* sscanf(strchr(line, ':')+2, "%d", &this_rem_time_sec); */ } fclose(fd); if ( !(flags & 0x1) ) /* battery isn't present */ continue; /* battery_charging = flags & 0x2; */ /* battery_full = !battery_charging && power_present; */ charge_mAh += this_charge_mAh; max_charge_mAh += this_max_charge_mAh; voltage_mV += this_voltage_mV; discharge_mA += this_discharge_mA; /* rem_time_sec += this_rem_time_sec; */ } show_pmu_power_line(voltage_mV, charge_mAh, max_charge_mAh, discharge_mA); return 1; } void print_battery_sysfs(void) { DIR *dir; struct dirent *dirent; FILE *file; double rate = 0; double cap = 0; char filename[256]; if (print_battery_proc_acpi()) return; if (print_battery_proc_pmu()) return; dir = opendir("/sys/class/power_supply"); if (!dir) { return; } while ((dirent = readdir(dir))) { int dontcount = 0; double voltage = 0.0; double amperes_drawn = 0.0; double watts_drawn = 0.0; double watts_left = 0.0; char line[1024]; if (strstr(dirent->d_name, "AC")) continue; sprintf(filename, "/sys/class/power_supply/%s/present", dirent->d_name); file = fopen(filename, "r"); if (!file) continue; int s; if ((s = getc(file)) != EOF) { if (s == 0) break; } fclose(file); sprintf(filename, "/sys/class/power_supply/%s/status", dirent->d_name); file = fopen(filename, "r"); if (!file) continue; memset(line, 0, 1024); if (fgets(line, 1024, file) != NULL) { if (!strstr(line, "Discharging")) dontcount = 1; } fclose(file); sprintf(filename, "/sys/class/power_supply/%s/voltage_now", dirent->d_name); file = fopen(filename, "r"); if (!file) continue; memset(line, 0, 1024); if (fgets(line, 1024, file) != NULL) { voltage = strtoull(line, NULL, 10) / 1000000.0; } fclose(file); sprintf(filename, "/sys/class/power_supply/%s/energy_now", dirent->d_name); file = fopen(filename, "r"); watts_left = 1; if (!file) { sprintf(filename, "/sys/class/power_supply/%s/charge_now", dirent->d_name); file = fopen(filename, "r"); if (!file) continue; /* W = A * V */ watts_left = voltage; } memset(line, 0, 1024); if (fgets(line, 1024, file) != NULL) watts_left *= strtoull(line, NULL, 10) / 1000000.0; fclose(file); sprintf(filename, "/sys/class/power_supply/%s/current_now", dirent->d_name); file = fopen(filename, "r"); if (!file) continue; memset(line, 0, 1024); if (fgets(line, 1024, file) != NULL) { amperes_drawn = strtoull(line, NULL, 10) / 1000000.0; } fclose(file); if (!dontcount) { rate += watts_drawn + voltage * amperes_drawn; } cap += watts_left; } closedir(dir); if (prev_bat_cap - cap < 0.001 && rate < 0.001) last_bat_time = 0; if (!last_bat_time) { last_bat_time = prev_bat_time = time(NULL); last_bat_cap = prev_bat_cap = cap; } if (time(NULL) - last_bat_time >= 400) { prev_bat_cap = last_bat_cap; prev_bat_time = last_bat_time; last_bat_time = time(NULL); last_bat_cap = cap; } show_acpi_power_line(rate, cap, prev_bat_cap - cap, time(NULL) - prev_bat_time); } char cstate_lines[12][200]; void usage() { printf(_("Usage: powertop [OPTION...]\n")); printf(_(" -d, --dump read wakeups once and print list of top offenders\n")); printf(_(" -t, --time=DOUBLE default time to gather data in seconds\n")); printf(_(" -r, --reset Reset PM stats data\n")); printf(_(" -h, --help Show this help message\n")); printf(_(" -v, --version Show version information and exit\n")); exit(0); } void version() { printf(_("powertop version %s\n"), VERSION); exit(0); } int main(int argc, char **argv) { char line[1024]; int ncursesinited=0; FILE *file = NULL; uint64_t cur_usage[8], cur_duration[8]; double wakeups_per_second = 0; setlocale (LC_ALL, ""); bindtextdomain ("powertop", "/usr/share/locale"); textdomain ("powertop"); while (1) { static struct option opts[] = { { "dump", 0, NULL, 'd' }, { "time", 1, NULL, 't' }, { "reset", 0, NULL, 'r' }, { "pids", 0, NULL, 'p' }, { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'v' }, { 0, 0, NULL, 0 } }; int index2 = 0, c; c = getopt_long(argc, argv, "dt:rphv", opts, &index2); if (c == -1) break; switch (c) { case 'd': dump = 1; break; case 't': ticktime = strtod(optarg, NULL); break; case 'r': reset_pm_stats = 1; break; case 'p': showpids = 1; break; case 'h': usage(); break; case 'v': version(); break; default: ; } } if (!dump) ticktime = 5.0; system("/sbin/modprobe cpufreq_stats &> /dev/null"); read_data(&start_usage[0], &start_duration[0]); memcpy(last_usage, start_usage, sizeof(last_usage)); memcpy(last_duration, start_duration, sizeof(last_duration)); do_proc_irq(); do_proc_irq(); do_cpufreq_stats(); count_usb_urbs(); count_usb_urbs(); memset(cur_usage, 0, sizeof(cur_usage)); memset(cur_duration, 0, sizeof(cur_duration)); printf("PowerTOP " VERSION " (C) 2007, 2008 Intel Corporation \n\n"); if (geteuid() != 0) printf(_("PowerTOP needs to be run as root to collect enough information\n")); printf(_("Collecting data for %i seconds \n"), (int)ticktime); printf("\n\n"); print_intel_cstates(); stop_timerstats(); while (1) { double maxsleep = 0.0; int64_t totalticks; int64_t totalevents; fd_set rfds; struct timeval tv; int key; int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); int i = 0; double c0 = 0; char *c; FD_ZERO(&rfds); #ifndef NO_NCURSES /* Do not check for stdin (fd: 0) * This would cause select to return immediately * when the USB is re-attached on ADB shell. */ FD_SET(0, &rfds); #endif tv.tv_sec = ticktime; tv.tv_usec = (ticktime - tv.tv_sec) * 1000000;; if (reset_pm_stats) reset_msm_pm_stats(); do_proc_irq(); start_timerstats(); key = select(1, &rfds, NULL, NULL, &tv); if (key && tv.tv_sec) ticktime = ticktime - tv.tv_sec - tv.tv_usec/1000000.0; stop_timerstats(); msm_pm_stats(); clear_lines(); do_proc_irq(); read_data(&cur_usage[0], &cur_duration[0]); totalticks = 0; totalevents = 0; for (i = 0; i < 8; i++) if (cur_usage[i]) { totalticks += cur_duration[i] - last_duration[i]; totalevents += cur_usage[i] - last_usage[i]; } if (!dump) { if (!ncursesinited) { initialize_curses(); ncursesinited++; } setup_windows(); show_title_bar(); } memset(&cstate_lines, 0, sizeof(cstate_lines)); topcstate = -4; if (totalevents == 0 && maxcstate <= 1) { sprintf(cstate_lines[5],_("< Detailed C-state information is not available.>\n")); } else { double sleept, percentage; if (cpu_count) num_cpus = cpu_count; c0 = num_cpus * ticktime * 1000 * FREQ - totalticks; if (c0 < 0) c0 = 0; /* rounding errors in measurement might make c0 go slightly negative.. this is confusing */ sprintf(cstate_lines[0], _("Cn\t Avg residency\n")); percentage = c0 * 100.0 / (num_cpus * ticktime * 1000 * FREQ); sprintf(cstate_lines[1], _("C0 (cpu running) (%4.1f%%)\n"), percentage); if (percentage > 50) topcstate = 0; for (i = 0; i < 8; i++) if (cur_usage[i]) { sleept = (cur_duration[i] - last_duration[i]) / (cur_usage[i] - last_usage[i] + 0.1) / FREQ; percentage = (cur_duration[i] - last_duration[i]) * 100 / (num_cpus * ticktime * 1000 * FREQ); if (cnames[i][0]==0) sprintf(cnames[i],"C%i",i+1); sprintf (cstate_lines[2+i], _("%s\t%5.1fms (%4.1f%%)\n"), cnames[i], sleept, percentage); if (maxsleep < sleept) maxsleep = sleept; if (percentage > 50) topcstate = i+1; } } do_cpufreq_stats(); show_cstates(); show_msm_pm_stats(); /* now the timer_stats info */ memset(line, 0, sizeof(line)); totalticks = 0; file = NULL; if (!nostats) file = fopen("/proc/timer_stats", "r"); while (file && !feof(file)) { char *count, *pid, *process, *func; char line2[1024]; int cnt; int deferrable = 0; memset(line, 0, 1024); if (fgets(line, 1024, file) == NULL) break; if (strstr(line, "total events")) break; c = count = &line[0]; c = strchr(c, ','); if (!c) continue; *c = 0; c++; while (*c != 0 && *c == ' ') c++; pid = c; c = strchr(c, ' '); if (!c) continue; *c = 0; c++; while (*c != 0 && *c == ' ') c++; process = c; c = strchr(c, ' '); if (!c) continue; *c = 0; c++; while (*c != 0 && *c == ' ') c++; func = c; if (strcmp(process, "insmod") == 0) process = _(""); if (strcmp(process, "modprobe") == 0) process = _(""); if (strcmp(process, "swapper") == 0) process = _(""); c = strchr(c, '\n'); if (strncmp(func, "tick_nohz_", 10) == 0) continue; if (strncmp(func, "tick_setup_sched_timer", 20) == 0) continue; if (strcmp(process, "powertop") == 0) continue; if (c) *c = 0; cnt = strtoull(count, &c, 10); while (*c != 0) { if (*c++ == 'D') deferrable = 1; } if (deferrable) continue; sprintf(line2, "%15s : %s", process, func); push_line_pid(line2, cnt, pid); } if (file) pclose(file); #ifdef PLATFORM_NO_INT0 totalevents = total_interrupt; #endif if (strstr(line, "total events")) { int d; d = strtoull(line, NULL, 10) / num_cpus; if (totalevents == 0) { /* No c-state info available, use timerstats instead */ totalevents = d * num_cpus + total_interrupt; if (d < interrupt_0) totalevents += interrupt_0 - d; } if (d>0 && d < interrupt_0) push_line(_(" : extra timer interrupt"), interrupt_0 - d); } if (totalevents && ticktime) { wakeups_per_second = totalevents * 1.0 / ticktime / num_cpus; show_wakeups(wakeups_per_second, ticktime, c0 * 100.0 / (num_cpus * ticktime * 1000 * FREQ)); } count_usb_urbs(); print_battery_sysfs(); count_lines(); sort_lines(); displaytime = displaytime - ticktime; show_timerstats(nostats, ticktime); if (maxsleep < 5.0) ticktime = 10; else if (maxsleep < 30.0) ticktime = 15; else if (maxsleep < 100.0) ticktime = 20; else if (maxsleep < 400.0) ticktime = 30; else ticktime = 45; if (key) { char keychar; int keystroke = fgetc(stdin); #ifndef NO_NCURSES /* Do not handle EOF when not using ncurses as * the shell would pass the EOF to the app causing * it to stop. */ if (keystroke == EOF) exit(EXIT_SUCCESS); #endif keychar = toupper(keystroke); if (keychar == 'Q') exit(EXIT_SUCCESS); if (keychar == 'R') ticktime = 3; #ifndef NO_SUGGESTIONS if (keychar == suggestion_key && suggestion_activate) { suggestion_activate(); ticktime = 2; displaytime = -1.0; } else #endif if (keychar == 'P') showpids = !showpids; } if (wakeups_per_second < 0) ticktime = 2; #ifndef NO_SUGGESTIONS reset_suggestions(); suggest_kernel_config("CONFIG_USB_SUSPEND", 1, _("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); suggest_kernel_config("CONFIG_CPU_FREQ_GOV_ONDEMAND", 1, _("Suggestion: Enable the CONFIG_CPU_FREQ_GOV_ONDEMAND kernel configuration option.\n" "The 'ondemand' CPU speed governor will minimize the CPU power usage while\n" "giving you performance when it is needed."), 5); 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); suggest_kernel_config("CONFIG_ACPI_BATTERY", 1, _("Suggestion: Enable the CONFIG_ACPI_BATTERY kernel configuration option.\n " "This option is required to get power estimages from PowerTOP"), 5); suggest_kernel_config("CONFIG_HPET_TIMER", 1, _("Suggestion: Enable the CONFIG_HPET_TIMER kernel configuration option.\n" "Without HPET support the kernel needs to wake up every 20 milliseconds for \n" "some housekeeping tasks."), 10); if (!access("/sys/module/snd_ac97_codec", F_OK) && access("/sys/module/snd_ac97_codec/parameters/power_save", F_OK)) suggest_kernel_config("CONFIG_SND_AC97_POWER_SAVE", 1, _("Suggestion: Enable the CONFIG_SND_AC97_POWER_SAVE kernel configuration option.\n" "This option will automatically power down your sound codec when not in use,\n" "and can save approximately half a Watt of power."), 20); suggest_kernel_config("CONFIG_IRQBALANCE", 0, _("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); suggest_kernel_config("CONFIG_CPU_FREQ_STAT", 1, _("Suggestion: Enable the CONFIG_CPU_FREQ_STAT kernel configuration option.\n" "This option allows PowerTOP to show P-state percentages \n" "P-states correspond to CPU frequencies."), 2); suggest_kernel_config("CONFIG_INOTIFY", 1, _("Suggestion: Enable the CONFIG_INOTIFY kernel configuration option.\n" "This option allows programs to wait for changes in files and directories\n" "instead of having to poll for these changes"), 5); /* suggest to stop beagle if it shows up in the top 20 and wakes up more than 10 times in the measurement */ suggest_process_death("beagled : schedule_timeout", "beagled", lines, min(linehead,20), 10.0, _("Suggestion: Disable or remove 'beagle' from your system. \n" "Beagle is the program that indexes for easy desktop search, however it's \n" "not very efficient and costs a significant amount of battery life."), 30); suggest_process_death("beagled : futex_wait (hrtimer_wakeup)", "beagled", lines, min(linehead,20), 10.0, _("Suggestion: Disable or remove 'beagle' from your system. \n" "Beagle is the program that indexes for easy desktop search, however it's \n" "not very efficient and costs a significant amount of battery life."), 30); /* 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 */ /* note to distribution makers: There is no need to patch this out! */ /* If you ship a recent enough g-p-m, the warning will not be there, */ /* and if you ship a really old one the warning is really justified. */ suggest_process_death("gnome-power-man : schedule_timeout (process_timeout)", "gnome-power-manager", lines, min(linehead,10), 10.0, _("Suggestion: Disable or remove 'gnome-power-manager' from your system. \n" "Older versions of gnome-power-manager wake up far more often than \n" "needed costing you some power."), 5); /* suggest to stop pcscd if it shows up in the top 50 and wakes up at all*/ suggest_process_death("pcscd : ", "pcscd", lines, min(linehead,50), 1.0, _("Suggestion: Disable or remove 'pcscd' from your system. \n" "pcscd tends to keep the USB subsystem out of power save mode\n" "and your processor out of deeper powersave states."), 30); /* suggest to stop hal polilng if it shows up in the top 50 and wakes up too much*/ suggest_process_death("hald-addon-stor : ", "hald-addon-storage", lines, min(linehead,50), 2.0, _( "Suggestion: Disable 'hal' from polling your cdrom with: \n" "hal-disable-polling --device /dev/cdrom 'hal' is the component that auto-opens a\n" "window if you plug in a CD but disables SATA power saving from kicking in."), 30); /* suggest to kill sealert; it wakes up 10 times/second on a default F7 install*/ suggest_process_death("/usr/bin/sealer : schedule_timeout (process_timeout)", "-/usr/bin/sealert", lines, min(linehead,20), 20.0, _("Disable the SE-Alert software by removing the 'setroubleshoot-server' rpm\n" "SE-Alert alerts you about SELinux policy violations, but also\n" "has a bug that wakes it up 10 times per second."), 20); suggest_bluetooth_off(); suggest_nmi_watchdog(); suggest_laptop_mode(); if (maxsleep > 15.0) suggest_hpet(); suggest_ac97_powersave(); suggest_wireless_powersave(); suggest_ondemand_governor(); suggest_noatime(); suggest_sata_alpm(); suggest_powersched(); suggest_xrandr_TV_off(); suggest_WOL_off(); suggest_writeback_time(); suggest_usb_autosuspend(); usb_activity_hint(); #endif if (dump) { #ifndef NO_SUGGESTIONS print_all_suggestions(); display_usb_activity(); #endif exit(EXIT_SUCCESS); } #ifndef NO_SUGGESTIONS if (!key) pick_suggestion(); #endif #ifndef NO_NCURSES show_title_bar(); #endif fflush(stdout); if (!key && ticktime >= 4.8) { /* quiet down the effects of any IO to xterms */ FD_ZERO(&rfds); FD_SET(0, &rfds); tv.tv_sec = 3; tv.tv_usec = 0; key = select(1, &rfds, NULL, NULL, &tv); } read_data(&cur_usage[0], &cur_duration[0]); memcpy(last_usage, cur_usage, sizeof(last_usage)); memcpy(last_duration, cur_duration, sizeof(last_duration)); } return 0; }