diff options
author | Eugeni Dodonov <eugeni@dodonov.net> | 2011-08-26 12:19:14 -0300 |
---|---|---|
committer | Eugeni Dodonov <eugeni@dodonov.net> | 2011-08-26 12:19:14 -0300 |
commit | 1bec2dbc30d220d07dbffde53a44539ab3289fc2 (patch) | |
tree | 331affec291b39cdeb5d90de91643240f6497f3b /powertop.c |
imported to git for android build
Diffstat (limited to 'powertop.c')
-rw-r--r-- | powertop.c | 1300 |
1 files changed, 1300 insertions, 0 deletions
diff --git a/powertop.c b/powertop.c new file mode 100644 index 0000000..74eb328 --- /dev/null +++ b/powertop.c @@ -0,0 +1,1300 @@ +/* + * 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 <arjan@linux.intel.com> + */ + +#include <getopt.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <dirent.h> +#include <libintl.h> +#include <ctype.h> +#include <assert.h> +#include <locale.h> +#include <time.h> +#include <limits.h> +#include <sys/stat.h> + +#include "powertop.h" + + +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; + +#define IRQCOUNT 150 + +struct irqdata { + int active; + int number; + uint64_t count; +}; + +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)); + memset(&lines[linehead], 0, sizeof(&lines[0])); + lines[linehead].string = strdup (string); + lines[linehead].count = count; + lines[linehead].disk_count = 0; + lines[linehead].pid[0] = 0; + linehead++; +} + +void push_line_pid(char *string, int cpu_count, int disk_count, char *pid) +{ + int i; + assert(string != NULL); + assert(strlen(string) > 0); + for (i = 0; i < linehead; i++) + if (strcmp(string, lines[i].string) == 0) { + lines[i].count += cpu_count; + lines[i].disk_count += disk_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)); + memset(&lines[linehead], 0, sizeof(&lines[0])); + lines[linehead].string = strdup (string); + lines[linehead].count = cpu_count; + lines[linehead].disk_count = disk_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; + return count; +} + +static int percpu_hpet_timer(char *name) +{ + static int timer_list_read; + static int percpu_hpet_start = INT_MAX, percpu_hpet_end = INT_MIN; + char *c; + long hpet_chan; + + if (!timer_list_read) { + char file_name[20]; + char ln[80]; + FILE *fp; + + timer_list_read = 1; + snprintf(file_name, sizeof(file_name), "/proc/timer_list"); + fp = fopen(file_name, "r"); + if (fp == NULL) + return 0; + + while (fgets(ln, sizeof(ln), fp) != NULL) + { + c = strstr(ln, "Clock Event Device: hpet"); + if (!c) + continue; + + c += 24; + if (!isdigit(c[0])) + continue; + + hpet_chan = strtol(c, NULL, 10); + if (hpet_chan < percpu_hpet_start) + percpu_hpet_start = hpet_chan; + if (hpet_chan > percpu_hpet_end) + percpu_hpet_end = hpet_chan; + } + fclose(fp); + } + + c = strstr(name, "hpet"); + if (!c) + return 0; + + c += 4; + if (!isdigit(c[0])) + return 0; + + hpet_chan = strtol(c, NULL, 10); + if (percpu_hpet_start <= hpet_chan && hpet_chan <= percpu_hpet_end) + return 1; + + return 0; +} + +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)); + memset(line2, 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; + delta = update_irq(nr, count, name); + c = strchr(name, '\n'); + if (c) + *c = 0; + + /* deal with multi-queue NICs */ + if (strncmp(name, "eth",3) == 0 && strchr(name,' ') == NULL) { + c = strchr(name, '-'); + if (c) + *c = 0; + } + + if (strcmp(name, "i8042")) { + if (special) + sprintf(line2, _("[%s] <kernel IPI>"), name); + else + sprintf(line2, _("[%s] <interrupt>"), name); + } + else + sprintf(line2, _("%s interrupt"), _("PS/2 keyboard/mouse/touchpad")); + + /* skip per CPU timer interrupts */ + if (percpu_hpet_timer(name)) + delta = 0; + + if (nr > 0 && delta > 0) + push_line(line2, delta); + if (nr==0) + interrupt_0 = delta; + else + total_interrupt += delta; + } + 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; + + 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 = sprintf(filename, "/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); +} + +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); +} + +int line_compare (const void *av, const void *bv) +{ + const struct line *a = av, *b = bv; + return (b->count + 50 * b->disk_count) - (a->count + 50 * a->disk_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) { + watts_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(_(" -p, --pids show pids in wakeups list\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"); + + start_data_dirty_capture(); + + while (1) { + static struct option opts[] = { + { "dump", 0, NULL, 'd' }, + { "time", 1, NULL, 't' }, + { "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:phv", opts, &index2); + if (c == -1) + break; + switch (c) { + case 'd': + dump = 1; + break; + case 't': + ticktime = strtod(optarg, NULL); + 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 2>&1"); + 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(); + count_device_pm(); + count_device_pm(); + do_alsa_stats(); + do_alsa_stats(); + do_ahci_stats(); + do_ahci_stats(); + + + memset(cur_usage, 0, sizeof(cur_usage)); + memset(cur_duration, 0, sizeof(cur_duration)); + printf("PowerTOP " VERSION " (C) 2007 - 2010 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 = 0; + + int i = 0; + double c0 = 0; + char *c; + + + FD_ZERO(&rfds); + if (!dump) + FD_SET(0, &rfds); + tv.tv_sec = ticktime; + tv.tv_usec = (ticktime - tv.tv_sec) * 1000000; + 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(); + 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; + c0 = sysconf(_SC_NPROCESSORS_ONLN) * 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 / (sysconf(_SC_NPROCESSORS_ONLN) * 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 / + (sysconf(_SC_NPROCESSORS_ONLN) * 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(); + /* 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 = 0; + int deferrable = 0; + memset(line, 0, 1024); + memset(line2, 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, "swapper")==0 && + strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n")==0) { + process = _("[kernel scheduler]"); + func = _("Load balancing tick"); + } + if (strcmp(process, "insmod") == 0) + process = _("[kernel module]"); + if (strcmp(process, "modprobe") == 0) + process = _("[kernel module]"); + if (strcmp(process, "swapper") == 0) + process = _("[kernel core]"); + 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; + if (strchr(process, '[')) + sprintf(line2, "%s %s", process, func); + else + sprintf(line2, "%s", process); + push_line_pid(line2, cnt, 0, pid); + } + + if (file) + pclose(file); + + reset_suggestions(); + + parse_data_dirty_buffer(); + + if (strstr(line, "total events")) { + int d; + d = strtoull(line, NULL, 10) / sysconf(_SC_NPROCESSORS_ONLN); + if (totalevents == 0) { /* No c-state info available, use timerstats instead */ + totalevents = d * sysconf(_SC_NPROCESSORS_ONLN) + 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 / sysconf(_SC_NPROCESSORS_ONLN); + show_wakeups(wakeups_per_second, ticktime, c0 * 100.0 / (sysconf(_SC_NPROCESSORS_ONLN) * ticktime * 1000 * FREQ) ); + } + count_usb_urbs(); + count_device_pm(); + + do_alsa_stats(); + do_ahci_stats(); + 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); + if (keystroke == EOF) + exit(EXIT_SUCCESS); + + keychar = toupper(keystroke); + if (keychar == 'Q') + exit(EXIT_SUCCESS); + if (keychar == 'R') + ticktime = 3; + if (keychar == suggestion_key && suggestion_activate) { + suggestion_activate(); + ticktime = 2; + displaytime = -1.0; + } else + if (keychar == 'P') + showpids = !showpids; + } + + if (wakeups_per_second < 0) + ticktime = 2; + + reset_suggestions2(); + + 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_PM_ADVANCED_DEBUG", 1, + _("Suggestion: Enable the CONFIG_PM_ADVANCED_DEBUG kernel configuration option.\nThis option will allow PowerTOP to collect runtime power management statistics."), 10); + suggest_kernel_config("CONFIG_PM_RUNTIME", 1, + _("Suggestion: Enable the CONFIG_PM_RUNTIME kernel configuration option.\nThis option enables the kernel to manage power for various devices in your computer."), 40); + 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); + suggest_kernel_config("CONFIG_PCIEASPM", 1, + _("Suggestion: Enable the CONFIG_PCIEASPM kernel configuration option.\n" + "PCI Link Powermanagement (ASPM) allows the hardware to go to low power mode\n" "automatically when a PCI-E device is idle."), 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_on_dmesg("failed to find CxSR latency, disabling CxSR", + _("The Intel Integrated Graphics driver failed to enable Memory " + "self refresh.\nMemory Self Refresh is important for " + "good memory power savings.\nPlease check your OS " + "vendor for a kernel update and/or report a bug."), + 10); + + suggest_bluetooth_off(); + suggest_nmi_watchdog(); + if (maxsleep > 15.0) + suggest_hpet(); + suggest_ac97_powersave(); + suggest_hda_powersave(); + suggest_wireless_powersave(); + suggest_wifi_new_powersave(); + suggest_ondemand_governor(); + suggest_noatime(); + suggest_sata_alpm(); + suggest_powersched(); + suggest_xrandr_TV_off(); + suggest_WOL_off(); + suggest_writeback_time(); + suggest_usb_autosuspend(); + suggest_runtime_suspend(); + + usb_activity_hint(); + void devicepm_activity_hint(); + alsa_activity_hint(); + ahci_activity_hint(); + + if (dump) { + print_all_suggestions(); + display_usb_activity(); + display_runtime_activity(); + display_alsa_activity(); + display_ahci_activity(); + exit(EXIT_SUCCESS); + } + + if (!key) + pick_suggestion(); + show_title_bar(); + + 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)); + + + + } + + end_data_dirty_capture(); + return 0; +} |