| /* |
| * 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 <sys/stat.h> |
| |
| #include "powertop.h" |
| |
| #define VERSION "1.11" |
| |
| 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; |
| 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; |
| delta = update_irq(nr, count, name); |
| c = strchr(name, '\n'); |
| if (c) |
| *c = 0; |
| if (strcmp(name, "i8042")) { |
| if (special) |
| sprintf(line2, _(" <kernel IPI> : %s"), name); |
| else |
| sprintf(line2, _(" <interrupt> : %s"), name); |
| } |
| else |
| sprintf(line2, _(" <interrupt> : %s"), _("PS/2 keyboard/mouse/touchpad")); |
| |
| 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 - 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(_(" -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' }, |
| { "help", 0, NULL, 'h' }, |
| { "version", 0, NULL, 'v' }, |
| { 0, 0, NULL, 0 } |
| }; |
| int index2 = 0, c; |
| |
| c = getopt_long(argc, argv, "dt:hv", opts, &index2); |
| if (c == -1) |
| break; |
| switch (c) { |
| case 'd': |
| dump = 1; |
| break; |
| case 't': |
| ticktime = strtod(optarg, NULL); |
| 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 i = 0; |
| double c0 = 0; |
| char *c; |
| |
| |
| FD_ZERO(&rfds); |
| 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; |
| 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 = _("<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; |
| sprintf(line2, "%15s : %s", process, func); |
| push_line_pid(line2, cnt, pid); |
| } |
| if (file) |
| pclose(file); |
| |
| 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(_(" <interrupt> : 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(); |
| 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_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(); |
| |
| if (dump) { |
| print_all_suggestions(); |
| display_usb_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)); |
| |
| |
| |
| } |
| |
| return 0; |
| } |