summaryrefslogtreecommitdiff
path: root/powertop.c
diff options
context:
space:
mode:
authorEugeni Dodonov <eugeni@dodonov.net>2011-08-26 12:19:14 -0300
committerEugeni Dodonov <eugeni@dodonov.net>2011-08-26 12:19:14 -0300
commit1bec2dbc30d220d07dbffde53a44539ab3289fc2 (patch)
tree331affec291b39cdeb5d90de91643240f6497f3b /powertop.c
imported to git for android build
Diffstat (limited to 'powertop.c')
-rw-r--r--powertop.c1300
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;
+}