diff options
Diffstat (limited to '')
128 files changed, 24725 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..6b523f6 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,176 @@ +AUTOMAKE_OPTIONS = subdir-objects + +sbin_PROGRAMS = powertop +nodist_powertop_SOURCES = css.h + +powertop_SOURCES = \ + css.h \ + devlist.cpp \ + devlist.h \ + display.cpp \ + display.h \ + lib.cpp \ + lib.h \ + main.cpp \ + powertop.css \ + \ + calibrate/calibrate.cpp \ + calibrate/calibrate.h \ + cpu/abstract_cpu.cpp \ + cpu/cpu.cpp \ + cpu/cpu.h \ + cpu/cpu_core.cpp \ + cpu/cpu_linux.cpp \ + cpu/cpu_package.cpp \ + cpu/cpu_rapl_device.cpp \ + cpu/cpu_rapl_device.h \ + cpu/cpudevice.cpp \ + cpu/cpudevice.h \ + cpu/dram_rapl_device.cpp \ + cpu/dram_rapl_device.h \ + cpu/intel_cpus.cpp \ + cpu/intel_cpus.h \ + cpu/intel_gpu.cpp \ + cpu/rapl/rapl_interface.cpp \ + cpu/rapl/rapl_interface.h \ + devices/ahci.cpp \ + devices/ahci.h \ + devices/alsa.cpp \ + devices/alsa.h \ + devices/backlight.cpp \ + devices/backlight.h \ + devices/devfreq.cpp \ + devices/devfreq.h \ + devices/device.cpp \ + devices/device.h \ + devices/gpu_rapl_device.cpp \ + devices/gpu_rapl_device.h \ + devices/i915-gpu.cpp \ + devices/i915-gpu.h \ + devices/network.cpp \ + devices/network.h \ + devices/rfkill.cpp \ + devices/rfkill.h \ + devices/runtime_pm.cpp \ + devices/runtime_pm.h \ + devices/thinkpad-fan.cpp \ + devices/thinkpad-fan.h \ + devices/thinkpad-light.cpp \ + devices/thinkpad-light.h \ + devices/usb.cpp \ + devices/usb.h \ + measurement/acpi.cpp \ + measurement/acpi.h \ + measurement/extech.cpp \ + measurement/extech.h \ + measurement/measurement.cpp \ + measurement/measurement.h \ + measurement/sysfs.cpp \ + measurement/sysfs.h \ + measurement/opal-sensors.cpp \ + measurement/opal-sensors.h \ + parameters/learn.cpp \ + parameters/parameters.cpp \ + parameters/parameters.h \ + parameters/persistent.cpp \ + perf/perf.cpp \ + perf/perf.h \ + perf/perf_bundle.cpp \ + perf/perf_bundle.h \ + perf/perf_event.h \ + process/do_process.cpp \ + process/interrupt.cpp \ + process/interrupt.h \ + process/powerconsumer.cpp \ + process/powerconsumer.h \ + process/process.cpp \ + process/process.h \ + process/processdevice.cpp \ + process/processdevice.h \ + process/timer.cpp \ + process/timer.h \ + process/work.cpp \ + process/work.h \ + report/report-data-html.cpp \ + report/report-data-html.h \ + report/report-formatter-base.cpp \ + report/report-formatter-base.h \ + report/report-formatter-csv.cpp \ + report/report-formatter-csv.h \ + report/report-formatter-html.cpp \ + report/report-formatter-html.h \ + report/report-formatter.h \ + report/report-maker.cpp \ + report/report-maker.h \ + report/report.cpp \ + report/report.h \ + tuning/bluetooth.cpp \ + tuning/bluetooth.h \ + tuning/ethernet.cpp \ + tuning/ethernet.h \ + tuning/iw.c \ + tuning/iw.h \ + tuning/nl80211.h \ + tuning/runtime.cpp \ + tuning/runtime.h \ + tuning/tunable.cpp \ + tuning/tunable.h \ + tuning/tuning.cpp \ + tuning/tuning.h \ + tuning/tuningsysfs.cpp \ + tuning/tuningsysfs.h \ + tuning/tuningusb.cpp \ + tuning/tuningusb.h \ + tuning/tuningi2c.cpp \ + tuning/tuningi2c.h \ + tuning/wifi.cpp \ + tuning/wifi.h \ + wakeup/wakeup.cpp \ + wakeup/waketab.cpp \ + wakeup/wakeup_ethernet.cpp \ + wakeup/wakeup_usb.cpp \ + wakeup/wakeup_usb.h \ + wakeup/wakeup.h \ + wakeup/wakeup_ethernet.h + +powertop_CXXFLAGS = \ + -Wall \ + -Wformat \ + -Wshadow \ + -fno-omit-frame-pointer \ + -fstack-protector \ + $(GLIB2_CFLAGS) \ + $(LIBNL_CFLAGS) \ + $(NCURSES_CFLAGS) \ + $(PCIUTILS_CFLAGS) \ + $(PTHREAD_CFLAGS) + + +powertop_CPPFLAGS = \ + -DLOCALEDIR=\"$(localedir)\" \ + $(GLIB2_CFLAGS) \ + $(LIBNL_CFLAGS) \ + $(LIBZ_CFLAGS) \ + $(NCURSES_CFLAGS) \ + $(PCIUTILS_CFLAGS) \ + $(PTHREAD_CFLAGS) + +powertop_LDADD = \ + ../traceevent/libtraceevnet.la + +AM_LDFLAGS = \ + $(LIBNL_LIBS) \ + $(LIBS) \ + $(LIBZ_LIBS) \ + $(NCURSES_LIBS) \ + $(PCIUTILS_LIBS) \ + $(PTHREAD_LIBS) \ + $(RESOLV_LIBS) + +BUILT_SOURCES = css.h +CLEANFILES = css.h + +css.h: powertop.css + $(SHELL) ${srcdir}/csstoh.sh ${srcdir}/powertop.css css.h + +EXTRA_DIST = ${srcdir}/csstoh.sh diff --git a/src/calibrate/calibrate.cpp b/src/calibrate/calibrate.cpp new file mode 100644 index 0000000..745d544 --- /dev/null +++ b/src/calibrate/calibrate.cpp @@ -0,0 +1,435 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <algorithm> + +#include "calibrate.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <pthread.h> +#include <math.h> +#include <sys/types.h> +#include <errno.h> +#include <limits.h> + +#include "../parameters/parameters.h" +extern "C" { +#include "../tuning/iw.h" +} + +#include <map> +#include <vector> +#include <string> + +using namespace std; + + +static vector<string> usb_devices; +static vector<string> rfkill_devices; +static vector<string> backlight_devices; +static vector<string> scsi_link_devices; +static int blmax; + +static map<string, string> saved_sysfs; + + +static volatile int stop_measurement; + +static int wireless_PS; + + +static void save_sysfs(const char *filename) +{ + char line[4096]; + ifstream file; + + file.open(filename, ios::in); + if (!file) + return; + file.getline(line, 4096); + file.close(); + + saved_sysfs[filename] = line; +} + +static void restore_all_sysfs(void) +{ + map<string, string>::iterator it; + + for (it = saved_sysfs.begin(); it != saved_sysfs.end(); it++) + write_sysfs(it->first, it->second); + + set_wifi_power_saving("wlan0", wireless_PS); +} + +static void find_all_usb_callback(const char *d_name) +{ + char filename[PATH_MAX]; + ifstream file; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/active_duration", d_name); + if (access(filename, R_OK) != 0) + return; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/idVendor", d_name); + file.open(filename, ios::in); + if (file) { + file.getline(filename, sizeof(filename)); + file.close(); + if (strcmp(filename, "1d6b") == 0) + return; + } + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/control", d_name); + save_sysfs(filename); + usb_devices.push_back(filename); +} + +static void find_all_usb(void) +{ + process_directory("/sys/bus/usb/devices/", find_all_usb_callback); +} + +static void suspend_all_usb_devices(void) +{ + unsigned int i; + + for (i = 0; i < usb_devices.size(); i++) + write_sysfs(usb_devices[i], "auto\n"); +} + +static void find_all_rfkill_callback(const char *d_name) +{ + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "/sys/class/rfkill/%s/soft", d_name); + if (access(filename, R_OK) != 0) + return; + save_sysfs(filename); + rfkill_devices.push_back(filename); +} + +static void find_all_rfkill(void) +{ + process_directory("/sys/class/rfkill/", find_all_rfkill_callback); +} + +static void rfkill_all_radios(void) +{ + unsigned int i; + + for (i = 0; i < rfkill_devices.size(); i++) + write_sysfs(rfkill_devices[i], "1\n"); +} +static void unrfkill_all_radios(void) +{ + unsigned int i; + + for (i = 0; i < rfkill_devices.size(); i++) + write_sysfs(rfkill_devices[i], "0\n"); +} + +static void find_backlight_callback(const char *d_name) +{ + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "/sys/class/backlight/%s/brightness", d_name); + if (access(filename, R_OK) != 0) + return; + + save_sysfs(filename); + backlight_devices.push_back(filename); + snprintf(filename, sizeof(filename), "/sys/class/backlight/%s/max_brightness", d_name); + blmax = read_sysfs(filename); +} + +static void find_backlight(void) +{ + process_directory("/sys/class/backlight/", find_backlight_callback); +} + +static void lower_backlight(void) +{ + unsigned int i; + + for (i = 0; i < backlight_devices.size(); i++) + write_sysfs(backlight_devices[i], "0\n"); +} + +static void find_scsi_link_callback(const char *d_name) +{ + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "/sys/class/scsi_host/%s/link_power_management_policy", d_name); + if (access(filename, R_OK)!=0) + return; + + save_sysfs(filename); + scsi_link_devices.push_back(filename); +} + +static void find_scsi_link(void) +{ + process_directory("/sys/class/scsi_host/", find_scsi_link_callback); +} + +static void set_scsi_link(const char *state) +{ + unsigned int i; + + for (i = 0; i < scsi_link_devices.size(); i++) + write_sysfs(scsi_link_devices[i], state); +} + + +static void *burn_cpu(void *dummy) +{ + volatile double d = 1.1; + + while (!stop_measurement) { + d = pow(d, 1.0001); + } + return NULL; +} + +static void *burn_cpu_wakeups(void *dummy) +{ + struct timespec tm; + + while (!stop_measurement) { + tm.tv_sec = 0; + tm.tv_nsec = (unsigned long)dummy; + nanosleep(&tm, NULL); + } + return NULL; +} + +static void *burn_disk(void *dummy) +{ + int fd; + char buffer[64*1024]; + char filename[256]; + + strcpy(filename ,"/tmp/powertop.XXXXXX"); + fd = mkstemp(filename); + + if (fd < 0) { + printf(_("Cannot create temp file\n")); + return NULL; + } + + while (!stop_measurement) { + lseek(fd, 0, SEEK_SET); + if(write(fd, buffer, 64*1024) == -1) + printf("Error: %s\n", strerror(errno)); + fdatasync(fd); + } + unlink(filename); + return NULL; +} + + +static void cpu_calibration(int threads) +{ + int i; + pthread_t thr; + + printf(_("Calibrating: CPU usage on %i threads\n"), threads); + + stop_measurement = 0; + for (i = 0; i < threads; i++) + pthread_create(&thr, NULL, burn_cpu, NULL); + + one_measurement(15, 15, NULL); + stop_measurement = 1; + sleep(1); +} + +static void wakeup_calibration(unsigned long interval) +{ + pthread_t thr; + + printf(_("Calibrating: CPU wakeup power consumption\n")); + + stop_measurement = 0; + + pthread_create(&thr, NULL, burn_cpu_wakeups, (void *)interval); + + one_measurement(15, 15, NULL); + stop_measurement = 1; + sleep(1); +} + +static void usb_calibration(void) +{ + unsigned int i; + + /* chances are one of the USB devices is bluetooth; unrfkill first */ + unrfkill_all_radios(); + printf(_("Calibrating USB devices\n")); + for (i = 0; i < usb_devices.size(); i++) { + printf(_(".... device %s \n"), usb_devices[i].c_str()); + suspend_all_usb_devices(); + write_sysfs(usb_devices[i], "on\n"); + one_measurement(15, 15, NULL); + suspend_all_usb_devices(); + sleep(3); + } + rfkill_all_radios(); + sleep(4); +} + +static void rfkill_calibration(void) +{ + unsigned int i; + + printf(_("Calibrating radio devices\n")); + for (i = 0; i < rfkill_devices.size(); i++) { + printf(_(".... device %s \n"), rfkill_devices[i].c_str()); + rfkill_all_radios(); + write_sysfs(rfkill_devices[i], "0\n"); + one_measurement(15, 15, NULL); + rfkill_all_radios(); + sleep(3); + } + for (i = 0; i < rfkill_devices.size(); i++) { + printf(_(".... device %s \n"), rfkill_devices[i].c_str()); + unrfkill_all_radios(); + write_sysfs(rfkill_devices[i], "1\n"); + one_measurement(15, 15, NULL); + unrfkill_all_radios(); + sleep(3); + } + rfkill_all_radios(); +} + +static void backlight_calibration(void) +{ + unsigned int i; + + printf(_("Calibrating backlight\n")); + for (i = 0; i < backlight_devices.size(); i++) { + char str[4096]; + printf(_(".... device %s \n"), backlight_devices[i].c_str()); + lower_backlight(); + one_measurement(15, 15, NULL); + sprintf(str, "%i\n", blmax / 4); + write_sysfs(backlight_devices[i], str); + one_measurement(15, 15, NULL); + + sprintf(str, "%i\n", blmax / 2); + write_sysfs(backlight_devices[i], str); + one_measurement(15, 15, NULL); + + sprintf(str, "%i\n", 3 * blmax / 4 ); + write_sysfs(backlight_devices[i], str); + one_measurement(15, 15, NULL); + + sprintf(str, "%i\n", blmax); + write_sysfs(backlight_devices[i], str); + one_measurement(15, 15, NULL); + lower_backlight(); + sleep(1); + } + printf(_("Calibrating idle\n")); + if(!system("DISPLAY=:0 /usr/bin/xset dpms force off")) + printf("System is not available\n"); + one_measurement(15, 15, NULL); + if(!system("DISPLAY=:0 /usr/bin/xset dpms force on")) + printf("System is not available\n"); +} + +static void idle_calibration(void) +{ + printf(_("Calibrating idle\n")); + if(!system("DISPLAY=:0 /usr/bin/xset dpms force off")) + printf("System is not available\n"); + one_measurement(15, 15, NULL); + if(!system("DISPLAY=:0 /usr/bin/xset dpms force on")) + printf("System is not available\n"); +} + + +static void disk_calibration(void) +{ + pthread_t thr; + + printf(_("Calibrating: disk usage \n")); + + set_scsi_link("min_power"); + + stop_measurement = 0; + pthread_create(&thr, NULL, burn_disk, NULL); + + one_measurement(15, 15, NULL); + stop_measurement = 1; + sleep(1); + + +} + + +void calibrate(void) +{ + find_all_usb(); + find_all_rfkill(); + find_backlight(); + find_scsi_link(); + wireless_PS = get_wifi_power_saving("wlan0"); + + save_sysfs("/sys/module/snd_hda_intel/parameters/power_save"); + + cout << _("Starting PowerTOP power estimate calibration \n"); + suspend_all_usb_devices(); + rfkill_all_radios(); + lower_backlight(); + set_wifi_power_saving("wlan0", 1); + + sleep(4); + + + idle_calibration(); + disk_calibration(); + backlight_calibration(); + + write_sysfs("/sys/module/snd_hda_intel/parameters/power_save", "1\n"); + cpu_calibration(1); + cpu_calibration(4); + wakeup_calibration(10000); + wakeup_calibration(100000); + wakeup_calibration(1000000); + set_wifi_power_saving("wlan0", 0); + usb_calibration(); + rfkill_calibration(); + + cout << _("Finishing PowerTOP power estimate calibration \n"); + + restore_all_sysfs(); + learn_parameters(300, 1); + printf(_("Parameters after calibration:\n")); + dump_parameter_bundle(); + save_parameters("saved_parameters.powertop"); + save_all_results("saved_results.powertop"); + +} diff --git a/src/calibrate/calibrate.h b/src/calibrate/calibrate.h new file mode 100644 index 0000000..de71938 --- /dev/null +++ b/src/calibrate/calibrate.h @@ -0,0 +1,32 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_CALIBRATE_H +#define __INCLUDE_GUARD_CALIBRATE_H + +extern void one_measurement(int seconds, int sample_interval, char *workload); +extern void calibrate(void); + + +#endif diff --git a/src/cpu/abstract_cpu.cpp b/src/cpu/abstract_cpu.cpp new file mode 100644 index 0000000..066891d --- /dev/null +++ b/src/cpu/abstract_cpu.cpp @@ -0,0 +1,535 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include "cpu.h" +#include "../lib.h" + +abstract_cpu::~abstract_cpu() +{ + unsigned int i=0; + for (i=0; i < cstates.size(); i++){ + delete cstates[i]; + } + cstates.clear(); + + for (i=0; i < pstates.size(); i++){ + delete pstates[i]; + } + pstates.clear(); +} + +void abstract_cpu::account_freq(uint64_t freq, uint64_t duration) +{ + struct frequency *state = NULL; + unsigned int i; + + for (i = 0; i < pstates.size(); i++) { + if (freq == pstates[i]->freq) { + state = pstates[i]; + break; + } + } + + + if (!state) { + state = new(std::nothrow) struct frequency; + + if (!state) + return; + + memset(state, 0, sizeof(*state)); + + pstates.push_back(state); + + state->freq = freq; + hz_to_human(freq, state->human_name); + if (freq == 0) + pt_strcpy(state->human_name, _("Idle")); + if (is_turbo(freq, max_frequency, max_minus_one_frequency)) + pt_strcpy(state->human_name, _("Turbo Mode")); + + state->after_count = 1; + } + + + state->time_after += duration; + + +} + +void abstract_cpu::freq_updated(uint64_t time) +{ + if(parent) + parent->calculate_freq(time); + old_idle = idle; +} + +void abstract_cpu::measurement_start(void) +{ + unsigned int i; + ifstream file; + char filename[4096]; + + last_stamp = 0; + + for (i = 0; i < cstates.size(); i++) + delete cstates[i]; + cstates.resize(0); + + for (i = 0; i < pstates.size(); i++) + delete pstates[i]; + pstates.resize(0); + + current_frequency = 0; + idle = false; + old_idle = true; + + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_available_frequencies", number); + file.open(filename, ios::in); + if (file) { + file >> max_frequency; + file >> max_minus_one_frequency; + file.close(); + } + + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->measurement_start(); + + gettimeofday(&stamp_before, NULL); + + last_stamp = 0; + + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->wiggle(); + +} + +void abstract_cpu::measurement_end(void) +{ + unsigned int i, j; + + total_stamp = 0; + gettimeofday(&stamp_after, NULL); + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->wiggle(); + + time_factor = 1000000.0 * (stamp_after.tv_sec - stamp_before.tv_sec) + stamp_after.tv_usec - stamp_before.tv_usec; + + + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->measurement_end(); + + for (i = 0; i < children.size(); i++) + if (children[i]) { + for (j = 0; j < children[i]->cstates.size(); j++) { + struct idle_state *state; + state = children[i]->cstates[j]; + if (!state) + continue; + + update_cstate( state->linux_name, state->human_name, state->usage_before, state->duration_before, state->before_count); + finalize_cstate(state->linux_name, state->usage_after, state->duration_after, state->after_count); + } + for (j = 0; j < children[i]->pstates.size(); j++) { + struct frequency *state; + state = children[i]->pstates[j]; + if (!state) + continue; + + update_pstate( state->freq, state->human_name, state->time_before, state->before_count); + finalize_pstate(state->freq, state->time_after, state->after_count); + } + } + + for (i = 0; i < cstates.size(); i++) { + struct idle_state *state = cstates[i]; + + if (state->after_count == 0) + continue; + + if (state->after_count != state->before_count) + continue; + + state->usage_delta = (state->usage_after - state->usage_before) / state->after_count; + state->duration_delta = (state->duration_after - state->duration_before) / state->after_count; + } +} + +void abstract_cpu::insert_cstate(const char *linux_name, const char *human_name, uint64_t usage, uint64_t duration, int count, int level) +{ + struct idle_state *state; + const char *c; + + state = new(std::nothrow) struct idle_state; + + if (!state) + return; + + memset(state, 0, sizeof(*state)); + + cstates.push_back(state); + + pt_strcpy(state->linux_name, linux_name); + pt_strcpy(state->human_name, human_name); + + state->line_level = -1; + + c = human_name; + while (*c) { + if (strcmp(linux_name, "active")==0) { + state->line_level = LEVEL_C0; + break; + } + if (*c >= '0' && *c <='9') { + state->line_level = strtoull(c, NULL, 10); + if(*(c+1) != '-'){ + int greater_line_level = strtoull(c, NULL, 10); + for(unsigned int pos = 0; pos < cstates.size(); pos++){ + if(*c == cstates[pos]->human_name[1]){ + if(*(c+1) != cstates[pos]->human_name[2]){ + greater_line_level = max(greater_line_level, cstates[pos]->line_level); + state->line_level = greater_line_level + 1; + } + } + } + } + break; + } + c++; + } + + /* some architectures (ARM) don't have good numbers in their human name.. fall back to the linux name for those */ + c = linux_name; + while (*c && state->line_level < 0) { + if (*c >= '0' && *c <='9') { + state->line_level = strtoull(c, NULL, 10); + break; + } + c++; + } + + if (level >= 0) + state->line_level = level; + + state->usage_before = usage; + state->duration_before = duration; + state->before_count = count; +} + +void abstract_cpu::finalize_cstate(const char *linux_name, uint64_t usage, uint64_t duration, int count) +{ + unsigned int i; + struct idle_state *state = NULL; + + for (i = 0; i < cstates.size(); i++) { + if (strcmp(linux_name, cstates[i]->linux_name) == 0) { + state = cstates[i]; + break; + } + } + + if (!state) { + cout << "Invalid C state finalize " << linux_name << " \n"; + return; + } + + state->usage_after += usage; + state->duration_after += duration; + state->after_count += count; +} + +void abstract_cpu::update_cstate(const char *linux_name, const char *human_name, uint64_t usage, uint64_t duration, int count, int level) +{ + unsigned int i; + struct idle_state *state = NULL; + + for (i = 0; i < cstates.size(); i++) { + if (strcmp(linux_name, cstates[i]->linux_name) == 0) { + state = cstates[i]; + break; + } + } + + if (!state) { + insert_cstate(linux_name, human_name, usage, duration, count, level); + return; + } + + state->usage_before += usage; + state->duration_before += duration; + state->before_count += count; + +} + +int abstract_cpu::has_cstate_level(int level) +{ + unsigned int i; + + if (level == LEVEL_HEADER) + return 1; + + for (i = 0; i < cstates.size(); i++) + if (cstates[i]->line_level == level) + return 1; + + for (i = 0; i < children.size(); i++) + if (children[i]) + if (children[i]->has_cstate_level(level)) + return 1; + return 0; +} + +int abstract_cpu::has_pstate_level(int level) +{ + unsigned int i; + + if (level == LEVEL_HEADER) + return 1; + + if (level >= 0 && level < (int)pstates.size()) + return 1; + + for (i = 0; i < children.size(); i++) + if (children[i]) + if (children[i]->has_pstate_level(level)) + return 1; + return 0; +} + + + +void abstract_cpu::insert_pstate(uint64_t freq, const char *human_name, uint64_t duration, int count) +{ + struct frequency *state; + + state = new(std::nothrow) struct frequency; + + if (!state) + return; + + memset(state, 0, sizeof(*state)); + + pstates.push_back(state); + + state->freq = freq; + pt_strcpy(state->human_name, human_name); + + + state->time_before = duration; + state->before_count = count; +} + +void abstract_cpu::finalize_pstate(uint64_t freq, uint64_t duration, int count) +{ + unsigned int i; + struct frequency *state = NULL; + + for (i = 0; i < pstates.size(); i++) { + if (freq == pstates[i]->freq) { + state = pstates[i]; + break; + } + } + + if (!state) { + cout << "Invalid P state finalize " << freq << " \n"; + return; + } + state->time_after += duration; + state->after_count += count; + +} + +void abstract_cpu::update_pstate(uint64_t freq, const char *human_name, uint64_t duration, int count) +{ + unsigned int i; + struct frequency *state = NULL; + + for (i = 0; i < pstates.size(); i++) { + if (freq == pstates[i]->freq) { + state = pstates[i]; + break; + } + } + + if (!state) { + insert_pstate(freq, human_name, duration, count); + return; + } + + state->time_before += duration; + state->before_count += count; +} + + +void abstract_cpu::calculate_freq(uint64_t time) +{ + uint64_t freq = 0; + bool is_idle = true; + unsigned int i; + + /* calculate the maximum frequency of all children */ + for (i = 0; i < children.size(); i++) + if (children[i] && children[i]->has_pstates()) { + uint64_t f = 0; + if (!children[i]->idle) { + f = children[i]->current_frequency; + is_idle = false; + } + if (f > freq) + freq = f; + } + + current_frequency = freq; + idle = is_idle; + freq_updated(time); +} + +void abstract_cpu::change_effective_frequency(uint64_t time, uint64_t frequency) +{ + unsigned int i; + uint64_t time_delta, fr; + + if (last_stamp) + time_delta = time - last_stamp; + else + time_delta = 1; + + fr = effective_frequency; + if (old_idle) + fr = 0; + + account_freq(fr, time_delta); + + effective_frequency = frequency; + last_stamp = time; + + /* propagate to all children */ + for (i = 0; i < children.size(); i++) + if (children[i]) { + children[i]->change_effective_frequency(time, frequency); + } +} + + +void abstract_cpu::wiggle(void) +{ + char filename[PATH_MAX]; + ifstream ifile; + ofstream ofile; + uint64_t minf,maxf; + uint64_t setspeed = 0; + + /* wiggle a CPU so that we have a record of it at the start and end of the perf trace */ + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_max_freq", first_cpu); + ifile.open(filename, ios::in); + ifile >> maxf; + ifile.close(); + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_min_freq", first_cpu); + ifile.open(filename, ios::in); + ifile >> minf; + ifile.close(); + + /* In case of the userspace governor, remember the old setspeed setting, it will be affected by wiggle */ + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_setspeed", first_cpu); + ifile.open(filename, ios::in); + /* Note that non-userspace governors report "<unsupported>". In that case ifile will fail and setspeed remains 0 */ + ifile >> setspeed; + ifile.close(); + + ofile.open(filename, ios::out); + ofile << maxf; + ofile.close(); + ofile.open(filename, ios::out); + ofile << minf; + ofile.close(); + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_max_freq", first_cpu); + ofile.open(filename, ios::out); + ofile << minf; + ofile.close(); + ofile.open(filename, ios::out); + ofile << maxf; + ofile.close(); + + if (setspeed != 0) { + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/scaling_setspeed", first_cpu); + ofile.open(filename, ios::out); + ofile << setspeed; + ofile.close(); + } +} +uint64_t abstract_cpu::total_pstate_time(void) +{ + unsigned int i; + uint64_t stamp = 0; + + for (i = 0; i < pstates.size(); i++) + stamp += pstates[i]->time_after; + + return stamp; +} + + +void abstract_cpu::validate(void) +{ + unsigned int i; + + for (i = 0; i < children.size(); i++) { + if (children[i]) + children[i]->validate(); + } +} + +void abstract_cpu::reset_pstate_data(void) +{ + unsigned int i; + + for (i = 0; i < pstates.size(); i++) { + pstates[i]->time_before = 0; + pstates[i]->time_after = 0; + } + for (i = 0; i < cstates.size(); i++) { + cstates[i]->duration_before = 0; + cstates[i]->duration_after = 0; + cstates[i]->before_count = 0; + cstates[i]->after_count = 0; + } + + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->reset_pstate_data(); +} diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp new file mode 100644 index 0000000..1c12765 --- /dev/null +++ b/src/cpu/cpu.cpp @@ -0,0 +1,1075 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <vector> +#include <string.h> +#include <stdlib.h> +#include <ncurses.h> +#include <unistd.h> +#include "cpu.h" +#include "cpudevice.h" +#include "cpu_rapl_device.h" +#include "dram_rapl_device.h" +#include "intel_cpus.h" +#include "../parameters/parameters.h" + +#include "../perf/perf_bundle.h" +#include "../lib.h" +#include "../display.h" +#include "../report/report.h" +#include "../report/report-maker.h" +#include "../report/report-data-html.h" + +static class abstract_cpu system_level; + +vector<class abstract_cpu *> all_cpus; + +static class perf_bundle * perf_events; + + + +class perf_power_bundle: public perf_bundle +{ + virtual void handle_trace_point(void *trace, int cpu, uint64_t time); + +}; + + +static class abstract_cpu * new_package(int package, int cpu, char * vendor, int family, int model) +{ + class abstract_cpu *ret = NULL; + class cpudevice *cpudev; + class cpu_rapl_device *cpu_rapl_dev; + class dram_rapl_device *dram_rapl_dev; + + char packagename[128]; + if (strcmp(vendor, "GenuineIntel") == 0) + if (family == 6) + if (is_supported_intel_cpu(model, cpu)) { + ret = new class nhm_package(model); + ret->set_intel_MSR(true); + } + + if (!ret) { + ret = new class cpu_package; + ret->set_intel_MSR(false); + } + + ret->set_number(package, cpu); + ret->set_type("Package"); + ret->childcount = 0; + + snprintf(packagename, sizeof(packagename), _("cpu package %i"), cpu); + cpudev = new class cpudevice(_("cpu package"), packagename, ret); + all_devices.push_back(cpudev); + + snprintf(packagename, sizeof(packagename), _("package-%i"), cpu); + cpu_rapl_dev = new class cpu_rapl_device(cpudev, _("cpu rapl package"), packagename, ret); + if (cpu_rapl_dev->device_present()) + all_devices.push_back(cpu_rapl_dev); + else + delete cpu_rapl_dev; + + snprintf(packagename, sizeof(packagename), _("package-%i"), cpu); + dram_rapl_dev = new class dram_rapl_device(cpudev, _("dram rapl package"), packagename, ret); + if (dram_rapl_dev->device_present()) + all_devices.push_back(dram_rapl_dev); + else + delete dram_rapl_dev; + + return ret; +} + +static class abstract_cpu * new_core(int core, int cpu, char * vendor, int family, int model) +{ + class abstract_cpu *ret = NULL; + + if (strcmp(vendor, "GenuineIntel") == 0) + if (family == 6) + if (is_supported_intel_cpu(model, cpu)) { + ret = new class nhm_core(model); + ret->set_intel_MSR(true); + } + + if (!ret) { + ret = new class cpu_core; + ret->set_intel_MSR(false); + } + + ret->set_number(core, cpu); + ret->childcount = 0; + ret->set_type("Core"); + + return ret; +} + +static class abstract_cpu * new_i965_gpu(void) +{ + class abstract_cpu *ret = NULL; + + ret = new class i965_core; + ret->childcount = 0; + ret->set_type("GPU"); + + return ret; +} + +static class abstract_cpu * new_cpu(int number, char * vendor, int family, int model) +{ + class abstract_cpu * ret = NULL; + + if (strcmp(vendor, "GenuineIntel") == 0) + if (family == 6) + if (is_supported_intel_cpu(model, number)) { + ret = new class nhm_cpu; + ret->set_intel_MSR(true); + } + + if (!ret) { + ret = new class cpu_linux; + ret->set_intel_MSR(false); + } + ret->set_number(number, number); + ret->set_type("CPU"); + ret->childcount = 0; + + return ret; +} + + + + +static void handle_one_cpu(unsigned int number, char *vendor, int family, int model) +{ + char filename[PATH_MAX]; + ifstream file; + unsigned int package_number = 0; + unsigned int core_number = 0; + class abstract_cpu *package, *core, *cpu; + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/topology/core_id", number); + file.open(filename, ios::in); + if (file) { + file >> core_number; + file.close(); + } + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/topology/physical_package_id", number); + file.open(filename, ios::in); + if (file) { + file >> package_number; + if (package_number == (unsigned int) -1) + package_number = 0; + file.close(); + } + + + if (system_level.children.size() <= package_number) + system_level.children.resize(package_number + 1, NULL); + + if (!system_level.children[package_number]) { + system_level.children[package_number] = new_package(package_number, number, vendor, family, model); + system_level.childcount++; + } + + package = system_level.children[package_number]; + package->parent = &system_level; + + if (package->children.size() <= core_number) + package->children.resize(core_number + 1, NULL); + + if (!package->children[core_number]) { + package->children[core_number] = new_core(core_number, number, vendor, family, model); + package->childcount++; + } + + core = package->children[core_number]; + core->parent = package; + + if (core->children.size() <= number) + core->children.resize(number + 1, NULL); + if (!core->children[number]) { + core->children[number] = new_cpu(number, vendor, family, model); + core->childcount++; + } + + cpu = core->children[number]; + cpu->parent = core; + + if (number >= all_cpus.size()) + all_cpus.resize(number + 1, NULL); + all_cpus[number] = cpu; +} + +static void handle_i965_gpu(void) +{ + unsigned int core_number = 0; + class abstract_cpu *package; + + + package = system_level.children[0]; + + core_number = package->children.size(); + + if (package->children.size() <= core_number) + package->children.resize(core_number + 1, NULL); + + if (!package->children[core_number]) { + package->children[core_number] = new_i965_gpu(); + package->childcount++; + } +} + + +void enumerate_cpus(void) +{ + ifstream file; + char line[4096]; + + int number = -1; + char vendor[128]; + int family = 0; + int model = 0; + + file.open("/proc/cpuinfo", ios::in); + + if (!file) + return; + /* Not all /proc/cpuinfo include "vendor_id\t". */ + vendor[0] = '\0'; + + while (file) { + + file.getline(line, sizeof(line)); + if (strncmp(line, "vendor_id\t",10) == 0) { + char *c; + c = strchr(line, ':'); + if (c) { + c++; + if (*c == ' ') + c++; + pt_strcpy(vendor, c); + } + } + if (strncmp(line, "processor\t",10) == 0) { + char *c; + c = strchr(line, ':'); + if (c) { + c++; + number = strtoull(c, NULL, 10); + } + } + if (strncmp(line, "cpu family\t",11) == 0) { + char *c; + c = strchr(line, ':'); + if (c) { + c++; + family = strtoull(c, NULL, 10); + } + } + if (strncmp(line, "model\t",6) == 0) { + char *c; + c = strchr(line, ':'); + if (c) { + c++; + model = strtoull(c, NULL, 10); + } + } + /* on x86 and others 'bogomips' is last + * on ARM it *can* be bogomips, or 'CPU revision' + * on POWER, it's revision + */ + if (strncasecmp(line, "bogomips\t", 9) == 0 + || strncasecmp(line, "CPU revision\t", 13) == 0 + || strncmp(line, "revision", 8) == 0) { + if (number == -1) { + /* Not all /proc/cpuinfo include "processor\t". */ + number = 0; + } + if (number >= 0) { + handle_one_cpu(number, vendor, family, model); + set_max_cpu(number); + number = -2; + } + } + } + + + file.close(); + + if (access("/sys/class/drm/card0/power/rc6_residency_ms", R_OK) == 0) + handle_i965_gpu(); + + perf_events = new perf_power_bundle(); + + if (!perf_events->add_event("power:cpu_idle")){ + perf_events->add_event("power:power_start"); + perf_events->add_event("power:power_end"); + } + if (!perf_events->add_event("power:cpu_frequency")) + perf_events->add_event("power:power_frequency"); + +} + +void start_cpu_measurement(void) +{ + perf_events->start(); + system_level.measurement_start(); +} + +void end_cpu_measurement(void) +{ + system_level.measurement_end(); + perf_events->stop(); +} + +static void expand_string(char *string, unsigned int newlen) +{ + while (strlen(string) < newlen) + strcat(string, " "); +} + +static int has_state_level(class abstract_cpu *acpu, int state, int line) +{ + switch (state) { + case PSTATE: + return acpu->has_pstate_level(line); + break; + case CSTATE: + return acpu->has_cstate_level(line); + break; + } + return 0; +} + +static const char * fill_state_name(class abstract_cpu *acpu, int state, int line, char *buf) +{ + switch (state) { + case PSTATE: + return acpu->fill_pstate_name(line, buf); + break; + case CSTATE: + return acpu->fill_cstate_name(line, buf); + break; + } + return "-EINVAL"; +} + +static const char * fill_state_line(class abstract_cpu *acpu, int state, int line, + char *buf, const char *sep = "") +{ + switch (state) { + case PSTATE: + return acpu->fill_pstate_line(line, buf); + break; + case CSTATE: + return acpu->fill_cstate_line(line, buf, sep); + break; + } + return "-EINVAL"; +} + +static int get_cstates_num(void) +{ + unsigned int package, core, cpu; + class abstract_cpu *_package, * _core, * _cpu; + unsigned int i; + int cstates_num; + + for (package = 0, cstates_num = 0; + package < system_level.children.size(); package++) { + _package = system_level.children[package]; + if (_package == NULL) + continue; + + /* walk package cstates and get largest cstates number */ + for (i = 0; i < _package->cstates.size(); i++) + cstates_num = std::max(cstates_num, + (_package->cstates[i])->line_level); + + /* + * for each core in this package, walk core cstates and get + * largest cstates number + */ + for (core = 0; core < _package->children.size(); core++) { + _core = _package->children[core]; + if (_core == NULL) + continue; + + for (i = 0; i < _core->cstates.size(); i++) + cstates_num = std::max(cstates_num, + (_core->cstates[i])->line_level); + + /* + * for each core, walk the logical cpus in case + * there is are more linux cstates than hw cstates + */ + for (cpu = 0; cpu < _core->children.size(); cpu++) { + _cpu = _core->children[cpu]; + if (_cpu == NULL) + continue; + + for (i = 0; i < _cpu->cstates.size(); i++) + cstates_num = std::max(cstates_num, + (_cpu->cstates[i])->line_level); + } + } + } + + return cstates_num; +} + +void report_display_cpu_cstates(void) +{ + char buffer[512], buffer2[512], tmp_num[50]; + unsigned int package, core, cpu; + int line, cstates_num, title=0, core_num=0; + class abstract_cpu *_package, *_core = NULL, * _cpu; + const char* core_type = NULL; + + cstates_num = get_cstates_num(); + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "cpuidle"); + + /* Set Table attributes, rows, and cols */ + table_attributes std_table_css; + table_size pkg_tbl_size; + table_size core_tbl_size; + table_size cpu_tbl_size; + + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Report add section */ + report.add_div(&div_attr); + report.add_title(&title_attr, __("Processor Idle State Report")); + + /* Set array of data in row Major order */ + int idx1, idx2, idx3; + string tmp_str; + + for (package = 0; package < system_level.children.size(); package++) { + bool first_core = true; + idx1=0; + idx2=0; + idx3=0; + + _package = system_level.children[package]; + if (!_package) + continue; + /* Tables for PKG, CORE, CPU */ + pkg_tbl_size.cols=2; + pkg_tbl_size.rows= ((cstates_num+1)-LEVEL_HEADER)+1; + string *pkg_data = new string[pkg_tbl_size.cols * pkg_tbl_size.rows]; + + core_tbl_size.cols=2; + core_tbl_size.rows=(cstates_num *_package->children.size()) + + _package->children.size(); + string *core_data = new string[core_tbl_size.cols * core_tbl_size.rows]; + int num_cpus=0, num_cores=0; + + for (core = 0; core < _package->children.size(); core++) { + _core = _package->children[core]; + if (!_core) + continue; + core_type = _core->get_type(); + if (core_type != NULL) + if (strcmp(core_type, "Core") == 0 ) + num_cores+=1; + + for (cpu = 0; cpu < _core->children.size(); cpu++) { + _cpu = _core->children[cpu]; + if (!_cpu) + continue; + num_cpus+=1; + } + } + cpu_tbl_size.cols=(2 * (num_cpus / num_cores)) + 1; + cpu_tbl_size.rows = ((cstates_num+1-LEVEL_HEADER) * _package->children.size()) + + _package->children.size(); + string *cpu_data = new string[cpu_tbl_size.cols * cpu_tbl_size.rows]; + + for (core = 0; core < _package->children.size(); core++) { + cpu_data[idx3]=" "; + idx3+=1; + _core = _package->children[core]; + + if (!_core) + continue; + + /* *** PKG STARTS *** */ + for (line = LEVEL_HEADER; line <= cstates_num; line++) { + bool first_cpu = true; + if (!_package->has_cstate_level(line)) + continue; + buffer[0] = 0; + buffer2[0] = 0; + if (line == LEVEL_HEADER) { + if (first_core) { + pkg_data[idx1]=__("Package"); + idx1+=1; + sprintf(tmp_num,"%d", _package->get_number()); + pkg_data[idx1]= string(tmp_num); + idx1+=1; + } + } else if (first_core) { + tmp_str=string(_package->fill_cstate_name(line, buffer)); + pkg_data[idx1]=(tmp_str=="" ? " " : tmp_str); + idx1+=1; + tmp_str=string(_package->fill_cstate_line(line, buffer2)); + pkg_data[idx1]=(tmp_str=="" ? " " : tmp_str); + idx1+=1; + } + + /* *** CORE STARTS *** */ + if (!_core->can_collapse()) { + buffer[0] = 0; + buffer2[0] = 0; + + /* + * Patch for compatibility with Ryzen processors + * See https://github.com/fenrus75/powertop/issues/64 + */ + if(idx2 >= core_tbl_size.cols * core_tbl_size.rows) break; + + if (line == LEVEL_HEADER) { + /* Here we need to check for which core type we + * are using. Do not use the core type for the + * report.addf as it breaks an important macro use + * for translation decision making for the reports. + * */ + core_type = _core->get_type(); + if (core_type != NULL) { + if (strcmp(core_type, "Core") == 0 ) { + core_data[idx2]=""; + idx2+=1; + snprintf(tmp_num, sizeof(tmp_num), __("Core %d"), _core->get_number()); + core_data[idx2]=string(tmp_num); + idx2+=1; + core_num+=1; + } else { + core_data[idx2]=""; + idx2+=1; + snprintf(tmp_num, sizeof(tmp_num), __("GPU %d"), _core->get_number()); + core_data[idx2]=string(tmp_num); + idx2+=1; + } + } + } else { + + + tmp_str=string(_core->fill_cstate_name(line, buffer)); + core_data[idx2]=(tmp_str=="" ? " " : tmp_str); + idx2+=1; + tmp_str=string(_core->fill_cstate_line(line, buffer2)); + core_data[idx2]=(tmp_str=="" ? " " : tmp_str); + idx2+=1; + } + } + // *** CPU STARTS *** + for (cpu = 0; cpu < _core->children.size(); cpu++) { + _cpu = _core->children[cpu]; + + if (!_cpu) + continue; + if (line == LEVEL_HEADER) { + cpu_data[idx3] = __("CPU"); + idx3+=1; + sprintf(tmp_num,"%d",_cpu->get_number()); + cpu_data[idx3]=string(tmp_num); + idx3+=1; + continue; + } + + if (first_cpu) { + title+=1; + cpu_data[idx3]=(string(_cpu->fill_cstate_name(line, buffer))); + idx3+=1; + first_cpu = false; + } + + buffer[0] = 0; + tmp_str=string(_cpu->fill_cstate_percentage(line, buffer)); + cpu_data[idx3]=(tmp_str=="" ? " " : tmp_str); + idx3+=1; + + if (line != LEVEL_C0){ + tmp_str=string(_cpu->fill_cstate_time(line, buffer)); + cpu_data[idx3]=(tmp_str=="" ? " " : tmp_str); + idx3+=1; + } else { + cpu_data[idx3]=" "; + idx3+=1; + } + } + } + first_core = false; + } + + /* Report Output */ + if(core_num > 0) + title=title/core_num; + else if(_core && _core->children.size() > 0) + title=title/_core->children.size(); + + init_pkg_table_attr(&std_table_css, pkg_tbl_size.rows, pkg_tbl_size.cols); + report.add_table(pkg_data, &std_table_css); + if (!_core->can_collapse()){ + init_core_table_attr(&std_table_css, title+1, core_tbl_size.rows, + core_tbl_size.cols); + report.add_table(core_data, &std_table_css); + } + init_cpu_table_attr(&std_table_css, title+1, cpu_tbl_size.rows, + cpu_tbl_size.cols); + report.add_table(cpu_data, &std_table_css); + delete [] pkg_data; + delete [] core_data; + delete [] cpu_data; + } + report.end_div(); +} + +void report_display_cpu_pstates(void) +{ + char buffer[512], buffer2[512], tmp_num[50]; + unsigned int package, core, cpu; + int line, title=0; + class abstract_cpu *_package, *_core = NULL, * _cpu; + unsigned int i, pstates_num; + const char* core_type = NULL; + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "cpufreq"); + + /* Set Table attributes, rows, and cols */ + table_attributes std_table_css; + table_size pkg_tbl_size; + table_size core_tbl_size; + table_size cpu_tbl_size; + + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Report add section */ + report.add_div(&div_attr); + report.add_title(&title_attr, __("Processor Frequency Report")); + + /* Set array of data in row Major order */ + int idx1, idx2, idx3, num_cpus=0, num_cores=0; + string tmp_str; + + for (i = 0, pstates_num = 0; i < all_cpus.size(); i++) { + if (all_cpus[i]) + pstates_num = std::max<unsigned int>(pstates_num, + all_cpus[i]->pstates.size()); + } + + for (package = 0; package < system_level.children.size(); package++) { + bool first_core = true; + idx1=0; + idx2=0; + idx3=0; + + _package = system_level.children[package]; + if (!_package) + continue; + + /* Tables for PKG, CORE, CPU */ + pkg_tbl_size.cols=2; + pkg_tbl_size.rows=((pstates_num+1)-LEVEL_HEADER)+2; + string *pkg_data = new string[pkg_tbl_size.cols * pkg_tbl_size.rows]; + + core_tbl_size.cols=2; + core_tbl_size.rows=((pstates_num+2) *_package->children.size()); + string *core_data = new string[core_tbl_size.cols * core_tbl_size.rows]; + + /* PKG */ + num_cpus=0; + num_cores=0; + for (core = 0; core < _package->children.size(); core++) { + _core = _package->children[core]; + if (!_core) + continue; + + core_type = _core->get_type(); + if (core_type != NULL) + if (strcmp(core_type, "Core") == 0 ) + num_cores+=1; + + for (cpu = 0; cpu < _core->children.size(); cpu++) { + _cpu = _core->children[cpu]; + if (!_cpu) + continue; + num_cpus+=1; + } + } + cpu_tbl_size.cols= (num_cpus/ num_cores) + 1; + cpu_tbl_size.rows= (pstates_num+2) * _package->children.size() + + _package->children.size(); + string *cpu_data = new string[cpu_tbl_size.cols * cpu_tbl_size.rows]; + + /* Core */ + for (core = 0; core < _package->children.size(); core++) { + cpu_data[idx3]=" "; + idx3+=1; + _core = _package->children[core]; + if (!_core) + continue; + + if (!_core->has_pstates()) + continue; + + for (line = LEVEL_HEADER; line < (int)pstates_num; line++) { + bool first_cpu = true; + + if (!_package->has_pstate_level(line)) + continue; + + buffer[0] = 0; + buffer2[0] = 0; + if (first_core) { + if (line == LEVEL_HEADER) { + pkg_data[idx1]=__("Package"); + idx1+=1; + sprintf(tmp_num,"%d", _package->get_number()); + pkg_data[idx1]= string(tmp_num); + idx1+=1; + } else { + tmp_str=string(_package->fill_pstate_name(line, buffer)); + pkg_data[idx1]=(tmp_str=="" ? " " : tmp_str); + idx1+=1; + tmp_str=string(_package->fill_pstate_line(line, buffer2)); + pkg_data[idx1]=(tmp_str=="" ? " " : tmp_str); + idx1+=1; + } + } + + + if (!_core->can_collapse()) { + buffer[0] = 0; + buffer2[0] = 0; + if (line == LEVEL_HEADER) { + core_data[idx2]=""; + idx2+=1; + snprintf(tmp_num, sizeof(tmp_num), __("Core %d"), _core->get_number()); + core_data[idx2]=string(tmp_num); + idx2+=1; + } else { + tmp_str=string(_core->fill_pstate_name(line, buffer)); + core_data[idx2]= (tmp_str=="" ? " " : tmp_str); + idx2+=1; + tmp_str=string(_core->fill_pstate_line(line, buffer2)); + core_data[idx2]= (tmp_str=="" ? " " : tmp_str); + idx2+=1; + } + } + + /* CPU */ + for (cpu = 0; cpu < _core->children.size(); cpu++) { + buffer[0] = 0; + _cpu = _core->children[cpu]; + if (!_cpu) + continue; + + if (line == LEVEL_HEADER) { + snprintf(tmp_num, sizeof(tmp_num), __("CPU %d"), _cpu->get_number()); + cpu_data[idx3] = string(tmp_num); + idx3+=1; + continue; + } + + if (first_cpu) { + tmp_str=string(_cpu->fill_pstate_name(line, buffer)); + cpu_data[idx3]=(tmp_str=="" ? " " : tmp_str); + idx3+=1; + first_cpu = false; + } + + buffer[0] = 0; + tmp_str=string(_cpu->fill_pstate_line(line, buffer)); + cpu_data[idx3]=(tmp_str=="" ? " " : tmp_str); + idx3+=1; + } + } + first_core = false; + } + init_pkg_table_attr(&std_table_css, pkg_tbl_size.rows, pkg_tbl_size.cols); + report.add_table(pkg_data, &std_table_css); + if(_core && !_core->can_collapse()){ + title=pstates_num+2; + init_core_table_attr(&std_table_css, title, + core_tbl_size.rows, core_tbl_size.cols); + report.add_table(core_data, &std_table_css); + } else { + title=pstates_num+1; + } + + init_cpu_table_attr(&std_table_css, title, + cpu_tbl_size.rows, cpu_tbl_size.cols); + report.add_table(cpu_data, &std_table_css); + delete [] pkg_data; + delete [] core_data; + delete [] cpu_data; + } + report.end_div(); +} + +void impl_w_display_cpu_states(int state) +{ + WINDOW *win; + char buffer[128]; + char linebuf[1024]; + unsigned int package, core, cpu; + int line, loop, cstates_num, pstates_num; + class abstract_cpu *_package, * _core, * _cpu; + int ctr = 0; + unsigned int i; + + cstates_num = get_cstates_num(); + + for (i = 0, pstates_num = 0; i < all_cpus.size(); i++) { + if (!all_cpus[i]) + continue; + + pstates_num = std::max<int>(pstates_num, all_cpus[i]->pstates.size()); + } + + if (state == PSTATE) { + win = get_ncurses_win("Frequency stats"); + loop = pstates_num; + } else { + win = get_ncurses_win("Idle stats"); + loop = cstates_num; + } + + if (!win) + return; + + wclear(win); + wmove(win, 2,0); + + for (package = 0; package < system_level.children.size(); package++) { + int first_pkg = 0; + _package = system_level.children[package]; + if (!_package) + continue; + + for (core = 0; core < _package->children.size(); core++) { + _core = _package->children[core]; + if (!_core) + continue; + if (!_core->has_pstates() && state == PSTATE) + continue; + + for (line = LEVEL_HEADER; line <= loop; line++) { + int first = 1; + ctr = 0; + linebuf[0] = 0; + + if (!has_state_level(_package, state, line)) + continue; + + buffer[0] = 0; + if (first_pkg == 0) { + strcat(linebuf, fill_state_name(_package, state, line, buffer)); + expand_string(linebuf, ctr + 10); + strcat(linebuf, fill_state_line(_package, state, line, buffer)); + } + ctr += 20; + expand_string(linebuf, ctr); + + strcat(linebuf, "| "); + ctr += strlen("| "); + + if (!_core->can_collapse()) { + buffer[0] = 0; + strcat(linebuf, fill_state_name(_core, state, line, buffer)); + expand_string(linebuf, ctr + 10); + strcat(linebuf, fill_state_line(_core, state, line, buffer)); + ctr += 20; + expand_string(linebuf, ctr); + + strcat(linebuf, "| "); + ctr += strlen("| "); + } + + for (cpu = 0; cpu < _core->children.size(); cpu++) { + _cpu = _core->children[cpu]; + if (!_cpu) + continue; + + if (first == 1) { + strcat(linebuf, fill_state_name(_cpu, state, line, buffer)); + expand_string(linebuf, ctr + 10); + first = 0; + ctr += 12; + } + buffer[0] = 0; + strcat(linebuf, fill_state_line(_cpu, state, line, buffer)); + ctr += 10; + expand_string(linebuf, ctr); + + } + strcat(linebuf, "\n"); + wprintw(win, "%s", linebuf); + } + wprintw(win, "\n"); + first_pkg++; + } + } +} + +void w_display_cpu_pstates(void) +{ + impl_w_display_cpu_states(PSTATE); +} + +void w_display_cpu_cstates(void) +{ + impl_w_display_cpu_states(CSTATE); +} + +struct power_entry { +#ifndef __i386__ + int dummy; +#endif + int64_t type; + int64_t value; +} __attribute__((packed)); + + +void perf_power_bundle::handle_trace_point(void *trace, int cpunr, uint64_t time) +{ + struct event_format *event; + struct pevent_record rec; /* holder */ + class abstract_cpu *cpu; + int type; + + rec.data = trace; + + type = pevent_data_type(perf_event::pevent, &rec); + event = pevent_find_event(perf_event::pevent, type); + + if (!event) + return; + + if (cpunr >= (int)all_cpus.size()) { + cout << "INVALID cpu nr in handle_trace_point\n"; + return; + } + + cpu = all_cpus[cpunr]; + +#if 0 + unsigned int i; + printf("Time is %llu \n", time); + for (i = 0; i < system_level.children.size(); i++) + if (system_level.children[i]) + system_level.children[i]->validate(); +#endif + unsigned long long val; + int ret; + if (strcmp(event->name, "cpu_idle")==0) { + + ret = pevent_get_field_val(NULL, event, "state", &rec, &val, 0); + if (ret < 0) { + fprintf(stderr, _("cpu_idle event returned no state?\n")); + exit(-1); + } + + if (val == (unsigned int)-1) + cpu->go_unidle(time); + else + cpu->go_idle(time); + } + + if (strcmp(event->name, "power_frequency") == 0 + || strcmp(event->name, "cpu_frequency") == 0){ + + ret = pevent_get_field_val(NULL, event, "state", &rec, &val, 0); + if (ret < 0) { + fprintf(stderr, _("power or cpu_frequency event returned no state?\n")); + exit(-1); + } + + cpu->change_freq(time, val); + } + + if (strcmp(event->name, "power_start")==0) + cpu->go_idle(time); + if (strcmp(event->name, "power_end")==0) + cpu->go_unidle(time); + +#if 0 + unsigned int i; + for (i = 0; i < system_level.children.size(); i++) + if (system_level.children[i]) + system_level.children[i]->validate(); +#endif +} + +void process_cpu_data(void) +{ + unsigned int i; + system_level.reset_pstate_data(); + + perf_events->process(); + + for (i = 0; i < system_level.children.size(); i++) + if (system_level.children[i]) + system_level.children[i]->validate(); + +} + +void end_cpu_data(void) +{ + system_level.reset_pstate_data(); + + perf_events->clear(); +} + +void clear_cpu_data(void) +{ + if (perf_events) + perf_events->release(); + delete perf_events; +} + + +void clear_all_cpus(void) +{ + unsigned int i; + for (i = 0; i < all_cpus.size(); i++) { + delete all_cpus[i]; + } + all_cpus.clear(); +} diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h new file mode 100644 index 0000000..9114764 --- /dev/null +++ b/src/cpu/cpu.h @@ -0,0 +1,234 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#ifndef __INCLUDE_GUARD_CPUDEV_H +#define __INCLUDE_GUARD_CPUDEV_H + +#include <iostream> +#include <vector> +#include <string> +#include <stdint.h> +#include <sys/time.h> + +using namespace std; + +class abstract_cpu; + +#define LEVEL_C0 -1 +#define LEVEL_HEADER -2 + +#define PSTATE 1 +#define CSTATE 2 + +struct idle_state { + char linux_name[16]; /* state0 etc.. cpuidle name */ + char human_name[32]; + + uint64_t usage_before; + uint64_t usage_after; + uint64_t usage_delta; + + uint64_t duration_before; + uint64_t duration_after; + uint64_t duration_delta; + + int before_count; + int after_count; + + int line_level; +}; + +struct frequency { + char human_name[32]; + int line_level; + + uint64_t freq; + + uint64_t time_after; + uint64_t time_before; + + int before_count; + int after_count; + + double display_value; +}; + +class abstract_cpu +{ +protected: + int first_cpu; + struct timeval stamp_before, stamp_after; + double time_factor; + uint64_t max_frequency = 0; + uint64_t max_minus_one_frequency = 0; + + virtual void account_freq(uint64_t frequency, uint64_t duration); + virtual void freq_updated(uint64_t time); + +public: + uint64_t last_stamp; + uint64_t total_stamp; + int number; + int childcount; + const char* name; + bool idle, old_idle, has_intel_MSR; + uint64_t current_frequency; + uint64_t effective_frequency; + + vector<class abstract_cpu *> children; + vector<struct idle_state *> cstates; + vector<struct frequency *> pstates; + + virtual ~abstract_cpu(); + + class abstract_cpu *parent; + + + int get_first_cpu() { return first_cpu; } + void set_number(int _number, int cpu) {this->number = _number; this->first_cpu = cpu;}; + void set_intel_MSR(bool _bool_value) {this->has_intel_MSR = _bool_value;}; + void set_type(const char* _name) {this->name = _name;}; + int get_number(void) { return number; }; + const char* get_type(void) { return name; }; + + virtual void measurement_start(void); + virtual void measurement_end(void); + + virtual int can_collapse(void) { return 0;}; + + + /* C state related methods */ + + void insert_cstate(const char *linux_name, const char *human_name, uint64_t usage, uint64_t duration, int count, int level = -1); + void update_cstate(const char *linux_name, const char *human_name, uint64_t usage, uint64_t duration, int count, int level = -1); + void finalize_cstate(const char *linux_name, uint64_t usage, uint64_t duration, int count); + + virtual int has_cstate_level(int level); + + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator="") { return buffer;}; + virtual char * fill_cstate_percentage(int line_nr, char *buffer) { return buffer; }; + virtual char * fill_cstate_time(int line_nr, char *buffer) { return buffer; }; + virtual char * fill_cstate_name(int line_nr, char *buffer) { return buffer;}; + + + /* P state related methods */ + void insert_pstate(uint64_t freq, const char *human_name, uint64_t duration, int count); + void update_pstate(uint64_t freq, const char *human_name, uint64_t duration, int count); + void finalize_pstate(uint64_t freq, uint64_t duration, int count); + + + virtual char * fill_pstate_line(int line_nr, char *buffer) { return buffer;}; + virtual char * fill_pstate_name(int line_nr, char *buffer) { return buffer;}; + virtual int has_pstate_level(int level); + virtual int has_pstates(void) { return 1; }; + + /* Frequency micro accounting methods */ + virtual void calculate_freq(uint64_t time); + virtual void go_idle(uint64_t time) { idle = true; freq_updated(time); } + virtual void go_unidle(uint64_t time) { idle = false; freq_updated(time); } + virtual void change_freq(uint64_t time, int freq) { current_frequency = freq; freq_updated(time); } + + virtual void change_effective_frequency(uint64_t time, uint64_t freq); + + virtual void wiggle(void); + + virtual uint64_t total_pstate_time(void); + + virtual void validate(void); + virtual void reset_pstate_data(void); +}; + +extern vector<class abstract_cpu *> all_cpus; + +class cpu_linux: public abstract_cpu +{ + void parse_pstates_start(void); + void parse_cstates_start(void); + void parse_pstates_end(void); + void parse_cstates_end(void); + +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator=""); + virtual char * fill_cstate_name(int line_nr, char *buffer); + virtual char * fill_cstate_percentage(int line_nr, char *buffer); + virtual char * fill_cstate_time(int line_nr, char *buffer); + + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual char * fill_pstate_name(int line_nr, char *buffer); +}; + +class cpu_core: public abstract_cpu +{ +public: + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator=""); + virtual char * fill_cstate_name(int line_nr, char *buffer); + + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual char * fill_pstate_name(int line_nr, char *buffer); + + virtual int can_collapse(void) { return childcount == 1;}; +}; + +class cpu_package: public abstract_cpu +{ +protected: + virtual void freq_updated(uint64_t time); +public: + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator=""); + virtual char * fill_cstate_name(int line_nr, char *buffer); + + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual char * fill_pstate_name(int line_nr, char *buffer); + virtual int can_collapse(void) { return childcount == 1;}; +}; + +extern void enumerate_cpus(void); + +extern void report_display_cpu_pstates(void); +extern void report_display_cpu_cstates(void); + + + +extern void display_cpu_cstates(const char *start= "", + const char *end = "", + const char *linestart = "", + const char *separator = "| ", + const char *lineend = "\n"); + +extern void w_display_cpu_cstates(void); +extern void w_display_cpu_pstates(void); + + +extern void start_cpu_measurement(void); +extern void end_cpu_measurement(void); +extern void process_cpu_data(void); +extern void end_cpu_data(void); +extern void clear_cpu_data(void); +extern void clear_all_cpus(void); + +#endif diff --git a/src/cpu/cpu_core.cpp b/src/cpu/cpu_core.cpp new file mode 100644 index 0000000..e1372f7 --- /dev/null +++ b/src/cpu/cpu_core.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <stdio.h> +#include "cpu.h" +#include "../lib.h" + +#include "../parameters/parameters.h" + +char * cpu_core::fill_cstate_line(int line_nr, char *buffer, const char *separator) +{ + unsigned int i; + buffer[0] = 0; + + if (line_nr == LEVEL_HEADER) + sprintf(buffer, this->has_intel_MSR ? _(" Core(HW)"): _(" Core(OS)")); + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + sprintf(buffer,"%5.1f%%", percentage(cstates[i]->duration_delta / time_factor)); + } + + return buffer; +} + + +char * cpu_core::fill_cstate_name(int line_nr, char *buffer) +{ + unsigned int i; + buffer[0] = 0; + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + sprintf(buffer,"%s", cstates[i]->human_name); + } + + return buffer; +} + + + +char * cpu_core::fill_pstate_name(int line_nr, char *buffer) +{ + buffer[0] = 0; + + if (line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer,"%s", pstates[line_nr]->human_name); + + return buffer; +} + +char * cpu_core::fill_pstate_line(int line_nr, char *buffer) +{ + buffer[0] = 0; + unsigned int i; + + if (total_stamp ==0) { + for (i = 0; i < pstates.size(); i++) + total_stamp += pstates[i]->time_after; + if (total_stamp == 0) + total_stamp = 1; + } + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" Core")); + return buffer; + } + + if (line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer," %5.1f%% ", percentage(1.0* (pstates[line_nr]->time_after) / total_stamp)); + return buffer; +} diff --git a/src/cpu/cpu_linux.cpp b/src/cpu/cpu_linux.cpp new file mode 100644 index 0000000..d7ce93d --- /dev/null +++ b/src/cpu/cpu_linux.cpp @@ -0,0 +1,350 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include "cpu.h" +#include "../lib.h" + +#include <stdlib.h> + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> + +void cpu_linux::parse_cstates_start(void) +{ + ifstream file; + DIR *dir; + struct dirent *entry; + char filename[256]; + int len; + + len = snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpuidle", number); + + dir = opendir(filename); + if (!dir) + return; + + /* For each C-state, there is a stateX directory which + * contains a 'usage' and a 'time' (duration) file */ + while ((entry = readdir(dir))) { + char linux_name[64]; + char human_name[64]; + uint64_t usage = 0; + uint64_t duration = 0; + + + if (strlen(entry->d_name) < 3) + continue; + + pt_strcpy(linux_name, entry->d_name); + pt_strcpy(human_name, linux_name); + + snprintf(filename + len, sizeof(filename) - len, "/%s/name", entry->d_name); + + file.open(filename, ios::in); + if (file) { + file.getline(human_name, sizeof(human_name)); + file.close(); + } + + if (strcmp(human_name, "C0")==0) + pt_strcpy(human_name, _("C0 polling")); + + snprintf(filename + len, sizeof(filename) - len, "/%s/usage", entry->d_name); + file.open(filename, ios::in); + if (file) { + file >> usage; + file.close(); + } else + continue; + + snprintf(filename + len, sizeof(filename) - len, "/%s/time", entry->d_name); + + file.open(filename, ios::in); + if (file) { + file >> duration; + file.close(); + } + + + update_cstate(linux_name, human_name, usage, duration, 1); + + } + closedir(dir); +} + + +void cpu_linux::parse_pstates_start(void) +{ + ifstream file; + char filename[256]; + unsigned int i; + + last_stamp = 0; + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->wiggle(); + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/stats/time_in_state", first_cpu); + + file.open(filename, ios::in); + + if (file) { + char line[1024]; + + while (file) { + uint64_t f; + file.getline(line, sizeof(line)); + f = strtoull(line, NULL, 10); + account_freq(f, 0); + } + file.close(); + } + account_freq(0, 0); +} + +void cpu_linux::measurement_start(void) +{ + abstract_cpu::measurement_start(); + parse_cstates_start(); + parse_pstates_start(); +} + +void cpu_linux::parse_cstates_end(void) +{ + DIR *dir; + struct dirent *entry; + char filename[256]; + ifstream file; + int len; + + len = snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpuidle", number); + + dir = opendir(filename); + if (!dir) + return; + + /* For each C-state, there is a stateX directory which + * contains a 'usage' and a 'time' (duration) file */ + while ((entry = readdir(dir))) { + char linux_name[64]; + char human_name[64]; + uint64_t usage = 0; + uint64_t duration = 0; + + + if (strlen(entry->d_name) < 3) + continue; + + pt_strcpy(linux_name, entry->d_name); + pt_strcpy(human_name, linux_name); + + + snprintf(filename + len, sizeof(filename) - len, "/%s/usage", entry->d_name); + file.open(filename, ios::in); + if (file) { + file >> usage; + file.close(); + } else + continue; + + snprintf(filename + len, sizeof(filename) - len, "/%s/time", entry->d_name); + + file.open(filename, ios::in); + if (file) { + file >> duration; + file.close(); + } + + + finalize_cstate(linux_name, usage, duration, 1); + + } + closedir(dir); +} + +void cpu_linux::parse_pstates_end(void) +{ + char filename[256]; + ifstream file; + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/stats/time_in_state", number); + + file.open(filename, ios::in); + + if (file) { + char line[1024]; + + while (file) { + uint64_t f,count; + char *c; + + memset(line, 0, sizeof(line)); + + file.getline(line, sizeof(line)); + + f = strtoull(line, &c, 10); + if (!c) + break; + + count = strtoull(c, NULL, 10); + + if (f > 0) + finalize_pstate(f, count, 1); + + + } + file.close(); + } +} + +void cpu_linux::measurement_end(void) +{ + parse_cstates_end(); + parse_pstates_end(); + abstract_cpu::measurement_end(); +} + +char * cpu_linux::fill_cstate_line(int line_nr, char *buffer, const char *separator) +{ + unsigned int i; + buffer[0] = 0; + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" CPU(OS) %i"), number); + return buffer; + } + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + if (line_nr == LEVEL_C0) + sprintf(buffer,"%5.1f%%", percentage(cstates[i]->duration_delta / time_factor)); + else + sprintf(buffer,"%5.1f%%%s %6.1f ms", + percentage(cstates[i]->duration_delta / time_factor), + separator, + 1.0 * cstates[i]->duration_delta / (1 + cstates[i]->usage_delta) / 1000); + } + + return buffer; +} + +char * cpu_linux::fill_cstate_percentage(int line_nr, char *buffer) +{ + unsigned int i; + buffer[0] = 0; + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + sprintf(buffer,"%5.1f%%", + percentage(cstates[i]->duration_delta / time_factor)); + break; + } + + return buffer; +} + +char * cpu_linux::fill_cstate_time(int line_nr, char *buffer) +{ + unsigned int i; + buffer[0] = 0; + + if (line_nr == LEVEL_C0) + return buffer; + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + sprintf(buffer,"%6.1f ms", + 1.0 * cstates[i]->duration_delta / + (1 + cstates[i]->usage_delta) / 1000); + break; + } + + return buffer; +} + +char * cpu_linux::fill_cstate_name(int line_nr, char *buffer) +{ + unsigned int i; + buffer[0] = 0; + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + sprintf(buffer,"%s", cstates[i]->human_name); + } + + return buffer; +} + + +char * cpu_linux::fill_pstate_name(int line_nr, char *buffer) +{ + buffer[0] = 0; + + if (line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer,"%s", pstates[line_nr]->human_name); + + return buffer; +} + +char * cpu_linux::fill_pstate_line(int line_nr, char *buffer) +{ + buffer[0] = 0; + + if (total_stamp ==0) { + unsigned int i; + for (i = 0; i < pstates.size(); i++) + total_stamp += pstates[i]->time_after; + if (total_stamp == 0) + total_stamp = 1; + } + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" CPU %i"), number); + return buffer; + } + + if (line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer," %5.1f%% ", percentage(1.0* (pstates[line_nr]->time_after) / total_stamp)); + return buffer; +} diff --git a/src/cpu/cpu_package.cpp b/src/cpu/cpu_package.cpp new file mode 100644 index 0000000..926a484 --- /dev/null +++ b/src/cpu/cpu_package.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <stdio.h> +#include "cpu.h" +#include "../lib.h" +#include "../parameters/parameters.h" + +void cpu_package::freq_updated(uint64_t time) +{ + if (parent) + parent->calculate_freq(time); + /* + * Make the frequency changes to propagate to all cores in a package. + */ + change_effective_frequency(time, current_frequency); + old_idle = idle; +} + +char * cpu_package::fill_cstate_line(int line_nr, char *buffer, const char *separator) +{ + unsigned int i; + buffer[0] = 0; + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer, this->has_intel_MSR ? _(" Pkg(HW)"): _(" Pkg(OS)")); + } + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + sprintf(buffer,"%5.1f%%", percentage(cstates[i]->duration_delta / time_factor)); + } + + return buffer; +} + + +char * cpu_package::fill_cstate_name(int line_nr, char *buffer) +{ + unsigned int i; + buffer[0] = 0; + + for (i = 0; i < cstates.size(); i++) { + if (cstates[i]->line_level != line_nr) + continue; + + sprintf(buffer,"%s", cstates[i]->human_name); + } + + return buffer; +} + + + +char * cpu_package::fill_pstate_name(int line_nr, char *buffer) +{ + buffer[0] = 0; + + if (line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer,"%s", pstates[line_nr]->human_name); + + return buffer; +} + +char * cpu_package::fill_pstate_line(int line_nr, char *buffer) +{ + buffer[0] = 0; + unsigned int i; + + if (total_stamp ==0) { + for (i = 0; i < pstates.size(); i++) + total_stamp += pstates[i]->time_after; + if (total_stamp == 0) + total_stamp = 1; + } + + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" Package")); + return buffer; + } + + if (line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer," %5.1f%% ", percentage(1.0* (pstates[line_nr]->time_after) / total_stamp)); + return buffer; +} diff --git a/src/cpu/cpu_rapl_device.cpp b/src/cpu/cpu_rapl_device.cpp new file mode 100644 index 0000000..357a1c8 --- /dev/null +++ b/src/cpu/cpu_rapl_device.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Srinivas Pandruvada<Srinivas.Pandruvada@linux.intel.com> + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "../parameters/parameters.h" +#include "cpu_rapl_device.h" + +cpu_rapl_device::cpu_rapl_device(cpudevice *parent, const char *classname, const char *dev_name, class abstract_cpu *_cpu) + : cpudevice(classname, dev_name, _cpu), + device_valid(false) +{ + if (_cpu) + rapl = new c_rapl_interface(dev_name, cpu->get_first_cpu()); + else + rapl = new c_rapl_interface(); + last_time = time(NULL); + if (rapl->pp0_domain_present()) { + device_valid = true; + parent->add_child(this); + rapl->get_pp0_energy_status(&last_energy); + } +} + +void cpu_rapl_device::start_measurement(void) +{ + last_time = time(NULL); + + rapl->get_pp0_energy_status(&last_energy); +} + +void cpu_rapl_device::end_measurement(void) +{ + time_t curr_time = time(NULL); + double energy; + + consumed_power = 0.0; + if ((curr_time - last_time) > 0) { + rapl->get_pp0_energy_status(&energy); + consumed_power = (energy-last_energy)/(curr_time-last_time); + last_energy = energy; + last_time = curr_time; + } +} + +double cpu_rapl_device::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + if (rapl->pp0_domain_present()) + return consumed_power; + else + return 0.0; +} diff --git a/src/cpu/cpu_rapl_device.h b/src/cpu/cpu_rapl_device.h new file mode 100644 index 0000000..407f2da --- /dev/null +++ b/src/cpu/cpu_rapl_device.h @@ -0,0 +1,57 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Srinivas Pandruvada <Srinivas.Pandruvada@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_CPU_RAPL_DEVICE_H +#define _INCLUDE_GUARD_CPU_RAPL_DEVICE_H + +#include <vector> +#include <string> + +using namespace std; + +#include <sys/time.h> +#include "cpudevice.h" +#include "rapl/rapl_interface.h" + +class cpu_rapl_device: public cpudevice { + + c_rapl_interface *rapl; + time_t last_time; + double last_energy; + double consumed_power; + bool device_valid; + +public: + cpu_rapl_device(cpudevice *parent, const char *classname = "cpu_core", const char *device_name = "cpu_core", class abstract_cpu *_cpu = NULL); + ~cpu_rapl_device() { delete rapl; } + virtual const char * device_name(void) {return "CPU core";}; + bool device_present() { return device_valid;} + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual void start_measurement(void); + virtual void end_measurement(void); + +}; + + +#endif diff --git a/src/cpu/cpudevice.cpp b/src/cpu/cpudevice.cpp new file mode 100644 index 0000000..4c7ca7b --- /dev/null +++ b/src/cpu/cpudevice.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "cpudevice.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "../lib.h" +#include "../parameters/parameters.h" + + +cpudevice::cpudevice(const char *classname, const char *dev_name, class abstract_cpu *_cpu) +{ + pt_strcpy(_class, classname); + pt_strcpy(_cpuname, dev_name); + cpu = _cpu; + wake_index = get_param_index("cpu-wakeups");; + consumption_index = get_param_index("cpu-consumption");; + r_wake_index = get_result_index("cpu-wakeups");; + r_consumption_index = get_result_index("cpu-consumption");; +} + +const char * cpudevice::device_name(void) +{ + if (child_devices.size()) + return "CPU misc"; + else + return "CPU use"; +} + +double cpudevice::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double _utilization; + double child_power; + + power = 0; + factor = get_parameter_value(wake_index, bundle); + _utilization = get_result_value(r_wake_index, result); + + power += _utilization * factor / 10000.0; + + factor = get_parameter_value(consumption_index, bundle); + _utilization = get_result_value(r_consumption_index, result); + + power += _utilization * factor; + + for (unsigned int i = 0; i < child_devices.size(); ++i) { + child_power = child_devices[i]->power_usage(result, bundle); + if ((power - child_power) > 0.0) + power -= child_power; + } + + return power; +} + +double cpudevice::utilization(void) +{ + double _utilization; + _utilization = get_result_value(r_consumption_index); + + return _utilization * 100; + +} diff --git a/src/cpu/cpudevice.h b/src/cpu/cpudevice.h new file mode 100644 index 0000000..841a101 --- /dev/null +++ b/src/cpu/cpudevice.h @@ -0,0 +1,63 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_CPUDEVICE_H +#define _INCLUDE_GUARD_CPUDEVICE_H + +#include <vector> +#include <string> + +using namespace std; + +#include "../devices/device.h" +#include "cpu.h" + +class cpudevice: public device { +protected: + char _class[128]; + char _cpuname[128]; + + vector<string> params; + class abstract_cpu *cpu; + int wake_index; + int consumption_index; + int r_wake_index; + int r_consumption_index; + + vector<device *>child_devices; + +public: + cpudevice(const char *classname = "cpu", const char *device_name = "cpu0", class abstract_cpu *_cpu = NULL); + virtual const char * class_name(void) { return _class;}; + + virtual const char * device_name(void); + + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual bool show_in_list(void) {return false;}; + virtual double utilization(void); /* percentage */ + void add_child(device *dev_ptr) { child_devices.push_back(dev_ptr);} +}; + + +#endif
\ No newline at end of file diff --git a/src/cpu/dram_rapl_device.cpp b/src/cpu/dram_rapl_device.cpp new file mode 100644 index 0000000..36d47a2 --- /dev/null +++ b/src/cpu/dram_rapl_device.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Srinivas Pandruvada <Srinivas.Pandruvada@linux.intel.com> + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "../parameters/parameters.h" +#include "dram_rapl_device.h" + + +dram_rapl_device::dram_rapl_device(cpudevice *parent, const char *classname, const char *dev_name, class abstract_cpu *_cpu) + : cpudevice(classname, dev_name, _cpu), + device_valid(false) +{ + if (_cpu) + rapl = new c_rapl_interface(dev_name, cpu->get_first_cpu()); + else + rapl = new c_rapl_interface(); + last_time = time(NULL); + if (rapl->dram_domain_present()) { + device_valid = true; + parent->add_child(this); + rapl->get_dram_energy_status(&last_energy); + } +} + +void dram_rapl_device::start_measurement(void) +{ + last_time = time(NULL); + + rapl->get_dram_energy_status(&last_energy); +} + +void dram_rapl_device::end_measurement(void) +{ + time_t curr_time = time(NULL); + double energy; + + consumed_power = 0.0; + if ((curr_time - last_time) > 0) { + rapl->get_dram_energy_status(&energy); + consumed_power = (energy-last_energy)/(curr_time-last_time); + last_energy = energy; + last_time = curr_time; + } +} + +double dram_rapl_device::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + if (rapl->dram_domain_present()) + return consumed_power; + else + return 0.0; +} diff --git a/src/cpu/dram_rapl_device.h b/src/cpu/dram_rapl_device.h new file mode 100644 index 0000000..dc53094 --- /dev/null +++ b/src/cpu/dram_rapl_device.h @@ -0,0 +1,57 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Srinivas Pandruvada <Srinivas.Pandruvada@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_DRAM_RAPL_DEVICE_H +#define _INCLUDE_GUARD_DRAM_RAPL_DEVICE_H + +#include <vector> +#include <string> + +using namespace std; + +#include <sys/time.h> +#include "cpudevice.h" +#include "rapl/rapl_interface.h" + +class dram_rapl_device: public cpudevice { + + c_rapl_interface *rapl; + time_t last_time; + double last_energy; + double consumed_power; + bool device_valid; + +public: + dram_rapl_device(cpudevice *parent, const char *classname = "dram_core", const char *device_name = "dram_core", class abstract_cpu *_cpu = NULL); + ~dram_rapl_device() { delete rapl; } + virtual const char * device_name(void) {return "DRAM";}; + bool device_present() { return device_valid;} + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + void start_measurement(void); + void end_measurement(void); + +}; + + +#endif diff --git a/src/cpu/intel_cpus.cpp b/src/cpu/intel_cpus.cpp new file mode 100644 index 0000000..a7145d7 --- /dev/null +++ b/src/cpu/intel_cpus.cpp @@ -0,0 +1,757 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "intel_cpus.h" +#include <iostream> +#include <fstream> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/time.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> + +#include "../lib.h" +#include "../parameters/parameters.h" +#include "../display.h" + +static int intel_cpu_models[] = { + 0x1A, /* Core i7, Xeon 5500 series */ + 0x1E, /* Core i7 and i5 Processor - Lynnfield Jasper Forest */ + 0x1F, /* Core i7 and i5 Processor - Nehalem */ + 0x25, /* Westmere */ + 0x27, /* Medfield Atom */ + 0x2A, /* SNB */ + 0x2C, /* Westmere */ + 0x2D, /* SNB Xeon */ + 0x2E, /* Nehalem-EX Xeon */ + 0x2F, /* Westmere-EX Xeon */ + 0x37, /* BYT-M */ + 0x3A, /* IVB */ + 0x3C, /* HSW */ + 0x3D, /* BDW */ + 0x3E, /* IVB Xeon */ + 0x3F, /* HSX */ + 0x45, /* HSW-ULT */ + 0x46, /* HSW-G */ + 0x47, /* BDW-H */ + 0x4C, /* BSW */ + 0x4D, /* AVN */ + 0x4F, /* BDX */ + 0x4E, /* SKY */ + 0x55, /* SKY-X */ + 0x56, /* BDX-DE */ + 0x5C, /* BXT-P */ + 0x5E, /* SKY */ + 0x5F, /* DNV */ + 0x66, /* CNL-U/Y */ + 0x6A, /* ICL_X*/ + 0x7A, /* GLK */ + 0x7D, /* ICL_DESKTOP */ + 0x7E, /* ICL_MOBILE */ + 0x8A, /* LKF */ + 0x8C, /* TGL_MOBILE */ + 0x8D, /* TGL_DESKTOP */ + 0x8E, /* KBL_MOBILE */ + 0X8F, /* SAPPHIRERAPIDS_X */ + 0x96, /* EHL */ + 0x97, /* ADL_DESKTOP */ + 0x9A, /* ADL_MOBILE */ + 0x9C, /* JSL */ + 0x9D, /* ICL_NNPI */ + 0x9E, /* KBL_DESKTOP */ + 0xA5, /* CML_DESKTOP */ + 0xA6, /* CML_MOBILE */ + 0xA7, /* RKL_DESKTOP */ + 0xAA, /* MTL_MOBILE */ + 0xAC, /* MTL_DESKTOP */ + 0xB7, /* RPL_DESKTOP */ + 0xBA, /* RPL_P */ + 0xBE, /* ADL_N */ + 0xBF, /* RPL_S */ + 0 /* last entry must be zero */ +}; + +static int intel_pstate_driver_loaded = -1; + +int is_supported_intel_cpu(int model, int cpu) +{ + int i; + uint64_t msr; + + for (i = 0; intel_cpu_models[i] != 0; i++) + if (model == intel_cpu_models[i]) + if (cpu < 0 || read_msr(cpu, MSR_APERF, &msr) >= 0) + return 1; + + return 0; +} + +int is_intel_pstate_driver_loaded() +{ + const char *filename = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver"; + const string intel_pstate("intel_pstate"); + char line[32] = { '\0' }; + ifstream file; + + if (intel_pstate_driver_loaded > -1) + return intel_pstate_driver_loaded; + + file.open(filename, ios::in); + + if (!file) + return -1; + + file.getline(line, sizeof(line)-1); + file.close(); + + const string scaling_driver(line); + if (scaling_driver == intel_pstate) { + intel_pstate_driver_loaded = 1; + } else { + intel_pstate_driver_loaded = 0; + } + + return intel_pstate_driver_loaded; +} + +static uint64_t get_msr(int cpu, uint64_t offset) +{ + ssize_t retval; + uint64_t msr; + + retval = read_msr(cpu, offset, &msr); + if (retval < 0) { + reset_display(); + fprintf(stderr, _("read_msr cpu%d 0x%llx : "), cpu, (unsigned long long)offset); + fprintf(stderr, "%s\n", strerror(errno)); + exit(-2); + } + + return msr; +} + +intel_util::intel_util() +{ + byt_ahci_support=0; +} + +void intel_util::byt_has_ahci() +{ + dir = opendir("/sys/bus/pci/devices/0000:00:13.0"); + if (!dir) + byt_ahci_support=0; + else { + byt_ahci_support=1; + closedir(dir); + } +} + +int intel_util::get_byt_ahci_support() +{ + return byt_ahci_support; +} + +nhm_core::nhm_core(int model) +{ + has_c7_res = 0; + + switch(model) { + case 0x2A: /* SNB */ + case 0x2D: /* SNB Xeon */ + case 0x3A: /* IVB */ + case 0x3C: /* HSW */ + case 0x3D: /* BDW */ + case 0x3E: /* IVB Xeon */ + case 0x45: /* HSW-ULT */ + case 0x4E: /* SKY */ + case 0x55: /* SKY-X */ + case 0x5E: /* SKY */ + case 0x5F: /* DNV */ + case 0x5C: /* BXT-P */ + case 0x66: /* CNL-U/Y */ + case 0x6A: /* ICL_X*/ + case 0x7A: /* GLK */ + case 0x7D: /* ICL_DESKTOP */ + case 0x7E: /* ICL_MOBILE */ + case 0x8A: /* LKF */ + case 0x8C: /* TGL_MOBILE */ + case 0x8D: /* TGL_DESKTOP */ + case 0x8E: /* KBL_MOBILE */ + case 0x8F: /* SAPPHIRERAPIDS_X */ + case 0x96: /* EHL */ + case 0x97: /* ADL_DESKTOP */ + case 0x9A: /* ADL_MOBILE */ + case 0x9C: /* JSL */ + case 0x9D: /* ICL_NNPI */ + case 0x9E: /* KBL_DESKTOP */ + case 0xA5: /* CML_DESKTOP */ + case 0xA6: /* CML_MOBILE */ + case 0xA7: /* RKL_DESKTOP */ + case 0xAA: /* MTL_MOBILE */ + case 0xAC: /* MTL_DESKTOP */ + case 0xB7: /* RPL_DESKTOP */ + case 0xBA: /* RPL_P */ + case 0xBE: /* ADL_N */ + case 0xBF: /* RPL_S */ + has_c7_res = 1; + } + + has_c3_res = 1; + has_c1_res = 0; + + switch (model) { + case 0x37: /* BYT-M does not support C3/C4 */ + case 0x4C: /* BSW does not support C3 */ + has_c3_res = 0; + has_c1_res = 1; + } + +} + +void nhm_core::measurement_start(void) +{ + ifstream file; + char filename[PATH_MAX]; + + /* the abstract function needs to be first since it clears all state */ + abstract_cpu::measurement_start(); + + last_stamp = 0; + + if (this->has_c1_res) + c1_before = get_msr(first_cpu, MSR_CORE_C1_RESIDENCY); + if (this->has_c3_res) + c3_before = get_msr(first_cpu, MSR_CORE_C3_RESIDENCY); + c6_before = get_msr(first_cpu, MSR_CORE_C6_RESIDENCY); + if (this->has_c7_res) + c7_before = get_msr(first_cpu, MSR_CORE_C7_RESIDENCY); + tsc_before = get_msr(first_cpu, MSR_TSC); + + if (this->has_c1_res) + insert_cstate("core c1", "C1 (cc1)", 0, c1_before, 1); + if (this->has_c3_res) + insert_cstate("core c3", "C3 (cc3)", 0, c3_before, 1); + insert_cstate("core c6", "C6 (cc6)", 0, c6_before, 1); + if (this->has_c7_res) { + insert_cstate("core c7", "C7 (cc7)", 0, c7_before, 1); + } + + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/stats/time_in_state", first_cpu); + + file.open(filename, ios::in); + + if (file) { + char line[1024]; + + while (file) { + uint64_t f; + file.getline(line, 1024); + f = strtoull(line, NULL, 10); + account_freq(f, 0); + } + file.close(); + } + account_freq(0, 0); + +} + +void nhm_core::measurement_end(void) +{ + unsigned int i; + uint64_t time_delta; + double ratio; + + if (this->has_c1_res) + c1_after = get_msr(first_cpu, MSR_CORE_C1_RESIDENCY); + if (this->has_c3_res) + c3_after = get_msr(first_cpu, MSR_CORE_C3_RESIDENCY); + c6_after = get_msr(first_cpu, MSR_CORE_C6_RESIDENCY); + if (this->has_c7_res) + c7_after = get_msr(first_cpu, MSR_CORE_C7_RESIDENCY); + tsc_after = get_msr(first_cpu, MSR_TSC); + + if (this->has_c1_res) + finalize_cstate("core c1", 0, c1_after, 1); + if (this->has_c3_res) + finalize_cstate("core c3", 0, c3_after, 1); + finalize_cstate("core c6", 0, c6_after, 1); + if (this->has_c7_res) + finalize_cstate("core c7", 0, c7_after, 1); + + gettimeofday(&stamp_after, NULL); + + time_factor = 1000000.0 * (stamp_after.tv_sec - stamp_before.tv_sec) + stamp_after.tv_usec - stamp_before.tv_usec; + + for (i = 0; i < children.size(); i++) + if (children[i]) { + children[i]->measurement_end(); + children[i]->wiggle(); + } + + time_delta = 1000000 * (stamp_after.tv_sec - stamp_before.tv_sec) + stamp_after.tv_usec - stamp_before.tv_usec; + + ratio = 1.0 * time_delta / (tsc_after - tsc_before); + + for (i = 0; i < cstates.size(); i++) { + struct idle_state *state = cstates[i]; + + if (state->after_count == 0) + continue; + + if (state->after_count != state->before_count) + continue; + + state->usage_delta = ratio * (state->usage_after - state->usage_before) / state->after_count; + state->duration_delta = ratio * (state->duration_after - state->duration_before) / state->after_count; + } + +#if 0 + for (i = 0; i < children.size(); i++) + if (children[i]) { + for (j = 0; j < children[i]->pstates.size(); j++) { + struct frequency *state; + state = children[i]->pstates[j]; + if (!state) + continue; + + update_pstate( state->freq, state->human_name, state->time_before, state->before_count); + finalize_pstate(state->freq, state->time_after, state->after_count); + } + } +#endif + total_stamp = 0; +} + +char * nhm_core::fill_pstate_line(int line_nr, char *buffer) +{ + const int intel_pstate = is_intel_pstate_driver_loaded(); + buffer[0] = 0; + unsigned int i; + + if (!intel_pstate && total_stamp ==0) { + for (i = 0; i < pstates.size(); i++) + total_stamp += pstates[i]->time_after; + if (total_stamp == 0) + total_stamp = 1; + } + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" Core")); + return buffer; + } + + if (intel_pstate > 0 || line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer," %5.1f%% ", percentage(1.0* (pstates[line_nr]->time_after) / total_stamp)); + + return buffer; +} + +nhm_package::nhm_package(int model) +{ + has_c8c9c10_res = 0; + has_c2c6_res = 0; + has_c7_res = 0; + has_c6c_res = 0; + + switch(model) { + case 0x2A: /* SNB */ + case 0x2D: /* SNB Xeon */ + case 0x3A: /* IVB */ + case 0x3C: /* HSW */ + case 0x3D: /* BDW */ + case 0x3E: /* IVB Xeon */ + case 0x45: /* HSW-ULT */ + case 0x4E: /* SKY */ + case 0x55: /* SKY-X */ + case 0x5C: /* BXT-P */ + case 0x5E: /* SKY */ + case 0x5F: /* DNV */ + case 0x66: /* CNL-U/Y */ + case 0x6A: /* ICL_X*/ + case 0x7A: /* GLK */ + case 0x7D: /* ICL_DESKTOP */ + case 0x7E: /* ICL_MOBILE */ + case 0x8A: /* LKF */ + case 0x8C: /* TGL_MOBILE */ + case 0x8D: /* TGL_DESKTOP */ + case 0x8E: /* KBL_MOBILE */ + case 0x8F: /* SAPPHIRERAPIDS_X */ + case 0x96: /* EHL */ + case 0x97: /* ADL_DESKTOP */ + case 0X9A: /* ADL_MOBILE */ + case 0x9C: /* JSL */ + case 0x9D: /* ICL_NNPI */ + case 0x9E: /* KBL_DESKTOP */ + case 0xA5: /* CML_DESKTOP */ + case 0xA6: /* CML_MOBILE */ + case 0xA7: /* RKL_DESKTOP */ + case 0xAA: /* MTL_MOBILE */ + case 0xAC: /* MTL_DESKTOP */ + case 0xB7: /* RPL_DESKTOP */ + case 0xBA: /* RPL_P */ + case 0xBE: /* ADL_N */ + case 0xBF: /* RPL_S */ + has_c2c6_res=1; + has_c7_res = 1; + } + + has_c3_res = 1; + + switch(model) { + /* BYT-M doesn't have C3 or C7 */ + /* BYT-T doesn't have C3 but it has C7 */ + case 0x37: + has_c2c6_res=1; + this->byt_has_ahci(); + if ((this->get_byt_ahci_support()) == 0) + has_c7_res = 1;/*BYT-T PC7 <- S0iX*/ + else + has_c7_res = 0; + break; + case 0x4C: /* BSW doesn't have C3 */ + has_c3_res = 0; + has_c6c_res = 1; /* BSW only exposes package C6 */ + break; + } + + /*Has C8/9/10*/ + switch(model) { + case 0x3D: /* BDW */ + case 0x45: /* HSW */ + case 0x4E: /* SKY */ + case 0x5C: /* BXT-P */ + case 0x5E: /* SKY */ + case 0x5F: /* DNV */ + case 0x66: /* CNL-U/Y */ + case 0x7A: /* GLK */ + case 0x7D: /* ICL_DESKTOP */ + case 0x7E: /* ICL_MOBILE */ + case 0x8A: /* LKF */ + case 0x8C: /* TGL_MOBILE */ + case 0x8D: /* TGL_DESKTOP */ + case 0x8E: /* KBL_MOBILE */ + case 0x96: /* EHL */ + case 0x97: /* ADL_DESKTOP */ + case 0x9A: /* ADL_MOBILE */ + case 0x9C: /* JSL */ + case 0x9D: /* ICL_NNPI */ + case 0x9E: /* KBL_DESKTOP */ + case 0xA5: /* CML_DESKTOP */ + case 0xA6: /* CML_MOBILE */ + case 0xA7: /* RKL_DESKTOP */ + case 0xAA: /* MTL_MOBILE */ + case 0xAC: /* MTL_DESKTOP */ + case 0xB7: /* RPL_DESKTOP */ + case 0xBA: /* RPL_P */ + case 0xBE: /* ADL_N */ + case 0xBF: /* RPL_S */ + has_c8c9c10_res = 1; + break; + } +} + +char * nhm_package::fill_pstate_line(int line_nr, char *buffer) +{ + const int intel_pstate = is_intel_pstate_driver_loaded(); + buffer[0] = 0; + unsigned int i; + + if (!intel_pstate && total_stamp ==0) { + for (i = 0; i < pstates.size(); i++) + total_stamp += pstates[i]->time_after; + if (total_stamp == 0) + total_stamp = 1; + } + + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" Package")); + return buffer; + } + + if (intel_pstate > 0 || line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer," %5.1f%% ", percentage(1.0* (pstates[line_nr]->time_after) / total_stamp)); + + return buffer; +} + + + +void nhm_package::measurement_start(void) +{ + abstract_cpu::measurement_start(); + + last_stamp = 0; + + if (this->has_c2c6_res) + c2_before = get_msr(number, MSR_PKG_C2_RESIDENCY); + + if (this->has_c3_res) + c3_before = get_msr(number, MSR_PKG_C3_RESIDENCY); + + /* + * Hack for Braswell where C7 MSR is actually BSW C6 + */ + if (this->has_c6c_res) + c6_before = get_msr(number, MSR_PKG_C7_RESIDENCY); + else + c6_before = get_msr(number, MSR_PKG_C6_RESIDENCY); + + if (this->has_c7_res) + c7_before = get_msr(number, MSR_PKG_C7_RESIDENCY); + if (this->has_c8c9c10_res) { + c8_before = get_msr(number, MSR_PKG_C8_RESIDENCY); + c9_before = get_msr(number, MSR_PKG_C9_RESIDENCY); + c10_before = get_msr(number, MSR_PKG_C10_RESIDENCY); + } + tsc_before = get_msr(first_cpu, MSR_TSC); + + if (this->has_c2c6_res) + insert_cstate("pkg c2", "C2 (pc2)", 0, c2_before, 1); + + if (this->has_c3_res) + insert_cstate("pkg c3", "C3 (pc3)", 0, c3_before, 1); + insert_cstate("pkg c6", "C6 (pc6)", 0, c6_before, 1); + if (this->has_c7_res) + insert_cstate("pkg c7", "C7 (pc7)", 0, c7_before, 1); + if (this->has_c8c9c10_res) { + insert_cstate("pkg c8", "C8 (pc8)", 0, c8_before, 1); + insert_cstate("pkg c9", "C9 (pc9)", 0, c9_before, 1); + insert_cstate("pkg c10", "C10 (pc10)", 0, c10_before, 1); + } +} + +void nhm_package::measurement_end(void) +{ + uint64_t time_delta; + double ratio; + unsigned int i, j; + + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->wiggle(); + + + if (this->has_c2c6_res) + c2_after = get_msr(number, MSR_PKG_C2_RESIDENCY); + + if (this->has_c3_res) + c3_after = get_msr(number, MSR_PKG_C3_RESIDENCY); + + if (this->has_c6c_res) + c6_after = get_msr(number, MSR_PKG_C7_RESIDENCY); + else + c6_after = get_msr(number, MSR_PKG_C6_RESIDENCY); + + if (this->has_c7_res) + c7_after = get_msr(number, MSR_PKG_C7_RESIDENCY); + if (has_c8c9c10_res) { + c8_after = get_msr(number, MSR_PKG_C8_RESIDENCY); + c9_after = get_msr(number, MSR_PKG_C9_RESIDENCY); + c10_after = get_msr(number, MSR_PKG_C10_RESIDENCY); + } + tsc_after = get_msr(first_cpu, MSR_TSC); + + gettimeofday(&stamp_after, NULL); + + time_factor = 1000000.0 * (stamp_after.tv_sec - stamp_before.tv_sec) + stamp_after.tv_usec - stamp_before.tv_usec; + + + if (this->has_c2c6_res) + finalize_cstate("pkg c2", 0, c2_after, 1); + + if (this->has_c3_res) + finalize_cstate("pkg c3", 0, c3_after, 1); + finalize_cstate("pkg c6", 0, c6_after, 1); + if (this->has_c7_res) + finalize_cstate("pkg c7", 0, c7_after, 1); + if (has_c8c9c10_res) { + finalize_cstate("pkg c8", 0, c8_after, 1); + finalize_cstate("pkg c9", 0, c9_after, 1); + finalize_cstate("pkg c10", 0, c10_after, 1); + } + + for (i = 0; i < children.size(); i++) + if (children[i]) + children[i]->measurement_end(); + + time_delta = 1000000 * (stamp_after.tv_sec - stamp_before.tv_sec) + stamp_after.tv_usec - stamp_before.tv_usec; + + ratio = 1.0 * time_delta / (tsc_after - tsc_before); + + + for (i = 0; i < cstates.size(); i++) { + struct idle_state *state = cstates[i]; + + if (state->after_count == 0) + continue; + + if (state->after_count != state->before_count) + continue; + + state->usage_delta = ratio * (state->usage_after - state->usage_before) / state->after_count; + state->duration_delta = ratio * (state->duration_after - state->duration_before) / state->after_count; + } + for (i = 0; i < children.size(); i++) + if (children[i]) { + for (j = 0; j < children[i]->pstates.size(); j++) { + struct frequency *state; + state = children[i]->pstates[j]; + if (!state) + continue; + + update_pstate( state->freq, state->human_name, state->time_before, state->before_count); + finalize_pstate(state->freq, state->time_after, state->after_count); + } + } + total_stamp = 0; + +} + +void nhm_cpu::measurement_start(void) +{ + ifstream file; + char filename[PATH_MAX]; + + cpu_linux::measurement_start(); + + last_stamp = 0; + + aperf_before = get_msr(number, MSR_APERF); + mperf_before = get_msr(number, MSR_MPERF); + tsc_before = get_msr(number, MSR_TSC); + + insert_cstate("active", _("C0 active"), 0, aperf_before, 1); + + snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/cpufreq/stats/time_in_state", first_cpu); + + file.open(filename, ios::in); + + if (file) { + char line[1024]; + + while (file) { + uint64_t f; + file.getline(line, sizeof(line)); + f = strtoull(line, NULL, 10); + account_freq(f, 0); + } + file.close(); + } + account_freq(0, 0); +} + +void nhm_cpu::measurement_end(void) +{ + uint64_t time_delta; + double ratio; + unsigned int i; + + aperf_after = get_msr(number, MSR_APERF); + mperf_after = get_msr(number, MSR_MPERF); + tsc_after = get_msr(number, MSR_TSC); + + + + finalize_cstate("active", 0, aperf_after, 1); + + + cpu_linux::measurement_end(); + + time_delta = 1000000 * (stamp_after.tv_sec - stamp_before.tv_sec) + stamp_after.tv_usec - stamp_before.tv_usec; + + ratio = 1.0 * time_delta / (tsc_after - tsc_before); + + + for (i = 0; i < cstates.size(); i++) { + struct idle_state *state = cstates[i]; + if (state->line_level != LEVEL_C0) + continue; + + state->usage_delta = ratio * (state->usage_after - state->usage_before) / state->after_count; + state->duration_delta = ratio * (state->duration_after - state->duration_before) / state->after_count; + } + + total_stamp = 0; + +} + +char * nhm_cpu::fill_pstate_name(int line_nr, char *buffer) +{ + if (line_nr == LEVEL_C0) { + sprintf(buffer, _("Average")); + return buffer; + } + return cpu_linux::fill_pstate_name(line_nr, buffer); +} + +char * nhm_cpu::fill_pstate_line(int line_nr, char *buffer) +{ + const int intel_pstate = is_intel_pstate_driver_loaded(); + + if (!intel_pstate && total_stamp ==0) { + unsigned int i; + for (i = 0; i < pstates.size(); i++) + total_stamp += pstates[i]->time_after; + if (total_stamp == 0) + total_stamp = 1; + } + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" CPU %i"), number); + return buffer; + } + + if (line_nr == LEVEL_C0) { + double F; + F = 1.0 * (tsc_after - tsc_before) * (aperf_after - aperf_before) / (mperf_after - mperf_before) / time_factor * 1000; + hz_to_human(F, buffer, 1); + return buffer; + } + if (intel_pstate > 0 || line_nr >= (int)pstates.size() || line_nr < 0) + return buffer; + + sprintf(buffer," %5.1f%% ", percentage(1.0* (pstates[line_nr]->time_after) / total_stamp)); + + return buffer; +} + + +int nhm_cpu::has_pstate_level(int level) +{ + if (level == LEVEL_C0) + return 1; + return cpu_linux::has_pstate_level(level); +} diff --git a/src/cpu/intel_cpus.h b/src/cpu/intel_cpus.h new file mode 100644 index 0000000..79afb98 --- /dev/null +++ b/src/cpu/intel_cpus.h @@ -0,0 +1,180 @@ +#ifndef PowerTop_INTEL_CPUS_H_84F09FB4F519470FA914AA9B02453221 +#define PowerTop_INTEL_CPUS_H_84F09FB4F519470FA914AA9B02453221 +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <stdint.h> +#include <sys/time.h> +#include <dirent.h> + +#include "cpu.h" + + +#define MSR_TSC 0x10 +#define MSR_NEHALEM_PLATFORM_INFO 0xCE +#define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1AD +#define MSR_APERF 0xE8 +#define MSR_MPERF 0xE7 +#define MSR_PKG_C2_RESIDENCY 0x60D +#define MSR_PKG_C3_RESIDENCY 0x3F8 +#define MSR_PKG_C6_RESIDENCY 0x3F9 +#define MSR_PKG_C7_RESIDENCY 0x3FA +#define MSR_PKG_C8_RESIDENCY 0x630 +#define MSR_PKG_C9_RESIDENCY 0x631 +#define MSR_PKG_C10_RESIDENCY 0x632 +#define MSR_CORE_C1_RESIDENCY 0x660 +#define MSR_CORE_C3_RESIDENCY 0x3FC +#define MSR_CORE_C6_RESIDENCY 0x3FD +#define MSR_CORE_C7_RESIDENCY 0x3FE + +class intel_util +{ +protected: + int byt_ahci_support; + DIR *dir; +public: + intel_util(); + virtual void byt_has_ahci(); + virtual int get_byt_ahci_support(); +}; + +class nhm_package: public cpu_package, public intel_util +{ +private: + uint64_t c2_before, c2_after; + uint64_t c3_before, c3_after; + uint64_t c6_before, c6_after; + uint64_t c7_before, c7_after; + uint64_t c8_before, c8_after; + uint64_t c9_before, c9_after; + uint64_t c10_before, c10_after; + uint64_t tsc_before, tsc_after; + + uint64_t last_stamp; + uint64_t total_stamp; +public: + int has_c7_res; + int has_c2c6_res; + int has_c3_res; + int has_c6c_res; /* BSW */ + int has_c8c9c10_res; + nhm_package(int model); + virtual void measurement_start(void); + virtual void measurement_end(void); + virtual int can_collapse(void) { return 0;}; + + virtual char * fill_pstate_line(int line_nr, char *buffer); +}; + +class nhm_core: public cpu_core, public intel_util +{ +private: + uint64_t c1_before, c1_after; + uint64_t c3_before, c3_after; + uint64_t c6_before, c6_after; + uint64_t c7_before, c7_after; + uint64_t tsc_before, tsc_after; + + uint64_t last_stamp; + uint64_t total_stamp; +public: + int has_c1_res; + int has_c7_res; + int has_c3_res; + nhm_core(int model); + virtual void measurement_start(void); + virtual void measurement_end(void); + virtual int can_collapse(void) { return 0;}; + + virtual char * fill_pstate_line(int line_nr, char *buffer); +}; + +class nhm_cpu: public cpu_linux, public intel_util +{ +private: + uint64_t aperf_before; + uint64_t aperf_after; + uint64_t mperf_before; + uint64_t mperf_after; + uint64_t tsc_before, tsc_after; + + uint64_t last_stamp; + uint64_t total_stamp; +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + virtual int can_collapse(void) { return 0;}; + + virtual char * fill_pstate_name(int line_nr, char *buffer); + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual int has_pstate_level(int level); +}; + +class atom_package: public cpu_package +{ +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + +}; + +class atom_core: public cpu_core +{ +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + +}; + + +class i965_core: public cpu_core +{ +private: + uint64_t rc6_before, rc6_after; + uint64_t rc6p_before, rc6p_after; + uint64_t rc6pp_before, rc6pp_after; + + struct timeval before; + struct timeval after; + +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + virtual int can_collapse(void) { return 0;}; + + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual char * fill_pstate_name(int line_nr, char *buffer); + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator); + virtual int has_pstate_level(int level) { return 0; }; + virtual int has_pstates(void) { return 0; }; + virtual void wiggle(void) { }; + +}; + +int is_supported_intel_cpu(int model, int cpu); +int byt_has_ahci(); + +int is_intel_pstate_driver_loaded(); + +#endif diff --git a/src/cpu/intel_gpu.cpp b/src/cpu/intel_gpu.cpp new file mode 100644 index 0000000..e0f4ac2 --- /dev/null +++ b/src/cpu/intel_gpu.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2012, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "intel_cpus.h" +#include <iostream> +#include <fstream> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/time.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "../lib.h" +#include "../parameters/parameters.h" +#include "../display.h" + +void i965_core::measurement_start(void) +{ + ifstream file; + + gettimeofday(&before, NULL); + rc6_before = read_sysfs("/sys/class/drm/card0/power/rc6_residency_ms", NULL); + rc6p_before = read_sysfs("/sys/class/drm/card0/power/rc6p_residency_ms", NULL); + rc6pp_before = read_sysfs("/sys/class/drm/card0/power/rc6pp_residency_ms", NULL); + + update_cstate("gpu c0", "Powered On", 0, 0, 1, 0); + update_cstate("gpu rc6", "RC6", 0, rc6_before, 1, 1); + update_cstate("gpu rc6p", "RC6p", 0, rc6p_before, 1, 2); + update_cstate("gpu rc6pp", "RC6pp", 0, rc6pp_before, 1, 3); +} + +char * i965_core::fill_cstate_line(int line_nr, char *buffer, const char *separator) +{ + buffer[0] = 0; + double ratio, d = -1.0, time_delta; + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" GPU ")); + return buffer; + } + + buffer[0] = 0; + + time_delta = 1000000 * (after.tv_sec - before.tv_sec) + after.tv_usec - before.tv_usec; + ratio = 100000.0/time_delta; + + switch (line_nr) { + case 0: + d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); + break; + case 1: + d = ratio * (rc6_after - rc6_before); + break; + case 2: + d = ratio * (rc6p_after - rc6p_before); + break; + case 3: + d = ratio * (rc6pp_after - rc6pp_before); + break; + default: + return buffer; + } + + /* cope with rounding errors due to the measurement interval */ + if (d < 0.0) + d = 0.0; + if (d > 100.0) + d = 100.0; + + sprintf(buffer,"%5.1f%%", d); + + return buffer; +} + + +void i965_core::measurement_end(void) +{ + gettimeofday(&after, NULL); + + rc6_after = read_sysfs("/sys/class/drm/card0/power/rc6_residency_ms", NULL); + rc6p_after = read_sysfs("/sys/class/drm/card0/power/rc6p_residency_ms", NULL); + rc6pp_after = read_sysfs("/sys/class/drm/card0/power/rc6pp_residency_ms", NULL); +} + +char * i965_core::fill_pstate_line(int line_nr, char *buffer) +{ + buffer[0] = 0; + return buffer; +} + +char * i965_core::fill_pstate_name(int line_nr, char *buffer) +{ + buffer[0] = 0; + return buffer; +} + diff --git a/src/cpu/rapl/rapl_interface.cpp b/src/cpu/rapl/rapl_interface.cpp new file mode 100644 index 0000000..d6bd4c8 --- /dev/null +++ b/src/cpu/rapl/rapl_interface.cpp @@ -0,0 +1,699 @@ +/* rapl_interface.cpp: rapl interface for power top implementation + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name <Srinivas.Pandruvada@linux.intel.com> + * + */ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <math.h> +#include <stdlib.h> +#include <dirent.h> +#include "lib.h" +#include "rapl_interface.h" + +#ifdef DEBUG +#define RAPL_DBG_PRINT printf +#define RAPL_ERROR_PRINT printf +#else +#define RAPL_DBG_PRINT(...) ((void) 0) +#define RAPL_ERROR_PRINT(...) ((void) 0) +#endif +#define RAPL_INFO_PRINT(format, m) fprintf(stderr, format, m) + +#define MAX_TEMP_STR_SIZE 20 + +// RAPL interface +#define MSR_RAPL_POWER_UNIT 0x606 +#define MSR_PKG_POWER_LIMIT 0x610 + +#define MSR_PKG_ENERY_STATUS 0x611 +#define MSR_PKG_POWER_INFO 0x614 +#define MSR_PKG_PERF_STATUS 0x613 + +#define MSR_DRAM_POWER_LIMIT 0x618 +#define MSR_DRAM_ENERY_STATUS 0x619 +#define MSR_DRAM_PERF_STATUS 0x61B +#define MSR_DRAM_POWER_INFO 0x61c + +#define MSR_PP0_POWER_LIMIT 0x638 +#define MSR_PP0_ENERY_STATUS 0x639 +#define MSR_PP0_POLICY 0x63A +#define MSR_PP0_PERF_STATUS 0x63B + +#define MSR_PP1_POWER_LIMIT 0x640 +#define MSR_PP1_ENERY_STATUS 0x641 +#define MSR_PP1_POLICY 0x642 + +#define PKG_DOMAIN_PRESENT 0x01 +#define DRAM_DOMAIN_PRESENT 0x02 +#define PP0_DOMAIN_PRESENT 0x04 +#define PP1_DOMAIN_PRESENT 0x08 + +c_rapl_interface::c_rapl_interface(const char *dev_name, int cpu) : + powercap_sysfs_present(false), + powercap_core_path(), + powercap_uncore_path(), + powercap_dram_path(), + first_cpu(cpu), + measurment_interval(def_sampling_interval), + last_pkg_energy_status(0.0), + last_dram_energy_status(0.0), + last_pp0_energy_status(0.0), + last_pp1_energy_status(0.0) +{ + uint64_t value; + int ret; + string package_path; + DIR *dir; + struct dirent *entry; + + RAPL_INFO_PRINT("RAPL device for cpu %d\n", cpu); + + rapl_domains = 0; + + if (dev_name) { + string base_path = "/sys/class/powercap/intel-rapl/"; + if ((dir = opendir(base_path.c_str())) != NULL) { + while ((entry = readdir(dir)) != NULL) { + string path = base_path + entry->d_name + "/name"; + string str = read_sysfs_string(path); + if (str.length() > 0) { + if (str == dev_name) { + package_path = base_path + entry->d_name + "/"; + powercap_sysfs_present = true; + rapl_domains |= PKG_DOMAIN_PRESENT; + break; + } + } + } + closedir(dir); + } + } + + if (powercap_sysfs_present) { + if ((dir = opendir(package_path.c_str())) != NULL) { + while ((entry = readdir(dir)) != NULL) { + string path = package_path + entry->d_name; + string str = read_sysfs_string(path + "/name"); + if (str.length() > 0) { + if (str == "core") { + rapl_domains |= PP0_DOMAIN_PRESENT; + powercap_core_path = path + "/"; + } + else if (str == "dram") { + rapl_domains |= DRAM_DOMAIN_PRESENT; + powercap_dram_path = path + "/"; + } + else if (str == "uncore") { + rapl_domains |= PP1_DOMAIN_PRESENT; + powercap_uncore_path = path + "/"; + } + } + } + closedir(dir); + } + + RAPL_INFO_PRINT("RAPL Using PowerCap Sysfs : Domain Mask %x\n", rapl_domains); + return; + } + + // Fallback to using MSRs + + // presence of each domain + // Check presence of PKG domain + ret = read_msr(first_cpu, MSR_PKG_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= PKG_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : PKG present\n"); + } else { + RAPL_DBG_PRINT("Domain : PKG Not present\n"); + } + + // Check presence of DRAM domain + ret = read_msr(first_cpu, MSR_DRAM_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= DRAM_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : DRAM present\n"); + } else { + RAPL_DBG_PRINT("Domain : DRAM Not present\n"); + } + + // Check presence of PP0 domain + ret = read_msr(first_cpu, MSR_PP0_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= PP0_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : PP0 present\n"); + } else { + RAPL_DBG_PRINT("Domain : PP0 Not present\n"); + } + + // Check presence of PP1 domain + ret = read_msr(first_cpu, MSR_PP1_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= PP1_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : PP1 present\n"); + } else { + RAPL_DBG_PRINT("Domain : PP1 Not present\n"); + } + + power_units = get_power_unit(); + energy_status_units = get_energy_status_unit(); + time_units = get_time_unit(); + + RAPL_DBG_PRINT("RAPL Domain mask: %x\n", rapl_domains); +} + +bool c_rapl_interface::pkg_domain_present() +{ + if ((rapl_domains & PKG_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +bool c_rapl_interface::dram_domain_present() +{ + if ((rapl_domains & DRAM_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +bool c_rapl_interface::pp0_domain_present() +{ + if ((rapl_domains & PP0_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +bool c_rapl_interface::pp1_domain_present() +{ + if ((rapl_domains & PP1_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +int c_rapl_interface::read_msr(int cpu, unsigned int idx, uint64_t *val) +{ + return ::read_msr(cpu, idx, val); +} + +int c_rapl_interface::write_msr(int cpu, unsigned int idx, uint64_t val) +{ + return ::write_msr(cpu, idx, val); +} + +int c_rapl_interface::get_rapl_power_unit(uint64_t *value) +{ + int ret; + + ret = read_msr(first_cpu, MSR_RAPL_POWER_UNIT, value); + + return ret; +} + +double c_rapl_interface::get_power_unit() +{ + int ret; + uint64_t value; + + ret = get_rapl_power_unit(&value); + if(ret < 0) + { + return ret; + } + + return (double) 1/pow((double)2, (double)(value & 0xf)); +} + +double c_rapl_interface::get_energy_status_unit() +{ + int ret; + uint64_t value; + + ret = get_rapl_power_unit(&value); + if(ret < 0) + { + return ret; + } + + return (double)1/ pow((double)2, (double)((value & 0x1f00) >> 8)); +} + +double c_rapl_interface::get_time_unit() +{ + int ret; + uint64_t value; + + ret = get_rapl_power_unit(&value); + if(ret < 0) + { + return ret; + } + + return (double)1 / pow((double)2, (double)((value & 0xf0000) >> 16)); +} + +int c_rapl_interface::get_pkg_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!pkg_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PKG_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pkg_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_pkg_power_info(double *thermal_spec_power, + double *max_power, double *min_power, double *max_time_window) +{ + int ret; + uint64_t value; + + if (!pkg_domain_present()) { + return -1; + } + ret = read_msr(first_cpu, MSR_PKG_POWER_INFO, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pkg_power_info failed\n"); + return ret; + } + *thermal_spec_power = (value & 0x7FFF) * power_units; + *min_power = ((value & 0x7FFF0000) >> 16) * power_units; + *max_power = ((value & 0x7FFF00000000) >> 32) * power_units; + *max_time_window = ((value & 0x3f000000000000)>>48) * time_units; + + return ret; +} + +int c_rapl_interface::get_pkg_power_limit(uint64_t *value) +{ + int ret; + + if (!pkg_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PKG_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pkg_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_pkg_power_limit(uint64_t value) +{ + int ret; + + if (!pkg_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_PKG_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_pkg_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_dram_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!dram_domain_present()) { + return -1; + } + + if (powercap_sysfs_present) { + string str = read_sysfs_string(powercap_dram_path + "energy_uj"); + if (str.length() > 0) { + *status = atof(str.c_str()) / 1000000; // uj to Js + return 0; + } + + return -EINVAL; + } + + ret = read_msr(first_cpu, MSR_DRAM_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_dram_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_dram_power_info(double *thermal_spec_power, + double *max_power, double *min_power, double *max_time_window) +{ + int ret; + uint64_t value; + + if (!dram_domain_present()) { + return -1; + } + ret = read_msr(first_cpu, MSR_DRAM_POWER_INFO, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_dram_power_info failed\n"); + return ret; + } + + *thermal_spec_power = (value & 0x7FFF) * power_units; + *min_power = ((value & 0x7FFF0000) >> 16) * power_units; + *max_power = ((value & 0x7FFF00000000) >> 32) * power_units; + *max_time_window = ((value & 0x3f000000000000)>>48) * time_units; + + return ret; +} + +int c_rapl_interface::get_dram_power_limit(uint64_t *value) +{ + int ret; + + if (!dram_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_DRAM_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_dram_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_dram_power_limit(uint64_t value) +{ + int ret; + + if (!dram_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_DRAM_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_dram_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_pp0_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!pp0_domain_present()) { + return -1; + } + + if (powercap_sysfs_present) { + string str = read_sysfs_string(powercap_core_path + "energy_uj"); + if (str.length() > 0) { + *status = atof(str.c_str()) / 1000000; // uj to Js + return 0; + } + + return -EINVAL; + } + + ret = read_msr(first_cpu, MSR_PP0_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp0_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_pp0_power_limit(uint64_t *value) +{ + int ret; + + if (!pp0_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP0_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp0_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_pp0_power_limit(uint64_t value) +{ + int ret; + + if (!pp0_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_PP0_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_pp0_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_pp0_power_policy(unsigned int *pp0_power_policy) +{ + int ret; + uint64_t value; + + if (!pp0_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP0_POLICY, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp0_power_policy failed\n"); + return ret; + } + + *pp0_power_policy = value & 0x0f; + + return ret; +} + +int c_rapl_interface::get_pp1_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!pp1_domain_present()) { + return -1; + } + + if (powercap_sysfs_present) { + string str = read_sysfs_string(powercap_uncore_path + "energy_uj"); + if (str.length() > 0) { + *status = atof(str.c_str()) / 1000000; // uj to Js + return 0; + } + + return -EINVAL; + } + + ret = read_msr(first_cpu, MSR_PP1_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp1_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_pp1_power_limit(uint64_t *value) +{ + int ret; + + if (!pp1_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP1_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp1_power_info failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_pp1_power_limit(uint64_t value) +{ + int ret; + + if (!pp1_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_PP1_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_pp1_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_pp1_power_policy(unsigned int *pp1_power_policy) +{ + int ret; + uint64_t value; + + if (!pp1_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP1_POLICY, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp1_power_policy failed\n"); + return ret; + } + + *pp1_power_policy = value & 0x0f; + + return ret; +} + +void c_rapl_interface::rapl_measure_energy() +{ +#ifdef RAPL_TEST_MODE + int ret; + double energy_status; + double thermal_spec_power; + double max_power; + double min_power; + double max_time_window; + double pkg_watts = 0; + double dram_watts = 0; + double pp0_watts = 0; + double pp1_watts = 0; + double pkg_joules = 0; + double dram_joules = 0; + double pp0_joules = 0; + double pp1_joules = 0; + + get_pkg_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); + RAPL_DBG_PRINT("Pkg Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); + get_dram_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); + RAPL_DBG_PRINT("DRAM Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); + + for (;;) { + if (pkg_domain_present()) { + ret = get_pkg_energy_status(&energy_status); + if (last_pkg_energy_status == 0) + last_pkg_energy_status = energy_status; + if (ret > 0) { + pkg_joules = energy_status; + pkg_watts = (energy_status-last_pkg_energy_status)/measurment_interval; + } + last_pkg_energy_status = energy_status; + } + if (dram_domain_present()) { + ret = get_dram_energy_status(&energy_status); + if (last_dram_energy_status == 0) + last_dram_energy_status = energy_status; + if (ret > 0){ + dram_joules = energy_status; + dram_watts = (energy_status-last_dram_energy_status)/measurment_interval; + } + last_dram_energy_status = energy_status; + } + if (pp0_domain_present()) { + ret = get_pp0_energy_status(&energy_status); + if (last_pp0_energy_status == 0) + last_pp0_energy_status = energy_status; + if (ret > 0){ + pp0_joules = energy_status; + pp0_watts = (energy_status-last_pp0_energy_status)/measurment_interval; + } + last_pp0_energy_status = energy_status; + } + if (pp1_domain_present()) { + ret = get_pp1_energy_status(&energy_status); + if (last_pp1_energy_status == 0) + last_pp1_energy_status = energy_status; + if (ret > 0){ + pp1_joules = energy_status; + pp1_watts = (energy_status-last_pp1_energy_status)/measurment_interval; + } + last_pp1_energy_status = energy_status; + } + RAPL_DBG_PRINT("%f, %f, %f, %f\n", pkg_watts, dram_watts, pp0_watts, pp1_watts); + sleep(measurment_interval); + } +#endif +} diff --git a/src/cpu/rapl/rapl_interface.h b/src/cpu/rapl/rapl_interface.h new file mode 100644 index 0000000..c8e9683 --- /dev/null +++ b/src/cpu/rapl/rapl_interface.h @@ -0,0 +1,91 @@ +/* rapl_interface.h: rapl interface for power top + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name <Srinivas.Pandruvada@linux.intel.com> + * + */ + +#ifndef RAPL_INTERFACE_H +#define RAPL_INTERFACE_H + +class c_rapl_interface +{ +private: + static const int def_sampling_interval = 1; //In seconds + bool powercap_sysfs_present; + string powercap_core_path; + string powercap_uncore_path; + string powercap_dram_path; + + unsigned char rapl_domains; + int first_cpu; + + double power_units; + double energy_status_units; + double time_units; + + int read_msr(int cpu, unsigned int idx, uint64_t *val); + int write_msr(int cpu, unsigned int idx, uint64_t val); + +protected: + int measurment_interval; + double last_pkg_energy_status; + double last_dram_energy_status; + double last_pp0_energy_status; + double last_pp1_energy_status; + +public: + c_rapl_interface(const char *dev_name = "package-0", int cpu = 0); + + int get_rapl_power_unit(uint64_t *value); + double get_power_unit(); + double get_energy_status_unit(); + double get_time_unit(); + + int get_pkg_energy_status(double *status); + int get_pkg_power_info(double *thermal_spec_power, + double *max_power, double *min_power, double *max_time_window); + int get_pkg_power_limit(uint64_t *value); + int set_pkg_power_limit(uint64_t value); + + int get_dram_energy_status(double *status); + int get_dram_power_info(double *thermal_spec_power, + double *max_power, double *min_power, double *max_time_window); + int get_dram_power_limit(uint64_t *value); + int set_dram_power_limit(uint64_t value); + + int get_pp0_energy_status(double *status); + int get_pp0_power_limit(uint64_t *value); + int set_pp0_power_limit(uint64_t value); + int get_pp0_power_policy(unsigned int *pp0_power_policy); + + int get_pp1_energy_status(double *status); + int get_pp1_power_limit(uint64_t *value); + int set_pp1_power_limit(uint64_t value); + int get_pp1_power_policy(unsigned int *pp1_power_policy); + + bool pkg_domain_present(); + bool dram_domain_present(); + bool pp0_domain_present(); + bool pp1_domain_present(); + + void rapl_measure_energy(); +}; + +#endif diff --git a/src/csstoh.sh b/src/csstoh.sh new file mode 100755 index 0000000..681e6a5 --- /dev/null +++ b/src/csstoh.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# 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 +# or just google for it. +# +# Written by Igor Zhbanov <i.zhbanov at samsung.com> + +if [ $# -ne 2 ]; then + echo "Usage: csstoh.sh cssfile header.h" >&2 + exit 1 +fi +if [ ! -f "$1" ]; then + echo "$1: no such file or directory" >&2 + exit 1 +fi +# redirect stdout to a file +exec 1> "$2" || exit $? + +# header +cat <<HERE || exit $? +#ifndef __INCLUDE_GUARD_CCS_H +#define __INCLUDE_GUARD_CCS_H + +const char css[] = +HERE +# body +sed -r 's/^[ \t]*//; s/^(.*)$/\t\"\1\\n\"/' "$1" || exit $? +# footer +cat <<HERE || exit $? +; +#endif +HERE + +# close output file +exec 1>&- +# return status of output file write +exit $? diff --git a/src/devices/ahci.cpp b/src/devices/ahci.cpp new file mode 100644 index 0000000..efa66b3 --- /dev/null +++ b/src/devices/ahci.cpp @@ -0,0 +1,428 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> + + +using namespace std; + +#include "device.h" +#include "report/report.h" +#include "report/report-maker.h" +#include "ahci.h" +#include "../parameters/parameters.h" +#include "report/report-data-html.h" +#include <string.h> + +vector <class ahci *> links; + +static string disk_name(char *path, char *target, char *shortname) +{ + + DIR *dir; + struct dirent *dirent; + char pathname[PATH_MAX]; + string diskname = ""; + + snprintf(pathname, sizeof(pathname), "%s/%s", path, target); + dir = opendir(pathname); + if (!dir) + return diskname; + + while ((dirent = readdir(dir))) { + char line[4096], *c; + FILE *file; + if (dirent->d_name[0]=='.') + continue; + + if (!strchr(dirent->d_name, ':')) + continue; + + snprintf(line, sizeof(line), "%s/%s/model", pathname, dirent->d_name); + file = fopen(line, "r"); + if (file) { + if (fgets(line, sizeof(line), file) == NULL) { + fclose(file); + break; + } + fclose(file); + c = strchr(line, '\n'); + if (c) + *c = 0; + diskname = line; + break; + } + } + closedir(dir); + + return diskname; +} + +static string model_name(char *path, char *shortname) +{ + + DIR *dir; + struct dirent *dirent; + char pathname[PATH_MAX]; + + snprintf(pathname, sizeof(pathname), "%s/device", path); + + dir = opendir(pathname); + if (!dir) + return strdup(shortname); + + while ((dirent = readdir(dir))) { + if (dirent->d_name[0]=='.') + continue; + + if (!strchr(dirent->d_name, ':')) + continue; + if (!strstr(dirent->d_name, "target")) + continue; + return disk_name(pathname, dirent->d_name, shortname); + } + closedir(dir); + + return ""; +} + +ahci::ahci(char *_name, char *path): device() +{ + char buffer[4096]; + char devname[128]; + string diskname; + + end_active = 0; + end_slumber = 0; + end_devslp = 0; + end_partial = 0; + start_active = 0; + start_slumber = 0; + start_devslp = 0; + start_partial = 0; + pt_strcpy(sysfs_path, path); + + register_sysfs_path(sysfs_path); + + snprintf(devname, sizeof(devname), "ahci:%s", _name); + pt_strcpy(name, devname); + active_index = get_param_index("ahci-link-power-active"); + partial_index = get_param_index("ahci-link-power-partial"); + + snprintf(buffer, sizeof(buffer), "%s-active", name); + active_rindex = get_result_index(buffer); + + snprintf(buffer, sizeof(buffer), "%s-partial", name); + partial_rindex = get_result_index(buffer); + + snprintf(buffer, sizeof(buffer), "%s-slumber", name); + slumber_rindex = get_result_index(buffer); + + snprintf(buffer, sizeof(buffer), "%s-devslp", name); + devslp_rindex = get_result_index(buffer); + + diskname = model_name(path, _name); + + if (strlen(diskname.c_str()) == 0) + snprintf(humanname, sizeof(humanname), _("SATA link: %s"), _name); + else + snprintf(humanname, sizeof(humanname), _("SATA disk: %s"), diskname.c_str()); +} + +void ahci::start_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + snprintf(filename, sizeof(filename), "%s/ahci_alpm_active", sysfs_path); + try { + file.open(filename, ios::in); + if (file) { + file >> start_active; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/ahci_alpm_partial", sysfs_path); + file.open(filename, ios::in); + + if (file) { + file >> start_partial; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/ahci_alpm_slumber", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> start_slumber; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/ahci_alpm_devslp", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> start_devslp; + } + file.close(); + } + catch (std::ios_base::failure &c) { + fprintf(stderr, "%s\n", c.what()); + } + +} + +void ahci::end_measurement(void) +{ + char filename[PATH_MAX]; + char powername[4096]; + ifstream file; + double p; + double total; + + try { + snprintf(filename, sizeof(filename), "%s/ahci_alpm_active", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_active; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/ahci_alpm_partial", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_partial; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/ahci_alpm_slumber", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_slumber; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/ahci_alpm_devslp", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_devslp; + } + file.close(); + } + catch (std::ios_base::failure &c) { + fprintf(stderr, "%s\n", c.what()); + } + if (end_active < start_active) + end_active = start_active; + if (end_partial < start_partial) + end_partial = start_partial; + if (end_slumber < start_slumber) + end_slumber = start_slumber; + + total = 0.001 + end_active + end_partial + end_slumber + end_devslp - + start_active - start_partial - start_slumber - start_devslp; + + /* percent in active */ + p = (end_active - start_active) / total * 100.0; + if (p < 0) + p = 0; + snprintf(powername, sizeof(powername), "%s-active", name); + report_utilization(powername, p); + + /* percent in partial */ + p = (end_partial - start_partial) / total * 100.0; + if (p < 0) + p = 0; + snprintf(powername, sizeof(powername), "%s-partial", name); + report_utilization(powername, p); + + /* percent in slumber */ + p = (end_slumber - start_slumber) / total * 100.0; + if (p < 0) + p = 0; + snprintf(powername, sizeof(powername), "%s-slumber", name); + report_utilization(powername, p); + + /* percent in devslp */ + p = (end_devslp - start_devslp) / total * 100.0; + if (p < 0) + p = 0; + snprintf(powername, sizeof(powername), "%s-devslp", name); + report_utilization(powername, p); +} + + +double ahci::utilization(void) +{ + double p; + + p = (end_partial - start_partial + end_active - start_active) / (0.001 + end_active + end_partial + end_slumber + end_devslp - start_active - start_partial - start_slumber - start_devslp) * 100.0; + + if (p < 0) + p = 0; + + return p; +} + +const char * ahci::device_name(void) +{ + return name; +} + +void create_all_ahcis(void) +{ + struct dirent *entry; + DIR *dir; + char filename[PATH_MAX]; + + dir = opendir("/sys/class/scsi_host/"); + if (!dir) + return; + while (1) { + class ahci *bl; + ofstream file; + ifstream check_file; + entry = readdir(dir); + if (!entry) + break; + if (entry->d_name[0] == '.') + continue; + snprintf(filename, sizeof(filename), "/sys/class/scsi_host/%s/ahci_alpm_accounting", entry->d_name); + + check_file.open(filename, ios::in); + check_file.get(); + check_file.close(); + if (check_file.bad()) + continue; + + file.open(filename, ios::in); + if (!file) + continue; + file << 1 ; + file.close(); + snprintf(filename, sizeof(filename), "/sys/class/scsi_host/%s", entry->d_name); + + bl = new class ahci(entry->d_name, filename); + all_devices.push_back(bl); + register_parameter("ahci-link-power-active", 0.6); /* active sata link takes about 0.6 W */ + register_parameter("ahci-link-power-partial"); + links.push_back(bl); + } + closedir(dir); + +} + + + +double ahci::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + power = 0; + + factor = get_parameter_value(active_index, bundle); + util = get_result_value(active_rindex, result); + power += util * factor / 100.0; + + + factor = get_parameter_value(partial_index, bundle); + util = get_result_value(partial_rindex, result); + power += util * factor / 100.0; + + return power; +} + +void ahci_create_device_stats_table(void) +{ + unsigned int i; + int cols=0; + int rows=0; + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "ahci"); + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Add section */ + report.add_div(&div_attr); + + if (links.size() == 0) { + report.add_title(&title_attr, __("AHCI ALPM Residency Statistics - Not supported on this macine")); + report.end_div(); + return; + } + + /* Set Table attributes, rows, and cols */ + table_attributes std_table_css; + cols=5; + rows=links.size()+1; + init_std_side_table_attr(&std_table_css, rows, cols); + + + + /* Set array of data in row Major order */ + string *ahci_data = new string[cols * rows]; + ahci_data[0]=__("Link"); + ahci_data[1]=__("Active"); + ahci_data[2]=__("Partial"); + ahci_data[3]=__("Slumber"); + ahci_data[4]=__("Devslp"); + + /* traverse list of all devices and put their residency in the table */ + for (i = 0; i < links.size(); i++){ + links[i]->report_device_stats(ahci_data, i); + } + report.add_title(&title_attr, __("AHCI ALPM Residency Statistics")); + report.add_table(ahci_data, &std_table_css); + report.end_div(); + delete [] ahci_data; +} + +void ahci::report_device_stats(string *ahci_data, int idx) +{ + int offset=(idx*5+5); + char util[128]; + double active_util = get_result_value(active_rindex, &all_results); + double partial_util = get_result_value(partial_rindex, &all_results); + double slumber_util = get_result_value(slumber_rindex, &all_results); + double devslp_util = get_result_value(devslp_rindex, &all_results); + + snprintf(util, sizeof(util), "%5.1f", active_util); + ahci_data[offset]= util; + offset +=1; + + snprintf(util, sizeof(util), "%5.1f", partial_util); + ahci_data[offset]= util; + offset +=1; + + snprintf(util, sizeof(util), "%5.1f", slumber_util); + ahci_data[offset]= util; + offset +=1; + + snprintf(util, sizeof(util), "%5.1f", devslp_util); + ahci_data[offset]= util; +} diff --git a/src/devices/ahci.h b/src/devices/ahci.h new file mode 100644 index 0000000..7431fb5 --- /dev/null +++ b/src/devices/ahci.h @@ -0,0 +1,72 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_AHCI_H +#define _INCLUDE_GUARD_AHCI_H + + +#include <string> +#include <limits.h> +#include "device.h" +#include "../parameters/parameters.h" +#include <stdint.h> + +class ahci: public device { + uint64_t start_active, end_active; + uint64_t start_partial, end_partial; + uint64_t start_slumber, end_slumber; + uint64_t start_devslp, end_devslp; + char sysfs_path[PATH_MAX]; + char name[4096]; + int partial_rindex; + int active_rindex; + int slumber_rindex; + int devslp_rindex; + int partial_index; + int active_index; + char humanname[4096]; +public: + + ahci(char *_name, char *path); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "ahci";}; + + virtual const char * device_name(void); + virtual const char * human_name(void) { return humanname;}; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int power_valid(void) { return utilization_power_valid(partial_rindex) + utilization_power_valid(active_rindex);}; + virtual int grouping_prio(void) { return 1; }; + virtual void report_device_stats(string *ahci_data, int idx); +}; + +extern void create_all_ahcis(void); +extern void ahci_create_device_stats_table(void); + + +#endif diff --git a/src/devices/alsa.cpp b/src/devices/alsa.cpp new file mode 100644 index 0000000..7e22975 --- /dev/null +++ b/src/devices/alsa.cpp @@ -0,0 +1,207 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +using namespace std; + +#include "device.h" +#include "alsa.h" +#include "../parameters/parameters.h" + +#include "../devlist.h" + +#include <string.h> +#include <unistd.h> + +alsa::alsa(const char *_name, const char *path): device() +{ + ifstream file; + + char devname[4096]; + char model[4096]; + char vendor[4096]; + end_active = 0; + start_active = 0; + end_inactive = 0; + start_inactive = 0; + pt_strcpy(sysfs_path, path); + + snprintf(devname, sizeof(devname), "alsa:%s", _name); + snprintf(humanname, sizeof(humanname), "alsa:%s", _name); + pt_strcpy(name, devname); + rindex = get_result_index(name); + + guilty[0] = 0; + model[0] = 0; + vendor[0] = 0; + snprintf(devname, sizeof(devname), "%s/modelname", path); + file.open(devname); + if (file) { + file.getline(model, sizeof(model)); + file.close(); + } + snprintf(devname, sizeof(devname), "%s/vendor_name", path); + file.open(devname); + if (file) { + file.getline(vendor, sizeof(vendor)); + file.close(); + } + if (strlen(model) && strlen(vendor)) + snprintf(humanname, sizeof(humanname), _("Audio codec %s: %s (%s)"), name, model, vendor); + else if (strlen(model)) + snprintf(humanname, sizeof(humanname), _("Audio codec %s: %s"), _name, model); + else if (strlen(vendor)) + snprintf(humanname, sizeof(humanname), _("Audio codec %s: %s"), _name, vendor); +} + +void alsa::start_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + snprintf(filename, sizeof(filename), "%s/power_off_acct", sysfs_path); + try { + file.open(filename, ios::in); + if (file) { + file >> start_inactive; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/power_on_acct", sysfs_path); + file.open(filename, ios::in); + + if (file) { + file >> start_active; + } + file.close(); + } + catch (std::ios_base::failure &c) { + fprintf(stderr, "%s\n", c.what()); + } +} + +void alsa::end_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + double p; + + snprintf(filename, sizeof(filename), "%s/power_off_acct", sysfs_path); + try { + file.open(filename, ios::in); + if (file) { + file >> end_inactive; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/power_on_acct", sysfs_path); + file.open(filename, ios::in); + + if (file) { + file >> end_active; + } + file.close(); + } + catch (std::ios_base::failure &c) { + fprintf(stderr, "%s\n", c.what()); + } + + p = (end_active - start_active) / (0.001 + end_active + end_inactive - start_active - start_inactive) * 100.0; + report_utilization(name, p); +} + + +double alsa::utilization(void) +{ + double p; + + p = (end_active - start_active) / (0.001 + end_active - start_active + end_inactive - start_inactive) * 100.0; + + return p; +} + +const char * alsa::device_name(void) +{ + return name; +} + +static void create_all_alsa_callback(const char *d_name) +{ + char filename[PATH_MAX]; + class alsa *bl; + + if (strncmp(d_name, "hwC", 3) != 0) + return; + + snprintf(filename, sizeof(filename), "/sys/class/sound/%s/power_on_acct", d_name); + if (access(filename, R_OK) != 0) + return; + + snprintf(filename, sizeof(filename), "/sys/class/sound/%s", d_name); + bl = new class alsa(d_name, filename); + all_devices.push_back(bl); + register_parameter("alsa-codec-power", 0.5); +} + +void create_all_alsa(void) +{ + process_directory("/sys/class/sound/", create_all_alsa_callback); +} + +double alsa::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + static int index = 0; + + power = 0; + if (!index) + index = get_param_index("alsa-codec-power"); + + factor = get_parameter_value(index, bundle); + + util = get_result_value(rindex, result); + + power += util * factor / 100.0; + + return power; +} + +void alsa::register_power_with_devlist(struct result_bundle *results, struct parameter_bundle *bundle) +{ + register_devpower(&name[7], power_usage(results, bundle), this); +} + +const char * alsa::human_name(void) +{ + pt_strcpy(temp_buf, humanname); + if (strlen(guilty) > 0) + snprintf(temp_buf, sizeof(temp_buf), "%s (%s)", humanname, guilty); + return temp_buf; +} diff --git a/src/devices/alsa.h b/src/devices/alsa.h new file mode 100644 index 0000000..b68203f --- /dev/null +++ b/src/devices/alsa.h @@ -0,0 +1,67 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_ALSA_H +#define _INCLUDE_GUARD_ALSA_H + + +#include "device.h" +#include "../parameters/parameters.h" + +#include <stdint.h> +#include <limits.h> + +class alsa: public device { + uint64_t start_active, end_active; + uint64_t start_inactive, end_inactive; + char sysfs_path[PATH_MAX]; + char name[4096]; + char humanname[4096]; + char temp_buf[4096]; + int rindex; +public: + + alsa(const char *_name, const char *path); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "alsa";}; + + virtual const char * device_name(void); + virtual const char * human_name(void); + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int power_valid(void) { return utilization_power_valid(rindex);}; + + virtual void register_power_with_devlist(struct result_bundle *results, struct parameter_bundle *bundle); + virtual int grouping_prio(void) { return 0; }; + +}; + +extern void create_all_alsa(void); + + +#endif diff --git a/src/devices/backlight.cpp b/src/devices/backlight.cpp new file mode 100644 index 0000000..c73e4c9 --- /dev/null +++ b/src/devices/backlight.cpp @@ -0,0 +1,225 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> + + +using namespace std; + +#include "device.h" +#include "backlight.h" +#include "../parameters/parameters.h" + +#include <string.h> + + +backlight::backlight(const char *_name, const char *path): device() +{ + min_level = 0; + max_level = 0; + start_level = 0; + end_level = 0; + pt_strcpy(sysfs_path, path); + register_sysfs_path(sysfs_path); + snprintf(name, sizeof(name) - 1, "backlight:%s", _name); + r_index = get_result_index(name); + r_index_power = 0; +} + +void backlight::start_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + snprintf(filename, sizeof(filename), "%s/max_brightness", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> max_level; + } + file.close(); + + snprintf(filename, sizeof(filename), "%s/actual_brightness", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> start_level; + file.close(); + } +} + +static int dpms_screen_on(void) +{ + DIR *dir; + struct dirent *entry; + char filename[PATH_MAX]; + char line[4096]; + ifstream file; + + dir = opendir("/sys/class/drm/card0"); + if (!dir) + return 1; + while (1) { + entry = readdir(dir); + if (!entry) + break; + + if (strncmp(entry->d_name, "card", 4) != 0) + continue; + snprintf(filename, sizeof(filename), "/sys/class/drm/card0/%s/enabled", entry->d_name); + file.open(filename, ios::in); + if (!file) + continue; + file.getline(line, sizeof(line)); + file.close(); + if (strcmp(line, "enabled") != 0) + continue; + snprintf(filename, sizeof(filename), "/sys/class/drm/card0/%s/dpms", entry->d_name); + file.open(filename, ios::in); + if (!file) + continue; + file.getline(line, sizeof(line)); + file.close(); + if (strcmp(line, "On") == 0) { + closedir(dir); + return 1; + } + } + closedir(dir); + return 0; +} + +void backlight::end_measurement(void) +{ + char filename[PATH_MAX]; + char powername[4096]; + ifstream file; + double p; + int _backlight = 0; + + snprintf(filename, sizeof(filename), "%s/actual_brightness", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_level; + } + file.close(); + + if (dpms_screen_on()) { + p = 100.0 * (end_level + start_level) / 2 / max_level; + _backlight = 100; + } else { + p = 0; + } + + report_utilization(name, p); + snprintf(powername, sizeof(powername), "%s-power", name); + report_utilization(powername, _backlight); +} + + +double backlight::utilization(void) +{ + double p; + + p = 100.0 * (end_level + start_level) / 2 / max_level; + return p; +} + +const char * backlight::device_name(void) +{ + return name; +} + +static void create_all_backlights_callback(const char *d_name) +{ + class backlight *bl; + char filename[PATH_MAX]; + snprintf(filename, sizeof(filename), "/sys/class/backlight/%s", d_name); + bl = new class backlight(d_name, filename); + all_devices.push_back(bl); +} + +void create_all_backlights(void) +{ + process_directory("/sys/class/backlight/", create_all_backlights_callback); + register_parameter("backlight"); + register_parameter("backlight-power"); + register_parameter("backlight-boost-40", 0, 0.5); + register_parameter("backlight-boost-80", 0, 0.5); + register_parameter("backlight-boost-100", 0, 0.5); +} + +double backlight::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double _utilization; + char powername[4096]; + static int bl_index = 0, blp_index = 0, bl_boost_index40 = 0, bl_boost_index80, bl_boost_index100; + + if (!bl_index) + bl_index = get_param_index("backlight"); + if (!blp_index) + blp_index = get_param_index("backlight-power"); + if (!bl_boost_index40) + bl_boost_index40 = get_param_index("backlight-boost-40"); + if (!bl_boost_index80) + bl_boost_index80 = get_param_index("backlight-boost-80"); + if (!bl_boost_index100) + bl_boost_index100 = get_param_index("backlight-boost-100"); + + power = 0; + factor = get_parameter_value(bl_index, bundle); + _utilization = get_result_value(r_index, result); + + power += _utilization * factor / 100.0; + + /* + * most machines have a non-linear backlight scale. to compensate, add a fixed value + * once the brightness hits 40% and 80% + */ + + if (_utilization >=99) + power += get_parameter_value(bl_boost_index100, bundle); + else if (_utilization >=80) + power += get_parameter_value(bl_boost_index80, bundle); + else if (_utilization >=40) + power += get_parameter_value(bl_boost_index40, bundle); + + factor = get_parameter_value(blp_index, bundle); + + if (!r_index_power) { + sprintf(powername, "%s-power", name); + r_index_power = get_result_index(powername); + } + _utilization = get_result_value(r_index_power, result); + + power += _utilization * factor / 100.0; + + return power; +} diff --git a/src/devices/backlight.h b/src/devices/backlight.h new file mode 100644 index 0000000..1dac778 --- /dev/null +++ b/src/devices/backlight.h @@ -0,0 +1,59 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_BACKLIGHT_H +#define _INCLUDE_GUARD_BACKLIGHT_H + +#include <limits.h> + +#include "device.h" + +class backlight: public device { + int min_level, max_level; + int start_level, end_level; + char sysfs_path[PATH_MAX]; + char name[4096]; + int r_index; + int r_index_power; +public: + + backlight(const char *_name, const char *path); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "backlight";}; + + virtual const char * device_name(void); + virtual const char * human_name(void) { return "Display backlight";}; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int grouping_prio(void) { return 10; }; +}; + +extern void create_all_backlights(void); + + +#endif diff --git a/src/devices/devfreq.cpp b/src/devices/devfreq.cpp new file mode 100644 index 0000000..b194ac4 --- /dev/null +++ b/src/devices/devfreq.cpp @@ -0,0 +1,334 @@ +/* + * Copyright 2012, Linaro + * + * 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 + * or just google for it. + * + * Authors: + * Rajagopal Venkat <rajagopal.venkat@linaro.org> + */ + +#include <iostream> +#include <fstream> + +#include <dirent.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "device.h" +#include "devfreq.h" +#include "../display.h" +#include "../cpu/cpu.h" +#include "../report/report.h" +#include "../report/report-maker.h" + +static bool is_enabled = true; +static DIR *dir = NULL; + +static vector<class devfreq *> all_devfreq; + +devfreq::devfreq(const char* dpath): device() +{ + pt_strcpy(dir_name, dpath); +} + +uint64_t devfreq::parse_freq_time(char* pchr) +{ + char *cptr, *pptr = pchr; + uint64_t ctime; + + cptr = strtok(pchr, " :"); + while (cptr != NULL) { + cptr = strtok(NULL, " :"); + if (cptr ) + pptr = cptr; + } + + ctime = strtoull(pptr, NULL, 10); + return ctime; +} + +void devfreq::process_time_stamps() +{ + unsigned int i; + uint64_t active_time = 0; + + sample_time = (1000000.0 * (stamp_after.tv_sec - stamp_before.tv_sec)) + + ((stamp_after.tv_usec - stamp_before.tv_usec) ); + + for (i=0; i < dstates.size()-1; i++) { + struct frequency *state = dstates[i]; + state->time_after = 1000 * (state->time_after - state->time_before); + active_time += state->time_after; + } + /* Compute idle time for the device */ + dstates[i]->time_after = sample_time - active_time; +} + +void devfreq::add_devfreq_freq_state(uint64_t freq, uint64_t time) +{ + struct frequency *state; + + state = new(std::nothrow) struct frequency; + if (!state) + return; + + memset(state, 0, sizeof(*state)); + dstates.push_back(state); + + state->freq = freq; + if (freq == 0) + strcpy(state->human_name, "Idle"); + else + hz_to_human(freq, state->human_name); + state->time_before = time; +} + +void devfreq::update_devfreq_freq_state(uint64_t freq, uint64_t time) +{ + unsigned int i; + struct frequency *state = NULL; + + for(i=0; i < dstates.size(); i++) { + if (freq == dstates[i]->freq) + state = dstates[i]; + } + + if (state == NULL) { + add_devfreq_freq_state(freq, time); + return; + } + + state->time_after = time; +} + +void devfreq::parse_devfreq_trans_stat(char *dname) +{ + ifstream file; + char filename[256]; + + snprintf(filename, sizeof(filename), "/sys/class/devfreq/%s/trans_stat", dir_name); + file.open(filename); + + if (!file) + return; + + char line[1024]; + char *c; + + while (file) { + uint64_t freq; + uint64_t time; + char *pchr; + + memset(line, 0, sizeof(line)); + file.getline(line, sizeof(line)); + + pchr = strchr(line, '*'); + pchr = (pchr != NULL) ? pchr+1 : line; + + freq = strtoull(pchr, &c, 10); + if (!freq) + continue; + + time = parse_freq_time(pchr); + update_devfreq_freq_state(freq, time); + } + file.close(); +} + +void devfreq::start_measurement(void) +{ + unsigned int i; + + for (i=0; i < dstates.size(); i++) + delete dstates[i]; + dstates.resize(0); + sample_time = 0; + + gettimeofday(&stamp_before, NULL); + parse_devfreq_trans_stat(dir_name); + /* add device idle state */ + update_devfreq_freq_state(0, 0); +} + +void devfreq::end_measurement(void) +{ + parse_devfreq_trans_stat(dir_name); + gettimeofday(&stamp_after, NULL); + process_time_stamps(); +} + +double devfreq::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + return 0; +} + +double devfreq::utilization(void) +{ + return 0; +} + +void devfreq::fill_freq_utilization(unsigned int idx, char *buf) +{ + buf[0] = 0; + + if (idx < dstates.size() && dstates[idx]) { + struct frequency *state = dstates[idx]; + sprintf(buf, " %5.1f%% ", percentage(1.0 * state->time_after / sample_time)); + } +} + +void devfreq::fill_freq_name(unsigned int idx, char *buf) +{ + buf[0] = 0; + + if (idx < dstates.size() && dstates[idx]) { + sprintf(buf, "%-15s", dstates[idx]->human_name); + } +} + +void start_devfreq_measurement(void) +{ + unsigned int i; + + for (i=0; i<all_devfreq.size(); i++) + all_devfreq[i]->start_measurement(); +} + +void end_devfreq_measurement(void) +{ + unsigned int i; + + for (i=0; i<all_devfreq.size(); i++) + all_devfreq[i]->end_measurement(); +} + +static void devfreq_dev_callback(const char *d_name) +{ + devfreq *df = new(std::nothrow) class devfreq(d_name); + if (df) + all_devfreq.push_back(df); +} + +void create_all_devfreq_devices(void) +{ + int num = 0; + + std::string p = "/sys/class/devfreq/"; + dir = opendir(p.c_str()); + if (dir == NULL) { + fprintf(stderr, "Devfreq not enabled\n"); + is_enabled = false; + return; + } + + while(readdir(dir) != NULL) + num++; + + if (num == 2) { + fprintf(stderr, "Devfreq not enabled\n"); + is_enabled = false; + closedir(dir); + dir = NULL; + return; + } + + callback fn = &devfreq_dev_callback; + process_directory(p.c_str(), fn); +} + +void initialize_devfreq(void) +{ + if (is_enabled) + create_tab("Device Freq stats", _("Device Freq stats")); +} + +void display_devfreq_devices(void) +{ + unsigned int i, j; + WINDOW *win; + char fline[1024]; + char buf[128]; + + win = get_ncurses_win("Device Freq stats"); + if (!win) + return; + + wclear(win); + wmove(win, 2,0); + + if (!is_enabled) { + wprintw(win, _(" Devfreq is not enabled")); + return; + } + + if (!all_devfreq.size()) { + wprintw(win, _(" No devfreq devices available")); + return; + } + + for (i=0; i<all_devfreq.size(); i++) { + + class devfreq *df = all_devfreq[i]; + wprintw(win, "\n%s\n", df->device_name()); + + for(j=0; j < df->dstates.size(); j++) { + memset(fline, 0, sizeof(fline)); + strcpy(fline, "\t"); + df->fill_freq_name(j, buf); + strcat(fline, buf); + df->fill_freq_utilization(j, buf); + strcat(fline, buf); + strcat(fline, "\n"); + wprintw(win, "%s", fline); + } + wprintw(win, "\n"); + } +} + +void report_devfreq_devices(void) +{ + if (!is_enabled) { + return; + } + +/* todo: adapt to new report format */ + +} + +void clear_all_devfreq() +{ + unsigned int i, j; + + for (i=0; i < all_devfreq.size(); i++) { + class devfreq *df = all_devfreq[i]; + + for(j=0; j < df->dstates.size(); j++) + delete df->dstates[j]; + + delete df; + } + all_devfreq.clear(); + /* close /sys/class/devfreq */ + if (dir != NULL) { + closedir(dir); + dir = NULL; + } +} diff --git a/src/devices/devfreq.h b/src/devices/devfreq.h new file mode 100644 index 0000000..4a8983b --- /dev/null +++ b/src/devices/devfreq.h @@ -0,0 +1,76 @@ +/* + * Copyright 2012, Linaro + * + * 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 + * or just google for it. + * + * Authors: + * Rajagopal Venkat <rajagopal.venkat@linaro.org> + */ +#ifndef _INCLUDE_GUARD_DEVFREQ_H +#define _INCLUDE_GUARD_DEVFREQ_H + +#include "device.h" +#include "../parameters/parameters.h" +#include <sys/time.h> + +struct frequency; + +class devfreq: public device { + char dir_name[128]; + struct timeval stamp_before, stamp_after; + double sample_time; + + uint64_t parse_freq_time(char *ptr); + void add_devfreq_freq_state(uint64_t freq, uint64_t time); + void update_devfreq_freq_state(uint64_t freq, uint64_t time); + void parse_devfreq_trans_stat(char *dname); + void process_time_stamps(); + +public: + + vector<struct frequency *> dstates; + + devfreq(const char *c); + void fill_freq_utilization(unsigned int idx, char *buf); + void fill_freq_name(unsigned int idx, char *buf); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "devfreq";}; + + virtual const char * device_name(void) { return dir_name;}; + virtual const char * human_name(void) { return "devfreq";}; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual const char * util_units(void) { return " rpm"; }; + virtual int power_valid(void) { return 0; /*utilization_power_valid(r_index);*/}; + virtual int grouping_prio(void) { return 1; }; +}; + +extern void create_all_devfreq_devices(void); +extern void clear_all_devfreq(void); +extern void display_devfreq_devices(void); +extern void report_devfreq_devices(void); +extern void initialize_devfreq(void); +extern void start_devfreq_measurement(void); +extern void end_devfreq_measurement(void); + +#endif diff --git a/src/devices/device.cpp b/src/devices/device.cpp new file mode 100644 index 0000000..f191072 --- /dev/null +++ b/src/devices/device.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "device.h" +#include <vector> +#include <algorithm> +#include <stdio.h> +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> + +using namespace std; + +#include "backlight.h" +#include "usb.h" +#include "ahci.h" +#include "alsa.h" +#include "rfkill.h" +#include "i915-gpu.h" +#include "thinkpad-fan.h" +#include "thinkpad-light.h" +#include "network.h" +#include "runtime_pm.h" + +#include "../parameters/parameters.h" +#include "../display.h" +#include "../lib.h" +#include "../report/report.h" +#include "../report/report-maker.h" +#include "../report/report-data-html.h" +#include "../measurement/measurement.h" +#include "../devlist.h" +#include <unistd.h> + +device::device(void) +{ + cached_valid = 0; + hide = 0; + + memset(guilty, 0, sizeof(guilty)); + memset(real_path, 0, sizeof(real_path)); +} + + +void device::register_sysfs_path(const char *path) +{ + char current_path[PATH_MAX + 1]; + int iter = 0; + pt_strcpy(current_path, path); + + while (iter++ < 10) { + char test_path[PATH_MAX + 1]; + snprintf(test_path, sizeof(test_path), "%s/device", current_path); + if (access(test_path, R_OK) == 0) + strcpy(current_path, test_path); + else + break; + } + + if (!realpath(current_path, real_path)) + real_path[0] = 0; +} + +void device::start_measurement(void) +{ + hide = false; +} + +void device::end_measurement(void) +{ +} + +double device::utilization(void) +{ + return 0.0; +} + + + +vector<class device *> all_devices; + + +void devices_start_measurement(void) +{ + unsigned int i; + for (i = 0; i < all_devices.size(); i++) + all_devices[i]->start_measurement(); +} + +void devices_end_measurement(void) +{ + unsigned int i; + for (i = 0; i < all_devices.size(); i++) + all_devices[i]->end_measurement(); + + clear_devpower(); + + for (i = 0; i < all_devices.size(); i++) { + all_devices[i]->hide = false; + all_devices[i]->register_power_with_devlist(&all_results, &all_parameters); + } +} + +static bool power_device_sort(class device * i, class device * j) +{ + double pI, pJ; + pI = i->power_usage(&all_results, &all_parameters); + pJ = j->power_usage(&all_results, &all_parameters); + + if (equals(pI, pJ)) { + int vI, vJ; + vI = i->power_valid(); + vJ = j->power_valid(); + + if (vI != vJ) + return vI > vJ; + + return i->utilization() > j->utilization(); + } + return pI > pJ; +} + + +void report_devices(void) +{ + WINDOW *win; + unsigned int i; + int show_power; + double pw; + + char util[128]; + char power[128]; + + win = get_ncurses_win("Device stats"); + if (!win) + return; + + show_power = global_power_valid(); + + wclear(win); + wmove(win, 2,0); + + sort(all_devices.begin(), all_devices.end(), power_device_sort); + + + + pw = global_power(); + if (pw > 0.0001) { + char buf[32]; + wprintw(win, _("The battery reports a discharge rate of %sW\n"), + fmt_prefix(pw, buf)); + wprintw(win, _("The energy consumed was %sJ\n"), + fmt_prefix(global_joules(), buf)); + } + + if (show_power) { + char buf[32]; + wprintw(win, _("System baseline power is estimated at %sW\n"), + fmt_prefix(get_parameter_value("base power"), buf)); + } + + if (pw > 0.0001 || show_power) + wprintw(win, "\n"); + if (show_power) + wprintw(win, _("Power est. Usage Device name\n")); + else + wprintw(win, _(" Usage Device name\n")); + + for (i = 0; i < all_devices.size(); i++) { + double P; + + util[0] = 0; + + if (all_devices[i]->util_units()) { + if (all_devices[i]->utilization() < 1000) + sprintf(util, "%5.1f%s", all_devices[i]->utilization(), all_devices[i]->util_units()); + else + sprintf(util, "%5i%s", (int)all_devices[i]->utilization(), all_devices[i]->util_units()); + } + while (strlen(util) < 13) strcat(util, " "); + + P = all_devices[i]->power_usage(&all_results, &all_parameters); + + format_watts(P, power, 11); + + if (!show_power || !all_devices[i]->power_valid()) + strcpy(power, " "); + + + wprintw(win, "%s %s %s\n", + power, + util, + all_devices[i]->human_name() + ); + } +} + +void show_report_devices(void) +{ + unsigned int i; + int show_power, cols, rows, idx; + double pw; + + show_power = global_power_valid(); + sort(all_devices.begin(), all_devices.end(), power_device_sort); + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "devinfo"); + + /* Set Table attributes, rows, and cols */ + table_attributes std_table_css; + cols=2; + if (show_power) + cols=3; + + idx = cols; + rows= all_devices.size() + 1; + init_std_side_table_attr(&std_table_css, rows, cols); + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Add section */ + report.add_div(&div_attr); + + /* Device Summary */ + int summary_size=2; + string *summary = new string[summary_size]; + pw = global_power(); + char buf[32]; + if (pw > 0.0001) { + summary[0]= __("The battery reports a discharge rate of: "); + summary[1]=string(fmt_prefix(pw, buf)); + summary[1].append(" W"); + report.add_summary_list(summary, summary_size); + + summary[0]= __("The energy consumed was : "); + summary[1]=string(fmt_prefix(global_joules(), buf)); + summary[1].append(" J"); + report.add_summary_list(summary, summary_size); + } + + if (show_power) { + summary[0]=__("The system baseline power is estimated at: "); + summary[1]=string(fmt_prefix(get_parameter_value("base power"), buf)); + summary[1].append(" W"); + report.add_summary_list(summary, summary_size); + } + delete [] summary; + + /* Set array of data in row Major order */ + string *device_data = new string[cols * rows]; + device_data[0]= __("Usage"); + device_data[1]= __("Device Name"); + if (show_power) + device_data[2]= __("PW Estimate"); + + for (i = 0; i < all_devices.size(); i++) { + double P; + char util[128]; + char power[128]; + + util[0] = 0; + if (all_devices[i]->util_units()) { + if (all_devices[i]->utilization() < 1000) + sprintf(util, "%5.1f%s", + all_devices[i]->utilization(), + all_devices[i]->util_units()); + else + sprintf(util, "%5i%s", + (int)all_devices[i]->utilization(), + all_devices[i]->util_units()); + } + + P = all_devices[i]->power_usage(&all_results, &all_parameters); + format_watts(P, power, 11); + + if (!show_power || !all_devices[i]->power_valid()) + strcpy(power, " "); + + device_data[idx]= string(util); + idx+=1; + + device_data[idx]= string(all_devices[i]->human_name()); + idx+=1; + + if (show_power) { + device_data[idx]= string(power); + idx+=1; + } + } + /* Report Output */ + report.add_title(&title_attr, __("Device Power Report")); + report.add_table(device_data, &std_table_css); + delete [] device_data; +} + + +void create_all_devices(void) +{ + create_all_backlights(); + create_all_usb_devices(); + create_all_ahcis(); + create_all_alsa(); + create_all_rfkills(); + create_i915_gpu(); + create_thinkpad_fan(); + create_thinkpad_light(); + create_all_nics(); + create_all_runtime_pm_devices(); +} + + +void clear_all_devices(void) +{ + unsigned int i; + for (i = 0; i < all_devices.size(); i++) { + delete all_devices[i]; + } + all_devices.clear(); +} diff --git a/src/devices/device.h b/src/devices/device.h new file mode 100644 index 0000000..a373875 --- /dev/null +++ b/src/devices/device.h @@ -0,0 +1,85 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_DEVICE_H +#define _INCLUDE_GUARD_DEVICE_H + + +#include <vector> +#include <limits.h> + +struct parameter_bundle; +struct result_bundle; + +class device { +public: + int cached_valid; + bool hide; + + char guilty[4096]; + char real_path[PATH_MAX+1]; + + virtual void start_measurement(void); + virtual void end_measurement(void); + + device(void); + + virtual ~device() {}; + + void register_sysfs_path(const char *path); + + virtual double utilization(void); /* percentage */ + + virtual const char * util_units(void) { return "%"; }; + + virtual const char * class_name(void) { return "abstract device";}; + virtual const char * device_name(void) { return "abstract device";}; + + virtual const char * human_name(void) { return device_name(); }; + + virtual double power_usage(struct result_bundle *results, struct parameter_bundle *bundle) { return 0.0; }; + + virtual bool show_in_list(void) {return !hide;}; + + virtual int power_valid(void) { return 1;}; + + virtual void register_power_with_devlist(struct result_bundle *results, struct parameter_bundle *bundle) { ; }; + + virtual int grouping_prio(void) { return 0; }; /* priority of this device class if multiple classes match to the same underlying device. 0 is lowest */ +}; + +using namespace std; + +extern vector<class device *> all_devices; + +extern void devices_start_measurement(void); +extern void devices_end_measurement(void); +extern void show_report_devices(void); +extern void report_devices(void); + + +extern void create_all_devices(void); +extern void clear_all_devices(void); + +#endif diff --git a/src/devices/gpu_rapl_device.cpp b/src/devices/gpu_rapl_device.cpp new file mode 100644 index 0000000..71d71a0 --- /dev/null +++ b/src/devices/gpu_rapl_device.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Srinivas Pandruvada<Srinivas.Pandruvada@linux.intel.com> + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "../parameters/parameters.h" +#include "gpu_rapl_device.h" + +gpu_rapl_device::gpu_rapl_device(i915gpu *parent) + : i915gpu(), + device_valid(false) +{ + last_time = time(NULL); + if (rapl.pp1_domain_present()) { + device_valid = true; + parent->add_child(this); + rapl.get_pp1_energy_status(&last_energy); + } +} + +void gpu_rapl_device::start_measurement(void) +{ + last_time = time(NULL); + + rapl.get_pp1_energy_status(&last_energy); +} + +void gpu_rapl_device::end_measurement(void) +{ + time_t curr_time = time(NULL); + double energy; + + consumed_power = 0.0; + if ((curr_time - last_time) > 0) { + rapl.get_pp1_energy_status(&energy); + consumed_power = (energy-last_energy)/(curr_time-last_time); + last_energy = energy; + last_time = curr_time; + } +} + +double gpu_rapl_device::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + if (rapl.pp1_domain_present()) + return consumed_power; + else + return 0.0; +} diff --git a/src/devices/gpu_rapl_device.h b/src/devices/gpu_rapl_device.h new file mode 100644 index 0000000..fbde246 --- /dev/null +++ b/src/devices/gpu_rapl_device.h @@ -0,0 +1,57 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Srinivas Pandruvada <Srinivas.Pandruvada@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_GPU_RAPL_DEVICE_H +#define _INCLUDE_GUARD_GPU_RAPL_DEVICE_H + +#include <vector> +#include <string> + +using namespace std; + +#include <sys/time.h> +#include "i915-gpu.h" +#include "cpu/rapl/rapl_interface.h" + +class gpu_rapl_device: public i915gpu { + + c_rapl_interface rapl; + time_t last_time; + double last_energy; + double consumed_power; + bool device_valid; + +public: + gpu_rapl_device(i915gpu *parent); + virtual const char * class_name(void) { return "GPU core";}; + virtual const char * device_name(void) { return "GPU core";}; + bool device_present() { return device_valid;} + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual void start_measurement(void); + virtual void end_measurement(void); + +}; + + +#endif diff --git a/src/devices/i915-gpu.cpp b/src/devices/i915-gpu.cpp new file mode 100644 index 0000000..d0f1d69 --- /dev/null +++ b/src/devices/i915-gpu.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> +#include <limits.h> +#include "../lib.h" + +using namespace std; + +#include "device.h" +#include "i915-gpu.h" +#include "../parameters/parameters.h" +#include "../process/powerconsumer.h" +#include "gpu_rapl_device.h" + +#include <string.h> +#include <unistd.h> + +i915gpu::i915gpu(): device() +{ + index = get_param_index("gpu-operations"); + rindex = get_result_index("gpu-operations"); +} + +const char * i915gpu::device_name(void) +{ + if (child_devices.size()) + return "GPU misc"; + else + return "GPU"; +} + +void i915gpu::start_measurement(void) +{ +} + +void i915gpu::end_measurement(void) +{ +} + + +double i915gpu::utilization(void) +{ + return get_result_value(rindex); + +} + +void create_i915_gpu(void) +{ + char filename[PATH_MAX]; + class i915gpu *gpu; + gpu_rapl_device *rapl_dev; + + pt_strcpy(filename, "/sys/kernel/debug/tracing/events/i915/i915_gem_ring_dispatch/format"); + + if (access(filename, R_OK) !=0) { + /* try an older tracepoint */ + pt_strcpy(filename, "/sys/kernel/debug/tracing/events/i915/i915_gem_request_submit/format"); + if (access(filename, R_OK) != 0) + return; + } + + register_parameter("gpu-operations"); + + gpu = new class i915gpu(); + all_devices.push_back(gpu); + + rapl_dev = new class gpu_rapl_device(gpu); + if (rapl_dev->device_present()) + all_devices.push_back(rapl_dev); +} + + + +double i915gpu::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + double child_power; + + power = 0; + factor = get_parameter_value(index, bundle); + util = get_result_value(rindex, result); + + power += util * factor / 100.0; + for (unsigned int i = 0; i < child_devices.size(); ++i) { + child_power = child_devices[i]->power_usage(result, bundle); + if ((power - child_power) > 0.0) + power -= child_power; + } + + return power; +} diff --git a/src/devices/i915-gpu.h b/src/devices/i915-gpu.h new file mode 100644 index 0000000..7653b94 --- /dev/null +++ b/src/devices/i915-gpu.h @@ -0,0 +1,58 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_i915_GPU_H +#define _INCLUDE_GUARD_i915_GPU_H + + +#include "device.h" + +class i915gpu: public device { + int index; + int rindex; + vector<device *>child_devices; + +public: + + i915gpu(); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "GPU";}; + + virtual const char * device_name(void); + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual bool show_in_list(void) {return false;}; + virtual const char * util_units(void) { return " ops/s"; }; + + virtual void add_child(device *dev_ptr) { child_devices.push_back(dev_ptr);} +}; + +extern void create_i915_gpu(void); + + +#endif
\ No newline at end of file diff --git a/src/devices/network.cpp b/src/devices/network.cpp new file mode 100644 index 0000000..8087b7f --- /dev/null +++ b/src/devices/network.cpp @@ -0,0 +1,441 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <vector> +#include <string> +#include <map> + +#include <stdio.h> +#include <sys/types.h> +#include <libgen.h> +#include <stdlib.h> +#include <unistd.h> + +#include <linux/ethtool.h> + +using namespace std; + +#include "device.h" +#include "network.h" +#include "../lib.h" +#include "../parameters/parameters.h" +#include "../process/process.h" +extern "C" { +#include "../tuning/iw.h" +} + +#include <string.h> +#include <net/if.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> +#include <unistd.h> + +static map<string, class network *> nics; + +#ifdef DISABLE_TRYCATCH + +static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + + ep->speed = (__u16)speed; + ep->speed_hi = (__u16)(speed >> 16); +} + +static inline __u32 ethtool_cmd_speed(struct ethtool_cmd *ep) +{ + return (ep->speed_hi << 16) | ep->speed; +} + +#endif + +static void do_proc_net_dev(void) +{ + static time_t last_time; + class network *dev; + ifstream file; + char line[4096]; + char *c, *c2; + + if (time(NULL) == last_time) + return; + + last_time = time(NULL); + + file.open("/proc/net/dev", ios::in); + if (!file) + return; + + file.getline(line, 4096); + file.getline(line, 4096); + + while (file) { + int i = 0; + unsigned long val = 0; + uint64_t pkt = 0; + file.getline(line, 4096); + c = strchr(line, ':'); + if (!c) + continue; + *c = 0; + c2 = c +1; + c = line; while (c && *c == ' ') c++; + /* c now points to the name of the nic */ + + dev = nics[c]; + if (!dev) + continue; + + c = c2++; + while (c != c2 && strlen(c) > 0) { + c2 = c; + val = strtoull(c, &c, 10); + i++; + if (i == 2 || i == 10) + pkt += val; + + } + dev->pkts = pkt; + } + file.close(); +} + + +network::network(const char *_name, const char *path): device() +{ + char line[4096]; + std::string filename(path); + char devname[128]; + start_up = 0; + end_up = 0; + start_speed = 0; + end_speed = 0; + start_pkts = 0; + end_pkts = 0; + pkts = 0; + valid_100 = -1; + valid_1000 = -1; + valid_high = -1; + valid_powerunsave = -1; + + pt_strcpy(sysfs_path, path); + register_sysfs_path(sysfs_path); + pt_strcpy(devname, _name); + sprintf(humanname, "nic:%s", _name); + pt_strcpy(name, devname); + + snprintf(devname, sizeof(devname), "%s-up", _name); + index_up = get_param_index(devname); + rindex_up = get_result_index(devname); + + snprintf(devname, sizeof(devname), "%s-powerunsave", _name); + index_powerunsave = get_param_index(devname); + rindex_powerunsave = get_result_index(devname); + + snprintf(devname, sizeof(devname), "%s-link-100", _name); + index_link_100 = get_param_index(devname); + rindex_link_100 = get_result_index(devname); + + snprintf(devname, sizeof(devname), "%s-link-1000", _name); + index_link_1000 = get_param_index(devname); + rindex_link_1000 = get_result_index(devname); + + snprintf(devname, sizeof(devname), "%s-link-high", _name); + index_link_high = get_param_index(devname); + rindex_link_high = get_result_index(devname); + + snprintf(devname, sizeof(devname), "%s-packets", _name); + index_pkts = get_param_index(devname); + rindex_pkts = get_result_index(devname); + + memset(line, 0, 4096); + filename.append("/device/driver"); + if (readlink(filename.c_str(), line, 4096) > 0) { + snprintf(humanname, sizeof(humanname), _("Network interface: %s (%s)"), _name, basename(line)); + }; +} + +static int net_iface_up(const char *iface) +{ + int sock; + struct ifreq ifr; + int ret; + + memset(&ifr, 0, sizeof(struct ifreq)); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock<0) + return 0; + + pt_strcpy(ifr.ifr_name, iface); + + /* Check if the interface is up */ + ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret<0) { + close(sock); + return 0; + } + + if (ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) { + close(sock); + return 1; + } + + close(sock); + + return 0; +} + +static int iface_link(const char *name) +{ + int sock; + struct ifreq ifr; + struct ethtool_value cmd; + int link; + + memset(&ifr, 0, sizeof(struct ifreq)); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock<0) + return 0; + + pt_strcpy(ifr.ifr_name, name); + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd = ETHTOOL_GLINK; + ifr.ifr_data = (caddr_t)&cmd; + ioctl(sock, SIOCETHTOOL, &ifr); + close(sock); + + link = cmd.data; + + return link; +} + + +static int iface_speed(const char *name) +{ + int sock; + struct ifreq ifr; + struct ethtool_cmd cmd; + int speed; + + memset(&ifr, 0, sizeof(struct ifreq)); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock<0) + return 0; + + pt_strcpy(ifr.ifr_name, name); + + memset(&cmd, 0, sizeof(cmd)); + + cmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (caddr_t)&cmd; + ioctl(sock, SIOCETHTOOL, &ifr); + close(sock); + + speed = ethtool_cmd_speed(&cmd); + + + if (speed > 0 && speed <= 100) + speed = 100; + if (speed > 100 && speed <= 1000) + speed = 1000; + if (speed == 65535 || !iface_link(name)) + speed = 0; /* no link */ + + return speed; +} + +void network::start_measurement(void) +{ + start_up = 1; + start_speed = 0; + end_up = 1; + end_speed = 0; + + start_speed = iface_speed(name); + + start_up = net_iface_up(name); + + do_proc_net_dev(); + start_pkts = pkts; + + gettimeofday(&before, NULL); +} + + +void network::end_measurement(void) +{ + int u_100, u_1000, u_high, u_powerunsave; + + gettimeofday(&after, NULL); + + end_speed = iface_speed(name); + end_up = net_iface_up(name); + do_proc_net_dev(); + end_pkts = pkts; + + duration = (after.tv_sec - before.tv_sec) + (after.tv_usec - before.tv_usec) / 1000000.0; + + u_100 = 0; + u_1000 = 0; + u_high = 0; + + if (start_speed == 100) + u_100 += 50; + if (start_speed == 1000) + u_1000 += 50; + if (start_speed > 1000) + u_high += 50; + if (end_speed == 100) + u_100 += 50; + if (end_speed == 1000) + u_1000 += 50; + if (end_speed > 1000) + u_high += 50; + + if (start_pkts > end_pkts) + end_pkts = start_pkts; + + u_powerunsave = 100 - 100 * get_wifi_power_saving(name); + + report_utilization(rindex_link_100, u_100); + report_utilization(rindex_link_1000, u_1000); + report_utilization(rindex_link_high, u_high); + report_utilization(rindex_up, (start_up+end_up) / 2.0); + report_utilization(rindex_pkts, (end_pkts - start_pkts)/(duration + 0.001)); + report_utilization(rindex_powerunsave, u_powerunsave); +} + + +double network::utilization(void) +{ + return (end_pkts - start_pkts) / (duration + 0.001); +} + +const char * network::device_name(void) +{ + return name; +} + +static void netdev_callback(const char *d_name) +{ + char devname[128]; + + std::string f_name("/sys/class/net/"); + if (strcmp(d_name, "lo") == 0) + return; + + f_name.append(d_name); + + snprintf(devname, sizeof(devname), "%s-up", d_name); + register_parameter(devname); + + snprintf(devname, sizeof(devname), "%s-powerunsave", d_name); + register_parameter(devname); + + snprintf(devname, sizeof(devname), "%s-link-100", d_name); + register_parameter(devname); + + snprintf(devname, sizeof(devname), "%s-link-1000", d_name); + register_parameter(devname); + + snprintf(devname, sizeof(devname), "%s-link-high", d_name); + register_parameter(devname); + + snprintf(devname, sizeof(devname), "%s-packets", d_name); + register_parameter(devname); + + network *bl = new(std::nothrow) class network(d_name, f_name.c_str()); + if (bl) { + all_devices.push_back(bl); + nics[d_name] = bl; + } +} + +void create_all_nics(callback fn) +{ + if (!fn) + fn = &netdev_callback; + process_directory("/sys/class/net/", fn); +} + +double network::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + power = 0; + factor = get_parameter_value(index_up, bundle); + util = get_result_value(rindex_up, result); + + power += util * factor; + + if (valid_100 == -1) { + valid_100 = utilization_power_valid(rindex_link_100); + valid_1000 = utilization_power_valid(rindex_link_1000); + valid_high = utilization_power_valid(rindex_link_high); + valid_powerunsave = utilization_power_valid(rindex_powerunsave); + } + + if (valid_100 > 0) { + factor = get_parameter_value(index_link_100, bundle); + util = get_result_value(rindex_link_100, result); + power += util * factor / 100; + } + + + if (valid_1000 > 0) { + factor = get_parameter_value(index_link_1000, bundle); + util = get_result_value(rindex_link_1000, result); + power += util * factor / 100; + } + + if (valid_high > 0) { + factor = get_parameter_value(index_link_high, bundle); + util = get_result_value(rindex_link_high, result); + power += util * factor / 100; + } + + if (valid_powerunsave > 0) { + factor = get_parameter_value(index_powerunsave, bundle); + util = get_result_value(rindex_powerunsave, result); + power += util * factor / 100; + } + + factor = get_parameter_value(index_pkts, bundle); + util = get_result_value(rindex_pkts, result); + if (util > 5000) + util = 5000; + + power += util * factor / 100; + + return power; +} diff --git a/src/devices/network.h b/src/devices/network.h new file mode 100644 index 0000000..7fb4cc6 --- /dev/null +++ b/src/devices/network.h @@ -0,0 +1,85 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_NETWORK_H +#define _INCLUDE_GUARD_NETWORK_H + +#include <sys/time.h> +#include <limits.h> + +#include "device.h" +#include "../parameters/parameters.h" + +class network: public device { + int start_up, end_up; + uint64_t start_pkts, end_pkts; + struct timeval before, after; + + int start_speed; /* 0 is "no link" */ + int end_speed; /* 0 is "no link" */ + + char sysfs_path[PATH_MAX]; + char name[4096]; + char humanname[4096]; + int index_up; + int rindex_up; + int index_link_100; + int rindex_link_100; + int index_link_1000; + int rindex_link_1000; + int index_link_high; + int rindex_link_high; + int index_pkts; + int rindex_pkts; + int index_powerunsave; + int rindex_powerunsave; + + int valid_100; + int valid_1000; + int valid_high; + int valid_powerunsave; +public: + uint64_t pkts; + double duration; + + network(const char *_name, const char *path); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); + virtual const char * util_units(void) { return " pkts/s"; }; + + virtual const char * class_name(void) { return "ethernet";}; + + virtual const char * device_name(void); + virtual const char * human_name(void) { return humanname; }; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int power_valid(void) { return utilization_power_valid(rindex_up) + utilization_power_valid(rindex_link_100) + utilization_power_valid(rindex_link_1000) + utilization_power_valid(rindex_link_high);}; + virtual int grouping_prio(void) { return 10; }; +}; + +extern void create_all_nics(callback fn = NULL); + +#endif diff --git a/src/devices/rfkill.cpp b/src/devices/rfkill.cpp new file mode 100644 index 0000000..99a652f --- /dev/null +++ b/src/devices/rfkill.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <libgen.h> +#include <unistd.h> +#include <limits.h> + + +using namespace std; + +#include "device.h" +#include "rfkill.h" +#include "../parameters/parameters.h" + +#include <string.h> +#include <unistd.h> + +rfkill::rfkill(char *_name, char *path): device() +{ + char line[4096]; + char filename[PATH_MAX]; + char devname[128]; + start_soft = 0; + start_hard = 0; + end_soft = 0; + end_hard = 0; + pt_strcpy(sysfs_path, path); + register_sysfs_path(sysfs_path); + snprintf(devname, sizeof(devname), "radio:%s", _name); + snprintf(humanname, sizeof(humanname), "radio:%s", _name); + pt_strcpy(name, devname); + register_parameter(devname); + index = get_param_index(devname); + rindex = get_result_index(name); + + memset(line, 0, 4096); + snprintf(filename, sizeof(filename), "%s/device/driver", path); + if (readlink(filename, line, sizeof(line)) > 0) { + snprintf(humanname, sizeof(humanname), _("Radio device: %s"), basename(line)); + } + snprintf(filename, sizeof(filename), "%s/device/device/driver", path); + if (readlink(filename, line, sizeof(line)) > 0) { + snprintf(humanname, sizeof(humanname), _("Radio device: %s"), basename(line)); + } +} + +void rfkill::start_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + start_hard = 1; + start_soft = 1; + end_hard = 1; + end_soft = 1; + + snprintf(filename, sizeof(filename), "%s/hard", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> start_hard; + } + file.close(); + + snprintf(filename, sizeof(filename), "%s/soft", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> start_soft; + } + file.close(); +} + +void rfkill::end_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + snprintf(filename, sizeof(filename), "%s/hard", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_hard; + } + file.close(); + snprintf(filename, sizeof(filename), "%s/soft", sysfs_path); + file.open(filename, ios::in); + if (file) { + file >> end_soft; + } + file.close(); + + report_utilization(name, utilization()); +} + + +double rfkill::utilization(void) +{ + double p; + int rfk; + + rfk = start_soft+end_soft; + if (rfk < start_hard+end_hard) + rfk = start_hard+end_hard; + + p = 100 - 50.0 * rfk; + + return p; +} + +const char * rfkill::device_name(void) +{ + return name; +} + +static void create_all_rfkills_callback(const char *d_name) +{ + char filename[PATH_MAX]; + char name[4096] = {0}; + class rfkill *bl; + ifstream file; + + snprintf(filename, sizeof(filename), "/sys/class/rfkill/%s/name", d_name); + strncpy(name, d_name, sizeof(name) - 1); + file.open(filename, ios::in); + if (file) { + file.getline(name, 100); + file.close(); + } + + snprintf(filename, sizeof(filename), "/sys/class/rfkill/%s", d_name); + bl = new class rfkill(name, filename); + all_devices.push_back(bl); +} + +void create_all_rfkills(void) +{ + process_directory("/sys/class/rfkill/", create_all_rfkills_callback); +} + +double rfkill::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + power = 0; + factor = get_parameter_value(index, bundle); + util = get_result_value(rindex, result); + + power += util * factor / 100.0; + + return power; +} diff --git a/src/devices/rfkill.h b/src/devices/rfkill.h new file mode 100644 index 0000000..429ba18 --- /dev/null +++ b/src/devices/rfkill.h @@ -0,0 +1,62 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_RFKILL_H +#define _INCLUDE_GUARD_RFKILL_H + +#include <limits.h> + +#include "device.h" +#include "../parameters/parameters.h" + +class rfkill: public device { + int start_soft, end_soft; + int start_hard, end_hard; + char sysfs_path[PATH_MAX]; + char name[4096]; + char humanname[4096]; + int index; + int rindex; +public: + + rfkill(char *_name, char *path); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "radio";}; + + virtual const char * device_name(void); + virtual const char * human_name(void) { return humanname; }; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int power_valid(void) { return utilization_power_valid(rindex);}; + virtual int grouping_prio(void) { return 5; }; +}; + +extern void create_all_rfkills(void); + + +#endif
\ No newline at end of file diff --git a/src/devices/runtime_pm.cpp b/src/devices/runtime_pm.cpp new file mode 100644 index 0000000..26f9d32 --- /dev/null +++ b/src/devices/runtime_pm.cpp @@ -0,0 +1,257 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "runtime_pm.h" + +#include <string.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> + +#include "../parameters/parameters.h" +#include "../lib.h" + +#include <iostream> +#include <fstream> + +runtime_pmdevice::runtime_pmdevice(const char *_name, const char *path) : device() +{ + pt_strcpy(sysfs_path, path); + register_sysfs_path(sysfs_path); + pt_strcpy(name, _name); + snprintf(humanname, sizeof(humanname), "runtime-%s", _name); + + index = get_param_index(humanname); + r_index = get_result_index(humanname); + + before_suspended_time = 0; + before_active_time = 0; + after_suspended_time = 0; + after_active_time = 0; + + register_parameter(humanname); +} + +void runtime_pmdevice::start_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + before_suspended_time = 0; + before_active_time = 0; + after_suspended_time = 0; + after_active_time = 0; + + snprintf(filename, sizeof(filename), "%s/power/runtime_suspended_time", sysfs_path); + file.open(filename, ios::in); + if (!file) + return; + file >> before_suspended_time; + file.close(); + + snprintf(filename, sizeof(filename), "%s/power/runtime_active_time", sysfs_path); + file.open(filename, ios::in); + if (!file) + return; + file >> before_active_time; + file.close(); +} + +void runtime_pmdevice::end_measurement(void) +{ + char filename[PATH_MAX]; + ifstream file; + + snprintf(filename, sizeof(filename), "%s/power/runtime_suspended_time", sysfs_path); + file.open(filename, ios::in); + if (!file) + return; + file >> after_suspended_time; + file.close(); + + snprintf(filename, sizeof(filename), "%s/power/runtime_active_time", sysfs_path); + file.open(filename, ios::in); + if (!file) + return; + file >> after_active_time; + file.close(); +} + +double runtime_pmdevice::utilization(void) /* percentage */ +{ + double d; + d = 100 * (after_active_time - before_active_time) / (0.0001 + after_active_time - before_active_time + after_suspended_time - before_suspended_time); + + if (d < 0.00) + d = 0.0; + if (d > 99.9) + d = 100.0; + return d; +} + +const char * runtime_pmdevice::device_name(void) +{ + return name; +} + +const char * runtime_pmdevice::human_name(void) +{ + return humanname; +} + + +double runtime_pmdevice::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + power = 0; + + factor = get_parameter_value(index, bundle); + util = get_result_value(r_index, result); + power += util * factor / 100.0; + + return power; +} + +void runtime_pmdevice::set_human_name(char *_name) +{ + pt_strcpy(humanname, _name); +} + + +int device_has_runtime_pm(const char *sysfs_path) +{ + char filename[PATH_MAX]; + ifstream file; + unsigned long value; + + snprintf(filename, sizeof(filename), "%s/power/runtime_suspended_time", sysfs_path); + file.open(filename, ios::in); + if (!file) + return 0; + file >> value; + file.close(); + if (value) + return 1; + + snprintf(filename, sizeof(filename), "%s/power/runtime_active_time", sysfs_path); + file.open(filename, ios::in); + if (!file) + return 0; + file >> value; + file.close(); + if (value) + return 1; + + return 0; +} + +static void do_bus(const char *bus) +{ + /* /sys/bus/pci/devices/0000\:00\:1f.0/power/runtime_suspended_time */ + + struct dirent *entry; + DIR *dir; + char filename[PATH_MAX]; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/", bus); + dir = opendir(filename); + if (!dir) + return; + while (1) { + ifstream file; + class runtime_pmdevice *dev; + entry = readdir(dir); + + if (!entry) + break; + if (entry->d_name[0] == '.') + continue; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s", bus, entry->d_name); + dev = new class runtime_pmdevice(entry->d_name, filename); + + if (strcmp(bus, "i2c") == 0) { + string devname; + char dev_name[4096]; + bool is_adapter = false; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/new_device", bus, entry->d_name); + if (access(filename, W_OK) == 0) + is_adapter = true; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/name", bus, entry->d_name); + file.open(filename, ios::in); + if (file) { + getline(file, devname); + file.close(); + } + + snprintf(dev_name, sizeof(dev_name), _("I2C %s (%s): %s"), (is_adapter ? _("Adapter") : _("Device")), entry->d_name, devname.c_str()); + dev->set_human_name(dev_name); + } + + if (strcmp(bus, "pci") == 0) { + uint16_t vendor = 0, device = 0; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/vendor", bus, entry->d_name); + + file.open(filename, ios::in); + if (file) { + file >> hex >> vendor; + file.close(); + } + + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/device", bus, entry->d_name); + file.open(filename, ios::in); + if (file) { + file >> hex >> device; + file.close(); + } + + if (vendor && device) { + char devname[4096]; + snprintf(devname, sizeof(devname), _("PCI Device: %s"), + pci_id_to_name(vendor, device, filename, 4095)); + dev->set_human_name(devname); + } + } + all_devices.push_back(dev); + } + closedir(dir); +} + +void create_all_runtime_pm_devices(void) +{ + do_bus("pci"); + do_bus("spi"); + do_bus("platform"); + do_bus("i2c"); +} diff --git a/src/devices/runtime_pm.h b/src/devices/runtime_pm.h new file mode 100644 index 0000000..77bf398 --- /dev/null +++ b/src/devices/runtime_pm.h @@ -0,0 +1,66 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_RUNTIMEPM_H +#define _INCLUDE_GUARD_RUNTIMEPM_H + +#include <limits.h> + +#include "device.h" +#include "../parameters/parameters.h" + +class runtime_pmdevice: public device { + uint64_t before_suspended_time, before_active_time; + uint64_t after_suspended_time, after_active_time; + char sysfs_path[PATH_MAX]; + char name[4096]; + char humanname[4096]; + int index; + int r_index; +public: + + runtime_pmdevice(const char *_name, const char *path); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "runtime_pm";}; + + virtual const char * device_name(void); + virtual const char * human_name(void); + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int power_valid(void) { return utilization_power_valid(r_index);}; + + void set_human_name(char *name); + virtual int grouping_prio(void) { return 1; }; +}; + +extern void create_all_runtime_pm_devices(void); + +extern int device_has_runtime_pm(const char *sysfs_path); + + +#endif
\ No newline at end of file diff --git a/src/devices/thinkpad-fan.cpp b/src/devices/thinkpad-fan.cpp new file mode 100644 index 0000000..8e2ce53 --- /dev/null +++ b/src/devices/thinkpad-fan.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <math.h> +#include <unistd.h> +#include <limits.h> + +#include "../lib.h" + + +#include "device.h" +#include "thinkpad-fan.h" +#include "../parameters/parameters.h" +#include "../process/powerconsumer.h" + +#include <string.h> +#include <unistd.h> + +thinkpad_fan::thinkpad_fan(): device() +{ + start_rate = 0; + end_rate = 0; + fan_index = get_param_index("thinkpad-fan"); + fansqr_index = get_param_index("thinkpad-fan-sqr"); + fancub_index = get_param_index("thinkpad-fan-cub"); + r_index = get_result_index("thinkpad-fan"); + register_sysfs_path("/sys/devices/platform/thinkpad_hwmon"); +} + +void thinkpad_fan::start_measurement(void) +{ + /* read the rpms of the fan */ + start_rate = read_sysfs("/sys/devices/platform/thinkpad_hwmon/fan1_input"); +} + +void thinkpad_fan::end_measurement(void) +{ + end_rate = read_sysfs("/sys/devices/platform/thinkpad_hwmon/fan1_input"); + + report_utilization("thinkpad-fan", utilization()); +} + + +double thinkpad_fan::utilization(void) +{ + return (start_rate+end_rate) / 2; +} + +void create_thinkpad_fan(void) +{ + char filename[PATH_MAX]; + class thinkpad_fan *fan; + + pt_strcpy(filename, "/sys/devices/platform/thinkpad_hwmon/fan1_input"); + + if (access(filename, R_OK) !=0) + return; + + register_parameter("thinkpad-fan", 10); + register_parameter("thinkpad-fan-sqr", 5); + register_parameter("thinkpad-fan-cub", 10); + + fan = new class thinkpad_fan(); + all_devices.push_back(fan); +} + + + +double thinkpad_fan::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + + power = 0; + util = get_result_value(r_index, result); + + if (util < 0) + util = 0; + + + /* physics dictact that fan power goes cubic with the rpms, but there's also a linear component for friction*/ + factor = get_parameter_value(fancub_index, bundle); + power += factor * pow(util / 3600.0, 3); + + factor = get_parameter_value(fansqr_index, bundle) - 5.0; + power += factor * pow(util / 3600.0, 2); + + factor = get_parameter_value(fan_index, bundle) - 10.0; + power += util / 5000.0 * factor; + + if (power <= 0.0) + power = 0.0; + + return power; +} diff --git a/src/devices/thinkpad-fan.h b/src/devices/thinkpad-fan.h new file mode 100644 index 0000000..34c4c43 --- /dev/null +++ b/src/devices/thinkpad-fan.h @@ -0,0 +1,58 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_THINKPAD_FAN_H +#define _INCLUDE_GUARD_THINKPAD_FAN_H + + +#include "device.h" +#include "../parameters/parameters.h" + +class thinkpad_fan: public device { + double start_rate, end_rate; + int fan_index, fansqr_index, fancub_index; + int r_index; +public: + + thinkpad_fan(); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "fan";}; + + virtual const char * device_name(void) { return "Fan-1";}; + virtual const char * human_name(void) { return "Laptop fan";}; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual const char * util_units(void) { return " rpm"; }; + virtual int power_valid(void) { return utilization_power_valid(r_index);}; + virtual int grouping_prio(void) { return 1; }; +}; + +extern void create_thinkpad_fan(void); + + +#endif
\ No newline at end of file diff --git a/src/devices/thinkpad-light.cpp b/src/devices/thinkpad-light.cpp new file mode 100644 index 0000000..d047ab3 --- /dev/null +++ b/src/devices/thinkpad-light.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <math.h> +#include <unistd.h> +#include <limits.h> + +#include "../lib.h" + + +#include "device.h" +#include "thinkpad-light.h" +#include "../parameters/parameters.h" +#include "../process/powerconsumer.h" + +#include <string.h> +#include <unistd.h> + +thinkpad_light::thinkpad_light(): device() +{ + start_rate = 0; + end_rate = 0; + light_index = get_param_index("thinkpad-light"); + r_index = get_result_index("thinkpad-light"); + register_sysfs_path("/sys/devices/platform/thinkpad_acpi/leds/tpacpi::thinklight"); +} + +void thinkpad_light::start_measurement(void) +{ + /* read the rpms of the light */ + start_rate = read_sysfs("/sys/devices/platform/thinkpad_acpi/leds/tpacpi::thinklight/brightness"); +} + +void thinkpad_light::end_measurement(void) +{ + end_rate = read_sysfs("/sys/devices/platform/thinkpad_acpi/leds/tpacpi::thinklight/brightness"); + + report_utilization("thinkpad-light", utilization()); +} + + +double thinkpad_light::utilization(void) +{ + return (start_rate+end_rate) / 2.55 / 2.0; +} + +void create_thinkpad_light(void) +{ + char filename[PATH_MAX]; + class thinkpad_light *light; + + pt_strcpy(filename, "/sys/devices/platform/thinkpad_acpi/leds/tpacpi::thinklight/brightness"); + + if (access(filename, R_OK) !=0) + return; + + register_parameter("thinkpad-light", 10); + + light = new class thinkpad_light(); + all_devices.push_back(light); +} + + + +double thinkpad_light::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + + power = 0; + util = get_result_value(r_index, result); + + if (util < 0) + util = 0; + + + factor = get_parameter_value(light_index, bundle) - 10.0; + power += util / 100.0 * factor; + + if (power <= 0.0) + power = 0.0; + + return power; +} diff --git a/src/devices/thinkpad-light.h b/src/devices/thinkpad-light.h new file mode 100644 index 0000000..64a1789 --- /dev/null +++ b/src/devices/thinkpad-light.h @@ -0,0 +1,58 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_THINKPAD_LIGHT_H +#define _INCLUDE_GUARD_THINKPAD_LIGHT_H + + +#include "device.h" +#include "../parameters/parameters.h" + +class thinkpad_light: public device { + double start_rate, end_rate; + int light_index; + int r_index; +public: + + thinkpad_light(); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "light";}; + + virtual const char * device_name(void) { return "Light-1";}; + virtual const char * human_name(void) { return "Thinkpad light";}; + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual const char * util_units(void) { return "%"; }; + virtual int power_valid(void) { return utilization_power_valid(r_index);}; + virtual int grouping_prio(void) { return 1; }; +}; + +extern void create_thinkpad_light(void); + + +#endif
\ No newline at end of file diff --git a/src/devices/usb.cpp b/src/devices/usb.cpp new file mode 100644 index 0000000..5042699 --- /dev/null +++ b/src/devices/usb.cpp @@ -0,0 +1,254 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "usb.h" + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <limits.h> + +#include "../lib.h" +#include "../devlist.h" +#include "../parameters/parameters.h" + +#include <iostream> +#include <fstream> + +usbdevice::usbdevice(const char *_name, const char *path, const char *devid): device() +{ + ifstream file; + char filename[PATH_MAX]; + char vendor[4096]; + char product[4096]; + + pt_strcpy(sysfs_path, path); + register_sysfs_path(sysfs_path); + pt_strcpy(name, _name); + pt_strcpy(devname, devid); + snprintf(humanname, sizeof(humanname), _("USB device: %s"), pretty_print(devid, vendor, 4096)); + active_before = 0; + active_after = 0; + connected_before = 0; + connected_after = 0; + busnum = 0; + devnum = 0; + + index = get_param_index(devname); + r_index = get_result_index(name); + rootport = 0; + cached_valid = 0; + + + /* root ports and hubs should count as 0 power ... their activity is derived */ + snprintf(filename, sizeof(filename), "%s/bDeviceClass", path); + file.open(filename, ios::in); + if (file) { + int dclass = 0; + + file >> dclass; + file.close(); + if (dclass == 9) + rootport = 1; + }; + + vendor[0] = 0; + product[0] = 0; + snprintf(filename, sizeof(filename), "%s/manufacturer", path); + file.open(filename, ios::in); + if (file) { + file.getline(vendor, 2047); + if (strstr(vendor, "Linux ")) + vendor[0] = 0; + file.close(); + }; + snprintf(filename, sizeof(filename), "%s/product", path); + file.open(filename, ios::in); + if (file) { + file.getline(product, 2040); + file.close(); + }; + if (strlen(vendor) && strlen(product)) + snprintf(humanname, sizeof(humanname), _("USB device: %s (%s)"), product, vendor); + else if (strlen(product)) + snprintf(humanname, sizeof(humanname), _("USB device: %s"), product); + else if (strlen(vendor)) + snprintf(humanname, sizeof(humanname), _("USB device: %s"), vendor); + + /* For usbdevfs we need bus number and device number */ + snprintf(filename, sizeof(filename), "%s/busnum", path); + file.open(filename, ios::in); + if (file) { + + file >> busnum; + file.close(); + }; + snprintf(filename, sizeof(filename), "%s/devnum", path); + file.open(filename, ios::in); + if (file) { + + file >> devnum; + file.close(); + }; +} + + + +void usbdevice::start_measurement(void) +{ + ifstream file; + char fullpath[PATH_MAX]; + + active_before = 0; + active_after = 0; + connected_before = 0; + connected_after = 0; + + snprintf(fullpath, sizeof(fullpath), "%s/power/active_duration", sysfs_path); + file.open(fullpath, ios::in); + if (file) { + file >> active_before; + } + file.close(); + + snprintf(fullpath, sizeof(fullpath), "%s/power/connected_duration", sysfs_path); + file.open(fullpath, ios::in); + if (file) { + file >> connected_before; + } + file.close(); +} + +void usbdevice::end_measurement(void) +{ + ifstream file; + char fullpath[PATH_MAX]; + + snprintf(fullpath, sizeof(fullpath), "%s/power/active_duration", sysfs_path); + file.open(fullpath, ios::in); + if (file) { + file >> active_after; + } + file.close(); + + snprintf(fullpath, sizeof(fullpath), "%s/power/connected_duration", sysfs_path); + file.open(fullpath, ios::in); + if (file) { + file >> connected_after; + } + file.close(); + report_utilization(name, utilization()); + +} + +double usbdevice::utilization(void) /* percentage */ +{ + double d; + d = 100.0 * (active_after - active_before) / (0.01 + connected_after - connected_before); + if (d < 0.0) + d = 0.0; + if (d > 99.8) + d = 100.0; + return d; +} + +const char * usbdevice::device_name(void) +{ + return name; +} + +const char * usbdevice::human_name(void) +{ + return humanname; +} + +void usbdevice::register_power_with_devlist(struct result_bundle *results, struct parameter_bundle *bundle) +{ + char devfs_name[1024]; + + snprintf(devfs_name, sizeof(devfs_name), "usb/%03d/%03d", busnum, + devnum); + + register_devpower(devfs_name, power_usage(results, bundle), this); +} + +double usbdevice::power_usage(struct result_bundle *result, struct parameter_bundle *bundle) +{ + double power; + double factor; + double util; + + if (rootport || !cached_valid) + return 0.0; + + + power = 0; + factor = get_parameter_value(index, bundle); + util = get_result_value(r_index, result); + + power += util * factor / 100.0; + + return power; +} + +static void create_all_usb_devices_callback(const char *d_name) +{ + char filename[PATH_MAX]; + ifstream file; + class usbdevice *usb; + char device_name[PATH_MAX]; + char vendorid[64], devid[64]; + char devid_name[4096]; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s", d_name); + snprintf(device_name, sizeof(device_name), "%s/power/active_duration", filename); + if (access(device_name, R_OK) != 0) + return; + + snprintf(device_name, sizeof(device_name), "%s/idVendor", filename); + file.open(device_name, ios::in); + if (file) + file.getline(vendorid, 64); + file.close(); + snprintf(device_name, sizeof(device_name), "%s/idProduct", filename); + file.open(device_name, ios::in); + if (file) + file.getline(devid, 64); + file.close(); + + snprintf(devid_name, sizeof(devid_name), "usb-device-%s-%s", vendorid, devid); + snprintf(device_name, sizeof(device_name), "usb-device-%s-%s-%s", d_name, vendorid, devid); + if (result_device_exists(device_name)) + return; + + usb = new class usbdevice(device_name, filename, devid_name); + all_devices.push_back(usb); + register_parameter(devid_name, 0.1); +} + +void create_all_usb_devices(void) +{ + process_directory("/sys/bus/usb/devices/", create_all_usb_devices_callback); +} diff --git a/src/devices/usb.h b/src/devices/usb.h new file mode 100644 index 0000000..7e76a55 --- /dev/null +++ b/src/devices/usb.h @@ -0,0 +1,67 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_USB_H +#define _INCLUDE_GUARD_USB_H + +#include <limits.h> + +#include "device.h" +#include "../parameters/parameters.h" + +class usbdevice: public device { + int active_before, active_after; + int connected_before, connected_after; + char sysfs_path[PATH_MAX]; + char name[4096]; + char devname[4096]; + char humanname[4096]; + int index; + int r_index; + int rootport; + int busnum; + int devnum; +public: + + usbdevice(const char *_name, const char *path, const char *devid); + + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double utilization(void); /* percentage */ + + virtual const char * class_name(void) { return "usb";}; + + virtual const char * device_name(void); + virtual const char * human_name(void); + virtual void register_power_with_devlist(struct result_bundle *results, struct parameter_bundle *bundle); + virtual double power_usage(struct result_bundle *result, struct parameter_bundle *bundle); + virtual int power_valid(void) { return utilization_power_valid(r_index);}; + virtual int grouping_prio(void) { return 4; }; +}; + +extern void create_all_usb_devices(void); + + +#endif
\ No newline at end of file diff --git a/src/devlist.cpp b/src/devlist.cpp new file mode 100644 index 0000000..70ae70e --- /dev/null +++ b/src/devlist.cpp @@ -0,0 +1,351 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +/* + * Code to track centrally which process has what /dev files open + */ +#include <iostream> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +#include <vector> +#include <algorithm> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> + +using namespace std; + +#include "devlist.h" +#include "lib.h" +#include "report/report.h" +#include "report/report-maker.h" +#include "report/report-data-html.h" + +#include "process/process.h" +#include "devices/device.h" +/* + +* collect list of processes that have devices open + (alternate between before and after lists) + +* charge a "surcharge" to a device (sub)string + - count how many openers + - add proprotion to each process that has it open + +* list of devices + power they use for processing + +*/ + +static vector<struct devuser *> one; +static vector<struct devuser *> two; +static vector<struct devpower *> devpower; + +static int phase; +/* + * 0 - one = before, two = after + * 1 - one = after, two = before + */ + +void clean_open_devices() +{ + unsigned int i=0; + + for (i = 0; i < one.size(); i++) { + free(one[i]); + } + + for (i = 0; i < two.size(); i++) { + free(two[i]); + } + + for (i = 0; i < devpower.size(); i++){ + free(devpower[i]); + } +} + +void collect_open_devices(void) +{ + struct dirent *entry; + DIR *dir; + char filename[PATH_MAX]; + char link[PATH_MAX]; + unsigned int i; + vector<struct devuser *> *target; + + if (phase == 1) + target = &one; + else + target = &two; + + for (i = 0; i < target->size(); i++) { + free((*target)[i]); + } + target->resize(0); + + + dir = opendir("/proc/"); + if (!dir) + return; + while (1) { + struct dirent *entry2; + DIR *dir2; + entry = readdir(dir); + + if (!entry) + break; + if (entry->d_name[0] == '.') + continue; + if (strcmp(entry->d_name, "self") == 0) + continue; + + snprintf(filename, sizeof(filename), "/proc/%s/fd/", entry->d_name); + + dir2 = opendir(filename); + if (!dir2) + continue; + while (1) { + int ret; + struct devuser * dev; + entry2 = readdir(dir2); + if (!entry2) + break; + if (!isdigit(entry2->d_name[0])) + continue; + snprintf(filename, sizeof(filename), "/proc/%s/fd/%s", entry->d_name, entry2->d_name); + memset(link, 0, sizeof(link)); + ret = readlink(filename, link, sizeof(link) - 1); + if (ret < 0) + continue; + + if (strcmp(link, "/dev/null") == 0) + continue; + if (strcmp(link, "/dev/.udev/queue.bin") == 0) + continue; + if (strcmp(link, "/dev/initctl") == 0) + continue; + if (strcmp(link, "/dev/ptmx") == 0) + continue; + if (strstr(link, "/dev/pts/")) + continue; + if (strstr(link, "/dev/shm/")) + continue; + if (strstr(link, "/dev/urandom")) + continue; + if (strstr(link, "/dev/tty")) + continue; + + if (strncmp(link, "/dev", 4)==0) { + dev = (struct devuser *)malloc(sizeof(struct devuser)); + if (!dev) + continue; + dev->pid = strtoull(entry->d_name, NULL, 10); + strncpy(dev->device, link, 251); + dev->device[251] = '\0'; + strncpy(dev->comm, read_sysfs_string("/proc/%s/comm", entry->d_name).c_str(), 31); + dev->comm[31] = '\0'; + target->push_back(dev); + + } + } + closedir(dir2); + } + closedir(dir); + + if (phase) + phase = 0; + else + phase = 1; +} + + +/* returns 0 if no process is identified as having the device open and a value > 0 otherwise */ +int charge_device_to_openers(const char *devstring, double power, class device *_dev) +{ + unsigned int i; + int openers = 0; + class process *proc; + /* 1. count the number of openers */ + + for (i = 0; i < one.size(); i++) { + if (strstr(one[i]->device, devstring)) + openers++; + } + for (i = 0; i < two.size(); i++) { + if (strstr(two[i]->device, devstring)) + openers++; + } + + + /* 2. divide power by this number */ + + if (!openers) + return 0; + power = power / openers; + + + /* 3. for each process that has it open, add the charge */ + + for (i = 0; i < one.size(); i++) + if (strstr(one[i]->device, devstring)) { + proc = find_create_process(one[i]->comm, one[i]->pid); + if (proc) { + proc->power_charge += power; + if (strlen(_dev->guilty) < 2000 && strstr(_dev->guilty, one[i]->comm) == NULL) { + strcat(_dev->guilty, one[i]->comm); + strcat(_dev->guilty, " "); + } + } + } + + for (i = 0; i < two.size(); i++) + if (strstr(two[i]->device, devstring)) { + proc = find_create_process(two[i]->comm, two[i]->pid); + if (proc) { + proc->power_charge += power; + if (strlen(_dev->guilty) < 2000 && strstr(_dev->guilty, two[i]->comm) == NULL) { + strcat(_dev->guilty, two[i]->comm); + strcat(_dev->guilty, " "); + } + } + } + + + + return openers; +} + +void clear_devpower(void) +{ + unsigned int i; + + for (i = 0; i < devpower.size(); i++) { + devpower[i]->power = 0.0; + devpower[i]->dev->guilty[0] = 0; + } +} + +void register_devpower(const char *devstring, double power, class device *_dev) +{ + unsigned int i; + struct devpower *dev = NULL; + + for (i = 0; i < devpower.size(); i++) + if (strcmp(devstring, devpower[i]->device) == 0) { + dev = devpower[i]; + } + + if (!dev) { + dev = (struct devpower *)malloc(sizeof (struct devpower)); + pt_strcpy(dev->device, devstring); + dev->power = 0.0; + devpower.push_back(dev); + } + dev->dev = _dev; + dev->power = power; +} + +void run_devpower_list(void) +{ + unsigned int i; + + for (i = 0; i < devpower.size(); i++) { + int ret; + ret = charge_device_to_openers(devpower[i]->device, devpower[i]->power, devpower[i]->dev); + if (ret) + devpower[i]->dev->hide = true; + else + devpower[i]->dev->hide = false; + + } + +} + +static bool devlist_sort(struct devuser * i, struct devuser * j) +{ + if (i->pid != j->pid) + return i->pid < j->pid; + + return (strcmp(i->device, j->device)< 0); +} + +void report_show_open_devices(void) +{ + vector<struct devuser *> *target; + unsigned int i; + char prev[128], proc[128]; + int idx, cols, rows; + + prev[0] = 0; + if (phase == 1) + target = &one; + else + target = &two; + + if (target->size() == 0) + return; + + + /* Set Table attributes, rows, and cols */ + table_attributes std_table_css; + cols = 2; + idx = cols; + rows= target->size() + 1; + init_std_table_attr(&std_table_css, rows, cols); + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Set array of data in row Major order */ + string *process_data = new string[cols * rows]; + + sort(target->begin(), target->end(), devlist_sort); + process_data[0]=__("Process"); + process_data[1]=__("Device"); + + for (i = 0; i < target->size(); i++) { + proc[0] = 0; + if (strcmp(prev, (*target)[i]->comm) != 0) + snprintf(proc, sizeof(proc), "%s", (*target)[i]->comm); + + process_data[idx]=string(proc); + idx+=1; + process_data[idx]=string((*target)[i]->device); + idx+=1; + snprintf(prev, sizeof(prev), "%s", (*target)[i]->comm); + } + + /* Report Output */ + /* No div attribute here inherits from device power report */ + report.add_title(&title_attr, __("Process Device Activity")); + report.add_table(process_data, &std_table_css); + delete [] process_data; + report.end_div(); +} diff --git a/src/devlist.h b/src/devlist.h new file mode 100644 index 0000000..35dfd6c --- /dev/null +++ b/src/devlist.h @@ -0,0 +1,27 @@ +#ifndef __INCLUDE_GUARD_DEVLIST_H__ +#define __INCLUDE_GUARD_DEVLIST_H__ + +struct devuser { + unsigned int pid; + char comm[32]; + char device[252]; +}; + +class device; + +struct devpower { + char device[252]; + double power; + class device *dev; +}; + +extern void clean_open_devices(); +extern void collect_open_devices(void); + +extern void clear_devpower(void); +extern void register_devpower(const char *devstring, double power, class device *dev); +extern void run_devpower_list(void); + +extern void report_show_open_devices(void); + +#endif diff --git a/src/display.cpp b/src/display.cpp new file mode 100644 index 0000000..cc03919 --- /dev/null +++ b/src/display.cpp @@ -0,0 +1,342 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "display.h" +#include "lib.h" + +#include <ncurses.h> + + +#include <vector> +#include <map> +#include <string> +#include <string.h> + +using namespace std; + +static int display = 0; + +vector<string> tab_names; +map<string, class tab_window *> tab_windows; +map<string, string> tab_translations; + +map<string, string> bottom_lines; + +void create_tab(const string &name, const string &translation, class tab_window *w, string bottom_line) +{ + if (!w) + w = new(class tab_window); + + w->win = newpad(1000,1000); + tab_names.push_back(name); + tab_windows[name] = w; + tab_translations[name] = translation; + bottom_lines[name] = bottom_line; +} + + +void init_display(void) +{ + initscr(); + start_color(); + + cbreak(); /* character at a time input */ + noecho(); /* don't show the user input */ + keypad(stdscr, TRUE); /* enable cursor/etc keys */ + + use_default_colors(); + + create_tab("Overview", _("Overview")); + create_tab("Idle stats", _("Idle stats")); + create_tab("Frequency stats", _("Frequency stats")); + create_tab("Device stats", _("Device stats")); + + display = 1; +} + +void reset_display(void) +{ + if (!display) + return; + + keypad(stdscr, FALSE); + echo(); + nocbreak(); + + resetterm(); +} + + +WINDOW *tab_bar = NULL; +WINDOW *bottom_line = NULL; + +static int current_tab; + +void show_tab(unsigned int tab) +{ + class tab_window *win; + unsigned int i; + int tab_pos = 17; + const char *c; + + if (!display) + return; + + if (tab_bar) { + delwin(tab_bar); + tab_bar = NULL; + } + + if (bottom_line) { + delwin(bottom_line); + bottom_line = NULL; + } + + tab_bar = newwin(1, 0, 0, 0); + + wattrset(tab_bar, A_REVERSE); + mvwprintw(tab_bar, 0,0, "%120s", ""); + mvwprintw(tab_bar, 0,0, "PowerTOP %s", PACKAGE_VERSION); + + bottom_line = newwin(1, 0, LINES-1, 0); + wattrset(bottom_line, A_REVERSE); + mvwprintw(bottom_line, 0,0, "%120s", ""); + + c = bottom_lines[tab_names[tab]].c_str(); + if (c && strlen(c) > 0) + mvwprintw(bottom_line, 0,0, "%s", c); + else + mvwprintw(bottom_line, 0, 0, + "<ESC> %s | <TAB> / <Shift + TAB> %s | ", _("Exit"), + _("Navigate")); + + + current_tab = tab; + + for (i = 0; i < tab_names.size(); i++) { + if (i == tab) + wattrset(tab_bar, A_NORMAL); + else + wattrset(tab_bar, A_REVERSE); + mvwprintw(tab_bar, 0, tab_pos, " %s ", tab_translations[tab_names[i]].c_str()); + + tab_pos += 3 + tab_names[i].length(); + } + + wrefresh(tab_bar); + wrefresh(bottom_line); + + win = tab_windows[tab_names[tab]]; + if (!win) + return; + + prefresh(win->win, win->ypad_pos, win->xpad_pos, 1, 0, LINES - 3, COLS - 1); +} + +WINDOW *get_ncurses_win(const char *name) +{ + class tab_window *w; + WINDOW *win; + + w= tab_windows[name]; + if (!w) + return NULL; + + win = w->win; + + return win; +} + +WINDOW *get_ncurses_win(int nr) +{ + class tab_window *w; + WINDOW *win; + + w= tab_windows[tab_names[nr]]; + if (!w) + return NULL; + + win = w->win; + + return win; +} + +WINDOW *get_ncurses_win(const string &name) +{ + return get_ncurses_win(name.c_str()); +} + +void show_prev_tab(void) +{ + class tab_window *w; + + if (!display) + return; + w = tab_windows[tab_names[current_tab]]; + if (w) + w->hide(); + + current_tab --; + if (current_tab < 0) + current_tab = tab_names.size() - 1; + + w = tab_windows[tab_names[current_tab]]; + if (w) + w->expose(); + + show_tab(current_tab); +} + +void show_next_tab(void) +{ + class tab_window *w; + + if (!display) + return; + + w = tab_windows[tab_names[current_tab]]; + if (w) + w->hide(); + + current_tab ++; + if (current_tab >= (int)tab_names.size()) + current_tab = 0; + + w = tab_windows[tab_names[current_tab]]; + if (w) + w->expose(); + + show_tab(current_tab); +} + +void show_cur_tab(void) +{ + if (!display) + return; + show_tab(current_tab); +} + +void cursor_down(void) +{ + class tab_window *w; + + w = tab_windows[tab_names[current_tab]]; + if (w) { + if (w->ypad_pos < 1000) { + if (tab_names[current_tab] == "Tunables" || tab_names[current_tab] == "WakeUp") { + if ((w->cursor_pos + 7) >= LINES) { + prefresh(w->win, ++w->ypad_pos, w->xpad_pos, + 1, 0, LINES - 3, COLS - 1); + } + w->cursor_down(); + } else { + prefresh(w->win, ++w->ypad_pos, w->xpad_pos, + 1, 0, LINES - 3, COLS - 1); + } + } + } + + show_cur_tab(); +} + +void cursor_up(void) +{ + class tab_window *w; + + w = tab_windows[tab_names[current_tab]]; + + if (w) { + w->cursor_up(); + if(w->ypad_pos > 0) { + prefresh(w->win, --w->ypad_pos, w->xpad_pos, + 1, 0, LINES - 3, COLS - 1); + } + } + + show_cur_tab(); +} + +void cursor_left(void) +{ + class tab_window *w; + + w = tab_windows[tab_names[current_tab]]; + + if (w) { + if (w->xpad_pos > 0) { + prefresh(w->win, w->ypad_pos,--w->xpad_pos, + 1, 0, LINES - 3, COLS - 1); + } + } +} + +void cursor_right(void) +{ + class tab_window *w; + + w = tab_windows[tab_names[current_tab]]; + + if (w) { + if (w->xpad_pos < 1000) { + prefresh(w->win, w->ypad_pos, ++w->xpad_pos, + 1, 0, LINES - 3, COLS - 1); + } + } +} + +void cursor_enter(void) +{ + class tab_window *w; + + w = tab_windows[tab_names[current_tab]]; + + if (w) { + w->cursor_enter(); + w->repaint(); + } + show_cur_tab(); +} + +void window_refresh() +{ + class tab_window *w; + + w = tab_windows[tab_names[current_tab]]; + + if (w) { + w->ypad_pos = 0; + w->xpad_pos = 0; + w->window_refresh(); + w->repaint(); + } + + show_cur_tab(); +} + +int ncurses_initialized(void) +{ + if (display) + return 1; + return 0; +} diff --git a/src/display.h b/src/display.h new file mode 100644 index 0000000..9db87ec --- /dev/null +++ b/src/display.h @@ -0,0 +1,99 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_DISPLAY_H_ +#define __INCLUDE_GUARD_DISPLAY_H_ + + +#include <map> +#include <string> +#include <ncurses.h> + +using namespace std; + +extern void init_display(void); +extern void reset_display(void); +extern int ncurses_initialized(void); +extern void show_tab(unsigned int tab); +extern void show_next_tab(void); +extern void show_prev_tab(void); +extern void show_cur_tab(void); +extern void cursor_up(void); +extern void cursor_down(void); +extern void cursor_right(void); +extern void cursor_left(void); +extern void cursor_enter(void); +extern void window_refresh(void); + +class tab_window { +public: + int cursor_pos; + int cursor_max; + short int xpad_pos, ypad_pos; + WINDOW *win; + + tab_window() { + cursor_pos = 0; + cursor_max = 0; + xpad_pos =0; + ypad_pos = 0; + win = NULL; + } + + virtual void cursor_down(void) { + if (cursor_pos < cursor_max) + cursor_pos++; + repaint(); + } ; + virtual void cursor_up(void) { + if (cursor_pos > 0) + cursor_pos--; + repaint(); + }; + virtual void cursor_left(void) { }; + virtual void cursor_right(void) { }; + + virtual void cursor_enter(void) { }; + virtual void window_refresh() { }; + + virtual void repaint(void) { }; + virtual void expose(void) { cursor_pos = 0; repaint();}; + virtual void hide(void) { }; + + virtual ~tab_window() + { + delwin(win); + win = NULL; + } +}; + +extern map<string, class tab_window *> tab_windows; + +WINDOW *get_ncurses_win(const char *name); +WINDOW *get_ncurses_win(const string &name); +WINDOW *get_ncurses_win(int nr); + +void create_tab(const string &name, const string &translation, class tab_window *w = NULL, string bottom_line = ""); + +#endif diff --git a/src/lib.cpp b/src/lib.cpp new file mode 100644 index 0000000..5cd1c4a --- /dev/null +++ b/src/lib.cpp @@ -0,0 +1,597 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + * Peter Anvin + */ +#include <map> +#include <string.h> +#include <iostream> +#include <utility> +#include <iostream> +#include <fstream> +#include <string> +#include <ctype.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <limits.h> + +#include "lib.h" + +#ifndef HAVE_NO_PCI +extern "C" { +#include <pci/pci.h> +} +#endif + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <locale.h> +#include <libintl.h> +#include <limits> +#include <math.h> +#include <ncurses.h> +#include <fcntl.h> +#include <glob.h> + +static int kallsyms_read = 0; + +int is_turbo(uint64_t freq, uint64_t max, uint64_t maxmo) +{ + if (freq != max) + return 0; + if (maxmo + 1000 != max) + return 0; + return 1; +} + +double percentage(double F) +{ + F = F * 100.0; +// if (F > 100.0) +// F = 100.0; + if (F < 0.0) + F = 0.0; + return F; +} + +char *hz_to_human(unsigned long hz, char *buffer, int digits) +{ + unsigned long long Hz; + + buffer[0] = 0; + + Hz = hz; + + /* default: just put the Number in */ + sprintf(buffer,"%9lli", Hz); + + if (Hz>1000) { + if (digits == 2) + sprintf(buffer, "%4lli MHz", (Hz+500)/1000); + else + sprintf(buffer, "%6lli MHz", (Hz+500)/1000); + } + + if (Hz>1500000) { + if (digits == 2) + sprintf(buffer, "%4.2f GHz", (Hz+5000.0)/1000000); + else + sprintf(buffer, "%3.1f GHz", (Hz+5000.0)/1000000); + } + + return buffer; +} + +using namespace std; + +map<unsigned long, string> kallsyms; + +static void read_kallsyms(void) +{ + ifstream file; + char line[1024]; + kallsyms_read = 1; + + file.open("/proc/kallsyms", ios::in); + + while (file) { + char *c = NULL, *c2 = NULL; + unsigned long address = 0; + memset(line, 0, 1024); + file.getline(line, 1024); + c = strchr(line, ' '); + if (!c) + continue; + *c = 0; + c2 = c + 1; + if (*c2) c2++; + if (*c2) c2++; + + address = strtoull(line, NULL, 16); + c = strchr(c2, '\t'); + if (c) + *c = 0; + if (address != 0) + kallsyms[address] = c2; + } + file.close(); +} + +const char *kernel_function(uint64_t address) +{ + const char *c; + if (!kallsyms_read) + read_kallsyms(); + + c = kallsyms[address].c_str(); + if (!c) + return ""; + return c; +} + +static int _max_cpu; +int get_max_cpu(void) +{ + return _max_cpu; +} + +void set_max_cpu(int cpu) +{ + if (cpu > _max_cpu) + _max_cpu = cpu; +} + + +void write_sysfs(const string &filename, const string &value) +{ + ofstream file; + + file.open(filename.c_str(), ios::out); + if (!file) + return; + try + { + file << value; + file.close(); + } catch (std::exception &exc) { + return; + } +} + +int read_sysfs(const string &filename, bool *ok) +{ + ifstream file; + int i; + + file.open(filename.c_str(), ios::in); + if (!file) { + if (ok) + *ok = false; + return 0; + } + try + { + file >> i; + if (ok) + *ok = true; + } catch (std::exception &exc) { + if (ok) + *ok = false; + i = 0; + } + file.close(); + return i; +} + +string read_sysfs_string(const string &filename) +{ + ifstream file; + char content[4096]; + char *c; + + file.open(filename.c_str(), ios::in); + if (!file) + return ""; + try + { + file.getline(content, 4096); + file.close(); + c = strchr(content, '\n'); + if (c) + *c = 0; + } catch (std::exception &exc) { + file.close(); + return ""; + } + return content; +} + +string read_sysfs_string(const char *format, const char *param) +{ + ifstream file; + char content[4096]; + char *c; + char filename[PATH_MAX]; + + + snprintf(filename, sizeof(filename), format, param); + + file.open(filename, ios::in); + if (!file) + return ""; + try + { + file.getline(content, 4096); + file.close(); + c = strchr(content, '\n'); + if (c) + *c = 0; + } catch (std::exception &exc) { + file.close(); + return ""; + } + return content; +} + +void align_string(char *buffer, size_t min_sz, size_t max_sz) +{ + size_t sz; + + /** mbsrtowcs() allows NULL dst and zero sz, + * comparing to mbstowcs(), which causes undefined + * behaviour under given circumstances*/ + + /* start with mbsrtowcs() local mbstate_t * and + * NULL dst pointer*/ + sz = mbsrtowcs(NULL, (const char **)&buffer, max_sz, NULL); + if (sz == (size_t)-1) { + buffer[min_sz] = 0x00; + return; + } + while (sz < min_sz) { + strcat(buffer, " "); + sz++; + } +} + +void format_watts(double W, char *buffer, unsigned int len) +{ + buffer[0] = 0; + char buf[32]; + sprintf(buffer, _("%7sW"), fmt_prefix(W, buf)); + + if (W < 0.0001) + sprintf(buffer, _(" 0 mW")); + + align_string(buffer, len, len); +} + +#ifndef HAVE_NO_PCI +static struct pci_access *pci_access; + +char *pci_id_to_name(uint16_t vendor, uint16_t device, char *buffer, int len) +{ + char *ret; + + buffer[0] = 0; + + if (!pci_access) { + pci_access = pci_alloc(); + pci_init(pci_access); + } + + ret = pci_lookup_name(pci_access, buffer, len, PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, vendor, device); + + return ret; +} + +void end_pci_access(void) +{ + if (pci_access) + pci_free_name_list(pci_access); +} + +#else + +char *pci_id_to_name(uint16_t vendor, uint16_t device, char *buffer, int len) +{ + return NULL; +} + +void end_pci_access(void) +{ +} + +#endif /* HAVE_NO_PCI */ + +int utf_ok = -1; + + + +/* pretty print numbers while limiting the precision */ +char *fmt_prefix(double n, char *buf) +{ + static const char prefixes[] = "yzafpnum kMGTPEZY"; + char tmpbuf[16]; + int omag, npfx; + char *p, *q, pfx, *c; + int i; + + if (utf_ok == -1) { + char *g; + g = getenv("LANG"); + if (g && strstr(g, "UTF-8")) + utf_ok = 1; + else + utf_ok = 0; + } + + p = buf; + + *p = ' '; + if (n < 0.0) { + *p = '-'; + n = -n; + p++; + } + + snprintf(tmpbuf, sizeof tmpbuf, "%.2e", n); + c = strchr(tmpbuf, 'e'); + if (!c) { + sprintf(buf, "NaN"); + return buf; + } + omag = atoi(c + 1); + + npfx = ((omag + 27) / 3) - (27/3); + omag = (omag + 27) % 3; + + q = tmpbuf; + if (omag == 2) + omag = -1; + + for (i = 0; i < 3; i++) { + while (!isdigit(*q)) + q++; + *p++ = *q++; + if (i == omag) + *p++ = '.'; + } + *p++ = ' '; + + pfx = prefixes[npfx + 8]; + + if (pfx == ' ') { + /* do nothing */ + } else if (pfx == 'u' && utf_ok > 0) { + strcpy(p, "µ"); /* Mu is a multibyte sequence */ + while (*p) + p++; + } else { + *p++ = pfx; + } + *p = '\0'; + + return buf; +} + +static map<string, string> pretty_prints; +static int pretty_print_init = 0; + +static void init_pretty_print(void) +{ + pretty_prints["[12] i8042"] = _("PS/2 Touchpad / Keyboard / Mouse"); + pretty_prints["ahci"] = _("SATA controller"); + pretty_prints["usb-device-8087-0020"] = _("Intel built in USB hub"); + + pretty_print_init = 1; +} + + +char *pretty_print(const char *str, char *buf, int len) +{ + const char *p; + + if (!pretty_print_init) + init_pretty_print(); + + p = pretty_prints[str].c_str(); + + if (strlen(p) == 0) + p = str; + + snprintf(buf, len, "%s", p); + + if (len) + buf[len - 1] = 0; + return buf; +} + +int equals(double a, double b) +{ + return fabs(a - b) <= std::numeric_limits<double>::epsilon(); +} + +void process_directory(const char *d_name, callback fn) +{ + struct dirent *entry; + DIR *dir; + dir = opendir(d_name); + if (!dir) + return; + while (1) { + entry = readdir(dir); + if (!entry) + break; + if (entry->d_name[0] == '.') + continue; + fn(entry->d_name); + } + closedir(dir); +} + +void process_glob(const char *d_glob, callback fn) +{ + glob_t g; + size_t c; + + switch (glob(d_glob, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) { + case GLOB_NOSPACE: + fprintf(stderr,_("glob returned GLOB_NOSPACE\n")); + globfree(&g); + return; + case GLOB_ABORTED: + fprintf(stderr,_("glob returned GLOB_ABORTED\n")); + globfree(&g); + return; + case GLOB_NOMATCH: + fprintf(stderr,_("glob returned GLOB_NOMATCH\n")); + globfree(&g); + return; + } + + for (c=0; c < g.gl_pathc; c++) { + fn(g.gl_pathv[c]); + } + globfree(&g); +} + +int get_user_input(char *buf, unsigned sz) +{ + fflush(stdout); + echo(); + /* Upon successful completion, these functions return OK. Otherwise, they return ERR. */ + int ret = getnstr(buf, sz); + noecho(); + fflush(stdout); + /* to distinguish between getnstr error and empty line */ + return ret || strlen(buf); +} + +int read_msr(int cpu, uint64_t offset, uint64_t *value) +{ +#if defined(__i386__) || defined(__x86_64__) + ssize_t retval; + uint64_t msr; + int fd; + char msr_path[256]; + + snprintf(msr_path, sizeof(msr_path), "/dev/cpu/%d/msr", cpu); + + if (access(msr_path, R_OK) != 0){ + snprintf(msr_path, sizeof(msr_path), "/dev/msr%d", cpu); + + if (access(msr_path, R_OK) != 0){ + fprintf(stderr, + _("Model-specific registers (MSR)\ + not found (try enabling CONFIG_X86_MSR).\n")); + return -1; + } + } + + fd = open(msr_path, O_RDONLY); + if (fd < 0) + return -1; + retval = pread(fd, &msr, sizeof msr, offset); + close(fd); + if (retval != sizeof msr) { + return -1; + } + *value = msr; + + return retval; +#else + return -1; +#endif +} + +int write_msr(int cpu, uint64_t offset, uint64_t value) +{ +#if defined(__i386__) || defined(__x86_64__) + ssize_t retval; + int fd; + char msr_path[256]; + + snprintf(msr_path, sizeof(msr_path), "/dev/cpu/%d/msr", cpu); + + if (access(msr_path, R_OK) != 0){ + snprintf(msr_path, sizeof(msr_path), "/dev/msr%d", cpu); + + if (access(msr_path, R_OK) != 0){ + fprintf(stderr, + _("Model-specific registers (MSR)\ + not found (try enabling CONFIG_X86_MSR).\n")); + return -1; + } + } + + fd = open(msr_path, O_WRONLY); + if (fd < 0) + return -1; + retval = pwrite(fd, &value, sizeof value, offset); + close(fd); + if (retval != sizeof value) { + return -1; + } + + return retval; +#else + return -1; +#endif +} + +#define UI_NOTIFY_BUFF_SZ 2048 + +void ui_notify_user_ncurses(const char *frmt, ...) +{ + char notify[UI_NOTIFY_BUFF_SZ]; + va_list list; + + start_color(); + init_pair(1, COLOR_BLACK, COLOR_WHITE); + attron(COLOR_PAIR(1)); + va_start(list, frmt); + /* there is no ncurses *print() function which takes + * int x, int y and va_list, this is why we use temp + * buffer */ + vsnprintf(notify, UI_NOTIFY_BUFF_SZ - 1, frmt, list); + va_end(list); + mvprintw(1, 0, "%s", notify); + attroff(COLOR_PAIR(1)); +} + +void ui_notify_user_console(const char *frmt, ...) +{ + va_list list; + + va_start(list, frmt); + vprintf(frmt, list); + va_end(list); +} diff --git a/src/lib.h b/src/lib.h new file mode 100644 index 0000000..6d85eb6 --- /dev/null +++ b/src/lib.h @@ -0,0 +1,95 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef INCLUDE_GUARD_LIB_H +#define INCLUDE_GUARD_LIB_H + +#include <libintl.h> +#include <stdint.h> +#include <stdlib.h> +#include <cstring> + +/* Include only for Automake builds */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef ENABLE_NLS +#define _(STRING) gettext(STRING) +#else +#define _(STRING) (STRING) +#endif + +extern int is_turbo(uint64_t freq, uint64_t max, uint64_t maxmo); + +extern int get_max_cpu(void); +extern void set_max_cpu(int cpu); + +extern double percentage(double F); +extern char *hz_to_human(unsigned long hz, char *buffer, int digits = 2); + + +extern const char *kernel_function(uint64_t address); + + + +#include <ctime> +#include <string> +using namespace std; + +extern void write_sysfs(const string &filename, const string &value); +extern int read_sysfs(const string &filename, bool *ok = NULL); +extern string read_sysfs_string(const string &filename); +extern string read_sysfs_string(const char *format, const char *param); + +extern void format_watts(double W, char *buffer, unsigned int len); + +extern char *pci_id_to_name(uint16_t vendor, uint16_t device, char *buffer, int len); +extern void end_pci_access(void); + + +extern char *fmt_prefix(double n, char *buf); +extern char *pretty_print(const char *str, char *buf, int len); +extern int equals(double a, double b); + +template<size_t N> void pt_strcpy(char (&d)[N], const char *s) +{ + strncpy(d, s, N); + d[N-1] = '\0'; +} + +typedef void (*callback)(const char*); +extern void process_directory(const char *d_name, callback fn); +extern void process_glob(const char *glob, callback fn); +extern int utf_ok; +extern int get_user_input(char *buf, unsigned sz); +extern int read_msr(int cpu, uint64_t offset, uint64_t *value); +extern int write_msr(int cpu, uint64_t offset, uint64_t value); + +extern void align_string(char *buffer, size_t min_sz, size_t max_sz); + +extern void ui_notify_user_ncurses(const char *frmt, ...); +extern void ui_notify_user_console(const char *frmt, ...); +extern void (*ui_notify_user) (const char *frmt, ...); +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..45aaa58 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,578 @@ +/* + * Copyright 2010, Intel Corporation + * + * This 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 + * or just google for it. + * + * getopt code is taken from "The GNU C Library" reference manual, + * section 24.2 "Parsing program options using getopt" + * http://www.gnu.org/s/libc/manual/html_node/Getopt-Long-Option-Example.html + * Manual published under the terms of the Free Documentation License. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <getopt.h> +#include <unistd.h> +#include <locale.h> +#include <sys/resource.h> +#include <limits.h> +#include <pthread.h> + +#include "cpu/cpu.h" +#include "process/process.h" +#include "perf/perf.h" +#include "perf/perf_bundle.h" +#include "lib.h" +#include "../config.h" + + +#include "devices/device.h" +#include "devices/devfreq.h" +#include "devices/usb.h" +#include "devices/ahci.h" +#include "measurement/measurement.h" +#include "parameters/parameters.h" +#include "calibrate/calibrate.h" + + +#include "tuning/tuning.h" +#include "wakeup/wakeup.h" + +#include "display.h" +#include "devlist.h" +#include "report/report.h" + +#define DEBUGFS_MAGIC 0x64626720 + +#define NR_OPEN_DEF 1024 * 1024 + +int debug_learning = 0; +unsigned time_out = 20; +int leave_powertop = 0; +void (*ui_notify_user) (const char *frmt, ...); + +enum { + OPT_AUTO_TUNE = CHAR_MAX + 1, + OPT_EXTECH, + OPT_DEBUG +}; + +static const struct option long_options[] = +{ + /* These options set a flag. */ + {"auto-tune", no_argument, NULL, OPT_AUTO_TUNE}, + {"calibrate", no_argument, NULL, 'c'}, + {"csv", optional_argument, NULL, 'C'}, + {"debug", no_argument, &debug_learning, OPT_DEBUG}, + {"extech", optional_argument, NULL, OPT_EXTECH}, + {"html", optional_argument, NULL, 'r'}, + {"iteration", optional_argument, NULL, 'i'}, + {"quiet", no_argument, NULL, 'q'}, + {"sample", optional_argument, NULL, 's'}, + {"time", optional_argument, NULL, 't'}, + {"workload", optional_argument, NULL, 'w'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} +}; + +static void print_version() +{ + printf(_("PowerTOP version " PACKAGE_VERSION "\n")); +} + +static bool set_refresh_timeout() +{ + static char buf[4]; + mvprintw(1, 0, "%s (currently %u): ", _("Set refresh time out"), time_out); + memset(buf, '\0', sizeof(buf)); + get_user_input(buf, sizeof(buf) - 1); + show_tab(0); + unsigned time = strtoul(buf, NULL, 0); + if (!time) return 0; + if (time > 32) time = 32; + time_out = time; + return 1; +} + +static void print_usage() +{ + printf("%s\n\n", _("Usage: powertop [OPTIONS]")); + printf(" --auto-tune\t %s\n", _("sets all tunable options to their GOOD setting")); + printf(" -c, --calibrate\t %s\n", _("runs powertop in calibration mode")); + printf(" -C, --csv%s\t %s\n", _("[=filename]"), _("generate a csv report")); + printf(" --debug\t\t %s\n", _("run in \"debug\" mode")); + printf(" --extech%s\t %s\n", _("[=devnode]"), _("uses an Extech Power Analyzer for measurements")); + printf(" -r, --html%s\t %s\n", _("[=filename]"), _("generate a html report")); + printf(" -i, --iteration%s\n", _("[=iterations] number of times to run each test")); + printf(" -q, --quiet\t\t %s\n", _("suppress stderr output")); + printf(" -s, --sample%s\t %s\n", _("[=seconds]"), _("interval for power consumption measurement")); + printf(" -t, --time%s\t %s\n", _("[=seconds]"), _("generate a report for 'x' seconds")); + printf(" -w, --workload%s %s\n", _("[=workload]"), _("file to execute for workload")); + printf(" -V, --version\t\t %s\n", _("print version information")); + printf(" -h, --help\t\t %s\n", _("print this help menu")); + printf("\n"); + printf("%s\n\n", _("For more help please refer to the 'man 8 powertop'")); +} + +static void do_sleep(int seconds) +{ + time_t target; + int delta; + + if (!ncurses_initialized()) { + sleep(seconds); + return; + } + target = time(NULL) + seconds; + delta = seconds; + do { + int c; + usleep(6000); + halfdelay(delta * 10); + + c = getch(); + switch (c) { + case KEY_BTAB: + show_prev_tab(); + break; + case '\t': + show_next_tab(); + break; + case KEY_RIGHT: + cursor_right(); + break; + case KEY_LEFT: + cursor_left(); + break; + case KEY_NPAGE: + case KEY_DOWN: + cursor_down(); + break; + case KEY_PPAGE: + case KEY_UP: + cursor_up(); + break; + case ' ': + case '\n': + cursor_enter(); + break; + case 's': + if (set_refresh_timeout()) + return; + break; + case 'r': + window_refresh(); + return; + case KEY_EXIT: + case 'q': + case 27: // Escape + leave_powertop = 1; + return; + } + + delta = target - time(NULL); + if (delta <= 0) + break; + + } while (1); +} + +extern "C" { + static volatile bool end_thread; + void* measure_background_thread(void *arg) + { + int sleep_time = *((int *) arg); + while (!end_thread) { + do_sleep(sleep_time); + global_sample_power(); + } + return 0; + } +} + +void one_measurement(int seconds, int sample_interval, char *workload) +{ + create_all_usb_devices(); + start_power_measurement(); + devices_start_measurement(); + start_devfreq_measurement(); + start_process_measurement(); + start_cpu_measurement(); + + if (workload && workload[0]) { + pthread_t thread = 0UL; + end_thread = false; + if (pthread_create(&thread, NULL, measure_background_thread, &sample_interval)) + fprintf(stderr, "ERROR: workload measurement thread creation failed\n"); + + if (system(workload)) + fprintf(stderr, _("Unknown issue running workload!\n")); + + if (thread) + { + end_thread = true; + pthread_join( thread, NULL); + } + global_sample_power(); + } else { + while (seconds > 0) + { + do_sleep(sample_interval > seconds ? seconds : sample_interval); + seconds -= sample_interval; + global_sample_power(); + } + } + end_cpu_measurement(); + end_process_measurement(); + collect_open_devices(); + end_devfreq_measurement(); + devices_end_measurement(); + end_power_measurement(); + + process_cpu_data(); + process_process_data(); + + /* output stats */ + process_update_display(); + report_summary(); + w_display_cpu_cstates(); + w_display_cpu_pstates(); + if (reporttype != REPORT_OFF) { + report_display_cpu_cstates(); + report_display_cpu_pstates(); + } + report_process_update_display(); + tuning_update_display(); + wakeup_update_display(); + end_process_data(); + + global_power(); + compute_bundle(); + + show_report_devices(); + report_show_open_devices(); + + report_devices(); + display_devfreq_devices(); + report_devfreq_devices(); + ahci_create_device_stats_table(); + store_results(measurement_time); + end_cpu_data(); +} + +void out_of_memory() +{ + reset_display(); + printf("%s...\n",_("PowerTOP is out of memory. PowerTOP is Aborting")); + abort(); +} + +void make_report(int time, char *workload, int iterations, int sample_interval, char *file) +{ + + /* one to warm up everything */ + fprintf(stderr, _("Preparing to take measurements\n")); + utf_ok = 0; + one_measurement(1, sample_interval, NULL); + + if (!workload[0]) + fprintf(stderr, _("Taking %d measurement(s) for a duration of %d second(s) each.\n"),iterations,time); + else + fprintf(stderr, _("Measuring workload %s.\n"), workload); + for (int i=0; i != iterations; i++){ + init_report_output(file, iterations); + initialize_tuning(); + initialize_wakeup(); + /* and then the real measurement */ + one_measurement(time, sample_interval, workload); + report_show_tunables(); + report_show_wakeup(); + finish_report_output(); + clear_tuning(); + } + /* and wrap up */ + learn_parameters(50, 0); + save_all_results("saved_results.powertop"); + save_parameters("saved_parameters.powertop"); + end_pci_access(); + exit(0); +} + +static void checkroot() { + int uid; + uid = getuid(); + + if (uid != 0) { + printf(_("PowerTOP " PACKAGE_VERSION " must be run with root privileges.\n")); + printf(_("exiting...\n")); + exit(EXIT_FAILURE); + } + +} + +static int get_nr_open(void) { + int nr_open = NR_OPEN_DEF; + ifstream file; + + file.open("/proc/sys/fs/nr_open", ios::in); + if (file) { + file >> nr_open; + file.close(); + } + return nr_open; +} + +static void powertop_init(int auto_tune) +{ + static char initialized = 0; + int ret; + struct statfs st_fs; + struct rlimit rlmt; + + if (initialized) + return; + + checkroot(); + + rlmt.rlim_cur = rlmt.rlim_max = get_nr_open(); + setrlimit (RLIMIT_NOFILE, &rlmt); + + if (system("/sbin/modprobe cpufreq_stats > /dev/null 2>&1")) + fprintf(stderr, _("modprobe cpufreq_stats failed\n")); +#if defined(__i386__) || defined(__x86_64__) + if (system("/sbin/modprobe msr > /dev/null 2>&1")) + fprintf(stderr, _("modprobe msr failed\n")); +#endif + statfs("/sys/kernel/debug", &st_fs); + + if (st_fs.f_type != (long) DEBUGFS_MAGIC) { + if (access("/bin/mount", X_OK) == 0) { + ret = system("/bin/mount -t debugfs debugfs /sys/kernel/debug > /dev/null 2>&1"); + } else { + ret = system("mount -t debugfs debugfs /sys/kernel/debug > /dev/null 2>&1"); + } + if (ret != 0) { + if (!auto_tune) { + fprintf(stderr, _("Failed to mount debugfs!\n")); + fprintf(stderr, _("exiting...\n")); + exit(EXIT_FAILURE); + } else { + fprintf(stderr, _("Failed to mount debugfs!\n")); + fprintf(stderr, _("Should still be able to auto tune...\n")); + } + } + } + + srand(time(NULL)); + + if (access("/var/cache/", W_OK) == 0) + mkdir("/var/cache/powertop", 0600); + else + mkdir("/data/local/powertop", 0600); + + load_results("saved_results.powertop"); + load_parameters("saved_parameters.powertop"); + + enumerate_cpus(); + create_all_devices(); + create_all_devfreq_devices(); + detect_power_meters(); + + register_parameter("base power", 100, 0.5); + register_parameter("cpu-wakeups", 39.5); + register_parameter("cpu-consumption", 1.56); + register_parameter("gpu-operations", 0.5576); + register_parameter("disk-operations-hard", 0.2); + register_parameter("disk-operations", 0.0); + register_parameter("xwakes", 0.1); + + load_parameters("saved_parameters.powertop"); + + initialized = 1; +} + +void clean_shutdown() +{ + close_results(); + clean_open_devices(); + clear_all_devices(); + clear_all_devfreq(); + clear_all_cpus(); + + return; +} + + +int main(int argc, char **argv) +{ + int option_index; + int c; + char filename[PATH_MAX]; + char workload[PATH_MAX] = {0}; + int iterations = 1, auto_tune = 0, sample_interval = 5; + + set_new_handler(out_of_memory); + + setlocale (LC_ALL, ""); + +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +#endif + ui_notify_user = ui_notify_user_ncurses; + while (1) { /* parse commandline options */ + c = getopt_long(argc, argv, "cC::r::i:qt:w:Vh", long_options, &option_index); + /* Detect the end of the options. */ + if (c == -1) + break; + switch (c) { + case OPT_AUTO_TUNE: + auto_tune = 1; + leave_powertop = 1; + ui_notify_user = ui_notify_user_console; + break; + case 'c': + powertop_init(0); + calibrate(); + break; + case 'C': /* csv report */ + reporttype = REPORT_CSV; + snprintf(filename, sizeof(filename), "%s", optarg ? optarg : "powertop.csv"); + if (!strlen(filename)) + { + fprintf(stderr, _("Invalid CSV filename\n")); + exit(1); + } + break; + case OPT_DEBUG: + /* implemented using getopt_long(3) flag */ + break; + case OPT_EXTECH: /* Extech power analyzer support */ + checkroot(); + extech_power_meter(optarg ? optarg : "/dev/ttyUSB0"); + break; + case 'r': /* html report */ + reporttype = REPORT_HTML; + snprintf(filename, sizeof(filename), "%s", optarg ? optarg : "powertop.html"); + if (!strlen(filename)) + { + fprintf(stderr, _("Invalid HTML filename\n")); + exit(1); + } + break; + case 'i': + iterations = (optarg ? atoi(optarg) : 1); + break; + case 'q': + if (freopen("/dev/null", "a", stderr)) + fprintf(stderr, _("Quiet mode failed!\n")); + break; + case 's': + sample_interval = (optarg ? atoi(optarg) : 5); + break; + case 't': + time_out = (optarg ? atoi(optarg) : 20); + break; + case 'w': /* measure workload */ + snprintf(workload, sizeof(workload), "%s", optarg ? optarg : ""); + break; + case 'V': + print_version(); + exit(0); + break; + case 'h': + print_usage(); + exit(0); + break; + case '?': /* Unknown option */ + /* getopt_long already printed an error message. */ + exit(1); + break; + } + } + + powertop_init(auto_tune); + + if (reporttype != REPORT_OFF) + make_report(time_out, workload, iterations, sample_interval, filename); + + if (debug_learning) + printf("Learning debugging enabled\n"); + + learn_parameters(250, 0); + save_parameters("saved_parameters.powertop"); + + + if (debug_learning) { + learn_parameters(1000, 1); + dump_parameter_bundle(); + end_pci_access(); + exit(0); + } + if (!auto_tune) + init_display(); + + initialize_devfreq(); + initialize_tuning(); + initialize_wakeup(); + /* first one is short to not let the user wait too long */ + one_measurement(1, sample_interval, NULL); + + if (!auto_tune) { + tuning_update_display(); + show_tab(0); + } else { + auto_toggle_tuning(); + } + + while (!leave_powertop) { + if (!auto_tune) + show_cur_tab(); + one_measurement(time_out, sample_interval, NULL); + learn_parameters(15, 0); + } + if (!auto_tune) + endwin(); + fprintf(stderr, "%s\n", _("Leaving PowerTOP")); + + end_process_data(); + clear_process_data(); + end_cpu_data(); + clear_cpu_data(); + + save_all_results("saved_results.powertop"); + save_parameters("saved_parameters.powertop"); + learn_parameters(500, 0); + save_parameters("saved_parameters.powertop"); + end_pci_access(); + clear_tuning(); + reset_display(); + + clean_shutdown(); + + return 0; +} diff --git a/src/measurement/acpi.cpp b/src/measurement/acpi.cpp new file mode 100644 index 0000000..572852c --- /dev/null +++ b/src/measurement/acpi.cpp @@ -0,0 +1,213 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "measurement.h" +#include "acpi.h" +#include <iostream> +#include <fstream> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include "../lib.h" + +using namespace std; + +acpi_power_meter::acpi_power_meter(const char *acpi_name) +{ + rate = 0.0; + capacity = 0.0; + voltage = 0.0; + pt_strcpy(battery_name, acpi_name); +} + +/* +present: yes +capacity state: ok +charging state: discharging +present rate: 8580 mW +remaining capacity: 34110 mWh +present voltage: 12001 mV +*/ + +void acpi_power_meter::measure(void) +{ + char filename[PATH_MAX]; + char line[4096]; + ifstream file; + + double _rate = 0; + double _capacity = 0; + double _voltage = 0; + + char rate_units[16]; + char capacity_units[16]; + char voltage_units[16]; + + rate_units[0] = 0; + capacity_units[0] = 0; + voltage_units[0] = 0; + + rate = 0; + voltage = 0; + capacity = 0; + + snprintf(filename, sizeof(filename), "/proc/acpi/battery/%s/state", battery_name); + + file.open(filename, ios::in); + if (!file) + return; + + while (file) { + char *c; + file.getline(line, sizeof(line)); + + if (strstr(line, "present:") && (strstr(line, "yes") == NULL)) { + return; + } + if (strstr(line, "charging state:") && (strstr(line, "discharging") == NULL)) { + return; /* not discharging */ + } + if (strstr(line, "present rate:")) { + c = strchr(line, ':'); + c++; + while (*c == ' ') c++; + _rate = strtoull(c, NULL, 10); + c = strchr(c, ' '); + if (c) { + c++; + pt_strcpy(rate_units, c); + } else { + _rate = 0; + strcpy(rate_units, "Unknown"); + } + + } + if (strstr(line, "remaining capacity:")) { + c = strchr(line, ':'); + c++; + while (*c == ' ') c++; + _capacity = strtoull(c, NULL, 10); + c = strchr(c, ' '); + if (c) { + c++; + pt_strcpy(capacity_units, c); + } else { + _capacity = 0; + strcpy(capacity_units, "Unknown"); + } + } + if (strstr(line, "present voltage:")) { + c = strchr(line, ':'); + c++; + while (*c == ' ') c++; + _voltage = strtoull(c, NULL, 10); + c = strchr(c, ' '); + if (c) { + c++; + pt_strcpy(voltage_units, c); + } else { + _voltage = 0; + strcpy(voltage_units, "Unknown"); + } + } + } + file.close(); + + /* BIOS report random crack-inspired units. Lets try to get to the Si-system units */ + + if (strcmp(voltage_units, "mV") == 0) { + _voltage = _voltage / 1000.0; + strcpy(voltage_units, "V"); + } + + if (strcmp(rate_units, "mW") == 0) { + _rate = _rate / 1000.0; + strcpy(rate_units, "W"); + } + + if (strcmp(rate_units, "mA") == 0) { + _rate = _rate / 1000.0; + strcpy(rate_units, "A"); + } + + if (strcmp(capacity_units, "mAh") == 0) { + _capacity = _capacity / 1000.0; + strcpy(capacity_units, "Ah"); + } + if (strcmp(capacity_units, "mWh") == 0) { + _capacity = _capacity / 1000.0; + strcpy(capacity_units, "Wh"); + } + if (strcmp(capacity_units, "Wh") == 0) { + _capacity = _capacity * 3600.0; + strcpy(capacity_units, "J"); + } + + + if (strcmp(capacity_units, "Ah") == 0 && strcmp(voltage_units, "V") == 0) { + _capacity = _capacity * 3600.0 * _voltage; + strcpy(capacity_units, "J"); + } + + if (strcmp(rate_units, "A") == 0 && strcmp(voltage_units, "V")==0 ) { + _rate = _rate * _voltage; + strcpy(rate_units, "W"); + } + + + + + if (strcmp(capacity_units, "J") == 0) + capacity = _capacity; + else + capacity = 0.0; + + if (strcmp(rate_units, "W")==0) + rate = _rate; + else + rate = 0.0; + + if (strcmp(voltage_units, "V")==0) + voltage = _voltage; + else + voltage = 0.0; +} + + +void acpi_power_meter::end_measurement(void) +{ + measure(); +} + +void acpi_power_meter::start_measurement(void) +{ + /* ACPI battery state is a lagging indication, lets only measure at the end */ +} + + +double acpi_power_meter::power(void) +{ + return rate; +} diff --git a/src/measurement/acpi.h b/src/measurement/acpi.h new file mode 100644 index 0000000..25bbe04 --- /dev/null +++ b/src/measurement/acpi.h @@ -0,0 +1,46 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_ACPI_H +#define __INCLUDE_GUARD_ACPI_H + +#include "measurement.h" + +class acpi_power_meter: public power_meter { + char battery_name[256]; + + double capacity; + double rate; + double voltage; + void measure(void); +public: + acpi_power_meter(const char *_battery_name); + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double power(void); + virtual double dev_capacity(void) { return capacity; }; +}; + +#endif diff --git a/src/measurement/extech.cpp b/src/measurement/extech.cpp new file mode 100644 index 0000000..867aca7 --- /dev/null +++ b/src/measurement/extech.cpp @@ -0,0 +1,354 @@ +/* + * extech - Program for controlling the extech Device + * This file is part of PowerTOP + * + * Based on earlier client by Patrick Mochel for Wattsup Pro device + * Copyright (c) 2005 Patrick Mochel + * Copyright (c) 2006 Intel Corporation + * Copyright (c) 2011 Intel Corporation + * + * Authors: + * Patrick Mochel + * Venkatesh Pallipadi + * Arjan van de Ven + * + * Thanks to Rajiv Kapoor for finding out the DTR, RTS line bits issue below + * Without that this program would never work. + * + * + * 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 + * or just google for it. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <termios.h> +#include <ctype.h> +#include <getopt.h> +#include <time.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <sys/stat.h> + +#include "measurement.h" +#include "extech.h" +#include "../lib.h" +#include <iostream> +#include <fstream> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +using namespace std; + +struct packet { + char buf[256]; + char op[32]; + double watts; + int len; +}; + + +static int open_device(const char *device_name) +{ + struct stat s; + int ret; + + ret = stat(device_name, &s); + if (ret < 0) + return -1; + + if (!S_ISCHR(s.st_mode)) + return -1; + + ret = access(device_name, R_OK | W_OK); + if (ret) + return -1; + + ret = open(device_name, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (ret < 0) + return -1; + + return ret; +} + + +static int setup_serial_device(int dev_fd) +{ + struct termios t; + int ret; + int flgs; + + ret = tcgetattr(dev_fd, &t); + if (ret) + return ret; + + cfmakeraw(&t); + cfsetispeed(&t, B9600); + cfsetospeed(&t, B9600); + tcflush(dev_fd, TCIFLUSH); + + t.c_iflag = IGNPAR; + t.c_cflag = B9600 | CS8 | CREAD | CLOCAL; + t.c_oflag = 0; + t.c_lflag = 0; + t.c_cc[VMIN] = 2; + t.c_cc[VTIME] = 0; + + t.c_iflag &= ~(IXON | IXOFF | IXANY); + t.c_oflag &= ~(IXON | IXOFF | IXANY); + + ret = tcsetattr(dev_fd, TCSANOW, &t); + + if (ret) + return ret; + + /* + * Root caused by Rajiv Kapoor. Without this extech reads + * will fail + */ + + /* get DTR and RTS line bits settings */ + ioctl(dev_fd, TIOCMGET, &flgs); + + /* set DTR to 1 and RTS to 0 */ + flgs |= TIOCM_DTR; + flgs &= ~TIOCM_RTS; + ioctl(dev_fd, TIOCMSET, &flgs); + + return 0; +} + + +static unsigned int decode_extech_value(unsigned char byt3, unsigned char byt4, char *a) +{ + unsigned int input = ((unsigned int)byt4 << 8) + byt3; + unsigned int i; + unsigned int idx; + unsigned char revnum[] = { 0x0, 0x8, 0x4, 0xc, + 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, + 0x3, 0xb, 0x7, 0xf}; + unsigned char revdec[] = { 0x0, 0x2, 0x1, 0x3}; + + unsigned int digit_map[] = {0x2, 0x3c, 0x3c0, 0x3c00}; + unsigned int digit_shift[] = {1, 2, 6, 10}; + + unsigned int sign; + unsigned int decimal; + + /* this is basically BCD encoded floating point... but kinda weird */ + + decimal = (input & 0xc000) >> 14; + decimal = revdec[decimal]; + + sign = input & 0x1; + + idx = 0; + if (sign) + a[idx++] = '+'; + else + a[idx++] = '-'; + + /* first digit is only one or zero */ + a[idx] = '0'; + if ((input & digit_map[0]) >> digit_shift[0]) + a[idx] += 1; + + idx++; + /* Reverse the remaining three digits and store in the array */ + for (i = 1; i < 4; i++) { + int dig = ((input & digit_map[i]) >> digit_shift[i]); + dig = revnum[dig]; + if (dig > 0xa) + goto error_exit; + + a[idx++] = '0' + dig; + } + + /* Fit the decimal point where appropriate */ + for (i = 0; i < decimal; i++) + a[idx - i] = a[idx - i - 1]; + + a[idx - decimal] = '.'; + a[++idx] = '0'; + a[++idx] = '\0'; + + return 0; +error_exit: + return -1; +} + +static int parse_packet(struct packet * p) +{ + int i; + int ret; + + p->buf[p->len] = '\0'; + + /* + * First character in 5 character block should be '02' + * Fifth character in 5 character block should be '03' + */ + for (i = 0; i < 4; i++) { + if (p->buf[i * 0] != 2 || p->buf[i * 0 + 4] != 3) { + printf("Invalid packet\n"); + return -1; + } + } + + for (i = 0; i < 1; i++) { + ret = decode_extech_value(p->buf[5 * i + 2], + p->buf[5 * i + 3], + &(p->op[8 * i])); + if (ret) { + printf("Invalid packet, conversion failed\n"); + return -1; + } + p->watts = strtod( &(p->op[8 * i]), NULL); + } + return 0; +} + + +static double extech_read(int fd) +{ + struct packet p; + fd_set read_fd; + struct timeval tv; + int ret; + + if (fd < 0) + return 0.0; + + FD_ZERO(&read_fd); + FD_SET(fd, &read_fd); + + tv.tv_sec = 0; + tv.tv_usec = 500000; + + memset(&p, 0, sizeof(p)); + + ret = select(fd + 1, &read_fd, NULL, NULL, &tv); + if (ret <= 0) + return -1; + + ret = read(fd, &p.buf, 250); + if (ret < 0) + return ret; + p.len = ret; + + if (!parse_packet(&p)) + return p.watts; + + return -1000.0; +} + +extech_power_meter::extech_power_meter(const char *extech_name) +{ + rate = 0.0; + pt_strcpy(dev_name, extech_name); + int ret; + + fd = open_device(dev_name); + if (fd < 0) + return; + + ret = setup_serial_device(fd); + if (ret) { + close(fd); + fd = -1; + return; + } +} + + +void extech_power_meter::measure(void) +{ + /* trigger the extech to send data */ + if(write(fd, " ", 1) == -1) + printf("Error: %s\n", strerror(errno)); + + rate = extech_read(fd); + +} + +void extech_power_meter::sample(void) +{ + ssize_t ret; + struct timespec tv; + + tv.tv_sec = 0; + tv.tv_nsec = 200000000; + while (!end_thread) { + nanosleep(&tv, NULL); + /* trigger the extech to send data */ + ret = write(fd, " ", 1); + if (ret < 0) + continue; + + sum += extech_read(fd); + samples++; + + } +} + +extern "C" +{ + void* thread_proc(void *arg) + { + class extech_power_meter *parent; + parent = (class extech_power_meter*)arg; + parent->sample(); + return 0; + } +} + +void extech_power_meter::end_measurement(void) +{ + end_thread = 1; + pthread_join( thread, NULL); + if (samples){ + rate = sum / samples; + } + else + measure(); +} + +void extech_power_meter::start_measurement(void) +{ + end_thread = 0; + sum = samples = 0; + + if (pthread_create(&thread, NULL, thread_proc, this)) + fprintf(stderr, "ERROR: extech measurement thread creation failed\n"); + +} + + +double extech_power_meter::power(void) +{ + return rate; +} diff --git a/src/measurement/extech.h b/src/measurement/extech.h new file mode 100644 index 0000000..b7b330a --- /dev/null +++ b/src/measurement/extech.h @@ -0,0 +1,51 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_EXTECH_H +#define __INCLUDE_GUARD_EXTECH_H + +#include <pthread.h> +#include "measurement.h" + +class extech_power_meter: public power_meter { + char dev_name[256]; + int fd; + + double rate; + void measure(void); + double sum; + int samples; + int end_thread; + pthread_t thread; +public: + extech_power_meter(const char *_dev_name); + virtual void start_measurement(void); + virtual void end_measurement(void); + virtual void sample(void); + + virtual double power(void); + virtual double dev_capacity(void) { return 0.0; }; +}; + +#endif diff --git a/src/measurement/measurement.cpp b/src/measurement/measurement.cpp new file mode 100644 index 0000000..caee24e --- /dev/null +++ b/src/measurement/measurement.cpp @@ -0,0 +1,188 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "measurement.h" +#include "acpi.h" +#include "extech.h" +#include "sysfs.h" +#include "opal-sensors.h" +#include "../parameters/parameters.h" +#include "../lib.h" + +#include <string> +#include <sys/types.h> +#include <dirent.h> +#include <stdio.h> +#include <fstream> +#include <unistd.h> +#include <time.h> + +double min_power = 50000.0; + +void power_meter::start_measurement(void) +{ +} + + +void power_meter::end_measurement(void) +{ +} + + +double power_meter::power(void) +{ + return 0.0; +} + +vector<class power_meter *> power_meters; + +static struct timespec tlast; + +void start_power_measurement(void) +{ + unsigned int i; + clock_gettime(CLOCK_REALTIME, &tlast); + for (i = 0; i < power_meters.size(); i++) + power_meters[i]->start_measurement(); + all_results.joules = 0.0; +} +void end_power_measurement(void) +{ + unsigned int i; + for (i = 0; i < power_meters.size(); i++) + power_meters[i]->end_measurement(); +} + +double global_power(void) +{ + bool global_discharging = false; + double total = 0.0; + unsigned int i; + + for (i = 0; i < power_meters.size(); i++) { + global_discharging |= power_meters[i]->is_discharging(); + total += power_meters[i]->power(); + } + + /* report global time left if at least one battery is discharging */ + if (!global_discharging) + return 0.0; + + all_results.power = total; + if (total < min_power && total > 0.01) + min_power = total; + return total; +} + +void global_sample_power(void) +{ + struct timespec tnow; + + clock_gettime(CLOCK_REALTIME, &tnow); + /* power * time = joules */ + all_results.joules += global_power() * \ + ( ((double)tnow.tv_sec + 1.0e-9*tnow.tv_nsec) - \ + ((double)tlast.tv_sec + 1.0e-9*tlast.tv_nsec)); + tlast = tnow; +} + +double global_joules(void) +{ + return all_results.joules; +} + +double global_time_left(void) +{ + bool global_discharging = false; + double total_capacity = 0.0; + double total_rate = 0.0; + unsigned int i; + for (i = 0; i < power_meters.size(); i++) { + global_discharging |= power_meters[i]->is_discharging(); + total_capacity += power_meters[i]->dev_capacity(); + total_rate += power_meters[i]->power(); + } + /* report global time left if at least one battery is discharging */ + if (!global_discharging) + return 0.0; + + /* return 0.0 instead of INF+ */ + if (total_rate < 0.001) + return 0.0; + return total_capacity / total_rate; +} + +void sysfs_power_meters_callback(const char *d_name) +{ + std::string type = read_sysfs_string("/sys/class/power_supply/%s/type", d_name); + + if (type != "Battery" && type != "UPS") + return; + + class sysfs_power_meter *meter; + meter = new(std::nothrow) class sysfs_power_meter(d_name); + if (meter) + power_meters.push_back(meter); +} + +void acpi_power_meters_callback(const char *d_name) +{ + class acpi_power_meter *meter; + meter = new(std::nothrow) class acpi_power_meter(d_name); + if (meter) + power_meters.push_back(meter); +} + +void sysfs_opal_sensors_callback(const char *d_name) +{ + class opal_sensors_power_meter *meter; + const char *c; + + /* Those that end in / are directories and we don't want them */ + c = strrchr(d_name, '/'); + if (c && *(c+1) == '\0') + return; + + meter = new(std::nothrow) class opal_sensors_power_meter(d_name); + if (meter) + power_meters.push_back(meter); +} + +void detect_power_meters(void) +{ + process_directory("/sys/class/power_supply", sysfs_power_meters_callback); + process_glob("/sys/devices/platform/opal-sensor/hwmon/hwmon*/power*", sysfs_opal_sensors_callback); + if (power_meters.size() == 0) { + process_directory("/proc/acpi/battery", acpi_power_meters_callback); + } +} + +void extech_power_meter(const char *devnode) +{ + class extech_power_meter *meter; + + meter = new class extech_power_meter(devnode); + + power_meters.push_back(meter); +} diff --git a/src/measurement/measurement.h b/src/measurement/measurement.h new file mode 100644 index 0000000..9a84bc2 --- /dev/null +++ b/src/measurement/measurement.h @@ -0,0 +1,71 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_MEASUREMENT_H +#define __INCLUDE_GUARD_MEASUREMENT_H + +#include <vector> + +using namespace std; + +class power_meter { + bool discharging = false; +public: + virtual ~power_meter() {}; + + virtual void start_measurement(void); + virtual void end_measurement(void); + virtual double power(void); + + virtual double dev_capacity(void) + { + return 0.0; /* in Joules */ + } + + virtual void set_discharging(bool d) + { + discharging = d; + } + + virtual bool is_discharging() + { + return discharging; + } +}; + +extern vector<class power_meter *> power_meters; + +extern void start_power_measurement(void); +extern void end_power_measurement(void); +extern double global_power(void); +extern void global_sample_power(void); +extern double global_joules(void); +extern double global_time_left(void); + +extern void detect_power_meters(void); +extern void extech_power_meter(const char *devnode); + +extern double min_power; + +#endif diff --git a/src/measurement/opal-sensors.cpp b/src/measurement/opal-sensors.cpp new file mode 100644 index 0000000..a70ec56 --- /dev/null +++ b/src/measurement/opal-sensors.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 IBM Corp. + * + * 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 + * or just google for it. + * + * Authors: + * Stewart Smith <stewart@linux.vnet.ibm.com> + */ +#include "measurement.h" +#include "opal-sensors.h" +#include "../lib.h" +#include <string.h> +#include <stdio.h> +#include <limits.h> + +opal_sensors_power_meter::opal_sensors_power_meter(const char *power_supply_name) +{ + strncpy(name, power_supply_name, sizeof(name)); +} + +double opal_sensors_power_meter::power(void) +{ + bool ok; + int value; + double r = 0; + + value = read_sysfs(name, &ok); + + if(ok) + r = value / 1000000.0; + return r; +} diff --git a/src/measurement/opal-sensors.h b/src/measurement/opal-sensors.h new file mode 100644 index 0000000..7962091 --- /dev/null +++ b/src/measurement/opal-sensors.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015 IBM Corp. + * + * 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 + * or just google for it. + * + * Authors: + * Stewart Smith <stewart@linux.vnet.ibm.com> + */ +#ifndef INCLUDE_GUARD_OPAL_SENSORS_H +#define INCLUDE_GUARD_OPAL_SENSORS_H + +#include "measurement.h" +#include <limits.h> + +class opal_sensors_power_meter: public power_meter { + char name[PATH_MAX]; +public: + opal_sensors_power_meter(const char *power_supply_name); + virtual void start_measurement(void) {}; + virtual void end_measurement(void) {}; + + virtual double power(void); + virtual double dev_capacity(void) { return 0.0; } +}; + +#endif diff --git a/src/measurement/sysfs.cpp b/src/measurement/sysfs.cpp new file mode 100644 index 0000000..c5a7a12 --- /dev/null +++ b/src/measurement/sysfs.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2011 Anssi Hannula + * + * 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 + * or just google for it. + * + * Authors: + * Anssi Hannula <anssi.hannula@iki.fi> + */ +#include "measurement.h" +#include "sysfs.h" +#include "../lib.h" +#include <string.h> +#include <stdio.h> +#include <limits.h> + +sysfs_power_meter::sysfs_power_meter(const char *power_supply_name) +{ + rate = 0.0; + capacity = 0.0; + pt_strcpy(name, power_supply_name); +} + +bool sysfs_power_meter::get_sysfs_attr(const char *attribute, int *value) +{ + char filename[PATH_MAX]; + bool ok; + + snprintf(filename, sizeof(filename), "/sys/class/power_supply/%s/%s", name, attribute); + *value = read_sysfs(filename, &ok); + + return ok; +} + +bool sysfs_power_meter::is_present() +{ + int present = 0; + + if (!get_sysfs_attr("present", &present)) + return true; /* assume always present */ + + return present; +} + +double sysfs_power_meter::get_voltage() +{ + int voltage; + + if (!get_sysfs_attr("voltage_now", &voltage)) + return -1.0; + + /* µV to V */ + return voltage / 1000000.0; +} + +bool sysfs_power_meter::set_rate_from_power() +{ + int power; + + if (!get_sysfs_attr("power_now", &power)) + return false; + + /* µW to W */ + rate = power / 1000000.0; + return true; +} + +bool sysfs_power_meter::set_rate_from_current(double voltage) +{ + int current; + + if (!get_sysfs_attr("current_now", ¤t)) + return false; + + /* current: µA + * voltage: V + * rate: W */ + rate = (current / 1000000.0) * voltage; + return true; +} + +bool sysfs_power_meter::set_capacity_from_energy() +{ + int energy; + + if (!get_sysfs_attr("energy_now", &energy)) + return false; + + /* µWh to J */ + capacity = energy / 1000000.0 * 3600.0; + return true; +} + +bool sysfs_power_meter::set_capacity_from_charge(double voltage) +{ + int charge; + + if (!get_sysfs_attr("charge_now", &charge)) + return false; + + /* charge: µAh + * voltage: V + * capacity: J */ + capacity = (charge / 1000000.0) * voltage * 3600.0; + return true; +} + +void sysfs_power_meter::measure() +{ + bool got_rate = false; + bool got_capacity = false; + + rate = 0.0; + capacity = 0.0; + this->set_discharging(false); + + if (!is_present()) + return; + /** do not jump over. we may have discharging battery */ + if (read_sysfs_string("/sys/class/power_supply/%s/status", name) == "Discharging") + this->set_discharging(true); + + got_rate = set_rate_from_power(); + got_capacity = set_capacity_from_energy(); + + if (!got_rate || !got_capacity) { + double voltage = get_voltage(); + if (voltage < 0.0) + return; + if (!got_rate) + set_rate_from_current(voltage); + if (!got_capacity) + set_capacity_from_charge(voltage); + } +} + +void sysfs_power_meter::end_measurement(void) +{ + measure(); +} + +void sysfs_power_meter::start_measurement(void) +{ + /* Battery state is generally a lagging indication, lets only measure at the end */ +} diff --git a/src/measurement/sysfs.h b/src/measurement/sysfs.h new file mode 100644 index 0000000..5b440f5 --- /dev/null +++ b/src/measurement/sysfs.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Anssi Hannula + * + * 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 + * or just google for it. + * + * Authors: + * Anssi Hannula <anssi.hannula@iki.fi> + */ +#ifndef INCLUDE_GUARD_SYSFS_H +#define INCLUDE_GUARD_SYSFS_H + +#include "measurement.h" + +class sysfs_power_meter: public power_meter { + char name[256]; + + double capacity; + double rate; + + bool get_sysfs_attr(const char *attribute, int *value); + bool is_present(); + double get_voltage(); + + bool set_rate_from_power(); + bool set_rate_from_current(double voltage); + bool set_capacity_from_energy(); + bool set_capacity_from_charge(double voltage); + + void measure(); +public: + sysfs_power_meter(const char *power_supply_name); + virtual void start_measurement(void); + virtual void end_measurement(void); + + virtual double power(void) { return rate; } + virtual double dev_capacity(void) { return capacity; } +}; + +#endif diff --git a/src/parameters/learn.cpp b/src/parameters/learn.cpp new file mode 100644 index 0000000..aaef5a2 --- /dev/null +++ b/src/parameters/learn.cpp @@ -0,0 +1,282 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "parameters.h" +#include "../measurement/measurement.h" + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +extern int debug_learning; + +double calculate_params(struct parameter_bundle *params) +{ + unsigned int i; + + params->score = 0; + + + for (i = 0; i < past_results.size(); i++) + compute_bundle(params, past_results[i]); + + return params->score; +} + + +/* + * gradual linear convergence of non-independent variables works better if once in a while + * you make a wrong move.... + */ +static int random_disturb(int retry_left) +{ + if (retry_left < 10) + return 0; + + if ( (rand() % 500) == 7) + return 1; + return 0; +} + +static int try_zero(double value) +{ + if (value > 0.01) + if ( (rand() % 100) == 1) + return 1; + + if ( (rand() % 5) == 1) + return 1; + return 0; +} + +static unsigned int previous_measurements; + +static void weed_empties(struct parameter_bundle *best_so_far) +{ + double best_score; + unsigned int i; + + best_score = best_so_far->score; + + + for (i = 0; i < best_so_far->parameters.size(); i++) { + double orgvalue; + + orgvalue = best_so_far->parameters[i]; + + + best_so_far->parameters[i] = 0.0; + + calculate_params(best_so_far); + if (best_so_far->score > best_score) { + best_so_far->parameters[i] = orgvalue; + } else { + best_score = best_so_far->score; + } + + } + calculate_params(best_so_far); + +} + +/* leaks like a sieve */ +void learn_parameters(int iterations, int do_base_power) +{ + struct parameter_bundle *best_so_far; + double best_score = 10000000000000000.0; + int retry = iterations; + int prevparam = -1; + int locked = 0; + static unsigned int bpi = 0; + unsigned int i; + time_t start; + + /* don't start fitting anything until we have at least 1 more measurement than we have parameters */ + if (past_results.size() <= all_parameters.parameters.size()) + return; + + + +// if (past_results.size() == previous_measurements) +// return; + + precompute_valid(); + + + previous_measurements = past_results.size(); + + double delta = 0.50; + + best_so_far = &all_parameters; + + if (!bpi) + bpi = get_param_index("base power"); + + calculate_params(best_so_far); + best_score = best_so_far->score; + + delta = 0.001 / pow(0.8, iterations / 2.0); + if (iterations < 25) + delta = 0.001 / pow(0.5, iterations / 2.0); + + if (delta > 0.2) + delta = 0.2; + + if (1.0 * best_score / past_results.size() < 4 && delta > 0.05) + delta = 0.05; + + if (debug_learning) + printf("Delta starts at %5.3f\n", delta); + + if (best_so_far->parameters[bpi] > min_power * 0.9) + best_so_far->parameters[bpi] = min_power * 0.9; + + /* We want to give up a little of base power, to give other parameters room to change; + base power is the end post for everything after all + */ + if (do_base_power && !debug_learning) + best_so_far->parameters[bpi] = best_so_far->parameters[bpi] * 0.9998; + + start = time(NULL); + + while (retry--) { + int changed = 0; + int bestparam; + double newvalue = 0; + double orgscore; + double weight; + + bestparam = -1; + + if (time(NULL) - start > 1 && !debug_learning) + retry = 0; + + calculate_params(best_so_far); + orgscore = best_score = best_so_far->score; + + + for (i = 1; i < best_so_far->parameters.size(); i++) { + double value, orgvalue; + + weight = delta * best_so_far->weights[i]; + + orgvalue = value = best_so_far->parameters[i]; + if (value <= 0.001) { + value = 0.1; + } else + value = value * (1 + weight); + + if (i == bpi && value > min_power) + value = min_power; + + if (i == bpi && orgvalue > min_power) + orgvalue = min_power; + + if (value > 5000) + value = 5000; + +// printf("Trying %s %4.2f -> %4.2f\n", param.c_str(), best_so_far->parameters[param], value); + best_so_far->parameters[i] = value; + + calculate_params(best_so_far); + if (best_so_far->score < best_score || random_disturb(retry)) { + best_score = best_so_far->score; + newvalue = value; + bestparam = i; + changed++; + } + + value = orgvalue * 1 / (1 + weight); + + if (value < 0.0001) + value = 0.0; + + if (try_zero(value)) + value = 0.0; + + + if (value > 5000) + value = 5000; + + +// printf("Trying %s %4.2f -> %4.2f\n", param.c_str(), orgvalue, value); + + if (orgvalue != value) { + best_so_far->parameters[i] = value; + + calculate_params(best_so_far); + + if (best_so_far->score + 0.00001 < best_score || (random_disturb(retry) && value > 0.0)) { + best_score = best_so_far->score; + newvalue = value; + bestparam = i; + changed++; + } + } + best_so_far->parameters[i] = orgvalue; + + } + if (!changed) { + double mult; + + if (!locked) { + mult = 0.8; + if (iterations < 25) + mult = 0.5; + delta = delta * mult; + } + locked = 0; + prevparam = -1; + } else { + if (debug_learning) { + printf("Retry is %i \n", retry); + printf("delta is %5.4f\n", delta); + printf("Best parameter is %i \n", bestparam); + printf("Changing score from %4.3f to %4.3f\n", orgscore, best_score); + printf("Changing value from %4.3f to %4.3f\n", best_so_far->parameters[bestparam], newvalue); + } + best_so_far->parameters[bestparam] = newvalue; + if (prevparam == bestparam) + delta = delta * 1.1; + prevparam = bestparam; + locked = 1; + } + + if (delta < 0.001 && !locked) + break; + + if (retry % 50 == 49) + weed_empties(best_so_far); + } + + + /* now we weed out all parameters that don't have value */ + if (iterations > 50) + weed_empties(best_so_far); + + if (debug_learning) + printf("Final score %4.2f (%i points)\n", best_so_far->score / past_results.size(), (int)past_results.size()); +// dump_parameter_bundle(best_so_far); +// dump_past_results(); +} diff --git a/src/parameters/parameters.cpp b/src/parameters/parameters.cpp new file mode 100644 index 0000000..38e1752 --- /dev/null +++ b/src/parameters/parameters.cpp @@ -0,0 +1,462 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "parameters.h" +#include "../measurement/measurement.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> +#include <vector> +#include <unistd.h> +#include <limits.h> + + +struct parameter_bundle all_parameters; +struct result_bundle all_results; + +vector <struct result_bundle *> past_results; + +map <string, int> param_index; +static int maxindex = 1; +map <string, int> result_index; +static int maxresindex = 1; + +int get_param_index(const char *name) +{ + std::map<string, int>::iterator it; + int index = 0; + + it = param_index.find(name); + if (it == param_index.end()) { + param_index[name] = ++maxindex; + index = maxindex; + } else + index = it->second; + + if (index == 0) + printf("OH BLA\n"); + return index; +} + +int get_result_index(const char *name) +{ + std::map<string, int>::iterator it; + int index = 0; + + it = result_index.find(name); + if (it == result_index.end()) { + result_index[name] = ++maxresindex; + index = maxresindex; + } else + index = it->second; + + return index; +} + + +void register_parameter(const char *name, double default_value, double weight) +{ + int index; + + index = get_param_index(name); + + if (index >= (int)all_parameters.parameters.size()) { + all_parameters.parameters.resize(index+1, 0.0); + all_parameters.weights.resize(index+1, 1.0); + } + + if (all_parameters.parameters[index] <= 0.0001) + all_parameters.parameters[index] = default_value; + + all_parameters.weights[index] = weight; +} + +void set_parameter_value(const char *name, double value, struct parameter_bundle *bundle) +{ + int index; + + index = get_param_index(name); + + if (index >= (int)bundle->parameters.size()) { + bundle->parameters.resize(index+1, 0.0); + bundle->weights.resize(index+1, 1.0); + } + + bundle->parameters[index] = value; +} + +double get_parameter_value(const char *name, struct parameter_bundle *the_bundle) +{ + unsigned int index; + index = get_param_index(name); + return get_parameter_value(index, the_bundle); +} + +double get_parameter_value(unsigned int index, struct parameter_bundle *the_bundle) +{ + if (index >= the_bundle->parameters.size()) { + fprintf(stderr, "BUG: requesting unregistered parameter %d\n", index); + return 0; + } + return the_bundle->parameters[index]; +} + +double get_parameter_weight(int index, struct parameter_bundle *the_bundle) +{ + return the_bundle->weights[index]; +} + +double get_result_value(const char *name, struct result_bundle *the_bundle) +{ + return get_result_value(get_result_index(name), the_bundle); +} + +void set_result_value(const char *name, double value, struct result_bundle *the_bundle) +{ + unsigned int index = get_result_index(name); + if (index >= the_bundle->utilization.size()) + the_bundle->utilization.resize(index+1); + the_bundle->utilization[index] = value; +} + +void set_result_value(unsigned int index, double value, struct result_bundle *the_bundle) +{ + if (index >= the_bundle->utilization.size()) + the_bundle->utilization.resize(index+1); + the_bundle->utilization[index] = value; +} + +double get_result_value(int index, struct result_bundle *the_bundle) +{ + if (!the_bundle) + return 0; + if (index >= (int) the_bundle->utilization.size()) + return 0; + return the_bundle->utilization[index]; +} + + +int result_device_exists(const char *name) +{ + unsigned int i; + for (i = 0; i < all_devices.size(); i++) { + if (strcmp(all_devices[i]->device_name(), name) == 0) + return 1; + } + return 0; +} + +void report_utilization(const char *name, double value, struct result_bundle *bundle) +{ + set_result_value(name, value, bundle); +} +void report_utilization(int index, double value, struct result_bundle *bundle) +{ + set_result_value(index, value, bundle); +} + + + +double compute_bundle(struct parameter_bundle *parameters, struct result_bundle *results) +{ + double power = 0; + unsigned int i; + + static int bpi = 0; + + if (!bpi) + bpi = get_param_index("base power"); + + for (i = 0; i < all_devices.size(); i++) + power += all_devices[i]->power_usage(results, parameters); + + parameters->actual_power = results->power; + parameters->guessed_power = power; + /* scale the squared error by the actual power so that non-idle data points weigh heavier */ + parameters->score += results->power * (power - results->power) * (power - results->power); + parameters->parameters[bpi] = power; + return power; +} + +static int precomputed_valid = 0; +void precompute_valid(void) +{ + unsigned int i; + + + for (i = 0; i < all_devices.size(); i++) { + all_devices[i]->cached_valid = all_devices[i]->power_valid(); + } + precomputed_valid = 1; +} + +double bundle_power(struct parameter_bundle *parameters, struct result_bundle *results) +{ + double power = 0; + unsigned int i; + static int bpi = 0; + + if (!bpi) + bpi = get_param_index("base power"); + + if (!precomputed_valid) + precompute_valid(); + + + power = parameters->parameters[bpi]; + + for (i = 0; i < all_devices.size(); i++) { + + if (all_devices[i]->cached_valid) + power += all_devices[i]->power_usage(results, parameters); + } + + return power; +} + + +void dump_parameter_bundle(struct parameter_bundle *para) +{ + map<string, int>::iterator it; + int index; + + printf("\n\n"); + printf("Parameter state \n"); + printf("----------------------------------\n"); + printf("Value\t\tName\n"); + for (it = param_index.begin(); it != param_index.end(); it++) { + index = it->second; + printf("%5.2f\t\t%s (%i)\n", para->parameters[index], it->first.c_str(), index); + } + + printf("\n"); + printf("Score: %5.1f (%5.1f)\n", sqrt(para->score / (0.001 + past_results.size()) / average_power()), para->score); + printf("Guess: %5.1f\n", para->guessed_power); + printf("Actual: %5.1f\n", para->actual_power); + + printf("----------------------------------\n"); +} + +void dump_result_bundle(struct result_bundle *res) +{ + map<string, int>::iterator it; + unsigned int index; + + printf("\n\n"); + printf("Utilisation state \n"); + printf("----------------------------------\n"); + printf("Value\t\tName\n"); + for (it = result_index.begin(); it != result_index.end(); it++) { + index = get_result_index(it->first.c_str()); + printf("%5.2f%%\t\t%s(%i)\n", res->utilization[index], it->first.c_str(), index); + } + + printf("\n"); + printf("Power: %5.1f\n", res->power); + + printf("----------------------------------\n"); +} + +struct result_bundle * clone_results(struct result_bundle *bundle) +{ + struct result_bundle *b2; + map<string, double>::iterator it; + unsigned int i; + + b2 = new struct result_bundle; + + if (!b2) + return NULL; + + b2->power = bundle->power; + b2->utilization.resize(bundle->utilization.size()); + + for (i = 0; i < bundle->utilization.size(); i++) { + b2->utilization[i] = bundle->utilization[i]; + } + + return b2; +} + + +struct parameter_bundle * clone_parameters(struct parameter_bundle *bundle) +{ + struct parameter_bundle *b2; + unsigned int i; + + b2 = new struct parameter_bundle; + + if (!b2) + return NULL; + + b2->score = 0; + b2->guessed_power = 0; + b2->actual_power = bundle->actual_power; + b2->parameters.resize(bundle->parameters.size()); + for (i = 0; i < bundle->parameters.size(); i++) { + b2->parameters[i] = bundle->parameters[i]; + } + + return b2; +} + + +void store_results(double duration) +{ + if (duration < 5) + return; + global_power(); + if (all_results.power > 0.01) { + unsigned int overflow_index; + overflow_index = 50 + (rand() % MAX_KEEP); + if (past_results.size() >= MAX_PARAM) { + /* memory leak, must free old one first */ + past_results[overflow_index] = clone_results(&all_results); + } else { + past_results.push_back(clone_results(&all_results)); + } + if ((past_results.size() % 10) == 0) + save_all_results("saved_results.powertop"); + } + +} + + + +void dump_past_results(void) +{ + unsigned int j; + unsigned int i; + struct result_bundle *result; + + for (j = 0; j < past_results.size(); j+=10) { + printf("Est "); + for (i = j; i < past_results.size() && i < j + 10; i++) { + result = past_results[i]; + printf("%6.2f ", bundle_power(&all_parameters, result)); + } + printf("\n"); + printf("Actual "); + for (i = j; i < past_results.size() && i < j + 10; i++) { + result = past_results[i]; + printf("%6.2f ", result->power); + } + printf("\n\n"); + } +} + +double average_power(void) +{ + double sum = 0.0; + unsigned int i; + for (i = 0; i < past_results.size(); i++) + sum += past_results[i]->power; + + if (past_results.size()) + sum = sum / past_results.size() + 0.0001; + else + sum = 0.0001; + return sum; +} + +int utilization_power_valid(const char *u) +{ + unsigned int i; + unsigned int index; + double first_value; + + index = get_result_index(u); + if (index <= 0) + return 0; + + first_value = past_results[0]->utilization[index]; + for (i = 1; i < past_results.size(); i++) { + if (get_result_value(index, past_results[i]) < first_value - 0.0001) + return 1; + if (get_result_value(index, past_results[i]) > first_value + 0.0001) + return 1; + } + + return 0; +} + +int utilization_power_valid(int index) +{ + unsigned int i; + double first_value; + + if (index <= 0) + return 0; + + if (past_results.size() == 0) + return 0; + + if (index >= (int)past_results[0]->utilization.size()) + return 0; + first_value = past_results[0]->utilization[index]; + for (i = 1; i < past_results.size(); i++) { + if (get_result_value(index, past_results[i]) < first_value - 0.0001) + return 1; + if (get_result_value(index, past_results[i]) > first_value + 0.0001) + return 1; + } + + return 0; +} + + +/* force power data to be valid to the rest of the system */ +int global_power_override = 0; +int global_run_times=0; +/* + * only report power numbers once we have 3* more measurements than + * we have parameters; anything less and our model fit is highly suspect + */ +int global_power_valid(void) +{ + if (past_results.size() > 3 * all_parameters.parameters.size()) + return 1; + + if (past_results.size() > 0 && global_run_times < 1){ + printf("To show power estimates do %ld measurement(s) connected to battery only\n", + (3 * all_parameters.parameters.size()) - past_results.size()); + global_run_times += 1; + } + + return global_power_override; +} + +/* find the directory to store powertop results/parameters based on distribution*/ +char* get_param_directory(const char *filename) +{ + static char tempfilename[PATH_MAX]; + + if (access("/var/cache/powertop", W_OK ) == 0) + snprintf(tempfilename, sizeof(tempfilename), "/var/cache/powertop/%s", filename); + if (access("/data/local/powertop", W_OK ) == 0) + snprintf(tempfilename, sizeof(tempfilename), "/data/local/powertop/%s", filename); + + return tempfilename; +}; diff --git a/src/parameters/parameters.h b/src/parameters/parameters.h new file mode 100644 index 0000000..1781a0e --- /dev/null +++ b/src/parameters/parameters.h @@ -0,0 +1,123 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_PARAMETERS_H_ +#define __INCLUDE_GUARD_PARAMETERS_H_ + + +#include <map> +#include <vector> +#include <string> + +#include "string.h" +#include "../devices/device.h" +#include "../lib.h" + +using namespace std; + +#define MAX_KEEP 700 +#define MAX_PARAM 750 + + +struct parameter_bundle +{ + double score; + double guessed_power; + double actual_power; + + vector<double> parameters; + vector<double> weights; +}; + +extern struct parameter_bundle all_parameters; +extern map <string, int> param_index; +extern map <string, int> result_index; + +extern int get_param_index(const char *param); +extern int get_result_index(const char *param); + + +extern void register_parameter(const char *name, double default_value = 0.00, double weight = 1.0); +extern double get_parameter_value(const char *name, struct parameter_bundle *bundle = &all_parameters); +extern double get_parameter_value(unsigned int index, struct parameter_bundle *bundle = &all_parameters); +extern void set_parameter_value(const char *name, double value, struct parameter_bundle *bundle = &all_parameters); + + +struct result_bundle +{ + double joules; + double power; + vector <double> utilization; /* device name, device utilization %age */ +}; + +extern struct result_bundle all_results; +extern vector <struct result_bundle *> past_results; + +extern double get_result_value(const char *name, struct result_bundle *bundle = &all_results); +extern double get_result_value(int index, struct result_bundle *bundle = &all_results); + +extern void set_result_value(const char *name, double value, struct result_bundle *bundle = &all_results); + + +extern int result_device_exists(const char *name); + +extern void report_utilization(const char *name, double value, struct result_bundle *bundle = &all_results); +extern void report_utilization(int index, double value, struct result_bundle *bundle = &all_results); + + +extern void precompute_valid(void); + +extern double compute_bundle(struct parameter_bundle *parameters = &all_parameters, struct result_bundle *results = &all_results); + + +void dump_parameter_bundle(struct parameter_bundle *patameters = &all_parameters); +void dump_result_bundle(struct result_bundle *res = &all_results); + +extern struct result_bundle * clone_results(struct result_bundle *bundle); +extern struct parameter_bundle * clone_parameters(struct parameter_bundle *bundle); + +extern void store_results(double duration); +extern void learn_parameters(int iterations, int do_base_power); +extern char *get_param_directory(const char *filename); +extern void save_all_results(const char *filename = "saved_results.powertop"); +extern void close_results(void); +extern void load_results(const char *filename); +extern void save_parameters(const char *filename); +extern void load_parameters(const char *filename); + +extern void dump_past_results(void); +extern double bundle_power(struct parameter_bundle *parameters, struct result_bundle *results); + +extern double average_power(void); + +extern int utilization_power_valid(const char *u); +extern int utilization_power_valid(int index); +extern double calculate_params(struct parameter_bundle *params = &all_parameters); +int global_power_valid(void); + + +extern int global_power_override; + + +#endif diff --git a/src/parameters/persistent.cpp b/src/parameters/persistent.cpp new file mode 100644 index 0000000..0711b4d --- /dev/null +++ b/src/parameters/persistent.cpp @@ -0,0 +1,202 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <fstream> +#include <iomanip> +#include <stdlib.h> + +#include "parameters.h" +#include "../measurement/measurement.h" + +using namespace std; + +void save_all_results(const char *filename) +{ + ofstream file; + unsigned int i; + struct result_bundle *bundle; + char* pathname; + + pathname = get_param_directory(filename); + + file.open(pathname, ios::out); + if (!file) { + cout << _("Cannot save to file") << " " << pathname << "\n"; + return; + } + for (i = 0; i < past_results.size(); i++) { + bundle = past_results[i]; + map<string, int>::iterator it; + file << setiosflags(ios::fixed) << setprecision(5) << bundle->power << "\n"; + + for (it = result_index.begin(); it != result_index.end(); it++) { + file << it->first << "\t" << setprecision(5) << get_result_value(it->second, bundle) << "\n"; + } + file << ":\n"; + } + + file.close(); + +} + +void close_results() +{ + for (unsigned int i = 0; i < past_results.size(); i++) { + delete past_results[i]; + } + + past_results.clear(); + return; +} + +void load_results(const char *filename) +{ + ifstream file; + char line[4096]; + char *c1; + struct result_bundle *bundle; + int first = 1; + unsigned int count = 0; + char* pathname; + int bundle_saved = 0; + + pathname = get_param_directory(filename); + + file.open(pathname, ios::in); + if (!file) { + cout << _("Cannot load from file") << " " << pathname << "\n"; + return; + } + + bundle = new struct result_bundle; + + while (file) { + double d; + if (first) { + file.getline(line, 4096); + if (strlen(line)>0) { + sscanf(line, "%lf", &bundle->power); + if (bundle->power < min_power) + min_power = bundle->power; + } + first = 0; + continue; + } + file.getline(line, 4096); + if (strlen(line) < 3) { + int overflow_index; + + bundle_saved = 1; + overflow_index = 50 + (rand() % MAX_KEEP); + if (past_results.size() >= MAX_PARAM) { + /* memory leak, must free old one first */ + past_results[overflow_index] = bundle; + } else { + past_results.push_back(bundle); + } + bundle = new struct result_bundle; + first = 1; + count++; + continue; + } + c1 = strchr(line, '\t'); + if (!c1) + continue; + *c1 = 0; + c1++; + sscanf(c1, "%lf", &d); + set_result_value(line, d, bundle); + } + + if (bundle_saved == 0) + delete bundle; + + file.close(); + // '%i" is for count, do not translate + fprintf(stderr, _("Loaded %i prior measurements\n"), count); +} + +void save_parameters(const char *filename) +{ + ofstream file; + char* pathname; + +// printf("result size is %i, #parameters is %i \n", (int)past_results.size(), (int)all_parameters.parameters.size()); + + if (!global_power_valid()) + return; + + pathname = get_param_directory(filename); + + file.open(pathname, ios::out); + if (!file) { + cout << _("Cannot save to file") << " " << pathname << "\n"; + return; + } + + map<string, int>::iterator it; + + for (it = param_index.begin(); it != param_index.end(); it++) { + int index; + index = it->second; + file << it->first << "\t" << setprecision(9) << all_parameters.parameters[index] << "\n"; + } + file.close(); +} + +void load_parameters(const char *filename) +{ + ifstream file; + char line[4096]; + char *c1; + char* pathname; + + pathname = get_param_directory(filename); + + file.open(pathname, ios::in); + if (!file) { + cout << _("Cannot load from file") << " " << pathname << "\n"; + cout << _("File will be loaded after taking minimum number of measurement(s) with battery only \n"); + return; + } + + while (file) { + double d; + memset(line, 0, 4096); + file.getline(line, 4095); + + c1 = strchr(line, '\t'); + if (!c1) + continue; + *c1 = 0; + c1++; + sscanf(c1, "%lf", &d); + + + set_parameter_value(line, d); + } + + file.close(); +} diff --git a/src/perf/perf.cpp b/src/perf/perf.cpp new file mode 100644 index 0000000..9ed0ba8 --- /dev/null +++ b/src/perf/perf.cpp @@ -0,0 +1,266 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include <iostream> +#include <fstream> + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <fcntl.h> + +#include "perf_event.h" +#include "perf.h" +#include "../lib.h" +#include "../display.h" + +struct pevent *perf_event::pevent; + +static int get_trace_type(const char *eventname) +{ + string str; + int this_trace; + + str = read_sysfs_string("/sys/kernel/debug/tracing/events/%s/id", + eventname); + if (str.length() < 1) + return -1; + + this_trace = strtoull(str.c_str(), NULL, 10); + return this_trace; +} + +static inline int sys_perf_event_open(struct perf_event_attr *attr, + pid_t pid, int cpu, int group_fd, + unsigned long flags) +{ + attr->size = sizeof(*attr); + return syscall(__NR_perf_event_open, attr, pid, cpu, + group_fd, flags); +} + +void perf_event::create_perf_event(char *eventname, int _cpu) +{ + struct perf_event_attr attr; + int ret; + int err; + + struct { + __u64 count; + __u64 time_enabled; + __u64 time_running; + __u64 id; + } read_data; + + if (perf_fd != -1) + clear(); + + memset(&attr, 0, sizeof(attr)); + + attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | + PERF_FORMAT_TOTAL_TIME_RUNNING | + PERF_FORMAT_ID; + + attr.sample_freq = 0; + attr.sample_period = 1; + attr.sample_type |= PERF_SAMPLE_RAW | PERF_SAMPLE_CPU | PERF_SAMPLE_TIME; + + attr.mmap = 1; + attr.comm = 1; + attr.inherit = 0; + attr.disabled = 1; + + attr.type = PERF_TYPE_TRACEPOINT; + attr.config = trace_type; + + if (attr.config <= 0) + return; + + perf_fd = sys_perf_event_open(&attr, -1, _cpu, -1, 0); + + if (perf_fd < 0) { + err = errno; + reset_display(); + if (err == EMFILE) + fprintf(stderr, _("Too many open files, please increase the limit of open file descriptors.\n")); + else { + fprintf(stderr, _("PowerTOP %s needs the kernel to support the 'perf' subsystem\n"), PACKAGE_VERSION); + fprintf(stderr, _("as well as support for trace points in the kernel:\n")); + fprintf(stderr, "CONFIG_PERF_EVENTS=y\nCONFIG_PERF_COUNTERS=y\nCONFIG_TRACEPOINTS=y\nCONFIG_TRACING=y\n"); + } + exit(EXIT_FAILURE); + } + if (read(perf_fd, &read_data, sizeof(read_data)) == -1) { + reset_display(); + perror("Unable to read perf file descriptor\n"); + exit(-1); + } + + fcntl(perf_fd, F_SETFL, O_NONBLOCK); + + perf_mmap = mmap(NULL, (bufsize+1)*getpagesize(), + PROT_READ | PROT_WRITE, MAP_SHARED, perf_fd, 0); + if (perf_mmap == MAP_FAILED) { + fprintf(stderr, "failed to mmap with %d (%s)\n", errno, strerror(errno)); + return; + } + + ret = ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0); + + if (ret < 0) { + fprintf(stderr, "failed to enable perf \n"); + } + + pc = (perf_event_mmap_page *)perf_mmap; + data_mmap = (unsigned char *)perf_mmap + getpagesize(); + + +} + +void perf_event::set_event_name(const char *event_name) +{ + free(name); + name = strdup(event_name); + if (!name) { + fprintf(stderr, "failed to allocate event name\n"); + return; + } + + char *c; + + c = strchr(name, ':'); + if (c) + *c = '/'; + + trace_type = get_trace_type(name); +} + +perf_event::~perf_event(void) +{ + free(name); + + if (perf_event::pevent->ref_count == 1) { + pevent_free(perf_event::pevent); + perf_event::pevent = NULL; + clear(); + } else + pevent_unref(perf_event::pevent); +} + +void perf_event::set_cpu(int _cpu) +{ + cpu = _cpu; +} + +static void allocate_pevent(void) +{ + if (!perf_event::pevent) + perf_event::pevent = pevent_alloc(); + else + pevent_ref(perf_event::pevent); +} + +perf_event::perf_event(const char *event_name, int _cpu, int buffer_size) +{ + allocate_pevent(); + name = NULL; + perf_fd = -1; + bufsize = buffer_size; + cpu = _cpu; + perf_mmap = NULL; + trace_type = 0; + set_event_name(event_name); +} + +perf_event::perf_event(void) +{ + allocate_pevent(); + name = NULL; + perf_fd = -1; + bufsize = 128; + perf_mmap = NULL; + cpu = 0; + trace_type = 0; +} + +void perf_event::start(void) +{ + create_perf_event(name, cpu); +} + +void perf_event::stop(void) +{ + int ret; + ret = ioctl(perf_fd, PERF_EVENT_IOC_DISABLE, 0); + if (ret) + cout << "stop failing\n"; +} + +void perf_event::process(void *cookie) +{ + struct perf_event_header *header; + + if (perf_fd < 0) + return; + + while (pc->data_tail != pc->data_head ) { + while (pc->data_tail >= (unsigned int)bufsize * getpagesize()) + pc->data_tail -= bufsize * getpagesize(); + + header = (struct perf_event_header *)( (unsigned char *)data_mmap + pc->data_tail); + + if (header->size == 0) + break; + + pc->data_tail += header->size; + + while (pc->data_tail >= (unsigned int)bufsize * getpagesize()) + pc->data_tail -= bufsize * getpagesize(); + + if (header->type == PERF_RECORD_SAMPLE) + handle_event(header, cookie); + } + pc->data_tail = pc->data_head; +} + +void perf_event::clear(void) +{ + if (perf_mmap) { +// memset(perf_mmap, 0, (bufsize)*getpagesize()); + munmap(perf_mmap, (bufsize+1)*getpagesize()); + perf_mmap = NULL; + } + if (perf_fd != -1) + close(perf_fd); + perf_fd = -1; +} diff --git a/src/perf/perf.h b/src/perf/perf.h new file mode 100644 index 0000000..ee072ae --- /dev/null +++ b/src/perf/perf.h @@ -0,0 +1,76 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_PERF_H_ +#define _INCLUDE_GUARD_PERF_H_ + +#include <iostream> + + +extern "C" { + #include "../traceevent/event-parse.h" +} + + +using namespace std; + +class perf_event { +protected: + int perf_fd; + void * perf_mmap; + void * data_mmap; + struct perf_event_mmap_page *pc; + + + + int bufsize; + char *name; + int cpu; + void create_perf_event(char *eventname, int cpu); + +public: + unsigned int trace_type; + + perf_event(void); + perf_event(const char *event_name, int cpu = 0, int buffer_size = 128); + + virtual ~perf_event(void); + + + void set_event_name(const char *event_name); + void set_cpu(int cpu); + + void start(void); + void stop(void); + void clear(void); + + void process(void *cookie); + + virtual void handle_event(struct perf_event_header *header, void *cookie) { }; + + static struct pevent *pevent; + +}; + +#endif diff --git a/src/perf/perf_bundle.cpp b/src/perf/perf_bundle.cpp new file mode 100644 index 0000000..3d216ff --- /dev/null +++ b/src/perf/perf_bundle.cpp @@ -0,0 +1,348 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <iostream> +#include <malloc.h> +#include <algorithm> +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +#include "perf_bundle.h" +#include "perf_event.h" +#include "perf.h" + +#include "../cpu/cpu.h" + +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) +# define USE_DECLTYPE +#endif + +class perf_bundle_event: public perf_event +{ +public: + perf_bundle_event(void); + virtual void handle_event(struct perf_event_header *header, void *cookie); +}; + +perf_bundle_event::perf_bundle_event(void) : perf_event() +{ +} + + +void perf_bundle_event::handle_event(struct perf_event_header *header, void *cookie) +{ + unsigned char *buffer; + vector<void *> *vector; + + buffer = (unsigned char *)malloc(header->size); + memcpy(buffer, header, header->size); + +#ifdef USE_DECLTYPE + vector = (decltype(vector))cookie; +#else + vector = (typeof(vector))cookie; +#endif + vector->push_back(buffer); +} + + +void perf_bundle::release(void) +{ + class perf_event *ev; + unsigned int i = 0; + + for (i = 0; i < events.size(); i++) { + ev = events[i]; + if (!ev) + continue; + ev->clear(); + delete ev; + } + events.clear(); + + for (i = 0; i < event_names.size(); i++) { + free((void*)event_names[i]); + } + event_names.clear(); + + for(i = 0; i < records.size(); i++) { + free(records[i]); + } + records.clear(); +} + +static char * read_file(const char *file) +{ + char *buffer = NULL; /* quient gcc */ + char buf[4096]; + int len = 0; + int fd; + int r; + + fd = open(file, O_RDONLY); + if (fd < 0) + exit(-1); + + while((r = read(fd, buf, 4096)) > 0) { + if (len) { + char *tmp = (char *)realloc(buffer, len + r + 1); + if (!tmp) + free(buffer); + buffer = tmp; + } else + buffer = (char *)malloc(r + 1); + if (!buffer) + goto out; + memcpy(buffer + len, buf, r); + len += r; + buffer[len] = '\0'; + } +out: + close(fd); + return buffer; +} + +static void parse_event_format(const char *event_name) +{ + char *tptr; + char *name = strdup(event_name); + char *sys = strtok_r(name, ":", &tptr); + char *event = strtok_r(NULL, ":", &tptr); + char *file; + char *buf; + + file = (char *)malloc(strlen(sys) + strlen(event) + + strlen("/sys/kernel/debug/tracing/events////format") + 2); + sprintf(file, "/sys/kernel/debug/tracing/events/%s/%s/format", sys, event); + + buf = read_file(file); + free(file); + if (!buf) { + free(name); + return; + } + + pevent_parse_event(perf_event::pevent, buf, strlen(buf), sys); + free(name); + free(buf); +} + +bool perf_bundle::add_event(const char *event_name) +{ + unsigned int i; + int event_added = false; + class perf_event *ev; + + + for (i = 0; i < all_cpus.size(); i++) { + + if (!all_cpus[i]) + continue; + + ev = new class perf_bundle_event(); + + ev->set_event_name(event_name); + ev->set_cpu(i); + + if ((int)ev->trace_type >= 0) { + if (event_names.find(ev->trace_type) == event_names.end()) { + event_names[ev->trace_type] = strdup(event_name); + parse_event_format(event_name); + } + events.push_back(ev); + event_added = true; + } else { + delete ev; + } + } + return event_added; +} + +void perf_bundle::start(void) +{ + unsigned int i; + class perf_event *ev; + + for (i = 0; i < events.size(); i++) { + ev = events[i]; + if (!ev) + continue; + ev->start(); + } +} +void perf_bundle::stop(void) +{ + unsigned int i; + class perf_event *ev; + + for (i = 0; i < events.size(); i++) { + ev = events[i]; + if (!ev) + continue; + ev->stop(); + } +} +void perf_bundle::clear(void) +{ + unsigned int i; + + class perf_event *ev; + + for (i = 0; i < events.size(); i++) { + ev = events[i]; + if (!ev) + continue; + ev->clear(); + } + + for (i = 0; i < records.size(); i++) { + free(records[i]); + } + records.resize(0); +} + + +struct trace_entry { + uint64_t time; + uint32_t cpu; + uint32_t res; + __u32 size; +} __attribute__((packed));; + + +struct perf_sample { + struct perf_event_header header; + struct trace_entry trace; + unsigned char data[0]; +} __attribute__((packed)); + +static uint64_t timestamp(perf_event_header *event) +{ + struct perf_sample *sample; + + if (event->type != PERF_RECORD_SAMPLE) + return 0; + + sample = (struct perf_sample *)event; + +#if 0 + int i; + unsigned char *x; + + printf("header:\n"); + printf(" type is %x \n", sample->header.type); + printf(" misc is %x \n", sample->header.misc); + printf(" size is %i \n", sample->header.size); + printf("sample:\n"); + printf(" time is %llx \n", sample->trace.time); + printf(" cpu is %i / %x \n", sample->trace.cpu, sample->trace.cpu); + printf(" res is %i / %x \n", sample->trace.res, sample->trace.res); + printf(" size is %i / %x \n", sample->trace.size, sample->trace.size); + printf(" type is %i / %x \n", sample->trace.type, sample->trace.type); + printf(" flags is %i / %x \n", sample->trace.flags, sample->trace.flags); + printf(" p/c is %i / %x \n", sample->trace.preempt_count, sample->trace.preempt_count); + printf(" pid is %i / %x \n", sample->trace.pid, sample->trace.pid); + printf(" lock dept is %i / %x \n", sample->trace.lock_depth, sample->trace.lock_depth); + + x = (unsigned char *)sample; + for (i = 0; i < sample->header.size; i++) + printf("%02x ", *(x+i)); + printf("\n"); +#endif + return sample->trace.time; + +} + +static bool event_sort_function (void *i, void *j) +{ + struct perf_event_header *I, *J; + + I = (struct perf_event_header *) i; + J = (struct perf_event_header *) j; + return (timestamp(I)<timestamp(J)); +} + +/* + * sample's PERF_SAMPLE_CPU cpu nr is a raw_smp_processor_id() by the + * time of perf_event_output(), which may differ from struct perf_event + * cpu, thus we need to fix sample->trace.cpu. + */ +static void fixup_sample_trace_cpu(struct perf_sample *sample) +{ + struct event_format *event; + struct pevent_record rec; + unsigned long long cpu_nr; + int type; + int ret; + + rec.data = &sample->data; + type = pevent_data_type(perf_event::pevent, &rec); + event = pevent_find_event(perf_event::pevent, type); + if (!event) + return; + /** don't touch trace if event does not contain cpu_id field*/ + ret = pevent_get_field_val(NULL, event, "cpu_id", &rec, &cpu_nr, 0); + if (ret < 0) + return; + sample->trace.cpu = cpu_nr; +} + +void perf_bundle::process(void) +{ + unsigned int i; + class perf_event *ev; + + /* fixme: reserve enough space in the array in one go */ + for (i = 0; i < events.size(); i++) { + ev = events[i]; + if (!ev) + continue; + ev->process(&records); + } + sort(records.begin(), records.end(), event_sort_function); + + for (i = 0; i < records.size(); i++) { + struct perf_sample *sample; + + sample = (struct perf_sample *)records[i]; + if (!sample) + continue; + + if (sample->header.type != PERF_RECORD_SAMPLE) + continue; + + fixup_sample_trace_cpu(sample); + handle_trace_point(&sample->data, sample->trace.cpu, sample->trace.time); + } +} + +void perf_bundle::handle_trace_point(void *trace, int cpu, uint64_t time) +{ + printf("UH OH... abstract handle_trace_point called\n"); +} diff --git a/src/perf/perf_bundle.h b/src/perf/perf_bundle.h new file mode 100644 index 0000000..ec50744 --- /dev/null +++ b/src/perf/perf_bundle.h @@ -0,0 +1,59 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_PERF_BUNDLE_H_ +#define _INCLUDE_GUARD_PERF_BUNDLE_H_ + +#include <iostream> +#include <vector> +#include <map> + +using namespace std; + +#include "perf.h" +class perf_event; + + +class perf_bundle { +protected: + vector<class perf_event *> events; + std::map<int, char*> event_names; +public: + vector<void *> records; + virtual ~perf_bundle() {}; + + virtual void release(void); + bool add_event(const char *event_name); + + void start(void); + void stop(void); + void clear(void); + + void process(void); + + virtual void handle_trace_point(void *trace, int cpu = 0, uint64_t time = 0); +}; + + +#endif diff --git a/src/perf/perf_event.h b/src/perf/perf_event.h new file mode 100644 index 0000000..92a38b8 --- /dev/null +++ b/src/perf/perf_event.h @@ -0,0 +1,910 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +/* + * Performance events: + * + * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de> + * Copyright (C) 2008-2009, Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2009, Red Hat, Inc., Peter Zijlstra + * + * Data type definitions, declarations, prototypes. + * + * Started by: Thomas Gleixner and Ingo Molnar + * + * For licencing details see kernel-base/COPYING + */ +#ifndef _LINUX_PERF_EVENT_H +#define _LINUX_PERF_EVENT_H + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <asm/byteorder.h> +#include <sys/syscall.h> + + +/* + * User-space ABI bits: + */ + +/* + * attr.type + */ +enum perf_type_id { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_TRACEPOINT = 2, + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, + + PERF_TYPE_MAX, /* non-ABI */ +}; + +/* + * Generalized performance event event_id types, used by the + * attr.event_id parameter of the sys_perf_event_open() + * syscall: + */ +enum perf_hw_id { + /* + * Common hardware events, generalized by the kernel: + */ + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + + PERF_COUNT_HW_MAX, /* non-ABI */ +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +/* + * Special "software" events provided by the kernel, even if the hardware + * does not support performance events. These events measure various + * physical and sw events of the kernel (and allow the profiling of them as + * well): + */ +enum perf_sw_ids { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + + PERF_COUNT_SW_MAX, /* non-ABI */ +}; + +/* + * Bits that can be set in attr.sample_type to request information + * in the overflow packets. + */ +enum perf_event_sample_format { + PERF_SAMPLE_IP = 1U << 0, + PERF_SAMPLE_TID = 1U << 1, + PERF_SAMPLE_TIME = 1U << 2, + PERF_SAMPLE_ADDR = 1U << 3, + PERF_SAMPLE_READ = 1U << 4, + PERF_SAMPLE_CALLCHAIN = 1U << 5, + PERF_SAMPLE_ID = 1U << 6, + PERF_SAMPLE_CPU = 1U << 7, + PERF_SAMPLE_PERIOD = 1U << 8, + PERF_SAMPLE_STREAM_ID = 1U << 9, + PERF_SAMPLE_RAW = 1U << 10, + + PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */ +}; + +/* + * The format of the data returned by read() on a perf event fd, + * as specified by attr.read_format: + * + * struct read_format { + * { u64 value; + * { u64 time_enabled; } && PERF_FORMAT_ENABLED + * { u64 time_running; } && PERF_FORMAT_RUNNING + * { u64 id; } && PERF_FORMAT_ID + * } && !PERF_FORMAT_GROUP + * + * { u64 nr; + * { u64 time_enabled; } && PERF_FORMAT_ENABLED + * { u64 time_running; } && PERF_FORMAT_RUNNING + * { u64 value; + * { u64 id; } && PERF_FORMAT_ID + * } cntr[nr]; + * } && PERF_FORMAT_GROUP + * }; + */ +enum perf_event_read_format { + PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, + PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, + PERF_FORMAT_ID = 1U << 2, + PERF_FORMAT_GROUP = 1U << 3, + + PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ +}; + +#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ + +/* + * Hardware event_id to monitor via a performance monitoring event: + */ +struct perf_event_attr { + + /* + * Major type: hardware/software/tracepoint/etc. + */ + __u32 type; + + /* + * Size of the attr structure, for fwd/bwd compat. + */ + __u32 size; + + /* + * Type specific configuration information. + */ + __u64 config; + + union { + __u64 sample_period; + __u64 sample_freq; + }; + + __u64 sample_type; + __u64 read_format; + + __u64 disabled : 1, /* off by default */ + inherit : 1, /* children inherit it */ + pinned : 1, /* must always be on PMU */ + exclusive : 1, /* only group on PMU */ + exclude_user : 1, /* don't count user */ + exclude_kernel : 1, /* ditto kernel */ + exclude_hv : 1, /* ditto hypervisor */ + exclude_idle : 1, /* don't count when idle */ + mmap : 1, /* include mmap data */ + comm : 1, /* include comm data */ + freq : 1, /* use freq, not period */ + inherit_stat : 1, /* per task counts */ + enable_on_exec : 1, /* next exec enables */ + task : 1, /* trace fork/exit */ + watermark : 1, /* wakeup_watermark */ + + __reserved_1 : 49; + + union { + __u32 wakeup_events; /* wakeup every n events */ + __u32 wakeup_watermark; /* bytes before wakeup */ + }; + __u32 __reserved_2; + + __u64 __reserved_3; +}; + +/* + * Ioctls that can be done on a perf event fd: + */ +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) + +enum perf_event_ioc_flags { + PERF_IOC_FLAG_GROUP = 1U << 0, +}; + +/* + * Structure of the page that can be mapped via mmap + */ +struct perf_event_mmap_page { + __u32 version; /* version number of this structure */ + __u32 compat_version; /* lowest version this is compat with */ + + /* + * Bits needed to read the hw events in user-space. + * + * u32 seq; + * s64 count; + * + * do { + * seq = pc->lock; + * + * barrier() + * if (pc->index) { + * count = pmc_read(pc->index - 1); + * count += pc->offset; + * } else + * goto regular_read; + * + * barrier(); + * } while (pc->lock != seq); + * + * NOTE: for obvious reason this only works on self-monitoring + * processes. + */ + __u32 lock; /* seqlock for synchronization */ + __u32 index; /* hardware event identifier */ + __s64 offset; /* add to hardware event value */ + __u64 time_enabled; /* time event active */ + __u64 time_running; /* time event on cpu */ + + /* + * Hole for extension of the self monitor capabilities + */ + + __u64 __reserved[123]; /* align to 1k */ + + /* + * Control data for the mmap() data buffer. + * + * User-space reading the @data_head value should issue an rmb(), on + * SMP capable platforms, after reading this value -- see + * perf_event_wakeup(). + * + * When the mapping is PROT_WRITE the @data_tail value should be + * written by userspace to reflect the last read data. In this case + * the kernel will not over-write unread data. + */ + __u64 data_head; /* head in the data section */ + __u64 data_tail; /* user-space written tail */ +}; + +#define PERF_RECORD_MISC_CPUMODE_MASK (3 << 0) +#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) +#define PERF_RECORD_MISC_KERNEL (1 << 0) +#define PERF_RECORD_MISC_USER (2 << 0) +#define PERF_RECORD_MISC_HYPERVISOR (3 << 0) + +struct perf_event_header { + __u32 type; + __u16 misc; + __u16 size; +}; + +enum perf_event_type { + + /* + * The MMAP events record the PROT_EXEC mappings so that we can + * correlate userspace IPs to code. They have the following structure: + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * char filename[]; + * }; + */ + PERF_RECORD_MMAP = 1, + + /* + * struct { + * struct perf_event_header header; + * u64 id; + * u64 lost; + * }; + */ + PERF_RECORD_LOST = 2, + + /* + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * char comm[]; + * }; + */ + PERF_RECORD_COMM = 3, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * }; + */ + PERF_RECORD_EXIT = 4, + + /* + * struct { + * struct perf_event_header header; + * u64 time; + * u64 id; + * u64 stream_id; + * }; + */ + PERF_RECORD_THROTTLE = 5, + PERF_RECORD_UNTHROTTLE = 6, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * }; + */ + PERF_RECORD_FORK = 7, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, tid; + * + * struct read_format values; + * }; + */ + PERF_RECORD_READ = 8, + + /* + * struct { + * struct perf_event_header header; + * + * { u64 ip; } && PERF_SAMPLE_IP + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 addr; } && PERF_SAMPLE_ADDR + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 period; } && PERF_SAMPLE_PERIOD + * + * { struct read_format values; } && PERF_SAMPLE_READ + * + * { u64 nr, + * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN + * + * # + * # The RAW record below is opaque data wrt the ABI + * # + * # That is, the ABI doesn't make any promises wrt to + * # the stability of its content, it may vary depending + * # on event, hardware, kernel version and phase of + * # the moon. + * # + * # In other words, PERF_SAMPLE_RAW contents are not an ABI. + * # + * + * { u32 size; + * char data[size];}&& PERF_SAMPLE_RAW + * }; + */ + PERF_RECORD_SAMPLE = 9, + + PERF_RECORD_MAX, /* non-ABI */ +}; + +enum perf_callchain_context { + PERF_CONTEXT_HV = (__u64)-32, + PERF_CONTEXT_KERNEL = (__u64)-128, + PERF_CONTEXT_USER = (__u64)-512, + + PERF_CONTEXT_GUEST = (__u64)-2048, + PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, + PERF_CONTEXT_GUEST_USER = (__u64)-2560, + + PERF_CONTEXT_MAX = (__u64)-4095, +}; + +#define PERF_FLAG_FD_NO_GROUP (1U << 0) +#define PERF_FLAG_FD_OUTPUT (1U << 1) + +#ifdef __KERNEL__ +/* + * Kernel-internal data types and definitions: + */ + +#ifdef CONFIG_PERF_EVENTS +# include <asm/perf_event.h> +#endif + +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/rculist.h> +#include <linux/rcupdate.h> +#include <linux/spinlock.h> +#include <linux/hrtimer.h> +#include <linux/fs.h> +#include <linux/pid_namespace.h> +#include <linux/workqueue.h> +#include <asm/atomic.h> + +#define PERF_MAX_STACK_DEPTH 255 + +struct perf_callchain_entry { + __u64 nr; + __u64 ip[PERF_MAX_STACK_DEPTH]; +}; + +struct perf_raw_record { + u32 size; + void *data; +}; + +struct task_struct; + +/** + * struct hw_perf_event - performance event hardware details: + */ +struct hw_perf_event { +#ifdef CONFIG_PERF_EVENTS + union { + struct { /* hardware */ + u64 config; + unsigned long config_base; + unsigned long event_base; + int idx; + }; + struct { /* software */ + s64 remaining; + struct hrtimer hrtimer; + }; + }; + atomic64_t prev_count; + u64 sample_period; + u64 last_period; + atomic64_t period_left; + u64 interrupts; + + u64 freq_count; + u64 freq_interrupts; + u64 freq_stamp; +#endif +}; + +struct perf_event; + +/** + * struct pmu - generic performance monitoring unit + */ +struct pmu { + int (*enable) (struct perf_event *event); + void (*disable) (struct perf_event *event); + void (*read) (struct perf_event *event); + void (*unthrottle) (struct perf_event *event); +}; + +/** + * enum perf_event_active_state - the states of a event + */ +enum perf_event_active_state { + PERF_EVENT_STATE_ERROR = -2, + PERF_EVENT_STATE_OFF = -1, + PERF_EVENT_STATE_INACTIVE = 0, + PERF_EVENT_STATE_ACTIVE = 1, +}; + +struct file; + +struct perf_mmap_data { + struct rcu_head rcu_head; +#ifdef CONFIG_PERF_USE_VMALLOC + struct work_struct work; +#endif + int data_order; + int nr_pages; /* nr of data pages */ + int writable; /* are we writable */ + int nr_locked; /* nr pages mlocked */ + + atomic_t poll; /* POLL_ for wakeups */ + atomic_t events; /* event_id limit */ + + atomic_long_t head; /* write position */ + atomic_long_t done_head; /* completed head */ + + atomic_t lock; /* concurrent writes */ + atomic_t wakeup; /* needs a wakeup */ + atomic_t lost; /* nr records lost */ + + long watermark; /* wakeup watermark */ + + struct perf_event_mmap_page *user_page; + void *data_pages[0]; +}; + +struct perf_pending_entry { + struct perf_pending_entry *next; + void (*func)(struct perf_pending_entry *); +}; + +/** + * struct perf_event - performance event kernel representation: + */ +struct perf_event { +#ifdef CONFIG_PERF_EVENTS + struct list_head group_entry; + struct list_head event_entry; + struct list_head sibling_list; + int nr_siblings; + struct perf_event *group_leader; + struct perf_event *output; + const struct pmu *pmu; + + enum perf_event_active_state state; + atomic64_t count; + + /* + * These are the total time in nanoseconds that the event + * has been enabled (i.e. eligible to run, and the task has + * been scheduled in, if this is a per-task event) + * and running (scheduled onto the CPU), respectively. + * + * They are computed from tstamp_enabled, tstamp_running and + * tstamp_stopped when the event is in INACTIVE or ACTIVE state. + */ + u64 total_time_enabled; + u64 total_time_running; + + /* + * These are timestamps used for computing total_time_enabled + * and total_time_running when the event is in INACTIVE or + * ACTIVE state, measured in nanoseconds from an arbitrary point + * in time. + * tstamp_enabled: the notional time when the event was enabled + * tstamp_running: the notional time when the event was scheduled on + * tstamp_stopped: in INACTIVE state, the notional time when the + * event was scheduled off. + */ + u64 tstamp_enabled; + u64 tstamp_running; + u64 tstamp_stopped; + + struct perf_event_attr attr; + struct hw_perf_event hw; + + struct perf_event_context *ctx; + struct file *filp; + + /* + * These accumulate total time (in nanoseconds) that children + * events have been enabled and running, respectively. + */ + atomic64_t child_total_time_enabled; + atomic64_t child_total_time_running; + + /* + * Protect attach/detach and child_list: + */ + struct mutex child_mutex; + struct list_head child_list; + struct perf_event *parent; + + int oncpu; + int cpu; + + struct list_head owner_entry; + struct task_struct *owner; + + /* mmap bits */ + struct mutex mmap_mutex; + atomic_t mmap_count; + struct perf_mmap_data *data; + + /* poll related */ + wait_queue_head_t waitq; + struct fasync_struct *fasync; + + /* delayed work for NMIs and such */ + int pending_wakeup; + int pending_kill; + int pending_disable; + struct perf_pending_entry pending; + + atomic_t event_limit; + + void (*destroy)(struct perf_event *); + struct rcu_head rcu_head; + + struct pid_namespace *ns; + u64 id; +#endif +}; + +/** + * struct perf_event_context - event context structure + * + * Used as a container for task events and CPU events as well: + */ +struct perf_event_context { + /* + * Protect the states of the events in the list, + * nr_active, and the list: + */ + spinlock_t lock; + /* + * Protect the list of events. Locking either mutex or lock + * is sufficient to ensure the list doesn't change; to change + * the list you need to lock both the mutex and the spinlock. + */ + struct mutex mutex; + + struct list_head group_list; + struct list_head event_list; + int nr_events; + int nr_active; + int is_active; + int nr_stat; + atomic_t refcount; + struct task_struct *task; + + /* + * Context clock, runs when context enabled. + */ + u64 time; + u64 timestamp; + + /* + * These fields let us detect when two contexts have both + * been cloned (inherited) from a common ancestor. + */ + struct perf_event_context *parent_ctx; + u64 parent_gen; + u64 generation; + int pin_count; + struct rcu_head rcu_head; +}; + +/** + * struct perf_event_cpu_context - per cpu event context structure + */ +struct perf_cpu_context { + struct perf_event_context ctx; + struct perf_event_context *task_ctx; + int active_oncpu; + int max_pertask; + int exclusive; + + /* + * Recursion avoidance: + * + * task, softirq, irq, nmi context + */ + int recursion[4]; +}; + +struct perf_output_handle { + struct perf_event *event; + struct perf_mmap_data *data; + unsigned long head; + unsigned long offset; + int nmi; + int sample; + int locked; + unsigned long flags; +}; + +#ifdef CONFIG_PERF_EVENTS + +/* + * Set by architecture code: + */ +extern int perf_max_events; + +extern const struct pmu *hw_perf_event_init(struct perf_event *event); + +extern void perf_event_task_sched_in(struct task_struct *task, int cpu); +extern void perf_event_task_sched_out(struct task_struct *task, + struct task_struct *next, int cpu); +extern void perf_event_task_tick(struct task_struct *task, int cpu); +extern int perf_event_init_task(struct task_struct *child); +extern void perf_event_exit_task(struct task_struct *child); +extern void perf_event_free_task(struct task_struct *task); +extern void set_perf_event_pending(void); +extern void perf_event_do_pending(void); +extern void perf_event_print_debug(void); +extern void __perf_disable(void); +extern bool __perf_enable(void); +extern void perf_disable(void); +extern void perf_enable(void); +extern int perf_event_task_disable(void); +extern int perf_event_task_enable(void); +extern int hw_perf_group_sched_in(struct perf_event *group_leader, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx, int cpu); +extern void perf_event_update_userpage(struct perf_event *event); + +struct perf_sample_data { + u64 type; + + u64 ip; + struct { + u32 pid; + u32 tid; + } tid_entry; + u64 time; + u64 addr; + u64 id; + u64 stream_id; + struct { + u32 cpu; + u32 reserved; + } cpu_entry; + u64 period; + struct perf_callchain_entry *callchain; + struct perf_raw_record *raw; +}; + +extern void perf_output_sample(struct perf_output_handle *handle, + struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event); +extern void perf_prepare_sample(struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event, + struct pt_regs *regs); + +extern int perf_event_overflow(struct perf_event *event, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs); + +/* + * Return 1 for a software event, 0 for a hardware event + */ +static inline int is_software_event(struct perf_event *event) +{ + return (event->attr.type != PERF_TYPE_RAW) && + (event->attr.type != PERF_TYPE_HARDWARE) && + (event->attr.type != PERF_TYPE_HW_CACHE); +} + +extern atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; + +extern void __perf_sw_event(u32, u64, int, struct pt_regs *, u64); + +static inline void +perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr) +{ + if (atomic_read(&perf_swevent_enabled[event_id])) + __perf_sw_event(event_id, nr, nmi, regs, addr); +} + +extern void __perf_event_mmap(struct vm_area_struct *vma); + +static inline void perf_event_mmap(struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_EXEC) + __perf_event_mmap(vma); +} + +extern void perf_event_comm(struct task_struct *tsk); +extern void perf_event_fork(struct task_struct *tsk); + +extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); + +extern int sysctl_perf_event_paranoid; +extern int sysctl_perf_event_mlock; +extern int sysctl_perf_event_sample_rate; + +extern void perf_event_init(void); +extern void perf_tp_event(int event_id, u64 addr, u64 count, + void *record, int entry_size); + +#ifndef perf_misc_flags +#define perf_misc_flags(regs) (user_mode(regs) ? PERF_RECORD_MISC_USER : \ + PERF_RECORD_MISC_KERNEL) +#define perf_instruction_pointer(regs) instruction_pointer(regs) +#endif + +extern int perf_output_begin(struct perf_output_handle *handle, + struct perf_event *event, unsigned int size, + int nmi, int sample); +extern void perf_output_end(struct perf_output_handle *handle); +extern void perf_output_copy(struct perf_output_handle *handle, + const void *buf, unsigned int len); +#else +static inline void +perf_event_task_sched_in(struct task_struct *task, int cpu) { } +static inline void +perf_event_task_sched_out(struct task_struct *task, + struct task_struct *next, int cpu) { } +static inline void +perf_event_task_tick(struct task_struct *task, int cpu) { } +static inline int perf_event_init_task(struct task_struct *child) { return 0; } +static inline void perf_event_exit_task(struct task_struct *child) { } +static inline void perf_event_free_task(struct task_struct *task) { } +static inline void perf_event_do_pending(void) { } +static inline void perf_event_print_debug(void) { } +static inline void perf_disable(void) { } +static inline void perf_enable(void) { } +static inline int perf_event_task_disable(void) { return -EINVAL; } +static inline int perf_event_task_enable(void) { return -EINVAL; } + +static inline void +perf_sw_event(u32 event_id, u64 nr, int nmi, + struct pt_regs *regs, u64 addr) { } + +static inline void perf_event_mmap(struct vm_area_struct *vma) { } +static inline void perf_event_comm(struct task_struct *tsk) { } +static inline void perf_event_fork(struct task_struct *tsk) { } +static inline void perf_event_init(void) { } + +#endif + +#define perf_output_put(handle, x) \ + perf_output_copy((handle), &(x), sizeof(x)) + +#endif /* __KERNEL__ */ + + +#if 0 +/* + * trace_flag_type is an enumeration that holds different + * states when a trace occurs. These are: + * IRQS_OFF - interrupts were disabled + * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags + * NEED_RESCED - reschedule is requested + * HARDIRQ - inside an interrupt handler + * SOFTIRQ - inside a softirq handler + */ +enum trace_flag_type { + TRACE_FLAG_IRQS_OFF = 0x01, + TRACE_FLAG_IRQS_NOSUPPORT = 0x02, + TRACE_FLAG_NEED_RESCHED = 0x04, + TRACE_FLAG_HARDIRQ = 0x08, + TRACE_FLAG_SOFTIRQ = 0x10, +}; +#endif + +#endif /* _LINUX_PERF_EVENT_H */ diff --git a/src/powertop.css b/src/powertop.css new file mode 100644 index 0000000..daa20c5 --- /dev/null +++ b/src/powertop.css @@ -0,0 +1,263 @@ +<!DOCTYPE html> +<html lang='en'> +<head> +<title>PowerTOP report</title> +<meta http-equiv='content-type' content='text/html;charset=utf-8'> + +<script type='text/javascript'> + +var powertop = { + blocks: { + summary: 'Summary', + cpuidle: 'CPU Idle', + cpufreq: 'CPU Frequency', + software: 'Software Info', + devinfo: 'Device Info', + tuning: 'Tuning', + ahci: 'AHCI' + }, + cadd: function(idx, c){ + var el = document.getElementById(idx); + if (el) { + var cn = el.className; + if (cn.indexOf(c) != -1) + return; + cn += ' ' + c; + el.className = cn; + } + }, + crm: function(id, c){ + var el = document.getElementById(id); + if (el) { + var cn = el.className + while (cn.indexOf(' ' + c) != -1) + cn = cn.replace(' ' + c,''); + el.className = cn; + } + }, + newbutton: function(id, txt) { + var x = document.createElement('div'); + x.id = id + '_button'; + x.className = 'nav_button'; + x.textContent = txt; + x.innerText = txt; + x.onclick = function() { powertop.toggle(id); }; + return x; + }, + setupbuttons: function() { + var t = document.getElementById('main_menu'); + if (t) { + for (var b in powertop.blocks) { + t.appendChild(powertop.newbutton(b, powertop.blocks[b])); + } + t.appendChild(powertop.newbutton('all', 'All')); + } + }, + toggle: function(b) { + powertop.baseall(); + if (b == 'all') { + for (var c in powertop.blocks) { + powertop.crm(c, 'hide'); + } + } else { + powertop.crm(b, 'hide'); + } + powertop.cadd(b + '_button', 'pressed'); + }, + baseall: function() { + for (var b in powertop.blocks) { + powertop.cadd(b, 'hide'); + powertop.crm(b + '_button', 'pressed'); + } + powertop.cadd('all', 'hide'); + powertop.crm('all_button', 'pressed'); + }, + onload: function() { + powertop.setupbuttons(); + powertop.toggle('summary'); + } +} +</script> + +<style type='text/css'> +/* General CSS */ +*{ + margin:0px; + padding:0px; + width: auto; +} + +body { + background-color: #eee; /* Background color */ + color: #222; /* Font color */ + font-family: Helvetica; + font-size: 14px; +} + +#main_container{ + margin: 2px auto; +} + +/* Top logo & system table css */ +#main_header{ + min-width: 960px; +} + +img.pwtop_logo{ + float:left; + height:40%; + width: 40%; + padding:20px; +} + +.sys_info +{ + float: right; + height:116px; + width:450px; + font-size: 12px; + text-align: left; +} + +th{ + text-align: left; +} + +/* CSS Main Content */ + +.content_title +{ + color: #296629; + padding:0px; + margin:2px; +} + +#chart_div{ + float: left; +} + +.small +{ + font-size: 10px; +} + +table.emphasis2 +{ + font-size: 13px; + max-width:95%; +} + + +th.emph_title { + padding:5px; +} + +tr.emph1:nth-child(odd) { + background: #ffffff; +} + +tr.emph1:nth-child(even) { + background: #ebebeb; +} + +tr.tune:nth-child(odd) { + background: #fffcfc; +} +tr.tune:nth-child(even) { + background: #fff0f0; +} + +td.no_wrap:first-child { + white-space:nowrap; +} + +.side_by_side_left{ + float:left; +} +.side_by_side_right{ + float:right; +} + +#device{ + display: inline-block; +} +.clear_block{ + clear:both; +} + +td.package{ + background-color: #e0ddfa; /*purple*/ +} + +td.core{ + background-color: #d1ddff; /*ccebff; /*blue*/ +} + +td.cpu{ + background-color: #ffffeb; /* yellow */ +} + +th.title{ + text-align: center; + /*border-bottom: 1px solid #666;*/ +} + + +li.summary_list +{ + display: inline; + padding: 5px; + background-color: #f6f6f9; + font-size: 12px; +} + + + +/* main menu css*/ +#main_menu { + clear:both; + font-weight: bold; + padding: 5px 0; + text-align: left; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#aaa), to(#eee)); + background: -moz-linear-gradient(top, #aaa, #eee); +} + + +#main_menu div { + font-size: 12px; + font-weight: bold; + color: white; +} + +#main_menu div.nav_button { + margin: 0 0.2em; + display: inline; + cursor: pointer; + color: #223232; + font-size: 13px; + font-weight: bold; + padding: 5px; + text-align: center; + text-decoration: none; +} + +div.pressed { + border: -webkit-gradient(linear, left top, left bottom, + from(#b2ffb2), to(#e0ffe0)); + border-width:0px 8px 0px 8px; + background: #999; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#b2ffb2), to(#e0ffe0)); + background: -moz-linear-gradient(top, #b2ffb2, #e0ffe0); +} + +div.hide { + display: none; +} +</style> +</head> + +<body onload='powertop.onload();'> +<div id=\"main_container\"> diff --git a/src/process/do_process.cpp b/src/process/do_process.cpp new file mode 100644 index 0000000..be8d3bd --- /dev/null +++ b/src/process/do_process.cpp @@ -0,0 +1,1230 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "process.h" +#include "interrupt.h" +#include "timer.h" +#include "work.h" +#include "processdevice.h" +#include "../lib.h" +#include "../report/report.h" +#include "../report/report-data-html.h" +#include "../report/report-maker.h" +#include "../devlist.h" + +#include <vector> +#include <algorithm> +#include <stack> + +#include <stdio.h> +#include <string.h> +#include <ncurses.h> + +#include "../perf/perf_bundle.h" +#include "../perf/perf_event.h" +#include "../parameters/parameters.h" +#include "../display.h" +#include "../measurement/measurement.h" + +static class perf_bundle * perf_events; + +vector <class power_consumer *> all_power; + +vector< vector<class power_consumer *> > cpu_stack; + +vector<int> cpu_level; +vector<int> cpu_credit; +vector<class power_consumer *> cpu_blame; + +#define LEVEL_HARDIRQ 1 +#define LEVEL_SOFTIRQ 2 +#define LEVEL_TIMER 3 +#define LEVEL_WAKEUP 4 +#define LEVEL_PROCESS 5 +#define LEVEL_WORK 6 + +static uint64_t first_stamp, last_stamp; + +double measurement_time; + +static void push_consumer(unsigned int cpu, class power_consumer *consumer) +{ + if (cpu_stack.size() <= cpu) + cpu_stack.resize(cpu + 1); + cpu_stack[cpu].push_back(consumer); +} + +static void pop_consumer(unsigned int cpu) +{ + if (cpu_stack.size() <= cpu) + cpu_stack.resize(cpu + 1); + + if (cpu_stack[cpu].size()) + cpu_stack[cpu].resize(cpu_stack[cpu].size()-1); +} + +static int consumer_depth(unsigned int cpu) +{ + if (cpu_stack.size() <= cpu) + cpu_stack.resize(cpu + 1); + return cpu_stack[cpu].size(); +} + +static class power_consumer *current_consumer(unsigned int cpu) +{ + if (cpu_stack.size() <= cpu) + cpu_stack.resize(cpu + 1); + if (cpu_stack[cpu].size()) + + return cpu_stack[cpu][cpu_stack[cpu].size()-1]; + + return NULL; +} + +static void clear_consumers(void) +{ + unsigned int i; + for (i = 0; i < cpu_stack.size(); i++) + cpu_stack[i].resize(0); +} + +static void consumer_child_time(unsigned int cpu, uint64_t time) +{ + unsigned int i; + if (cpu_stack.size() <= cpu) + cpu_stack.resize(cpu + 1); + for (i = 0; i < cpu_stack[cpu].size(); i++) + cpu_stack[cpu][i]->child_runtime += time; +} + +static void set_wakeup_pending(unsigned int cpu) +{ + if (cpu_credit.size() <= cpu) + cpu_credit.resize(cpu + 1); + + cpu_credit[cpu] = 1; +} + +static void clear_wakeup_pending(unsigned int cpu) +{ + if (cpu_credit.size() <= cpu) + cpu_credit.resize(cpu + 1); + + cpu_credit[cpu] = 0; +} + +static int get_wakeup_pending(unsigned int cpu) +{ + if (cpu_credit.size() <= cpu) + cpu_credit.resize(cpu + 1); + return cpu_credit[cpu]; +} + +static void change_blame(unsigned int cpu, class power_consumer *consumer, int level) +{ + if (cpu_level[cpu] >= level) + return; + cpu_blame[cpu] = consumer; + cpu_level[cpu] = level; +} + +static void consume_blame(unsigned int cpu) +{ + if (!get_wakeup_pending(cpu)) + return; + if (cpu_level.size() <= cpu) + return; + if (cpu_blame.size() <= cpu) + return; + if (!cpu_blame[cpu]) + return; + + cpu_blame[cpu]->wake_ups++; + cpu_blame[cpu] = NULL; + cpu_level[cpu] = 0; + clear_wakeup_pending(cpu); +} + + +class perf_process_bundle: public perf_bundle +{ + virtual void handle_trace_point(void *trace, int cpu, uint64_t time); +}; + +static bool comm_is_xorg(char *comm) +{ + return strcmp(comm, "Xorg") == 0 || strcmp(comm, "X") == 0; +} + +/* some processes shouldn't be blamed for the wakeup if they wake a process up... for now this is a hardcoded list */ +int dont_blame_me(char *comm) +{ + if (comm_is_xorg(comm)) + return 1; + if (strcmp(comm, "dbus-daemon") == 0) + return 1; + + return 0; +} + +static char * get_pevent_field_str(void *trace, struct event_format *event, struct format_field *field) +{ + unsigned long long offset, len; + if (field->flags & FIELD_IS_DYNAMIC) { + offset = field->offset; + len = field->size; + offset = pevent_read_number(event->pevent, (char *)trace + offset, len); + offset &= 0xffff; + return (char *)trace + offset; + } + /** no __data_loc field type*/ + return (char *)trace + field->offset; +} + +void perf_process_bundle::handle_trace_point(void *trace, int cpu, uint64_t time) +{ + struct event_format *event; + struct pevent_record rec; /* holder */ + struct format_field *field; + unsigned long long val; + int type; + int ret; + + rec.data = trace; + + type = pevent_data_type(perf_event::pevent, &rec); + event = pevent_find_event(perf_event::pevent, type); + if (!event) + return; + + if (time < first_stamp) + first_stamp = time; + + if (time > last_stamp) { + last_stamp = time; + measurement_time = (0.0001 + last_stamp - first_stamp) / 1000000000 ; + } + + if (strcmp(event->name, "sched_switch") == 0) { + class process *old_proc = NULL; + class process *new_proc = NULL; + const char *next_comm; + int next_pid; + int prev_pid; + + field = pevent_find_any_field(event, "next_comm"); + if (!field || !(field->flags & FIELD_IS_STRING)) + return; /* ?? */ + + next_comm = get_pevent_field_str(trace, event, field); + + ret = pevent_get_field_val(NULL, event, "next_pid", &rec, &val, 0); + if (ret < 0) + return; + next_pid = (int)val; + + ret = pevent_get_field_val(NULL, event, "prev_pid", &rec, &val, 0); + if (ret < 0) + return; + prev_pid = (int)val; + + /* find new process pointer */ + new_proc = find_create_process(next_comm, next_pid); + + /* find the old process pointer */ + + while (consumer_depth(cpu) > 1) { + pop_consumer(cpu); + } + + if (consumer_depth(cpu) == 1) + old_proc = (class process *)current_consumer(cpu); + + if (old_proc && strcmp(old_proc->name(), "process")) + old_proc = NULL; + + /* retire the old process */ + + if (old_proc) { + old_proc->deschedule_thread(time, prev_pid); + old_proc->waker = NULL; + } + + if (consumer_depth(cpu)) + pop_consumer(cpu); + + push_consumer(cpu, new_proc); + + /* start new process */ + new_proc->schedule_thread(time, next_pid); + + if (strncmp(next_comm,"migration/", 10) && strncmp(next_comm,"kworker/", 8) && strncmp(next_comm, "kondemand/",10)) { + if (next_pid) { + /* If someone woke us up.. blame him instead */ + if (new_proc->waker) { + change_blame(cpu, new_proc->waker, LEVEL_PROCESS); + } else { + change_blame(cpu, new_proc, LEVEL_PROCESS); + } + } + + consume_blame(cpu); + } + new_proc->waker = NULL; + } + else if (strcmp(event->name, "sched_wakeup") == 0) { + class power_consumer *from = NULL; + class process *dest_proc = NULL; + class process *from_proc = NULL; + const char *comm; + int flags; + int pid; + + ret = pevent_get_common_field_val(NULL, event, "common_flags", &rec, &val, 0); + if (ret < 0) + return; + flags = (int)val; + + if ( (flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) { + class timer *timer; + timer = (class timer *) current_consumer(cpu); + if (timer && strcmp(timer->name(), "timer")==0) { + if (strcmp(timer->handler, "delayed_work_timer_fn") && + strcmp(timer->handler, "hrtimer_wakeup") && + strcmp(timer->handler, "it_real_fn")) + from = timer; + } + /* woken from interrupt */ + /* TODO: find the current irq handler and set "from" to that */ + } else { + from = current_consumer(cpu); + } + + + field = pevent_find_any_field(event, "comm"); + + if (!field || !(field->flags & FIELD_IS_STRING)) + return; + + comm = get_pevent_field_str(trace, event, field); + + ret = pevent_get_field_val(NULL, event, "pid", &rec, &val, 0); + if (ret < 0) + return; + pid = (int)val; + + dest_proc = find_create_process(comm, pid); + + if (from && strcmp(from->name(), "process")!=0){ + /* not a process doing the wakeup */ + from = NULL; + from_proc = NULL; + } else { + from_proc = (class process *) from; + } + + if (from_proc && (dest_proc->running == 0) && (dest_proc->waker == NULL) && (pid != 0) && !dont_blame_me(from_proc->comm)) + dest_proc->waker = from; + if (from) + dest_proc->last_waker = from; + + /* Account processes that wake up X specially */ + if (from && dest_proc && comm_is_xorg(dest_proc->comm)) + from->xwakes ++ ; + + } + else if (strcmp(event->name, "irq_handler_entry") == 0) { + class interrupt *irq = NULL; + const char *handler; + int nr; + + field = pevent_find_any_field(event, "name"); + if (!field || !(field->flags & FIELD_IS_STRING)) + return; /* ?? */ + + handler = get_pevent_field_str(trace, event, field); + + ret = pevent_get_field_val(NULL, event, "irq", &rec, &val, 0); + if (ret < 0) + return; + nr = (int)val; + + irq = find_create_interrupt(handler, nr, cpu); + + + push_consumer(cpu, irq); + + irq->start_interrupt(time); + + if (strstr(irq->handler, "timer") ==NULL) + change_blame(cpu, irq, LEVEL_HARDIRQ); + + } + + else if (strcmp(event->name, "irq_handler_exit") == 0) { + class interrupt *irq = NULL; + uint64_t t; + + /* find interrupt (top of stack) */ + irq = (class interrupt *)current_consumer(cpu); + if (!irq || strcmp(irq->name(), "interrupt")) + return; + pop_consumer(cpu); + /* retire interrupt */ + t = irq->end_interrupt(time); + consumer_child_time(cpu, t); + } + + else if (strcmp(event->name, "softirq_entry") == 0) { + class interrupt *irq = NULL; + const char *handler = NULL; + int vec; + + ret = pevent_get_field_val(NULL, event, "vec", &rec, &val, 0); + if (ret < 0) { + fprintf(stderr, "softirq_entry event returned no vector number?\n"); + return; + } + vec = (int)val; + + if (vec <= 9) + handler = softirqs[vec]; + + if (!handler) + return; + + irq = find_create_interrupt(handler, vec, cpu); + + push_consumer(cpu, irq); + + irq->start_interrupt(time); + change_blame(cpu, irq, LEVEL_SOFTIRQ); + } + else if (strcmp(event->name, "softirq_exit") == 0) { + class interrupt *irq = NULL; + uint64_t t; + + irq = (class interrupt *) current_consumer(cpu); + if (!irq || strcmp(irq->name(), "interrupt")) + return; + pop_consumer(cpu); + /* pop irq */ + t = irq->end_interrupt(time); + consumer_child_time(cpu, t); + } + else if (strcmp(event->name, "timer_expire_entry") == 0) { + class timer *timer = NULL; + uint64_t function; + uint64_t tmr; + + ret = pevent_get_field_val(NULL, event, "function", &rec, &val, 0); + if (ret < 0) { + fprintf(stderr, "timer_expire_entry event returned no function value?\n"); + return; + } + function = (uint64_t)val; + + timer = find_create_timer(function); + + if (timer->is_deferred()) + return; + + ret = pevent_get_field_val(NULL, event, "timer", &rec, &val, 0); + if (ret < 0) { + fprintf(stderr, "softirq_entry event returned no timer ?\n"); + return; + } + tmr = (uint64_t)val; + + push_consumer(cpu, timer); + timer->fire(time, tmr); + + if (strcmp(timer->handler, "delayed_work_timer_fn")) + change_blame(cpu, timer, LEVEL_TIMER); + } + else if (strcmp(event->name, "timer_expire_exit") == 0) { + class timer *timer = NULL; + uint64_t tmr; + uint64_t t; + + ret = pevent_get_field_val(NULL, event, "timer", &rec, &val, 0); + if (ret < 0) + return; + tmr = (uint64_t)val; + + timer = (class timer *) current_consumer(cpu); + if (!timer || strcmp(timer->name(), "timer")) { + return; + } + pop_consumer(cpu); + t = timer->done(time, tmr); + if (t == ~0ULL) { + timer->fire(first_stamp, tmr); + t = timer->done(time, tmr); + } + consumer_child_time(cpu, t); + } + else if (strcmp(event->name, "hrtimer_expire_entry") == 0) { + class timer *timer = NULL; + uint64_t function; + uint64_t tmr; + + ret = pevent_get_field_val(NULL, event, "function", &rec, &val, 0); + if (ret < 0) + return; + function = (uint64_t)val; + + timer = find_create_timer(function); + + ret = pevent_get_field_val(NULL, event, "hrtimer", &rec, &val, 0); + if (ret < 0) + return; + tmr = (uint64_t)val; + + push_consumer(cpu, timer); + timer->fire(time, tmr); + + if (strcmp(timer->handler, "delayed_work_timer_fn")) + change_blame(cpu, timer, LEVEL_TIMER); + } + else if (strcmp(event->name, "hrtimer_expire_exit") == 0) { + class timer *timer = NULL; + uint64_t tmr; + uint64_t t; + + timer = (class timer *) current_consumer(cpu); + if (!timer || strcmp(timer->name(), "timer")) { + return; + } + + ret = pevent_get_field_val(NULL, event, "hrtimer", &rec, &val, 0); + if (ret < 0) + return; + tmr = (uint64_t)val; + + pop_consumer(cpu); + t = timer->done(time, tmr); + if (t == ~0ULL) { + timer->fire(first_stamp, tmr); + t = timer->done(time, tmr); + } + consumer_child_time(cpu, t); + } + else if (strcmp(event->name, "workqueue_execute_start") == 0) { + class work *work = NULL; + uint64_t function; + uint64_t wk; + + ret = pevent_get_field_val(NULL, event, "function", &rec, &val, 0); + if (ret < 0) + return; + function = (uint64_t)val; + + ret = pevent_get_field_val(NULL, event, "work", &rec, &val, 0); + if (ret < 0) + return; + wk = (uint64_t)val; + + work = find_create_work(function); + + + push_consumer(cpu, work); + work->fire(time, wk); + + + if (strcmp(work->handler, "do_dbs_timer") != 0 && strcmp(work->handler, "vmstat_update") != 0) + change_blame(cpu, work, LEVEL_WORK); + } + else if (strcmp(event->name, "workqueue_execute_end") == 0) { + class work *work = NULL; + uint64_t t; + uint64_t wk; + + ret = pevent_get_field_val(NULL, event, "work", &rec, &val, 0); + if (ret < 0) + return; + wk = (uint64_t)val; + + work = (class work *) current_consumer(cpu); + if (!work || strcmp(work->name(), "work")) { + return; + } + pop_consumer(cpu); + t = work->done(time, wk); + if (t == ~0ULL) { + work->fire(first_stamp, wk); + t = work->done(time, wk); + } + consumer_child_time(cpu, t); + } + else if (strcmp(event->name, "cpu_idle") == 0) { + pevent_get_field_val(NULL, event, "state", &rec, &val, 0); + if (val == (unsigned int)-1) + consume_blame(cpu); + else + set_wakeup_pending(cpu); + } + else if (strcmp(event->name, "power_start") == 0) { + set_wakeup_pending(cpu); + } + else if (strcmp(event->name, "power_end") == 0) { + consume_blame(cpu); + } + else if (strcmp(event->name, "i915_gem_ring_dispatch") == 0 + || strcmp(event->name, "i915_gem_request_submit") == 0) { + /* any kernel contains only one of the these tracepoints, + * the latter one got replaced by the former one */ + class power_consumer *consumer = NULL; + int flags; + + ret = pevent_get_common_field_val(NULL, event, "common_flags", &rec, &val, 0); + if (ret < 0) + return; + flags = (int)val; + + consumer = current_consumer(cpu); + /* currently we don't count graphic requests submitted from irq contect */ + if ( (flags & TRACE_FLAG_HARDIRQ) || (flags & TRACE_FLAG_SOFTIRQ)) { + consumer = NULL; + } + + + /* if we are X, and someone just woke us, account the GPU op to the guy waking us */ + if (consumer && strcmp(consumer->name(), "process")==0) { + class process *proc = NULL; + proc = (class process *) consumer; + if (comm_is_xorg(proc->comm) && proc->last_waker) { + consumer = proc->last_waker; + } + } + + + + if (consumer) { + consumer->gpu_ops++; + } + } + else if (strcmp(event->name, "writeback_inode_dirty") == 0) { + static uint64_t prev_time; + class power_consumer *consumer = NULL; + int dev; + + consumer = current_consumer(cpu); + + ret = pevent_get_field_val(NULL, event, "dev", &rec, &val, 0); + if (ret < 0) + + return; + dev = (int)val; + + if (consumer && strcmp(consumer->name(), + "process")==0 && dev > 0) { + + consumer->disk_hits++; + + /* if the previous inode dirty was > 1 second ago, it becomes a hard hit */ + if ((time - prev_time) > 1000000000) + consumer->hard_disk_hits++; + + prev_time = time; + } + } +} + +void start_process_measurement(void) +{ + if (!perf_events) { + perf_events = new perf_process_bundle(); + perf_events->add_event("sched:sched_switch"); + perf_events->add_event("sched:sched_wakeup"); + perf_events->add_event("irq:irq_handler_entry"); + perf_events->add_event("irq:irq_handler_exit"); + perf_events->add_event("irq:softirq_entry"); + perf_events->add_event("irq:softirq_exit"); + perf_events->add_event("timer:timer_expire_entry"); + perf_events->add_event("timer:timer_expire_exit"); + perf_events->add_event("timer:hrtimer_expire_entry"); + perf_events->add_event("timer:hrtimer_expire_exit"); + if (!perf_events->add_event("power:cpu_idle")){ + perf_events->add_event("power:power_start"); + perf_events->add_event("power:power_end"); + } + perf_events->add_event("workqueue:workqueue_execute_start"); + perf_events->add_event("workqueue:workqueue_execute_end"); + perf_events->add_event("i915:i915_gem_ring_dispatch"); + perf_events->add_event("i915:i915_gem_request_submit"); + perf_events->add_event("writeback:writeback_inode_dirty"); + } + + first_stamp = ~0ULL; + last_stamp = 0; + perf_events->start(); +} + +void end_process_measurement(void) +{ + if (!perf_events) + return; + + perf_events->stop(); +} + + +static bool power_cpu_sort(class power_consumer * i, class power_consumer * j) +{ + double iW, jW; + + iW = i->Witts(); + jW = j->Witts(); + + if (equals(iW, jW)) { + double iR, jR; + + iR = i->accumulated_runtime - i->child_runtime; + jR = j->accumulated_runtime - j->child_runtime; + + if (equals(iR, jR)) + return i->wake_ups > j->wake_ups; + return (iR > jR); + } + + return (iW > jW); +} + +double total_wakeups(void) +{ + double total = 0; + unsigned int i; + for (i = 0; i < all_power.size() ; i++) + total += all_power[i]->wake_ups; + + total = total / measurement_time; + + + return total; +} + +double total_gpu_ops(void) +{ + double total = 0; + unsigned int i; + for (i = 0; i < all_power.size() ; i++) + total += all_power[i]->gpu_ops; + + + total = total / measurement_time; + + + return total; +} + +double total_disk_hits(void) +{ + double total = 0; + unsigned int i; + for (i = 0; i < all_power.size() ; i++) + total += all_power[i]->disk_hits; + + + total = total / measurement_time; + + + return total; +} + + +double total_hard_disk_hits(void) +{ + double total = 0; + unsigned int i; + for (i = 0; i < all_power.size() ; i++) + total += all_power[i]->hard_disk_hits; + + + total = total / measurement_time; + + + return total; +} + +double total_xwakes(void) +{ + double total = 0; + unsigned int i; + for (i = 0; i < all_power.size() ; i++) + total += all_power[i]->xwakes; + + + total = total / measurement_time; + + + return total; +} + +void process_update_display(void) +{ + unsigned int i; + WINDOW *win; + double pw; + double joules; + int tl; + int tlt; + int tlr; + + int show_power; + int need_linebreak = 0; + + sort(all_power.begin(), all_power.end(), power_cpu_sort); + + show_power = global_power_valid(); + + win = get_ncurses_win("Overview"); + if (!win) + return; + + wclear(win); + + wmove(win, 1,0); + +#if 0 + double sum; + calculate_params(); + sum = 0.0; + sum += get_parameter_value("base power"); + for (i = 0; i < all_power.size(); i++) { + sum += all_power[i]->Witts(); + } + + wprintw(win, _("Estimated power: %5.1f Measured power: %5.1f Sum: %5.1f\n\n"), + all_parameters.guessed_power, global_power(), sum); +#endif + + pw = global_power(); + joules = global_joules(); + tl = global_time_left() / 60; + tlt = (tl /60); + tlr = tl % 60; + + if (pw > 0.0001) { + char buf[32]; + wprintw(win, _("The battery reports a discharge rate of %sW\n"), + fmt_prefix(pw, buf)); + wprintw(win, _("The energy consumed was %sJ\n"), + fmt_prefix(joules, buf)); + need_linebreak = 1; + } + if (tl > 0 && pw > 0.0001) { + wprintw(win, _("The estimated remaining time is %i hours, %i minutes\n"), tlt, tlr); + need_linebreak = 1; + } + + if (need_linebreak) + wprintw(win, "\n"); + + + wprintw(win, "%s: %3.1f %s, %3.1f %s, %3.1f %s %3.1f%% %s\n\n",_("Summary"), total_wakeups(), _("wakeups/second"), total_gpu_ops(), _("GPU ops/seconds"), total_disk_hits(), _("VFS ops/sec and"), total_cpu_time()*100, _("CPU use")); + + + if (show_power) + wprintw(win, "%s %s %s %s %s\n", _("Power est."), _("Usage"), _("Events/s"), _("Category"), _("Description")); + else + wprintw(win, " %s %s %s %s\n", _("Usage"), _("Events/s"), _("Category"), _("Description")); + + for (i = 0; i < all_power.size(); i++) { + char power[16]; + char name[20]; + char usage[20]; + char events[20]; + char descr[128]; + + format_watts(all_power[i]->Witts(), power, 10); + if (!show_power) + strcpy(power, " "); + snprintf(name, sizeof(name), "%s", all_power[i]->type()); + + align_string(name, 14, 20); + + if (all_power[i]->events() == 0 && all_power[i]->usage() == 0 && all_power[i]->Witts() == 0) + break; + + usage[0] = 0; + if (all_power[i]->usage_units()) { + if (all_power[i]->usage() < 1000) + snprintf(usage, sizeof(usage), "%5.1f%s", all_power[i]->usage(), all_power[i]->usage_units()); + else + snprintf(usage, sizeof(usage), "%5i%s", (int)all_power[i]->usage(), all_power[i]->usage_units()); + } + + align_string(usage, 14, 20); + + snprintf(events, sizeof(events), "%5.1f", all_power[i]->events()); + if (!all_power[i]->show_events()) + events[0] = 0; + else if (all_power[i]->events() <= 0.3) + snprintf(events, sizeof(events), "%5.2f", all_power[i]->events()); + + align_string(events, 12, 20); + wprintw(win, "%s %s %s %s %s\n", power, usage, events, name, pretty_print(all_power[i]->description(), descr, 128)); + } +} + +void report_process_update_display(void) +{ + unsigned int i; + unsigned int total; + int show_power, cols, rows, idx; + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "software"); + + /* Set Table attributes, rows, and cols */ + cols=7; + sort(all_power.begin(), all_power.end(), power_cpu_sort); + show_power = global_power_valid(); + if (show_power) + cols=8; + + idx=cols; + + total = all_power.size(); + if (total > 100) + total = 100; + + rows=total+1; + table_attributes std_table_css; + init_nowarp_table_attr(&std_table_css, rows, cols); + + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Set array of data in row Major order */ + string *software_data = new string[cols * rows]; + software_data[0]=__("Usage"); + software_data[1]=__("Wakeups/s"); + software_data[2]=__("GPU ops/s"); + software_data[3]=__("Disk IO/s"); + software_data[4]=__("GFX Wakeups/s"); + software_data[5]=__("Category"); + software_data[6]=__("Description"); + + if (show_power) + software_data[7]=__("PW Estimate"); + + + for (i = 0; i < total; i++) { + char power[16]; + char name[20]; + char usage[20]; + char wakes[20]; + char gpus[20]; + char disks[20]; + char xwakes[20]; + char descr[128]; + format_watts(all_power[i]->Witts(), power, 10); + + if (!show_power) + strcpy(power, " "); + snprintf(name, sizeof(name), "%s", all_power[i]->type()); + + if (strcmp(name, "Device") == 0) + continue; + + if (all_power[i]->events() == 0 && all_power[i]->usage() == 0 + && all_power[i]->Witts() == 0) + break; + + usage[0] = 0; + if (all_power[i]->usage_units()) { + if (all_power[i]->usage() < 1000) + snprintf(usage, sizeof(usage), "%5.1f%s", all_power[i]->usage(), all_power[i]->usage_units()); + else + snprintf(usage, sizeof(usage), "%5i%s", (int)all_power[i]->usage(), all_power[i]->usage_units()); + } + snprintf(wakes, sizeof(wakes), "%5.1f", all_power[i]->wake_ups / measurement_time); + if (all_power[i]->wake_ups / measurement_time <= 0.3) + snprintf(wakes, sizeof(wakes), "%5.2f", all_power[i]->wake_ups / measurement_time); + snprintf(gpus, sizeof(gpus), "%5.1f", all_power[i]->gpu_ops / measurement_time); + snprintf(disks, sizeof(disks), "%5.1f (%5.1f)", all_power[i]->hard_disk_hits / measurement_time, + all_power[i]->disk_hits / measurement_time); + snprintf(xwakes, sizeof(xwakes), "%5.1f", all_power[i]->xwakes / measurement_time); + if (!all_power[i]->show_events()) { + wakes[0] = 0; + gpus[0] = 0; + disks[0] = 0; + } + + if (all_power[i]->gpu_ops == 0) + gpus[0] = 0; + if (all_power[i]->wake_ups == 0) + wakes[0] = 0; + if (all_power[i]->disk_hits == 0) + disks[0] = 0; + if (all_power[i]->xwakes == 0) + xwakes[0] = 0; + + software_data[idx]=string(usage); + idx+=1; + + software_data[idx]=string(wakes); + idx+=1; + + software_data[idx]=string(gpus); + idx+=1; + + software_data[idx]=string(disks); + idx+=1; + + software_data[idx]=string(xwakes); + idx+=1; + + software_data[idx]=string(name); + idx+=1; + + software_data[idx]=string(pretty_print(all_power[i]->description(), descr, 128)); + idx+=1; + if (show_power) { + software_data[idx]=string(power); + idx+=1; + } + } + + /* Report Output */ + report.add_div(&div_attr); + report.add_title(&title_attr, __("Overview of Software Power Consumers")); + report.add_table(software_data, &std_table_css); + report.end_div(); + delete [] software_data; +} + +void report_summary(void) +{ + unsigned int i; + unsigned int total; + int show_power; + int rows, cols, idx; + + sort(all_power.begin(), all_power.end(), power_cpu_sort); + show_power = global_power_valid(); + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "summary"); + + + /* Set table attributes, rows, and cols */ + cols=4; + if (show_power) + cols=5; + idx=cols; + total = all_power.size(); + if (total > 10) + total = 10; + rows=total+1; + table_attributes std_table_css; + init_std_table_attr(&std_table_css, rows, cols); + + /* Set title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Set array for summary */ + int summary_size =12; + string *summary = new string [summary_size]; + summary[0]=__("Target:"); + summary[1]=__("1 units/s"); + summary[2]=__("System: "); + summary[3]= double_to_string(total_wakeups()); + summary[3].append(__(" wakeup/s")); + summary[4]=__("CPU: "); + summary[5]= double_to_string(total_cpu_time()*100); + summary[5].append(__("\% usage")); + summary[6]=__("GPU:"); + summary[7]=double_to_string(total_gpu_ops()); + summary[7].append(__(" ops/s")); + summary[8]=__("GFX:"); + summary[9]=double_to_string(total_xwakes()); + summary[9].append(__(" wakeups/s")); + summary[10]=__("VFS:"); + summary[11]= double_to_string(total_disk_hits()); + summary[11].append(__(" ops/s")); + + /* Set array of data in row Major order */ + string *summary_data = new string[cols * (rows + 1)]; + summary_data[0]=__("Usage"); + summary_data[1]=__("Events/s"); + summary_data[2]=__("Category"); + summary_data[3]=__("Description"); + if (show_power) + summary_data[4]=__("PW Estimate"); + + for (i = 0; i < all_power.size(); i++) { + char power[16]; + char name[20]; + char usage[20]; + char events[20]; + char descr[128]; + format_watts(all_power[i]->Witts(), power, 10); + + if (!show_power) + strcpy(power, " "); + snprintf(name, sizeof(name), "%s", all_power[i]->type()); + + if (i > total) + break; + + if (all_power[i]->events() == 0 && all_power[i]->usage() == 0 && + all_power[i]->Witts() == 0) + break; + + usage[0] = 0; + if (all_power[i]->usage_units()) { + if (all_power[i]->usage() < 1000) + snprintf(usage, sizeof(usage), "%5.1f%s", all_power[i]->usage_summary(), + all_power[i]->usage_units_summary()); + else + snprintf(usage, sizeof(usage), "%5i%s", (int)all_power[i]->usage_summary(), + all_power[i]->usage_units_summary()); + } + snprintf(events, sizeof(events), "%5.1f", all_power[i]->events()); + if (!all_power[i]->show_events()) + events[0] = 0; + else if (all_power[i]->events() <= 0.3) + snprintf(events, sizeof(events), "%5.2f", all_power[i]->events()); + + summary_data[idx]=string(usage); + idx+=1; + + summary_data[idx]=string(events); + idx+=1; + + summary_data[idx]=string(name); + idx+=1; + + summary_data[idx]=string(pretty_print(all_power[i]->description(), descr, 128)); + idx+=1; + + if (show_power){ + summary_data[idx]=power; + idx+=1; + } + } + + /* Report Summary for all */ + report.add_summary_list(summary, summary_size); + report.add_div(&div_attr); + report.add_title(&title_attr, __("Top 10 Power Consumers")); + report.add_table(summary_data, &std_table_css); + report.end_div(); + delete [] summary; + delete [] summary_data; +} + + +void process_process_data(void) +{ + if (!perf_events) + return; + + clear_processes(); + clear_interrupts(); + + all_power.erase(all_power.begin(), all_power.end()); + clear_consumers(); + + + cpu_credit.resize(0, 0); + cpu_credit.resize(get_max_cpu()+1, 0); + cpu_level.resize(0, 0); + cpu_level.resize(get_max_cpu()+1, 0); + cpu_blame.resize(0, NULL); + cpu_blame.resize(get_max_cpu()+1, NULL); + + + + /* process data */ + perf_events->process(); + perf_events->clear(); + + run_devpower_list(); + + merge_processes(); + + all_processes_to_all_power(); + all_interrupts_to_all_power(); + all_timers_to_all_power(); + all_work_to_all_power(); + all_devices_to_all_power(); + + sort(all_power.begin(), all_power.end(), power_cpu_sort); +} + + +double total_cpu_time(void) +{ + unsigned int i; + double total = 0.0; + for (i = 0; i < all_power.size() ; i++) { + if (all_power[i]->child_runtime > all_power[i]->accumulated_runtime) + all_power[i]->child_runtime = 0; + total += all_power[i]->accumulated_runtime - all_power[i]->child_runtime; + } + + total = (total / (0.0001 + last_stamp - first_stamp)); + + return total; +} + + + +void end_process_data(void) +{ + report_utilization("cpu-consumption", total_cpu_time()); + report_utilization("cpu-wakeups", total_wakeups()); + report_utilization("gpu-operations", total_gpu_ops()); + report_utilization("disk-operations", total_disk_hits()); + report_utilization("disk-operations-hard", total_hard_disk_hits()); + report_utilization("xwakes", total_xwakes()); + + all_power.erase(all_power.begin(), all_power.end()); + clear_processes(); + clear_proc_devices(); + clear_interrupts(); + clear_timers(); + clear_work(); + clear_consumers(); + + perf_events->clear(); + +} + +void clear_process_data(void) +{ + if (perf_events) + perf_events->release(); + delete perf_events; +} + diff --git a/src/process/interrupt.cpp b/src/process/interrupt.cpp new file mode 100644 index 0000000..a6553b1 --- /dev/null +++ b/src/process/interrupt.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include "process.h" +#include "interrupt.h" +#include "../lib.h" + +const char* softirqs[] = { + "HI_SOFTIRQ", + "timer(softirq)", + "net tx(softirq)", + "net_rx(softirq)", + "block(softirq)", + "block_iopoll(softirq)", + "tasklet(softirq)", + "sched(softirq)", + "hrtimer(softirq)", + "RCU(softirq)", + NULL +}; + + +interrupt::interrupt(const char *_handler, int _number) : power_consumer() +{ + char buf[128]; + running_since = 0; + number = _number; + pt_strcpy(handler, _handler); + raw_count = 0; + snprintf(desc, sizeof(desc), "[%i] %s", number, pretty_print(handler, buf, 128)); +} + + +vector <class interrupt *> all_interrupts; + +void interrupt::start_interrupt(uint64_t time) +{ + running_since = time; + raw_count ++; +} + +uint64_t interrupt::end_interrupt(uint64_t time) +{ + uint64_t delta; + + delta = time - running_since; + accumulated_runtime += delta; + return delta; +} + +const char * interrupt::description(void) +{ + if (child_runtime > accumulated_runtime) + child_runtime = 0; + return desc; +} + +double interrupt::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * interrupt::usage_units_summary(void) +{ + return "%"; +} + + +class interrupt * find_create_interrupt(const char *_handler, int nr, int cpu) +{ + char handler[64]; + unsigned int i; + class interrupt *new_irq; + + pt_strcpy(handler, _handler); + if (strcmp(handler, "timer")==0) + sprintf(handler, "timer/%i", cpu); + + + for (i = 0; i < all_interrupts.size(); i++) { + if (all_interrupts[i] && all_interrupts[i]->number == nr && strcmp(handler, all_interrupts[i]->handler) == 0) + return all_interrupts[i]; + } + + new_irq = new class interrupt(handler, nr); + all_interrupts.push_back(new_irq); + return new_irq; +} + +void all_interrupts_to_all_power(void) +{ + unsigned int i; + for (i = 0; i < all_interrupts.size() ; i++) + if (all_interrupts[i]->accumulated_runtime) + all_power.push_back(all_interrupts[i]); +} + +void clear_interrupts(void) +{ + std::vector<class interrupt *>::iterator it = all_interrupts.begin(); + while (it != all_interrupts.end()) { + delete *it; + it = all_interrupts.erase(it); + } +} diff --git a/src/process/interrupt.h b/src/process/interrupt.h new file mode 100644 index 0000000..8d3a4d7 --- /dev/null +++ b/src/process/interrupt.h @@ -0,0 +1,62 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_INTERRUPT_H +#define _INCLUDE_GUARD_INTERRUPT_H + +#include <stdint.h> + +#include "powerconsumer.h" + +class interrupt : public power_consumer { + uint64_t running_since; + char desc[256]; +public: + char handler[32]; + int number; + + int raw_count; + + interrupt(const char *_handler, int _number); + + virtual void start_interrupt(uint64_t time); + virtual uint64_t end_interrupt(uint64_t time); + + virtual const char * description(void); + + virtual const char * name(void) { return "interrupt"; }; + virtual const char * type(void) { return "Interrupt"; }; + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); +}; + +extern vector <class interrupt *> all_interrupts; +extern const char* softirqs[]; + + +extern class interrupt * find_create_interrupt(const char *_handler, int nr, int cpu); +extern void all_interrupts_to_all_power(void); +extern void clear_interrupts(void); + +#endif diff --git a/src/process/powerconsumer.cpp b/src/process/powerconsumer.cpp new file mode 100644 index 0000000..2ff2132 --- /dev/null +++ b/src/process/powerconsumer.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "powerconsumer.h" +#include "process.h" +#include "../parameters/parameters.h" + +double power_consumer::Witts(void) +{ + double cost; + double timecost; + double wakeupcost; + double gpucost; + double disk_cost; + double hard_disk_cost; + double xwake_cost; + + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + timecost = get_parameter_value("cpu-consumption"); + wakeupcost = get_parameter_value("cpu-wakeups"); + gpucost = get_parameter_value("gpu-operations"); + disk_cost = get_parameter_value("disk-operations"); + hard_disk_cost = get_parameter_value("disk-operations-hard"); + xwake_cost = get_parameter_value("xwakes"); + + cost = 0; + + cost += wakeupcost * wake_ups / 10000.0; + cost += ( (accumulated_runtime - child_runtime) / 1000000000.0) * timecost; + cost += gpucost * gpu_ops / 100.0; + cost += hard_disk_cost * hard_disk_hits / 100.0; + cost += disk_cost * disk_hits / 100.0; + cost += xwake_cost * xwakes / 100.0; + + cost = cost / measurement_time; + + cost += power_charge; + + return cost; +} + +power_consumer::power_consumer(void) +{ + accumulated_runtime = 0; + child_runtime = 0; + disk_hits = 0; + wake_ups = 0; + gpu_ops = 0; + hard_disk_hits = 0; + xwakes = 0; + waker = NULL; + last_waker = NULL; + power_charge = 0.0; +} + +double power_consumer::usage(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time; + if (t < 0.7) + t = t * 1000; + return t; +} + +const char * power_consumer::usage_units(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time; + if (t < 0.7) { + if (utf_ok) + return " µs/s"; + else + return " us/s"; + } + return " ms/s"; +} diff --git a/src/process/powerconsumer.h b/src/process/powerconsumer.h new file mode 100644 index 0000000..0ae384a --- /dev/null +++ b/src/process/powerconsumer.h @@ -0,0 +1,79 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_POWER_CONSUMER_ +#define __INCLUDE_GUARD_POWER_CONSUMER_ + +#include <stdint.h> +#include <vector> +#include <algorithm> + +using namespace std; + +extern double measurement_time; + +class power_consumer; + +class power_consumer { + +public: + uint64_t accumulated_runtime; + uint64_t child_runtime; + int disk_hits; + int wake_ups; + int gpu_ops; + int hard_disk_hits; /* those which are likely a wakeup of the disk */ + int xwakes; + + double power_charge; /* power consumed by devices opened by this process */ + class power_consumer *waker; + class power_consumer *last_waker; + + power_consumer(void); + virtual ~power_consumer() {}; + + virtual double Witts(void); + virtual const char * description(void) { return ""; }; + + virtual const char * name(void) { return "abstract"; }; + virtual const char * type(void) { return "abstract"; }; + + virtual double usage(void); + virtual const char * usage_units(void); + + virtual double usage_summary(void) { return usage();}; + virtual const char * usage_units_summary(void) { return usage_units(); }; + virtual double events(void) { return (wake_ups + gpu_ops + hard_disk_hits) / measurement_time;}; + virtual int show_events(void) { return 1; }; +}; + +extern vector <class power_consumer *> all_power; + +extern double total_wakeups(void); +extern double total_cpu_time(void); +extern double total_gpu_ops(void); + + + +#endif
\ No newline at end of file diff --git a/src/process/process.cpp b/src/process/process.cpp new file mode 100644 index 0000000..dad90ba --- /dev/null +++ b/src/process/process.cpp @@ -0,0 +1,246 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + + + +#include "process.h" +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +#include <iostream> +#include <fstream> +#include <algorithm> +#include <iterator> +#include "../lib.h" + + +vector <class process *> all_processes; + +void process::account_disk_dirty(void) +{ + disk_hits++; +} + +void process::schedule_thread(uint64_t time, int thread_id) +{ + running_since = time; + running = 1; +} + + +uint64_t process::deschedule_thread(uint64_t time, int thread_id) +{ + uint64_t delta; + + if (!running_since) + return 0; + + delta = time - running_since; + + if (time < running_since) + printf("%llu time %llu since \n", (unsigned long long)time, + (unsigned long long)running_since); + + if (thread_id == 0) /* idle thread */ + delta = 0; + accumulated_runtime += delta; + running = 0; + + return delta; +} + +static void cmdline_to_string(std::string& str) +{ + std::replace(str.begin(), str.end(), '\0', ' '); +} + + +process::process(const char *_comm, int _pid, int _tid) : power_consumer() +{ + char line[4097]; + ifstream file; + ssize_t pos; + + pt_strcpy(comm, _comm); + pid = _pid; + is_idle = 0; + running = 0; + last_waker = NULL; + waker = NULL; + is_kernel = 0; + tgid = _tid; + + if (_tid == 0) { + sprintf(line, "/proc/%i/status", _pid); + file.open(line); + while (file) { + file.getline(line, 4096); + line[4096] = '\0'; + if (strstr(line, "Tgid")) { + char *c; + c = strchr(line, ':'); + if (!c) + continue; + c++; + tgid = strtoull(c, NULL, 10); + break; + } + } + file.close(); + } + + if (strncmp(_comm, "kondemand/", 10) == 0) + is_idle = 1; + + pos = snprintf(desc, sizeof(desc), "[PID %d] ", pid); + + if (pos < 0) + pos = 0; + if ((size_t)pos > sizeof(desc)) + return; + + strncpy(desc + pos, comm, sizeof(desc) - pos - 1); + desc[sizeof(desc) - 1] = '\0'; + + sprintf(line, "/proc/%i/cmdline", _pid); + file.open(line, ios::binary); + if (file) { + std::string cmdline(std::istreambuf_iterator<char>(file), (std::istreambuf_iterator<char>())); + file.close(); + if (cmdline.size() < 1) { + is_kernel = 1; + snprintf(desc + pos, sizeof(desc) - pos, "[%s]", comm); + } else { + cmdline_to_string(cmdline); + strncpy(desc + pos, cmdline.c_str(), sizeof(desc) - pos - 1); + desc[sizeof(desc) - 1] = '\0'; + } + } +} + +const char * process::description(void) +{ + + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + return desc; +} + +double process::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * process::usage_units_summary(void) +{ + return "%"; +} + +class process * find_create_process(const char *comm, int pid) +{ + unsigned int i; + class process *new_proc; + + for (i = 0; i < all_processes.size(); i++) { + if (all_processes[i]->pid == pid && strcmp(comm, all_processes[i]->comm) == 0) + return all_processes[i]; + } + + new_proc = new class process(comm, pid); + all_processes.push_back(new_proc); + return new_proc; +} + +class process * find_create_process(char *comm, int pid) +{ + return find_create_process((const char*)comm, pid); +} + +static void merge_process(class process *one, class process *two) +{ + one->accumulated_runtime += two->accumulated_runtime; + one->child_runtime += two->child_runtime; + one->wake_ups += two->wake_ups; + one->disk_hits += two->disk_hits; + one->hard_disk_hits += two->hard_disk_hits; + one->xwakes += two->xwakes; + one->gpu_ops += two->gpu_ops; + one->power_charge += two->power_charge; +} + + +void merge_processes(void) +{ + std::vector<class process*>::iterator it1, it2; + class process *one, *two; + + it1 = all_processes.begin(); + while (it1 != all_processes.end()) { + it2 = it1 + 1; + one = *it1; + while (it2 != all_processes.end()) { + two = *it2; + /* fold threads */ + if (one->pid == two->tgid && two->tgid != 0) { + merge_process(one, two); + delete *it2; + it2 = all_processes.erase(it2); + continue; + } + /* find dupes and add up */ + if (!strcmp(one->desc, two->desc)) { + merge_process(one, two); + delete *it2; + it2 = all_processes.erase(it2); + continue; + } + ++it2; + } + ++it1; + } +} + +void all_processes_to_all_power(void) +{ + unsigned int i; + for (i = 0; i < all_processes.size() ; i++) + if (all_processes[i]->accumulated_runtime || + all_processes[i]->power_charge) + all_power.push_back(all_processes[i]); +} + +void clear_processes(void) +{ + std::vector <class process *>::iterator it = all_processes.begin(); + while (it != all_processes.end()) { + delete *it; + it = all_processes.erase(it); + } +} diff --git a/src/process/process.h b/src/process/process.h new file mode 100644 index 0000000..66293f7 --- /dev/null +++ b/src/process/process.h @@ -0,0 +1,96 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_PROCESS_H +#define _INCLUDE_GUARD_PROCESS_H + +#include <stdint.h> + +#include "powerconsumer.h" + +#ifdef __x86_64__ +#define BIT64 1 +#endif + +/* +Need to collect + * CPU time consumed by each application + * Number of wakeups out of idle -- received and wakeups sent + * Number of disk dirties (inode for now) + */ +class process : public power_consumer { + uint64_t running_since; +public: + char desc[256]; + int tgid; + char comm[16]; + int pid; + + + int is_idle; /* count this as if the cpu was idle */ + int running; + int is_kernel; /* kernel thread */ + + process(const char *_comm, int _pid, int _tid = 0); + + virtual void schedule_thread(uint64_t time, int thread_id); + virtual uint64_t deschedule_thread(uint64_t time, int thread_id = 0); + + virtual void account_disk_dirty(void); + + virtual const char * description(void); + virtual const char * name(void) { return "process"; }; + virtual const char * type(void) { return "Process"; }; + + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); + +}; + +extern vector <class process *> all_processes; + +extern double measurement_time; + + +extern void start_process_measurement(void); +extern void end_process_measurement(void); +extern void process_process_data(void); +extern void end_process_data(void); +extern void clear_process_data(void); +extern void merge_processes(void); + +extern class process * find_create_process(const char *comm, int pid); +extern class process * find_create_process(char *comm, int pid); +extern void all_processes_to_all_power(void); + +extern void clear_processes(void); +extern void process_update_display(void); +extern void report_process_update_display(void); +extern void report_summary(void); + + + +extern void clear_timers(void); + +#endif diff --git a/src/process/processdevice.cpp b/src/process/processdevice.cpp new file mode 100644 index 0000000..00f48c8 --- /dev/null +++ b/src/process/processdevice.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include "processdevice.h" +#include "../parameters/parameters.h" +#include <stdio.h> + +vector<class device_consumer *> all_proc_devices; + + +device_consumer::device_consumer(class device *dev) : power_consumer() +{ + device = dev; + power = device->power_usage(&all_results, &all_parameters); + prio = dev->grouping_prio(); +} + + +const char * device_consumer::description(void) +{ + snprintf(str, sizeof(str), "%s", device->human_name()); + return str; +} + +double device_consumer::Witts(void) +{ + return power; +} + +static void add_device(class device *device) +{ + class device_consumer *dev; + unsigned int i; + + /* first check if we want to be shown at all */ + + if (device->show_in_list() == 0) + return; + + /* then check if a device with the same underlying object is already registered */ + for (i = 0; i < all_proc_devices.size(); i++) { + class device_consumer *cdev; + cdev = all_proc_devices[i]; + if (device->real_path[0] != 0 && strcmp(cdev->device->real_path, device->real_path) == 0) { + /* we have a device with the same underlying object */ + + /* aggregate the power */ + cdev->power += device->power_usage(&all_results, &all_parameters); + + if (cdev->prio < device->grouping_prio()) { + cdev->device = device; + cdev->prio = device->grouping_prio(); + } + + return; + } + } + + dev = new class device_consumer(device); + all_power.push_back(dev); + all_proc_devices.push_back(dev); +} + +void all_devices_to_all_power(void) +{ + unsigned int i; + for (i = 0; i < all_devices.size(); i++) + add_device(all_devices[i]); +} + +void clear_proc_devices(void) +{ + std::vector<class device_consumer *>::iterator it = all_proc_devices.begin(); + while (it != all_proc_devices.end()) { + delete *it; + it = all_proc_devices.erase(it); + } +} diff --git a/src/process/processdevice.h b/src/process/processdevice.h new file mode 100644 index 0000000..3392d3e --- /dev/null +++ b/src/process/processdevice.h @@ -0,0 +1,55 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_DEVICE2_H +#define _INCLUDE_GUARD_DEVICE2_H + +#include <stdint.h> + +#include "powerconsumer.h" +#include "../devices/device.h" + +class device_consumer : public power_consumer { + char str[4096]; +public: + int prio; + double power; + class device *device; + device_consumer(class device *dev); + + virtual const char * description(void); + virtual const char * name(void) { return "device"; }; + virtual const char * type(void) { return "Device"; }; + virtual double Witts(void); + virtual double usage(void) { return device->utilization();}; + virtual const char * usage_units(void) {return device->util_units();}; + virtual int show_events(void) { return 0; }; +}; + +extern void all_devices_to_all_power(void); +extern vector<class device_consumer *> all_proc_devices; + +extern void clear_proc_devices(void); + +#endif diff --git a/src/process/timer.cpp b/src/process/timer.cpp new file mode 100644 index 0000000..5d9d2f8 --- /dev/null +++ b/src/process/timer.cpp @@ -0,0 +1,159 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <map> +#include <utility> + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "timer.h" +#include "../lib.h" +#include "process.h" + +using namespace std; + +static bool timer_is_deferred(const char *handler) +{ + FILE *file; + bool ret = false; + char line[4096]; + + file = fopen("/proc/timer_stats", "r"); + if (!file) { + return ret; + } + + while (!feof(file)) { + if (fgets(line, 4096, file) == NULL) + break; + if (strstr(line, handler)) { + ret = (strstr(line, "D,") != NULL); + if (ret == true) + break; + } + } + fclose(file); + return ret; +} + +timer::timer(unsigned long address) : power_consumer() +{ + pt_strcpy(handler, kernel_function(address)); + raw_count = 0; + deferred = timer_is_deferred(handler); +} + + +static map<unsigned long, class timer *> all_timers; +static map<unsigned long, uint64_t> running_since; + +void timer::fire(uint64_t time, uint64_t timer_struct) +{ + running_since[timer_struct] = time; +} + +uint64_t timer::done(uint64_t time, uint64_t timer_struct) +{ + int64_t delta; + + if (running_since.find(timer_struct) == running_since.end()) + return ~0ULL; + + if (running_since[timer_struct] > time) + return 0; + + delta = time - running_since[timer_struct]; + + accumulated_runtime += delta; + + raw_count++; + + return delta; +} + +double timer::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * timer::usage_units_summary(void) +{ + return "%"; +} + + + +static void add_timer(const pair<unsigned long, class timer*>& elem) +{ + all_power.push_back(elem.second); +} + +void all_timers_to_all_power(void) +{ + for_each(all_timers.begin(), all_timers.end(), add_timer); + +} + + +const char * timer::description(void) +{ + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + snprintf(desc, sizeof(desc), "%s", handler); + return desc; +} + + +class timer * find_create_timer(uint64_t func) +{ + class timer * timer; + if (all_timers.find(func) != all_timers.end()) + return all_timers[func]; + + timer = new class timer(func); + all_timers[func] = timer; + return timer; + +} + +void clear_timers(void) +{ + std::map<unsigned long, class timer *>::iterator it = all_timers.begin(); + while (it != all_timers.end()) { + delete it->second; + all_timers.erase(it); + it = all_timers.begin(); + } + running_since.clear(); +} + +bool timer::is_deferred(void) +{ + return deferred; +} diff --git a/src/process/timer.h b/src/process/timer.h new file mode 100644 index 0000000..7718c3b --- /dev/null +++ b/src/process/timer.h @@ -0,0 +1,64 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_TIMER_H +#define _INCLUDE_GUARD_TIMER_H + +#include <stdint.h> + +#include "powerconsumer.h" + +class timer : public power_consumer { + char desc[256]; +public: + char handler[32]; + int raw_count; + bool deferred; + + timer(unsigned long timer_func); + + void fire(uint64_t time, uint64_t timer_struct); + uint64_t done(uint64_t time, uint64_t timer_struct); + bool is_deferred(void); + + virtual const char * description(void); + virtual const char * name(void) { return "timer"; }; + virtual const char * type(void) { return "Timer"; }; + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); + +}; + +class timer_list { +public: + uint64_t timer_address; + uint64_t timer_func; +}; + + +extern void all_timers_to_all_power(void); +extern class timer * find_create_timer(uint64_t func); +extern void clear_timers(void); + +#endif diff --git a/src/process/work.cpp b/src/process/work.cpp new file mode 100644 index 0000000..1b5c71d --- /dev/null +++ b/src/process/work.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#include <map> +#include <utility> + +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include "work.h" +#include "../lib.h" +#include "process.h" + +using namespace std; + + +work::work(unsigned long address) : power_consumer() +{ + pt_strcpy(handler, kernel_function(address)); + raw_count = 0; + snprintf(desc, sizeof(desc), "%s", handler); +} + + +static map<unsigned long, class work *> all_work; +static map<unsigned long, uint64_t> running_since; + +void work::fire(uint64_t time, uint64_t work_struct) +{ + running_since[work_struct] = time; +} + +uint64_t work::done(uint64_t time, uint64_t work_struct) +{ + int64_t delta; + + if (running_since.find(work_struct) == running_since.end()) + return ~0ULL; + + if (running_since[work_struct] > time) + return 0; + + delta = time - running_since[work_struct]; + + accumulated_runtime += delta; + + raw_count++; + + return delta; +} + +double work::usage_summary(void) +{ + double t; + t = (accumulated_runtime - child_runtime) / 1000000.0 / measurement_time / 10; + return t; +} + +const char * work::usage_units_summary(void) +{ + return "%"; +} + + + + +static void add_work(const pair<unsigned long, class work*>& elem) +{ + all_power.push_back(elem.second); +} + +void all_work_to_all_power(void) +{ + for_each(all_work.begin(), all_work.end(), add_work); + +} + +void clear_work(void) +{ + std::map<unsigned long, class work *>::iterator it = all_work.begin(); + while (it != all_work.end()) { + delete it->second; + all_work.erase(it); + it = all_work.begin(); + } + running_since.clear(); +} + + +const char * work::description(void) +{ + if (child_runtime > accumulated_runtime) + child_runtime = 0; + + return desc; +} + + +class work * find_create_work(uint64_t func) +{ + class work * work; + if (all_work.find(func) != all_work.end()) + return all_work[func]; + + work = new class work(func); + all_work[func] = work; + return work; +} diff --git a/src/process/work.h b/src/process/work.h new file mode 100644 index 0000000..ddd7c87 --- /dev/null +++ b/src/process/work.h @@ -0,0 +1,57 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_WORK_H +#define _INCLUDE_GUARD_WORK_H + +#include <stdint.h> + +#include "powerconsumer.h" + +class work : public power_consumer { + char desc[256]; +public: + char handler[32]; + int raw_count; + + work(unsigned long work_func); + + void fire(uint64_t time, uint64_t work_struct); + uint64_t done(uint64_t time, uint64_t work_struct); + + virtual const char * description(void); + virtual const char * name(void) { return "work"; }; + virtual const char * type(void) { return "kWork"; }; + virtual double usage_summary(void); + virtual const char * usage_units_summary(void); + +}; + + +extern void all_work_to_all_power(void); +extern class work * find_create_work(uint64_t func); + +extern void clear_work(void); + +#endif diff --git a/src/report/report-data-html.cpp b/src/report/report-data-html.cpp new file mode 100644 index 0000000..d9b713e --- /dev/null +++ b/src/report/report-data-html.cpp @@ -0,0 +1,128 @@ +#include "report-data-html.h" + +void init_div(struct tag_attr *div_attr, const char *css_class, const char *css_id) +{ + div_attr->css_class=css_class; + div_attr->css_id=css_id; +} + +void +init_top_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->pos_table_title=L; + table_css->table_class="emphasis1"; + table_css->th_class="table_sysinfo"; + table_css->td_class=""; + table_css->tr_class=""; + table_css->title_mod=0; + table_css->rows=rows; + table_css->cols=cols; +} + +void init_title_attr(struct tag_attr *title_attr) +{ + title_attr->css_class="content_title"; + title_attr->css_id=""; +} + +void init_std_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->table_class="emphasis2"; + table_css->tr_class="emph1"; + table_css->th_class="emph_title"; + table_css->td_class=""; + table_css->pos_table_title=T; + table_css->title_mod=0; + table_css->rows=rows; + table_css->cols=cols; +} +void init_std_side_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->table_class="emphasis2 side_by_side_left"; + table_css->tr_class="emph1"; + table_css->th_class="emph_title"; + table_css->td_class=""; + table_css->pos_table_title=T; + table_css->title_mod=0; + table_css->rows=rows; + table_css->cols=cols; +} + + +void init_pkg_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->table_class="emphasis2 side_by_side_left"; + table_css->tr_class=""; + table_css->th_class="title"; + table_css->td_class="package"; + table_css->pos_table_title=T; + table_css->title_mod=0; + table_css->rows=rows; + table_css->cols=cols; +} + +void init_core_table_attr(struct table_attributes *table_css, int title_mod, + int rows, int cols){ + table_css->table_class="emphasis2 side_by_side_left"; + table_css->tr_class=""; + table_css->th_class="title"; + table_css->td_class="core"; + table_css->pos_table_title=TC; + table_css->title_mod=title_mod; + table_css->rows=rows; + table_css->cols=cols; +} + +void init_cpu_table_attr(struct table_attributes *table_css, int title_mod, + int rows, int cols){ + table_css->table_class="emphasis2 side_by_side_left"; + table_css->tr_class=""; + table_css->th_class="title"; + table_css->td_class="cpu"; + table_css->pos_table_title=TLC; + table_css->title_mod=title_mod; + table_css->rows=rows; + table_css->cols=cols; +} + + +void init_nowarp_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->table_class="emphasis2"; + table_css->tr_class="emph1"; + table_css->th_class="emph_title"; + table_css->td_class="no_wrap"; + table_css->pos_table_title=T; + table_css->title_mod=0; + table_css->rows=rows; + table_css->cols=cols; +} + +void init_tune_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->table_class="emphasis2"; + table_css->tr_class="tune"; + table_css->th_class="emph_title"; + table_css->td_class=""; + table_css->pos_table_title=T; + table_css->rows=rows; + table_css->cols=cols; +} + +void init_wakeup_table_attr(struct table_attributes *table_css, int rows, int cols){ + table_css->table_class="emphasis2"; + table_css->tr_class="tune"; + table_css->th_class="emph_title"; + table_css->td_class=""; + table_css->pos_table_title=T; + table_css->rows=rows; + table_css->cols=cols; +} + +/* Other Helper Functions */ +string +double_to_string(double dval) +{ + ostringstream dtmp; + string str; + dtmp << dval; + str= dtmp.str(); + str = str.substr(0, str.find(".")+2); + return str; +} + + diff --git a/src/report/report-data-html.h b/src/report/report-data-html.h new file mode 100644 index 0000000..e506f5a --- /dev/null +++ b/src/report/report-data-html.h @@ -0,0 +1,76 @@ +#ifndef PowerTop_REPORT_DATA_HTML_H_C58C116411234A34AC2EFB8D23A69713 +#define PowerTop_REPORT_DATA_HTML_H_C58C116411234A34AC2EFB8D23A69713 + +#include <string> +#include <sstream> + +using namespace std; + +struct tag_attr { + const char *css_class; + const char *css_id; +}; +/* T:Top, L:Left, TL:Top-Left, TLC: Top-Left-Center */ +enum position { T, L, TL, TC, TLC }; + +struct table_attributes { + const char *table_class; + const char *td_class; + const char *tr_class; + const char *th_class; + position pos_table_title; + int title_mod; + int rows; + int cols; +}; + +struct table_size { + int rows; + int cols; +}; + + +/* Definition of css attributes for the cases that apply to powertop + * html report + * */ + +void +init_div(struct tag_attr *div_attr, const char *css_class, const char *css_id); + +void +init_top_table_attr(struct table_attributes *table_css, int rows, int cols); + +void +init_title_attr(struct tag_attr *title_attr); + +void +init_std_table_attr(struct table_attributes *table_css, int rows, int cols); + +void +init_std_side_table_attr(struct table_attributes *table_css, int rows, + int cols); + +void +init_pkg_table_attr(struct table_attributes *table_css, int rows, int cols); + +void +init_core_table_attr(struct table_attributes *table_css, int title_mod, + int rows, int cols); + +void +init_cpu_table_attr(struct table_attributes *table_css, int title_mod, + int rows, int cols); +void +init_nowarp_table_attr(struct table_attributes *table_css, int rows, int cols); + +void +init_tune_table_attr(struct table_attributes *table_css, int rows, int cols); + +void +init_wakeup_table_attr(struct table_attributes *table_css, int rows, int cols); + +/* Other helper functions */ +string +double_to_string(double dval); + +#endif diff --git a/src/report/report-formatter-base.cpp b/src/report/report-formatter-base.cpp new file mode 100644 index 0000000..4e10603 --- /dev/null +++ b/src/report/report-formatter-base.cpp @@ -0,0 +1,114 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * Common part of report_formatter_csv and report_formatter_html. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +/* Uncomment to disable asserts */ +/*#define NDEBUG*/ + +#include <stdio.h> +#include <assert.h> +#include <stdarg.h> + +#include "report-formatter-base.h" + +/* ************************************************************************ */ + +const char * +report_formatter_string_base::get_result() +{ + return result.c_str(); +} + +/* ************************************************************************ */ + +void +report_formatter_string_base::clear_result() +{ + result.clear(); +} + +/* ************************************************************************ */ + +void +report_formatter_string_base::add(const char *str) +{ + assert(str); + + result += escape_string(str); +} + +/* ************************************************************************ */ + +void +report_formatter_string_base::add_exact(const char *str) +{ + assert(str); + + result += std::string(str); +} + +/* ************************************************************************ */ + +#define LINE_SIZE 8192 + +void +report_formatter_string_base::addv(const char *fmt, va_list ap) +{ + char str[LINE_SIZE]; + + assert(fmt); + + vsnprintf(str, sizeof(str), fmt, ap); + add(str); +} + +/* ************************************************************************ */ + +void +report_formatter_string_base::addf(const char *fmt, ...) +{ + va_list ap; + + assert(fmt); + + va_start(ap, fmt); + addv(fmt, ap); + va_end(ap); +} + +/* ************************************************************************ */ + +void +report_formatter_string_base::addf_exact(const char *fmt, ...) +{ + char str[LINE_SIZE]; + va_list ap; + + assert(fmt); + + va_start(ap, fmt); + vsnprintf(str, sizeof(str), fmt, ap); + add_exact(str); + va_end(ap); +} diff --git a/src/report/report-formatter-base.h b/src/report/report-formatter-base.h new file mode 100644 index 0000000..e35a2ff --- /dev/null +++ b/src/report/report-formatter-base.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * Common part of report_formatter_csv and report_formatter_html. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +#ifndef _REPORT_FORMATTER_BASE_H_ +#define _REPORT_FORMATTER_BASE_H_ + +#include "report-formatter.h" + +class report_formatter_string_base: public report_formatter +{ +public: + virtual const char *get_result(); + virtual void clear_result(); + + virtual void add(const char *str); + virtual void addv(const char *fmt, va_list ap); + +protected: + void add_exact(const char *str); + void addf(const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); + void addf_exact(const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); + + virtual std::string escape_string(const char *str) = 0; + + std::string result; +}; + +#endif /* _REPORT_FORMATTER_BASE_H_ */ diff --git a/src/report/report-formatter-csv.cpp b/src/report/report-formatter-csv.cpp new file mode 100644 index 0000000..0360fd8 --- /dev/null +++ b/src/report/report-formatter-csv.cpp @@ -0,0 +1,163 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * CSV report generator. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +/* Uncomment to disable asserts */ +/*#define NDEBUG*/ + +#include <stdio.h> +#include <assert.h> +#include <stdarg.h> + +#include "report-formatter-csv.h" +#include "report-data-html.h" + + +/* ************************************************************************ */ +report_formatter_csv::report_formatter_csv() +{ + /* Do nothing special */ +} + +/* ************************************************************************ */ +void +report_formatter_csv::finish_report() +{ + /* Do nothing special */ +} + + +string +report_formatter_csv::escape_string(const char *str) +{ + string res; + + assert(str); + + for (const char *i = str; *i; i++) { + switch (*i) { + case '"': + res += '"'; +#ifdef REPORT_CSV_SPACE_NEED_QUOTES + case ' ': +#endif /* REPORT_CSV_SPACE_NEED_QUOTES */ + case '\n': + case REPORT_CSV_DELIMITER: + csv_need_quotes = true; + break; + } + + res += *i; + } + + return res; +} + + + + +/* Report Style */ +void +report_formatter_csv::add_header() +{ + add_exact("____________________________________________________________________\n"); +} + +void +report_formatter_csv::end_header() +{ + /* Do nothing */ +} + +void +report_formatter_csv::add_logo() +{ + add_exact("\t\t\tP o w e r T O P\n"); +} + + +void +report_formatter_csv::add_div(struct tag_attr * div_attr) +{ + add_exact("\n"); +} + +void +report_formatter_csv::end_div() +{ + /*Do nothing*/ +} + +void +report_formatter_csv::add_title(struct tag_attr *title_att, const char *title) +{ + add_exact("____________________________________________________________________\n"); + addf_exact(" * * * %s * * *\n", title); +} + +void +report_formatter_csv::add_navigation() +{ + /* No nav in csv - thinking on table of contents */ +} + +void +report_formatter_csv::add_summary_list(string *list, int size) +{ + int i; + add_exact("\n"); + for (i=0; i < size; i+=2){ + addf_exact("%s %s", list[i].c_str(), list[i+1].c_str()); + if(i < (size - 1)) + add_exact(";"); + } + add_exact("\n"); +} + +void +report_formatter_csv::add_table(string *system_data, struct table_attributes* tb_attr) +{ + int i, j; + int offset=0; + string tmp_str=""; + int empty_row=0; + add_exact("\n"); + for (i=0; i < tb_attr->rows; i++){ + for (j=0; j < tb_attr->cols; j++){ + offset = i * (tb_attr->cols) + j; + tmp_str=system_data[offset]; + + if(tmp_str == " ") + empty_row+=1; + else{ + addf_exact("%s", system_data[offset].c_str()); + if(j < (tb_attr->cols - 1)) + add_exact(";"); + } + } + if(empty_row < tb_attr->cols) + add_exact("\n"); + empty_row=0; + } +} diff --git a/src/report/report-formatter-csv.h b/src/report/report-formatter-csv.h new file mode 100644 index 0000000..7edfc01 --- /dev/null +++ b/src/report/report-formatter-csv.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * CSV report generator. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +#ifndef _REPORT_FORMATTER_CSV_H_ +#define _REPORT_FORMATTER_CSV_H_ + +#include <string> + +#include "report-formatter-base.h" + +/* Offices like semicolon separated values instead of comma */ +#define REPORT_CSV_DELIMITER ',' + +/* "a,b,c" vs "a, b, c" */ +/*#define REPORT_CSV_ADD_SPACE*/ + +/* Whether to escape with quotes empty cell values */ +/*#define REPORT_CSV_ESCAPE_EMPTY*/ + +/* Whether to escape with quotes empty cell values with spaces */ +/*#define REPORT_CSV_SPACE_NEED_QUOTES*/ + +using namespace std; + +/* ************************************************************************ */ + +class report_formatter_csv: public report_formatter_string_base +{ +public: + report_formatter_csv(); + void finish_report(); + + /* Report Style */ + void add_logo(); + void add_header(); + void end_header(); + void add_div(struct tag_attr *div_attr); + void end_div(); + void add_title(struct tag_attr *title_att, const char *title); + void add_navigation(); + void add_summary_list(string *list, int size); + void add_table(string *system_data, struct table_attributes *tb_attr); + +private: + void add_quotes(); + string escape_string(const char *str); + bool csv_need_quotes; + size_t text_start; +}; + +#endif /* _REPORT_FORMATTER_CSV_H_ */ diff --git a/src/report/report-formatter-html.cpp b/src/report/report-formatter-html.cpp new file mode 100644 index 0000000..387bd76 --- /dev/null +++ b/src/report/report-formatter-html.cpp @@ -0,0 +1,376 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * HTML report generator. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +/* Uncomment to disable asserts */ +/*#define NDEBUG*/ + +#include <stdio.h> +#include <assert.h> +#include <stdarg.h> + +#include "report-formatter-html.h" +#include "css.h" /* For HTML-report header */ + +/* ************************************************************************ */ + +#ifdef EXTERNAL_CSS_FILE /* Where is it defined? */ +static const char report_html_alternative_head[] = + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" " + "\"http://www.w3.org/TR/html4/loose.dtd\">\n" + "<html>\n" + "<head>\n" + "<title>PowerTOP report</title>\n" + "<link rel=\"stylesheet\" href=\"powertop.css\">\n" + "</head>\n" + "<body>\n"; +#endif /* EXTERNAL_CSS_FILE */ + +/* ************************************************************************ */ + +static const char report_html_footer[] = + "</body>\n" + "</html>\n"; + +/* ************************************************************************ */ + +void +report_formatter_html::init_markup() +{ + /*here all html code*/ +} + +/* ************************************************************************ */ + +report_formatter_html::report_formatter_html() +{ + init_markup(); + add_doc_header(); +} + +/* ************************************************************************ */ + +void +report_formatter_html::finish_report() +{ + add_doc_footer(); +} + +/* ************************************************************************ */ + +void +report_formatter_html::add_doc_header() +{ +#ifdef EXTERNAL_CSS_FILE /* Where is it defined? */ + add_exact(report_html_alternative_head); +#else /* !EXTERNAL_CSS_FILE */ + add_exact(css); +#endif /* !EXTERNAL_CSS_FILE */ +} + +/* ************************************************************************ */ + +void +report_formatter_html::add_doc_footer() +{ + add_exact(report_html_footer); +} + +/* ************************************************************************ */ +string +report_formatter_html::escape_string(const char *str) +{ + string res; + + assert(str); + + for (const char *i = str; *i; i++) { + switch (*i) { + case '<': + res += "<"; + continue; + case '>': + res += ">"; + continue; + case '&': + res += "&"; + continue; +#ifdef REPORT_HTML_ESCAPE_QUOTES + case '"': + res += """; + continue; + case '\'': + res += "'"; + continue; +#endif /* REPORT_HTML_ESCAPE_QUOTES */ + } + + res += *i; + } + + return res; +} + + +/* Report Style */ +void +report_formatter_html::add_logo() +{ + add_exact("<img alt=\"PowerTop\" class=\"pwtop_logo\" src=\"data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAAbQAAABDCAYAAAD01PBTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA" + "GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAGsxJREFUeNrsXQt4FNXZ/nLhmutq" + "VdAKMXJHTMhiLGAuLUEfSg2xSFSUgIkkRPQ3QiHxqcRwswQrRq2EBKjhapvU3yCgtaQ+CRGUSyAB" + "vNDfRhArqEA2FwjksvnPN0xokpnZPWd2dnd297w851kyt3OZ+c4773e+c8aro6MDODg4ODg4XB3e" + "vAk4ODg4ODihcXBwcHBwcELj4ODg4ODQDr68CWxCrPgbTlJwj33l4m81SSbeVBwcHBz2hRcPCqEG" + "ElaCSGKYBldXV4PJZILO325MF3uN68LDwyE4OLheJLjOVM2bk4ODg4MTmqMxB4mMENa08vJyKC0t" + "FQispqaG+gKDBw8WiA1JLiEhAUJCQk6TzXkklZJ0ijcxBwcHByc0e6qxDEyExILy8vJgx44dml08" + "LCwMMjIyBHIj6m0T2ZTDiY2Dwz3xzsEbM3tsWkV5ahVJJV3/fizyQhlvURsJzcvLaw/5iXNQmcrE" + "dP1vUsYqRzUIyQuJLKeoqCho6dKlcPr0abvlFRQUJBDbc88910lsGaStVY23kfMyGQwFcQOpa52K" + "fIrJzwwVRVSbXwH5SdU6Hwc/0ywoIeVP1FHZe3aqNtsj6eDVPkMOvxeEQBJV1A+fVyPjc8uCWpIK" + "8V6Q8mnaN5KyO7Wvt7U+egwKievZoMSAQTQqNPYSe2Ta3t6OgR1FFRUVYcnJyXYlsk7U19cDkiYq" + "wOzs7NmE2HCMbo6Pj0+pyoeDBaFiZwUqzlMDo4oysuZXpYY0dQa9ld8oJi3t0eAi96JWhRJb5YBy" + "hXbmQ/JEG84lRFDigs+6pK8n9bn+bKmpkyuF7eMbXTExpsMkGTUls7b2DFOd6eiC5xeETZo0iZrM" + "vG+6EXxHD4e+UydD34d+DYFD7oDFixZD9pJsiI+Ph+joaGpiW7hwIURERATVVNe8R8pTSlIwo7Ks" + "YuwMbSEmR57HUk53cMfUuqA9xtnxnur+5YJ0wjNIuuggMpOzq2JUViSFgntghlinwyQx9RuuGLaP" + "FUQjSiOdeKGtF2trayuqqamZPX36dKtE5tW/H/SOjIBe946FXqNHgJdf/277J9x4C6yceL/kPKL6" + "hITjcMeOHVO8Pu4zjjPCxo0bpyUlJZWTss3x9fVliYisZSAO5offxhcJNfkZWBWaGxhznQva4x5G" + "ezS4y73QkfsUXyr+TcqT6KJqTbGvJ3VKI3WierZceWJ1ATEi1Q9Sa2trMEnVmzZtmm1NlaEK83sm" + "BQxb3gK/Z1MEUutJZohdsVNRKUkSKrUlS5bA4cOH4dChQzBr1iyLZUtJSYHk5OQwcm45KWO4nRSK" + "GnIKtfHhtHd+tW5gxHUeYI8u73IknawBFQTobyywWCYIxdVRgCrY3QkNsUp8i2fC1atXg81mczkh" + "s7CnnnpKcPnJwSfkdghYthgCl2VCn19OpLo2ua7FNGbMGFi/fj2cPHkSnnjiCcXrbNmyBUkt6MKF" + "CxWkvLSkxqJQ1JCTLQrNqOJesZSxzpHBQ5zQFEnNYEXRhLrJvdhjoz3YtV90Q1LDOlntP1yd0NA4" + "mCOJUPls3rw5bO7cubL70bXY/8nHIOjVpYJrkRY/f2CSrEKTS4MGDRKI7aOPPhL+L4etW7dCampq" + "IJb3ypUrNKRWxth2jlRoas73tPEzV1eZBgp7dHlCIx3rKh2TWVcCMIL7gKqvd4elr7CSubQHNzc3" + "FxH1E5aWlqaoyvyfSQGfOwYxF+TurOeBdV5fVFQUHDhwABYtWiQQWE/s3LkTCPEGFRYWlpKyh/fr" + "189kgajryBtyFaWxGcixoeQclg7UVgOJY1SRLIquyk0M19WjNDOt2KOruBvhscgLtTJkNkOsIyuu" + "h6iT61L1VySvuC42pybgBN2P40h+dW5iG1b7endYnDiUNlih+fLljGM1NbORPGTZffRwwb2ohswQ" + "x1a9hqzCnIICA6GwoABW5+YqKrWVK1YMJseWUhqO5gSlIkDD0QrNHcbPwA2mHRisRD26ekAIK7Gg" + "PSKpTEYioyUzkfjKOs8hCedKZNlD1bhSX29NdWqt0GqJQd6p9mRiCGonJFp987906VJ4fX39a4mP" + "PAINDQ3ShhgxVCAztWg5eAQufvRP2Lxli8WxMUt4ev58CAwKgnnz5kn2rXz5ZbgvKiqGKLocPz+/" + "HI069lA7HauVwjMydhz2gE3PtJPhLHu0NOewhDZiTUG10E78rSX5aHrfxHEpFjtAIsrSKn8kNlIG" + "bNdihnJkknMK7aDSbGpfGyafW+zrdaXQMOyXJPQFTtb6TZ5ctyg1NRW+/fZbyb7ekWMhcOULcHF6" + "Mlx6c6MqMmvK/ZPwfySjlStXUo+l9UyPP/44rFu3TjYfdJOaTKaXmpqaLI2n2SswRAt/vFHr+9pZ" + "ZzdQNnpUi532OE6r++zi7i+WzrdESzLr0n5o34lA75o2gA5VGr7UkKR5X++tU0MqY5TXFivZ2NiY" + "sXfv3rDdu3dL9uGYGYbkC41x041wtXwfM6m1f3Om29+VlZWqCQ3TzJkzIT09XZIPkrFIlnkW2o5l" + "grWjFRr1XDYc39OBOuP47zOV5ehnRU8QFQVtvdBLkmZHMhBWB2E4JZMmQtBJxKZpX6/nMTRNIvYa" + "GhqCiUHmyLnxMJoRA0A655T1eyRB+EVS06ATsCmtWrUK7rrrLsl18/PzcQJ2DKlXggZtx6KYtIqY" + "itPiwe0Brs7sj0KtOh0XBctqKLn2VqLiWBzt8IIB9LluqaZ9va4JjVFpKL59mM3mjK1btwadOXNG" + "sg8JrGsACM41w3lnmNS6HzvJzNp8NJq0bds2CAwMlL5uZWbi/jwLRaB1OxoY5obREFqhLffKhk6R" + "KzT722MdYwfqTuoM60M7gRrbyVErdbCoNN0uBi0qzjotni29RznaVElTXV0wscQMVDs9IazB+Bup" + "+xbnnWFS637s0gPYnAbdfrus63Hfvn1w/NixwaR+sRp08FaJSiQ9a51ULWW+Wis0d5lQ7QqgDjhi" + "dBm7kzordOA4YQlDHxmn8zb2CEKzKRSbcELCrt27FdTZNIvn+o4aDv0DfFW7H63x1enT38LsJ5+A" + "hYueB5OpXvG4zMWZcDshtp7Iz1+H+zM0ULc0HQ+NOquivF+hGpaLRY1ycKgFi7vdYc+jSJy0L68G" + "nU+01mTajbc7P4XmDnPG9u3bZdWZpRVAmtIXQEvFfrjc2Cb83Tx7LpNSE1yOHWbF9Ld3/wbhY8Ph" + "/R0fwJ83boKhw4fA8RPHFY9fvHix1A3yl3egzlQ37cKFCyE2PiCaERolkRooA0M4oXG4IqE52v1d" + "Zad6uCTcltDOnz8fYjKZwj788EPJPjlXYyda8wuh5cfui3FcudTO7H60FPDxcu6ybse2tbbDbx+O" + "Vzx+ypQpsmNpH3zwAe5PsNGwQjU6poohX05oHO5IaFVOmJZg7/VbOaFpCNWDy9jRY4cvqfBNNwqr" + "5cuec+kyNH18QOZa135Z3I+WCO3KlSuS4y/8VA9r89+QPR7JDEmtJ3AaAtkfa+ODbtTomCqGfC0a" + "FuWYnbPeiDk8CGJACO2z6IzVatyF0DQJJNI7oVEHBsgQSiwGT/SEEpkhWt4qgA6zdC3GXgZ/5uhH" + "S4Q2zig/T/WVV/6oeM6jjz4qOX7//v24b5oOFFrXic00BmbU6L7X8gnVurRHWZv0gDo7nNAYFWGo" + "G7RznUsSmjjOYlBbyQ6zOVyO0PDjnEq4cvQL2e39lyy6Hv3YqdQskZpAQmazYtpQ8Db06dtbcl5T" + "YzMc+Owz2XPG/+IXErcjLuF1/Phx+OHcuViZMtTRvr1ZGtMSo9UMDIasBaFRu3gcYWikDTo0Tv92" + "tV6ddS1PN3rRYFEOzqqzS0+nEINVDFq0sZ4VWpzaG3ru7Lng+vqGwd99952U0BSCQVqPHIP2lnYp" + "mYUOBJ/B/40yxFVFOkP6LRu05ZS5SH5y/Btr1yieM2H8BMnxJ46fwH3hdlRpLO5GWiK1FhhC+3Dz" + "8TPHIVWtPXJVqhtC06tCi9OqrrokNHG17lVqK0n0UThGDfYELnOlSEAywSMCAT7TfYURnHwdvO4V" + "q+7HDiv/5qXPIyqtl+S8z/YfVDxn9F2jJcef+e4M7gu244NOYwRlKog0VAPD4+NnLmCPHBwW1Jmm" + "z5avzgxH7QrMtT3cHcFyX6H2uflnihdo/Uaq5nz6+HZTZ3JKD5WaHFHSfBfNeM9Y2F95sNs2dDsq" + "nTtq1CipQjtxQovAEFsVWq2KfPG6JbYQGp9QrVt79NT7wsdz6YlMk77e3oQmjDc4oX26vambzeZw" + "7OilCk35O2ftzS2SbX1uu8lipuh+bP5rKbSfOiMhM1y6yhrGR0ZJCA3x+eefw8iRIyXbAwICJNuQ" + "uJXywg6f3A80MmsuPKMNaklupXtbIx1Zpglw6M8e3aljd6cxNCSSULkPl6p5tsi1nN7X94Q7zEOT" + "Ln3Uwd7ObVdapY1z60CL53S6H/slTpNjE6vp3shI2esKwSwyx1++dElJqqh+ADRQaFUyRFpLYdxx" + "CqqANviAuxv1ixLeBBz26OvFdR/dmtByZTpU2U6+7V/Xgstw7Kvr+JfSOFjTJ0fAGGCAvuu2XD8H" + "/4/buv49sE/fbuc1trVC0rPz4fZBg4R0qOFit7/x/7gNPzYqh+J3C8F09RA8k5F0/Rz8+72dm2S4" + "rM2ae5NGyRjk1t6jXNGjViXhGBTW++MTql0bZeILDQeH3fv6nvB18QqiCiiUIzS5Lv6p8VGQZfwV" + "wKlT3XcU/QoGh4RIjn/44Yfh1WFjAda/Lb1Yl2u8lpcHXZe+P9ncBGeTHoIbSBLyPXkEoMvfNeI2" + "DCxpzF4tufT0+U1w/OJceCQTSLpB2IZ/j/9tO+z43x6kAz7WCI3lUzK1KsilzALhzLBTnpzQXLjT" + "ccE+hhbOCounjl7UyN2om77e3RRamtx8F7O5Q7aT7927t7BPLvn28pEc/58fvlE8vmvq6NDWlezn" + "L39bmhqlY2UdYn2VwLBQcagC4ViEhcAMtUtg0RhnHVcBukSJ+HFeT4WBPwL26+tpJpG7skJDMpP1" + "1ROKMckFUOBEZHntBsJE57bW5m7bTn75teLxWqDtxEnZ7YOH+TBdh6KMtRTkZFBBLlVW8lRDojSd" + "giM7TVyN5E7en1Ap5jTeDBx2IjOqcVlvFzWcyaSTKbSgGqrlQty/+OILxaWlQkJ/Ljn+4vl6IYqQ" + "5gvTatBy8Ihk288GKN+S0/+STvzGaEisrwYEYFSh0Mos3AOaCdZxKhUadzfqT5mNc9NlyFg8AXp3" + "ObqaV0Po6wmZUX8t3ZUIDRk6UTQcix10h9lsuvXWW+UJTWE5qrFh8usrrs1/0+IyVpjURFWafzwv" + "CfdHjDL2YiK0QKJEsb4aEEA38qKMNrTWgdEEhhgYSZQTmn5QJr5cJrpxHd1pDM1VXjiEvp4Q2TiS" + "mLwxenQ5VkH3sN8y1gm0dw4ZUi2nmhobGwGXw7rtttsk+57/n4WwdfM7ku2bijbD4t9lWcyvZ07e" + "fv2tlrH5rztkt4+L6QW+XkHg7dUbWsw/9SC0NsnxkZGRqIbKNVBoArl0ecvWInSe5r7F9bjfXKHp" + "u3PPFRV4rofUmUXVOHxpKfFrAPaoi1P6emth+Y4mNN2MN5ByVJDOPubgwe4Tl/+xZw/MmT1bcjyO" + "ud12+83wnzM/dtve0tIGz2akwxuvrVXMa3ZSEuwh1/3qq6/Aq38/+XlpXdD6+Veya0Giu3FcTG9o" + "66i/zpLeXn2IjO4DZ783wen/kyq0ESNGWHU5IkkRsqoCulXuq7oQjcXOjeJFg2kOnEIYv8QI+Ar7" + "2tgjae/DQL8QdC65VpanNSAGIhDSoFmcwCmEBmwf7dSS0GpJ2+hubNltP/BJBFp5pMzE5bI9ZYpz" + "nRc+L2+vf//gn1BcXKJ4XkBAIOwovaa4DFveAp87lFckEb65tupN2X0xv+kj2WbuuEoIrgEOV7TI" + "khnJu2bosGEmiiZhHUcz2GocIvHUMeTJJ1Q7FolA74bKJASY6aHtRP1tQUbF5MqEpku4LaGZO8zl" + "kyZNkmw/eOigsKAv2S9J+BHNmwfcIHu9l3KyobyiXPa8zmS1gydk1pCdCx2Xm2XV2ZRH+yqeu3fX" + "Vck2JGysp4YPcyiDodASSxmDQYZyo3SoFwPbkiUycRXlZHtPJTQaz4YzCc3tXfVuS2hEvZQPHz68" + "Xi445L333lOMVixc92d0xUjOaW83Q1paGixd/pLiuX1iJyqWB92MpnmLZANBEEkL+kP/AC/ZfV8c" + "aZV1NyYkJGC+RRo+zCyEVqtRvqFdAkMM3CgdTmo4hsEyHlYsE8jDCU0dwdgEUQ3SEmidreNTnNCc" + "b61FD5FOvyc2b94MjQ0Nsv7DYUOHQnJKkuIl/7K9GGIIcVWUl0vO9Xs2RaoUfzwvLK2FK4LIKTNE" + "9NQ+wtiZEt5dLz2PkDWMGD789MiRI6vpmoJqgrVAaGKHZdDIyGmUXBxlZ1DHV9i3C6llAduXGfZ4" + "WBOxuLlTHeh2nAH0kZUe4ar3dnNDzZs1a5ZkO0Y7biKkpqS0FmQsgphfjle87k8/1kH600/DveMj" + "IWPhs1BOyA2jJ3FtR3Qrohq7smsPNOW+Cab0xRY/BjoywhfmZfsp7kd19uURaXRjEqkXgzqjfaiN" + "WhML5XGhMgrRY43SSWAZTzOSl55VntIw4goVtAsuG8D6km9agWVM0yMWjHZrQhs1evQpPz+/imnx" + "8ZJ9a9euFeal4adX5NKfXi+AyPFjLV6/seES/OPvH8PT8+fD/Q88cK2nT3pGUGOX334HWg4etXj+" + "4KG9YOHqAIvHbFlzWbIN3ajx8fH1pJx5WrtOxDESo63XYTzeSElofPzMfi9/rONpGCQyw4OaiOVl" + "KtPeKk38nhjL/DOu0NzDUCEnPf1p2X0vLlli8QsvG9ZtggcT7rdLudDN+IetAeAfoBwIgq5GubGz" + "9HnpWL68u8aMMdnBKEPBtiWv1OQbqrGbk0MdqbGOpxV4SpCIuFoFyxfgC+xIZnh9FoWcS7MOIic0" + "F8CYu8eUDxw4cMe8efMk+06ePAm5q3MtLmm1IucVWJ27Gvr2661Jefr7e8GC1f7X3YxmuCrMM+sJ" + "dDW+u0E6djZw4ABBnaE7VUWHRTuO5gyFRtMxcpej/UmNZTwNX0A8KUiEhexnEOLR3C1Lrol2sgfY" + "ViXxmO/TeXuEkUJHxsyZMxsIsUn2bdu2DXa8v0NY4Ffp3/0P3A+ffXoQ4hN+Db16+6gqAxLZ9Kf6" + "wRulwZIAkJ6k9tNZM6xZ1CR7neXLV2CZcu4Ou9uksjmsvWXOAO1dfzREZG08wFkTqoWvPjswFevA" + "ZFjG0+yqRlxYpQnPNCGgPSIJaUFm6GY8DGwTuLNc+JMxzPD1hEqGhYWdqj569LVlS5e+NDc1VbI/" + "OztbWJMxXmasrSuWZq8QEob9by/eBKe/+R5aW9oskhiS16gIX4iWmTQtR2pNjVcImTXC5Sbp0l2E" + "lMEYEVFB6pNnQ3OUWVFDmkcaiiuV1FoxRGvhx55ilE53DeF4GrlfOJ5GS64zcNK1hyyHlcXQLp3P" + "9WFCRmWdL3aEYKjaSSTCTrtQo/bQZgrBg+DrKRUNHzs2hxhcbFpqakxBofQev5STI6w2Ff/gg1av" + "hfO/ErpMB0CCu9zcDLs/fh0eTu0nbAsZ6qs4r0wJP5xtJmR2SXbcbOiwIfC7hQvR1TjHxqawdRyq" + "zIZ8Q51YbleBLogbx9MISeUCfSQdTrquVfqkkxuptBJCNCzt0pXY4kSiclSEaKKnjJ11wtuTKkuM" + "LSE1NbU+NiZGdn8OITV0QdJ8LqZrQnKb+dhjkL0ukKixXkJiJTNcSf+FJ+oJmbVK9vn794c1f1yD" + "ec0ZGxFxykmEZGuHW+XkcnOFxm4vWYz3rYByPU5XJ7Us0P8LVpYnTKT2aEKLMBpNqNKys7MbcAK1" + "HF5dswYWLFx47WOgjMS2btklVeXCaMYXZtXLuhn9/PtCYcF6GDhw4FJS/lINOqk6G41R7bm2EJIn" + "TajW2xs1y3iaJwWJTNYxqWXRujU5obk4jOPGVfv7B8zOz18HQxVIraKiAh6Mj4f3398JZnMHddq7" + "+ypTWTCSEVWZXDTjNTLrAwX5G2DIkKGbSLlzNGwGW8hFlRFTRlhqrQo5odn+AsQ6Pw3Hfdw+SARd" + "efi9LtBfBGGip5KZRxIa4p7Ie0oDAvyfXJef3xARESF7TFNTEyxbvgymTYuHXbt2wrXvuVhLdED3" + "Iqq5FemNsuNlCH9/P4HMhg0buomUd47GTaCWIOrEDs7R+XpSuL7uyFvF/LQZnrIyPyGPREYVay+g" + "jdyJY3zgwfD21IrfExlZ5O/vH5O/dm391KlTFY87e+4cIbblED9tGhSuXy/MXVNyOW4/cIPidS43" + "dggr5i9PbxDci5bUHCrHrVu24u9SUs45dqi+M9yGtpzvSWMBuhzEVzGehkEicZ5ww5BESELjd8b3" + "4qpEVTbZk8LzleDryZWPvPfe6s8+/TT2xd//vig6Kips+YoVgjKTwzlCbBs2bBDSgAEDAJUdjsN1" + "ui2HDRsGH354RfgEDCqwS01mOP+9GU6R/3+psFq+HB5JTISUlJSGgICA2aR8pfaoN7r/SGdD+9FC" + "LYnFWUTqMtD5x0tRiRxmeG5wPC2R1Mkj7p/o6ssV54uh6zXVjioeQ7XLPDHwwxK8UFl4Oj7dvz+Y" + "/OScPXv2ubzXX4fKykqHlwFJ8sUXX4SIsWNryJ8J4ydMOMUfTw4O1wchuK7uV3wZoHXH9pxHVkUI" + "jK+WwwmNDvv37YslP0VHjx4dvHHjRjhaXW33PP38/SAlOQWVWT2S6oSJE/P4neDg4ODghKYJ9n2y" + "bw6SCxJbcXExVH6ivWJDRZacnAzRUdHg7+//OuY38b6JJt76HBwcHJzQNMcnlZVIbBnnzp0L21tZ" + "Kbgiq21QbbcMuBmi74uGKVOm4NjbaVSDJOXdFxXFiYyDg4ODE5r9Ubl3bwj5QXLD9a7C0BVZffQo" + "NDY1wddffy0c0yT+f8Att8AAcRFkorxg6JAhMIQkDB4hqgxJrJyk0qjo6FLeshwcHByc0JyGvRUV" + "GEASTlIsSSFiQuD2MJJwLKxTxpnE/wspOibmFG9BDg4ODk5oHBwcHBwcivDmTcDBwcHB4Q74fwEG" + "APNNNssrHc8WAAAAAElFTkSuQmCC\">"); +} + +void +report_formatter_html::add_header() +{ + add_exact("<header id=\"main_header\">\n"); +} + +void +report_formatter_html::end_header() +{ + add_exact("</header>\n\n"); +} + +void +report_formatter_html::add_div(struct tag_attr * div_attr) +{ + string empty=""; + string tmp_str; + + if (div_attr->css_class == empty && div_attr->css_id == empty) + add_exact("<div>\n"); + + else if (div_attr->css_class == empty && div_attr->css_id != empty) + addf_exact("<div id=\"%s\">\n", div_attr->css_id); + + else if (div_attr->css_class != empty && div_attr->css_id == empty) + addf_exact("<div class=\"%s\">\n", div_attr->css_class); + + else if (div_attr->css_class != empty && div_attr->css_id != empty) + addf_exact("<div class=\"%s\" id=\"%s\">\n", + div_attr->css_class, div_attr->css_id); +} + +void +report_formatter_html::end_div() +{ + add_exact("</div>\n"); +} + +void +report_formatter_html::add_title(struct tag_attr *title_att, const char *title) +{ + addf_exact("<h2 class=\"%s\"> %s </h2>\n", title_att->css_class, title); +} + +void +report_formatter_html::add_navigation() +{ + add_exact("<br/><div id=\"main_menu\"> </div>\n"); +} + +void +report_formatter_html::add_summary_list(string *list, int size) +{ + int i; + add_exact("<div><br/> <ul>\n"); + for (i=0; i < size; i+=2){ + addf_exact("<li class=\"summary_list\"> <b> %s </b> %s </li>", + list[i].c_str(), list[i+1].c_str()); + } + add_exact("</ul> </div> <br />\n"); +} + + +void +report_formatter_html::add_table(string *system_data, struct table_attributes* tb_attr) +{ + int i, j; + int offset=0; + string empty=""; + + if (tb_attr->table_class == empty) + add_exact("<table>\n"); + else + addf_exact("<table class=\"%s\">\n", tb_attr->table_class); + + for (i=0; i < tb_attr->rows; i++){ + if (tb_attr->tr_class == empty) + add_exact("<tr> "); + else + addf_exact("<tr class=\"%s\"> ", tb_attr->tr_class); + + for (j=0; j < tb_attr->cols; j++){ + offset = i * (tb_attr->cols) + j; + + if (tb_attr->pos_table_title == T && i==0) + addf_exact("<th class=\"%s\"> %s </th> ", + tb_attr->th_class,system_data[offset].c_str()); + else if (tb_attr->pos_table_title == L && j==0) + addf_exact("<th class=\"%s\"> %s </th> ", + tb_attr->th_class, system_data[offset].c_str()); + else if (tb_attr->pos_table_title == TL && ( i==0 || j==0 )) + addf_exact("<th class=\"%s\"> %s </th> ", + tb_attr->th_class, system_data[offset].c_str()); + else if (tb_attr->pos_table_title == TC && ((i % tb_attr->title_mod ) == 0)) + addf_exact("<th class=\"%s\"> %s </th> ", tb_attr->th_class, + system_data[offset].c_str()); + else if (tb_attr->pos_table_title == TLC && ((i % tb_attr->title_mod) == 0 || j==0)) + addf_exact("<th class=\"%s\"> %s </th> ", tb_attr->th_class, + system_data[offset].c_str()); + else + if ( tb_attr->td_class == empty) + addf_exact("<td > %s </td> ", system_data[offset].c_str()); + else + addf_exact("<td class=\"%s\"> %s </td> ", tb_attr->td_class, + system_data[offset].c_str()); + } + add_exact("</tr>\n"); + } + add_exact("</table>\n"); +} + diff --git a/src/report/report-formatter-html.h b/src/report/report-formatter-html.h new file mode 100644 index 0000000..c003fd9 --- /dev/null +++ b/src/report/report-formatter-html.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * HTML report generator. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +#ifndef _REPORT_FORMATTER_HTML_H_ +#define _REPORT_FORMATTER_HTML_H_ + +#include <string> + +#include "report-formatter-base.h" +#include "report-data-html.h" + +using namespace std; + +/* Whether to replace " and ' in HTML by " and ' respectively */ +/*#define REPORT_HTML_ESCAPE_QUOTES*/ + +/* ************************************************************************ */ + +struct html_section { + const char *id; +}; + +/* ************************************************************************ */ + +class report_formatter_html: public report_formatter_string_base +{ +public: + report_formatter_html(); + void finish_report(); + + /* Report Style */ + void add_logo(); + void add_header(); + void end_header(); + void add_div(struct tag_attr *div_attr); + void end_div(); + void add_title(struct tag_attr *title_att, const char *title); + void add_navigation(); + void add_summary_list(string *list, int size); + void add_table(string *system_data, struct table_attributes *tb_attr); + +private: + /* Document structure related functions */ + void init_markup(); + void add_doc_header(); + void add_doc_footer(); + string escape_string(const char *str); + +}; + +#endif /* _REPORT_FORMATTER_HTML_H_ */ diff --git a/src/report/report-formatter.h b/src/report/report-formatter.h new file mode 100644 index 0000000..940b5cf --- /dev/null +++ b/src/report/report-formatter.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * report_formatter interface. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +#ifndef _REPORT_FORMATTER_H_ +#define _REPORT_FORMATTER_H_ + +#include "report-maker.h" +using namespace std; + +class report_formatter +{ +public: + virtual ~report_formatter() {} + + virtual void finish_report() {} + virtual const char *get_result() {return "Basic report_formatter::get_result() call\n";} + virtual void clear_result() {} + + virtual void add(const char *str) {} + virtual void addv(const char *fmt, va_list ap) {} + + /* *** Report Style *** */ + virtual void add_logo() {} + virtual void add_header() {} + virtual void end_header() {} + virtual void add_div(struct tag_attr *div_attr) {} + virtual void end_div() {} + virtual void add_title(struct tag_attr *att_title, const char *title) {} + virtual void add_navigation() {} + virtual void add_summary_list(string *list, int size) {} + virtual void add_table(string *system_data, + struct table_attributes *tb_attr) {} +}; + +#endif /* _REPORT_FORMATTER_H_ */ diff --git a/src/report/report-maker.cpp b/src/report/report-maker.cpp new file mode 100644 index 0000000..4049a54 --- /dev/null +++ b/src/report/report-maker.cpp @@ -0,0 +1,187 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * Generic report generator. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +/* Uncomment to disable asserts */ +/*#define NDEBUG*/ + +#include <assert.h> +#include <stdarg.h> + +#include "report-maker.h" +#include "report-formatter-csv.h" +#include "report-formatter-html.h" + +/* ************************************************************************ */ + +report_maker::report_maker(report_type t) +{ + type = t; + formatter = NULL; + setup_report_formatter(); + clear_result(); /* To reset state and add document header */ +} + +/* ************************************************************************ */ + +report_maker::~report_maker() +{ + delete formatter; +} + +/* ************************************************************************ */ + +void +report_maker::finish_report() +{ + formatter->finish_report(); +} + +/* ************************************************************************ */ + +const char * +report_maker::get_result() +{ + return formatter->get_result(); +} + +/* ************************************************************************ */ + +void +report_maker::clear_result() +{ + formatter->clear_result(); +} + +/* ************************************************************************ */ + +report_type +report_maker::get_type() +{ + return type; +} + +/* ************************************************************************ */ + +void +report_maker::set_type(report_type t) +{ + clear_result(); + type = t; + setup_report_formatter(); +} + +/* ************************************************************************ */ + +void +report_maker::setup_report_formatter() +{ + delete formatter; + + if (type == REPORT_HTML) + formatter = new report_formatter_html(); + else if (type == REPORT_CSV) + formatter = new report_formatter_csv(); + else if (type == REPORT_OFF) + formatter = new report_formatter(); + else + assert(false); +} + +/* ************************************************************************ */ + +void +report_maker::add(const char *str) +{ + assert(str); + formatter->add(str); +} + +/* ************************************************************************ */ + +void +report_maker::addf(const char *fmt, ...) +{ + va_list ap; + assert(fmt); + va_start(ap, fmt); + formatter->addv(fmt, ap); + va_end(ap); +} + +/* *** Report Style *** */ +void +report_maker::add_logo() +{ + formatter->add_logo(); +} + +void +report_maker::add_header() +{ + formatter->add_header(); +} + +void +report_maker::end_header() +{ + formatter->end_header(); +} + +void +report_maker::add_title(struct tag_attr *att_title, const char *title) +{ + formatter->add_title(att_title, title); +} + +void +report_maker::add_div(struct tag_attr * div_attr) +{ + formatter->add_div(div_attr); +} + +void +report_maker::end_div() +{ + formatter->end_div(); +} + +void +report_maker::add_navigation() +{ + formatter->add_navigation(); +} + +void +report_maker::add_summary_list(string *list, int size) +{ + formatter->add_summary_list(list, size); +} + +void +report_maker::add_table(string *system_data, struct table_attributes *tb_attr) +{ + formatter->add_table(system_data, tb_attr); +} + diff --git a/src/report/report-maker.h b/src/report/report-maker.h new file mode 100644 index 0000000..bda4cef --- /dev/null +++ b/src/report/report-maker.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * 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 + * or just google for it. + * + * Generic report generator. + * Written by Igor Zhbanov <i.zhbanov@samsung.com> + * 2012.10 */ + +#ifndef _REPORT_MAKER_H_ +#define _REPORT_MAKER_H_ + +/* This report generator implements the following document structure: + * body + * \---> section + * |---> Title + * |---> paragraph + * |---> table + * |---> list + * + * The report body consists of a number of sections (a.k.a. <div>s, + * a.k.a. tabs). + * Each section can contain titles (<h2>), paragraphs (<p>) + * and tables (<table>). + * + * A header is a single line of text. + * + * Paragraphs can contain only text. + * + * + * Each section, table, row or cell could have its own formatting. + * + * Example of usage: + * report_maker report(REPORT_OFF); + * + * report.set_type(REPORT_HTML); + * report.add_div + * report.add_title + * report.add_list + * report.add_table + * report.finish_report(); + * const char *result = report.get_result(); + */ + +#include <stdarg.h> + +#include <string> +using namespace std; +/* Conditional gettext. We need original strings for CSV. */ +#ifdef ENABLE_NLS +#define __(STRING) \ + ((report.get_type() == REPORT_CSV) ? (STRING) : gettext(STRING)) +#else +#define __(STRING) (STRING) +#endif + +#ifndef UNUSED +#define UNUSED __attribute__((unused)) +#endif /* UNUSED */ + +/* ************************************************************************ */ + +enum report_type { + REPORT_OFF, + REPORT_HTML, + REPORT_CSV +}; + +/* ************************************************************************ */ + +enum section_type { + SECTION_DEFAULT, + SECTION_SYSINFO, + SECTION_CPUIDLE, + SECTION_CPUFREQ, + SECTION_DEVFREQ, + SECTION_DEVPOWER, + SECTION_SOFTWARE, + SECTION_SUMMARY, + SECTION_TUNING, + SECTION_MAX /* Must be last in this enum */ +}; + + +/* ************************************************************************ */ + +class report_formatter; + +class report_maker +{ +public: + report_maker(report_type t); + ~report_maker(); + + report_type get_type(); + void set_type(report_type t); + + void addf(const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); + + void finish_report(); + const char *get_result(); + void clear_result(); + + void add(const char *str); + + /* *** Report Style *** */ + void add_header(); + void end_header(); + void add_logo(); + void add_div(struct tag_attr *div_attr); + void end_div(); + void add_title(struct tag_attr *att_title, const char *title); + void add_navigation(); + void add_summary_list(string *list, int size); + void add_table(string *system_data, struct table_attributes *tb_attr); + +private: + void setup_report_formatter(); + report_type type; + report_formatter *formatter; +}; +#endif /* _REPORT_MAKER_H_ */ diff --git a/src/report/report.cpp b/src/report/report.cpp new file mode 100644 index 0000000..2efe835 --- /dev/null +++ b/src/report/report.cpp @@ -0,0 +1,217 @@ +/* + * Copyright 2011, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + * Chris Ferron <chris.e.ferron@linux.intel.com> + */ + +#include "lib.h" +#include "report.h" +#include "report-maker.h" +#include <errno.h> +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <string.h> +#include <malloc.h> +#include <unistd.h> +#include <limits.h> +#include "report-data-html.h" + +using namespace std; + +struct reportstream reportout; +report_type reporttype = REPORT_OFF; +report_maker report(REPORT_OFF); + +string cpu_model(void) +{ + ifstream file; + + file.open("/proc/cpuinfo", ios::in); + + if (!file) + return ""; + + while (file) { + char line[4096]; + file.getline(line, 4096); + if (strstr(line, "model name")) { + char *c; + c = strchr(line, ':'); + if (c) { + file.close(); + c++; + return c; + } + } + } + file.close(); + return ""; +} + +static string read_os_release(const string &filename) +{ + ifstream file; + char content[4096]; + char *c; + const char *pname = "PRETTY_NAME="; + string os(""); + + file.open(filename.c_str(), ios::in); + if (!file) + return ""; + while (file.getline(content, 4096)) { + if (strncasecmp(pname, content, strlen(pname)) == 0) { + c = strchr(content, '='); + if (!c) + break; + c += 1; + if (*c == '"' || *c == '\'') + c += 1; + *strchrnul(c, '"') = 0; + *strchrnul(c, '\'') = 0; + os = c; + break; + } + } + file.close(); + return os; +} + +static void system_info(void) +{ + string str; + char version_date[64]; + time_t now = time(NULL); + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "sys_info", ""); + + /* Set Table attributes, rows, and cols */ + table_attributes sys_table; + init_top_table_attr(&sys_table, 5, 2); + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Set array of data in row Major order */ + string *system_data = new string[sys_table.rows * sys_table.cols]; + system_data[0]=__("PowerTOP Version"); + snprintf(version_date, sizeof(version_date), "%s ran at %s", PACKAGE_VERSION, ctime(&now)); + system_data[1]=version_date; + + str = read_sysfs_string("/proc/version"); + size_t found = str.find(" "); + found = str.find(" ", found+1); + found = str.find(" ", found+1); + str = str.substr(0,found); + system_data[2]=__("Kernel Version"); + system_data[3]=str.c_str(); + + str = read_sysfs_string("/sys/devices/virtual/dmi/id/board_vendor"); + system_data[4]=__("System Name"); + system_data[5]= str.c_str(); + str = read_sysfs_string("/sys/devices/virtual/dmi/id/board_name"); + system_data[5].append(str.c_str()); + str = read_sysfs_string("/sys/devices/virtual/dmi/id/product_version"); + system_data[5].append(str.c_str()); + str = cpu_model(); + system_data[6]=__("CPU Information"); + stringstream n_proc; + n_proc << sysconf(_SC_NPROCESSORS_ONLN); + system_data[7]= n_proc.str(); + system_data[7].append(str.c_str()); + + str = read_sysfs_string("/etc/system-release"); + if (str.length() < 1) + str = read_sysfs_string("/etc/redhat-release"); + if (str.length() < 1) + str = read_os_release("/etc/os-release"); + + system_data[8]=__("OS Information"); + system_data[9]=str; + + /* Report Output */ + report.add_header(); + report.add_logo(); + report.add_div(&div_attr); + report.add_title(&title_attr, __("System Information")); + report.add_table(system_data, &sys_table); + report.end_header(); + report.end_div(); + report.add_navigation(); + delete [] system_data; +} + +void init_report_output(char *filename_str, int iterations) +{ + size_t period; + string filename; + time_t stamp; + char datestr[200]; + + if (iterations == 1) + snprintf(reportout.filename, sizeof(reportout.filename), "%s", filename_str); + else + { + filename = string(filename_str); + period = filename.find_last_of("."); + if (period > filename.length()) + period = filename.length(); + memset(&datestr, 0, 200); + memset(&stamp, 0, sizeof(time_t)); + stamp = time(NULL); + strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M%S", localtime(&stamp)); + snprintf(reportout.filename, sizeof(reportout.filename), "%s-%s%s", + filename.substr(0, period).c_str(), datestr, + filename.substr(period).c_str()); + } + + reportout.report_file = fopen(reportout.filename, "wm"); + if (!reportout.report_file) { + fprintf(stderr, _("Cannot open output file %s (%s)\n"), + reportout.filename, strerror(errno)); + } + + report.set_type(reporttype); + system_info(); +} + +void finish_report_output(void) +{ + if (reporttype == REPORT_OFF) + return; + + report.finish_report(); + if (reportout.report_file) + { + fprintf(stderr, _("PowerTOP outputting using base filename %s\n"), reportout.filename); + fputs(report.get_result(), reportout.report_file); + fdatasync(fileno(reportout.report_file)); + fclose(reportout.report_file); + } + report.clear_result(); +} diff --git a/src/report/report.h b/src/report/report.h new file mode 100644 index 0000000..c1aee1b --- /dev/null +++ b/src/report/report.h @@ -0,0 +1,47 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef __INCLUDE_GUARD_REPORT_H_ +#define __INCLUDE_GUARD_REPORT_H_ + +#include <string> +#include <stdio.h> +#include <limits.h> + +#include "report-maker.h" + +using namespace std; + +struct reportstream { + FILE *report_file; + char filename[PATH_MAX]; +}; + +extern report_type reporttype; +extern report_maker report; +extern struct reportstream reportout; +extern void init_report_output(char *filename_str, int iterations); +extern void finish_report_output(void); + +#endif diff --git a/src/tuning/bluetooth.cpp b/src/tuning/bluetooth.cpp new file mode 100644 index 0000000..2958849 --- /dev/null +++ b/src/tuning/bluetooth.cpp @@ -0,0 +1,228 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include "../lib.h" +#include "bluetooth.h" + +bt_tunable::bt_tunable(void) : tunable("", 1.0, _("Good"), _("Bad"), _("Unknown")) +{ + sprintf(desc, _("Bluetooth device interface status")); + pt_strcpy(toggle_bad, "/usr/sbin/hciconfig hci0 up &> /dev/null &"); + pt_strcpy(toggle_good, "/usr/sbin/hciconfig hci0 down &> /dev/null"); +} + + + +/* structure definitions copied from include/net/bluetooth/hci.h from the 2.6.20 kernel */ +#define HCIGETDEVINFO _IOR('H', 211, int) +#define BTPROTO_HCI 1 + +#define __u16 uint16_t +#define __u8 uint8_t +#define __u32 uint32_t + +typedef struct { + __u8 b[6]; +} __attribute__((packed)) bdaddr_t; + +struct hci_dev_stats { + __u32 err_rx; + __u32 err_tx; + __u32 cmd_tx; + __u32 evt_rx; + __u32 acl_tx; + __u32 acl_rx; + __u32 sco_tx; + __u32 sco_rx; + __u32 byte_rx; + __u32 byte_tx; +}; + + +struct hci_dev_info { + __u16 dev_id; + char name[8]; + + bdaddr_t bdaddr; + + __u32 flags; + __u8 type; + + __u8 features[8]; + + __u32 pkt_type; + __u32 link_policy; + __u32 link_mode; + + __u16 acl_mtu; + __u16 acl_pkts; + __u16 sco_mtu; + __u16 sco_pkts; + + struct hci_dev_stats stat; +}; + +static int previous_bytes = -1; +static time_t last_check_time = 0; +static int last_check_result; + +int bt_tunable::good_bad(void) +{ + struct hci_dev_info devinfo; + FILE *file = 0; + int fd; + int thisbytes = 0; + int ret; + int result = TUNE_GOOD; + + fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (fd < 0) + return TUNE_GOOD; + + memset(&devinfo, 0, sizeof(devinfo)); + strcpy(devinfo.name, "hci0"); + ret = ioctl(fd, HCIGETDEVINFO, (void *) &devinfo); + if (ret < 0) + goto out; + + if ( (devinfo.flags & 1) == 0 && + access("/sys/module/hci_usb",F_OK)) /* interface down already */ + goto out; + + thisbytes += devinfo.stat.byte_rx; + thisbytes += devinfo.stat.byte_tx; + + /* device is active... so we need to leave it on */ + if (thisbytes != previous_bytes) + goto out; + + + /* this check is expensive.. only do it once per minute */ + if (time(NULL) - last_check_time > 60) { + last_check_result = TUNE_BAD; + /* now, also check for active connections */ + file = popen("/usr/bin/hcitool con 2> /dev/null", "r"); + if (file) { + char line[2048]; + /* first line is standard header */ + if (fgets(line, 2047, file) == NULL) + goto out; + memset(line, 0, 2048); + if (fgets(line, 2047, file) == NULL) { + result = last_check_result = TUNE_GOOD; + goto out; + } + + if (strlen(line) > 0) { + result = last_check_result = TUNE_GOOD; + goto out; + } + } + last_check_time = time(NULL); + }; + + result = last_check_result; + +out: + previous_bytes = thisbytes; + if (file) + pclose(file); + close(fd); + return result; +} + +void bt_tunable::toggle(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + if(system("/usr/sbin/hciconfig hci0 up &> /dev/null &")) + printf("System is not available\n"); + return; + } + if(system("/usr/sbin/hciconfig hci0 down &> /dev/null")) + printf("System is not available\n"); +} + +const char *bt_tunable::toggle_script(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + return toggle_bad; + } + return toggle_good; +} + + +void add_bt_tunable(void) +{ + struct hci_dev_info devinfo; + class bt_tunable *bt; + int fd; + int ret; + + /* first check if /sys/modules/bluetooth exists, if not, don't probe bluetooth because + it would trigger an autoload */ + +// if (access("/sys/module/bluetooth",F_OK)) +// return; + + + /* check if hci0 exists */ + fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (fd < 0) + return; + + memset(&devinfo, 0, sizeof(devinfo)); + strcpy(devinfo.name, "hci0"); + ret = ioctl(fd, HCIGETDEVINFO, (void *) &devinfo); + close(fd); + if (ret < 0) + return; + + + bt = new class bt_tunable(); + all_tunables.push_back(bt); +} diff --git a/src/tuning/bluetooth.h b/src/tuning/bluetooth.h new file mode 100644 index 0000000..ecb667d --- /dev/null +++ b/src/tuning/bluetooth.h @@ -0,0 +1,49 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_BLUETOOTH_TUNE_H +#define _INCLUDE_GUARD_BLUETOOTH_TUNE_H + +#include <vector> + +#include "tunable.h" + +using namespace std; + +class bt_tunable : public tunable { +public: + bt_tunable(void); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_bt_tunable(void); + + +#endif diff --git a/src/tuning/ethernet.cpp b/src/tuning/ethernet.cpp new file mode 100644 index 0000000..5b128d1 --- /dev/null +++ b/src/tuning/ethernet.cpp @@ -0,0 +1,157 @@ +;/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <sys/socket.h> +#include <errno.h> +#include <linux/types.h> +#include <net/if.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> + +#include <linux/ethtool.h> + +#include "../lib.h" +#include "ethernet.h" + +extern void create_all_nics(callback fn); + +ethernet_tunable::ethernet_tunable(const char *iface) : tunable("", 0.3, _("Good"), _("Bad"), _("Unknown")) +{ + memset(interf, 0, sizeof(interf)); + pt_strcpy(interf, iface); + sprintf(desc, _("Wake-on-lan status for device %s"), iface); + snprintf(toggle_good, sizeof(toggle_good), "ethtool -s %s wol d;", iface); + +} + + +int ethernet_tunable::good_bad(void) +{ + int sock; + struct ifreq ifr; + struct ethtool_wolinfo wol; + int ret; + int result = TUNE_GOOD; + + memset(&ifr, 0, sizeof(struct ifreq)); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock<0) + return result; + + pt_strcpy(ifr.ifr_name, interf); + + /* Check if the interf is up */ + ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret<0) { + close(sock); + return result; + } + + memset(&wol, 0, sizeof(wol)); + + wol.cmd = ETHTOOL_GWOL; + ifr.ifr_data = (caddr_t)&wol; + ioctl(sock, SIOCETHTOOL, &ifr); + + if (wol.wolopts) + result = TUNE_BAD; + + close(sock); + + return result; +} + +void ethernet_tunable::toggle(void) +{ + int sock; + struct ifreq ifr; + struct ethtool_wolinfo wol; + int ret; + + memset(&ifr, 0, sizeof(struct ifreq)); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock<0) + return; + + pt_strcpy(ifr.ifr_name, interf); + + /* Check if the interface is up */ + ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret<0) { + close(sock); + return; + } + + memset(&wol, 0, sizeof(wol)); + + wol.cmd = ETHTOOL_GWOL; + ifr.ifr_data = (caddr_t)&wol; + ioctl(sock, SIOCETHTOOL, &ifr); + wol.cmd = ETHTOOL_SWOL; + wol.wolopts = 0; + ioctl(sock, SIOCETHTOOL, &ifr); + + close(sock); +} + +const char *ethernet_tunable::toggle_script(void) +{ + int good; + good = good_bad(); + + if (good != TUNE_GOOD) { + return toggle_good; + } + + return NULL; +} + + +void ethtunable_callback(const char *d_name) +{ + class ethernet_tunable *eth; + if (strcmp(d_name, "lo") == 0) + return; + eth = new(std::nothrow) class ethernet_tunable(d_name); + if (eth) + all_tunables.push_back(eth); +} + +void add_ethernet_tunable(void) +{ + create_all_nics(ðtunable_callback); +} diff --git a/src/tuning/ethernet.h b/src/tuning/ethernet.h new file mode 100644 index 0000000..85810fb --- /dev/null +++ b/src/tuning/ethernet.h @@ -0,0 +1,49 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_ETHERNET_TUNE_H +#define _INCLUDE_GUARD_ETHERNET_TUNE_H + +#include <vector> + +#include "tunable.h" + +using namespace std; + +class ethernet_tunable : public tunable { +public: + char interf[4096]; + ethernet_tunable(const char *iface); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_ethernet_tunable(void); + +#endif diff --git a/src/tuning/iw.c b/src/tuning/iw.c new file mode 100644 index 0000000..68eb6dc --- /dev/null +++ b/src/tuning/iw.c @@ -0,0 +1,303 @@ +/* + * This code has been blatently stolen from + * + * nl80211 userspace tool + * + * Copyright 2007, 2008 Johannes Berg <johannes@sipsolutions.net> + * + * and has been stripped down to just the pieces needed. + */ + +/* + +Copyright (c) 2007, 2008 Johannes Berg +Copyright (c) 2007 Andy Lutomirski +Copyright (c) 2007 Mike Kershaw +Copyright (c) 2008-2009 Luis R. Rodriguez + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> + +#include "nl80211.h" +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/msg.h> +#include <netlink/attr.h> + +#include <asm/errno.h> +#include <linux/genetlink.h> +#include "iw.h" + + +#ifndef HAVE_LIBNL20 +/* libnl 2.0 compatibility code */ + +static inline struct nl_handle *nl_socket_alloc(void) +{ + return nl_handle_alloc(); +} + +static inline void nl_socket_free(struct nl_sock *h) +{ + nl_handle_destroy(h); +} + +static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache) +{ + struct nl_cache *tmp = genl_ctrl_alloc_cache(h); + if (!tmp) + return -ENOMEM; + *cache = tmp; + return 0; +} +#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache +#endif /* HAVE_LIBNL20 */ + + +static int nl80211_init(struct nl80211_state *state) +{ + int err; + + state->nl_sock = nl_socket_alloc(); + if (!state->nl_sock) { + fprintf(stderr, "Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + if (genl_connect(state->nl_sock)) { + fprintf(stderr, "Failed to connect to generic netlink.\n"); + err = -ENOLINK; + goto out_handle_destroy; + } + + if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) { + fprintf(stderr, "Failed to allocate generic netlink cache.\n"); + err = -ENOMEM; + goto out_handle_destroy; + } + + state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); + if (!state->nl80211) { + err = -ENOENT; + goto out_cache_free; + } + + return 0; + + out_cache_free: + nl_cache_free(state->nl_cache); + out_handle_destroy: + nl_socket_free(state->nl_sock); + return err; +} + +static void nl80211_cleanup(struct nl80211_state *state) +{ + genl_family_put(state->nl80211); + nl_cache_free(state->nl_cache); + nl_socket_free(state->nl_sock); +} + +static int enable_power_save; + + +static int set_power_save(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg) +{ + enum nl80211_ps_state ps_state; + + ps_state = NL80211_PS_DISABLED; + if (enable_power_save) + ps_state = NL80211_PS_ENABLED; + + NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); + + return 0; + + nla_put_failure: + return -ENOBUFS; +} + +static int print_power_save_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!attrs[NL80211_ATTR_PS_STATE]) + return 0; + + switch (nla_get_u32(attrs[NL80211_ATTR_PS_STATE])) { + case NL80211_PS_ENABLED: + enable_power_save = 1; + break; + case NL80211_PS_DISABLED: + default: + enable_power_save = 0; + break; + } + + return NL_SKIP; +} + +static int get_power_save(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg) +{ + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + print_power_save_handler, NULL); + return 0; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_STOP; +} + +static int __handle_cmd(struct nl80211_state *state, const char *iface, int get) +{ + struct nl_cb *cb; + struct nl_msg *msg; + int devidx = 0; + int err; + + devidx = if_nametoindex(iface); + if (devidx == 0) + devidx = -1; + if (devidx < 0) + return -errno; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + err = 2; + goto out_free_msg; + } + + if (get) + genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + 0, NL80211_CMD_GET_POWER_SAVE, 0); + else + genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + 0, NL80211_CMD_SET_POWER_SAVE, 0); + + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + + if (get) + err = get_power_save(state, cb, msg); + else + err = set_power_save(state, cb, msg); + + if (err) + goto out; + + err = nl_send_auto_complete(state->nl_sock, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + while (err > 0) + nl_recvmsgs(state->nl_sock, cb); + out: + nl_cb_put(cb); + out_free_msg: + nlmsg_free(msg); + return err; + nla_put_failure: + fprintf(stderr, "building message failed\n"); + return 2; +} + + +int set_wifi_power_saving(const char *iface, int state) +{ + struct nl80211_state nlstate; + int err; + + err = nl80211_init(&nlstate); + if (err) + return 1; + + enable_power_save = state; + err = __handle_cmd(&nlstate, iface, 0); + + nl80211_cleanup(&nlstate); + + return err; +} + + +int get_wifi_power_saving(const char *iface) +{ + struct nl80211_state nlstate; + int err; + + enable_power_save = 0; + + err = nl80211_init(&nlstate); + if (err) + return 1; + + err = __handle_cmd(&nlstate, iface, 1); + + nl80211_cleanup(&nlstate); + + if (err) /* not a wifi interface */ + return 1; + + return enable_power_save; +} diff --git a/src/tuning/iw.h b/src/tuning/iw.h new file mode 100644 index 0000000..6ea5a3d --- /dev/null +++ b/src/tuning/iw.h @@ -0,0 +1,69 @@ +#ifndef __IW_H +#define __IW_H + +/* + * This code has been blatently stolen from + * + * nl80211 userspace tool + * + * Copyright 2007, 2008 Johannes Berg <johannes@sipsolutions.net> + * + * and has been stripped down to just the pieces needed. + */ + +/* + +Copyright (c) 2007, 2008 Johannes Berg +Copyright (c) 2007 Andy Lutomirski +Copyright (c) 2007 Mike Kershaw +Copyright (c) 2008-2009 Luis R. Rodriguez + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#include <stdbool.h> + +#define ETH_ALEN 6 + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_LIBNL20 +#define nl_sock nl_handle +#endif + +struct nl80211_state { + struct nl_sock *nl_sock; + struct nl_cache *nl_cache; + struct genl_family *nl80211; +}; + +enum command_identify_by { + CIB_NONE, + CIB_PHY, + CIB_NETDEV, +}; + +enum id_input { + II_NONE, + II_NETDEV, + II_PHY_NAME, + II_PHY_IDX, +}; + +int get_wifi_power_saving(const char *iface); +int set_wifi_power_saving(const char *iface, int state); + +#endif /* __IW_H */ diff --git a/src/tuning/nl80211.h b/src/tuning/nl80211.h new file mode 100644 index 0000000..83b0514 --- /dev/null +++ b/src/tuning/nl80211.h @@ -0,0 +1,1897 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2008 Michael Wu <flamingice@sourmilk.net> + * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com> + * Copyright 2008 Michael Buesch <mb@bu3sch.de> + * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> + * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> + * Copyright 2008 Colin McCabe <colin@cozybit.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <linux/types.h> + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * TODO: need more info? + */ + +/** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * management entities such as wpa_supplicant react to management frames + * that are not being handled by the kernel. This includes, for example, + * certain classes of action frames that cannot be handled in the kernel + * for various reasons. + * + * Frame registration is done on a per-interface basis and registrations + * cannot be removed other than by closing the socket. It is possible to + * specify a registration filter to register, for example, only for a + * certain type of action frame. In particular with action frames, those + * that userspace registers for will not be returned as unhandled by the + * driver, so that the registered application has to take responsibility + * for doing that. + * + * The type of frame that can be registered for is also dependent on the + * driver and interface type. The frame types are advertised in wiphy + * attributes so applications know what to expect. + * + * NOTE: When an interface changes type while registrations are active, + * these registrations are ignored until the interface type is + * changed again. This means that changing the interface type can + * lead to a situation that couldn't otherwise be produced, but + * any such registrations will be dormant in the sense that they + * will not be serviced, i.e. they will not receive any frames. + * + * Frame transmission allows userspace to send for example the required + * responses to action frames. It is subject to some sanity checking, + * but many frames can be transmitted. When a frame was transmitted, its + * status is indicated to the sending socket. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get + * on an %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, + * and %NL80211_ATTR_KEY_SEQ attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a + * %NL80222_CMD_NEW_BEACON message) + * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. + * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, + * parameters are like for %NL80211_CMD_SET_BEACON. + * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * or noise level + * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to + * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) + * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. + * + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included %NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). This event is + * also used to indicate if the authentication attempt timed out. In that + * case the %NL80211_ATTR_FRAME attribute is replaced with a + * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which + * pending authentication timed out). + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). + * + * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael + * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the + * event includes %NL80211_ATTR_MAC to describe the source MAC address of + * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key + * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and + * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this + * event matches with MLME-MICHAELMICFAILURE.indication() primitive + * + * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a + * FREQ attribute (for the initial frequency if no peer can be found) + * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those + * should be fixed rather than automatically determined. Can only be + * executed on a network interface that is UP, and fixed BSSID/FREQ + * may be rejected. Another optional parameter is the beacon interval, + * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not + * given defaults to 100 TU (102.4ms). + * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is + * determined by the network interface. + * + * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute + * to identify the device, and the TESTDATA blob attribute to pass through + * to the driver. + * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, + * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the STATUS_CODE attribute. + * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), + * sent as an event when the card/driver roamed by itself. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * + * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified + * channel for the specified amount of time. This can be used to do + * off-channel operations like transmit a Public Action frame and wait for + * a response while being associated to an AP on another channel. + * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus + * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be + * optionally used to specify additional channel parameters. + * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds + * to remain on the channel. This command is also used as an event to + * notify when the requested duration starts (it may take a while for the + * driver to schedule this time due to other concurrent needs for the + * radio). + * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) + * that will be included with any events pertaining to this request; + * the cookie is also used to cancel the request. + * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a + * pending remain-on-channel duration if the desired operation has been + * completed prior to expiration of the originally requested duration. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the + * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to + * uniquely identify the request. + * This command is also used as an event to notify when a requested + * remain-on-channel duration has expired. + * + * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX + * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface + * and @NL80211_ATTR_TX_RATES the set of allowed rates. + * + * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames + * (via @NL80211_CMD_FRAME) for processing in userspace. This command + * requires an interface index, a frame type attribute (optional for + * backward compatibility reasons, if not given assumes action frames) + * and a match attribute containing the first few bytes of the frame + * that should match, e.g. a single byte for only a category match or + * four bytes for vendor frames including the OUI. The registration + * cannot be dropped, but is removed automatically when the netlink + * socket is closed. Multiple registrations can be made. + * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for + * backward compatibility + * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This + * command is used both as a request to transmit a management frame and + * as an event indicating reception of a frame that was not processed in + * kernel code, but is for us (i.e., which may need to be processed in a + * user space application). %NL80211_ATTR_FRAME is used to specify the + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and + * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on + * which channel the frame is to be transmitted or was received. If this + * channel is not the current channel (remain-on-channel or the + * operational channel) the device will switch to the given channel and + * transmit the frame, optionally waiting for a response for the time + * specified using %NL80211_ATTR_DURATION. When called, this operation + * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the + * TX status event pertaining to the TX request. + * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this + * command may be used with the corresponding cookie to cancel the wait + * time if it is known that it is no longer necessary. + * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. + * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame + * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies + * the TX command and %NL80211_ATTR_FRAME includes the contents of the + * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged + * the frame. + * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for + * backward compatibility. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. + * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed + * by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. + * + * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. + * + * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial + * mesh config parameters may be given. + * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_NEW_BEACON, + NL80211_CMD_DEL_BEACON, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_PARAMS, + NL80211_CMD_SET_MESH_PARAMS, + + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + NL80211_CMD_REG_CHANGE, + + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + + NL80211_CMD_MICHAEL_MIC_FAILURE, + + NL80211_CMD_REG_BEACON_HINT, + + NL80211_CMD_JOIN_IBSS, + NL80211_CMD_LEAVE_IBSS, + + NL80211_CMD_TESTMODE, + + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + + NL80211_CMD_SET_WIPHY_NETNS, + + NL80211_CMD_GET_SURVEY, + NL80211_CMD_NEW_SURVEY_RESULTS, + + NL80211_CMD_SET_PMKSA, + NL80211_CMD_DEL_PMKSA, + NL80211_CMD_FLUSH_PMKSA, + + NL80211_CMD_REMAIN_ON_CHANNEL, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + + NL80211_CMD_SET_TX_BITRATE_MASK, + + NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_FRAME, + NL80211_CMD_ACTION = NL80211_CMD_FRAME, + NL80211_CMD_FRAME_TX_STATUS, + NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, + + NL80211_CMD_SET_POWER_SAVE, + NL80211_CMD_GET_POWER_SAVE, + + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + + NL80211_CMD_SET_CHANNEL, + NL80211_CMD_SET_WDS_PEER, + + NL80211_CMD_FRAME_WAIT_CANCEL, + + NL80211_CMD_JOIN_MESH, + NL80211_CMD_LEAVE_MESH, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE +#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211/<phyname>/index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is + * less than or equal to the RTS threshold; allowed range: 1..255; + * dot11ShortRetryLimit; u8 + * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is + * greater than the RTS threshold; allowed range: 1..255; + * dot11ShortLongLimit; u8 + * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum + * length in octets for frames; allowed range: 256..8000, disable + * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 + * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length + * larger than or equal to this use RTS/CTS handshake); allowed range: + * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 + * section 7.3.2.9; dot11CoverageClass; u8 + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements + * that can be added to a scan request + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 + * + * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as + * a u32 + * + * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * + * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported + * cipher suites + * + * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look + * for other networks on different channels + * + * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this + * is used, e.g., with %NL80211_CMD_AUTHENTICATE event + * + * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is + * used for the association (&enum nl80211_mfp, represented as a u32); + * this attribute can be used + * with %NL80211_CMD_ASSOCIATE request + * + * @NL80211_ATTR_STA_FLAGS2: Attribute containing a + * &struct nl80211_sta_flag_update. + * + * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls + * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in + * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE + * request, the driver will assume that the port is unauthorized until + * authorized by user space. Otherwise, port is marked authorized by + * default in station mode. + * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the + * ethertype that will be used for key negotiation. It can be + * specified with the associate and connect commands. If it is not + * specified, the value defaults to 0x888E (PAE, 802.1X). This + * attribute is also used as a flag in the wiphy information to + * indicate that protocols other than PAE are supported. + * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom + * ethertype frames used for key negotiation must not be encrypted. + * + * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. + * We recommend using nested, driver-specific attributes within this. + * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to + * indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate + * which group key cipher will be used with the connection (a u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate + * which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate + * which key management algorithm(s) to use (an array of u32). + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE + * commands to specify using a reassociate frame + * + * @NL80211_ATTR_KEY: key information in a nested attribute with + * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes + * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * + * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface + * + * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of + * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute + * containing info as possible, see &enum survey_info. + * + * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. + * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can + * cache, a wiphy attribute. + * + * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * + * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. + * + * @NL80211_ATTR_TX_RATES: Nested set of attributes + * (enum nl80211_tx_rate_attributes) describing TX rates per band. The + * enum nl80211_band value is used as the index (nla_type() of the nested + * data. If a band is not included, it will be configured to allow all + * rates based on negotiated supported rates information. This attribute + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * + * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain + * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. + * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the + * @NL80211_CMD_REGISTER_FRAME command. + * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be transmitted with + * %NL80211_CMD_FRAME. + * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be registered for RX. + * + * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was + * acknowledged by the recipient. + * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * + * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command + * is requesting a local authentication/association state change without + * invoking actual management frame exchange. This can be used with + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, + * NL80211_CMD_DISASSOCIATE. + * + * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * + * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See + * &enum nl80211_tx_power_setting for possible values. + * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. + * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING + * for non-automatic settings. + * + * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly + * means support for per-station GTKs. + * + * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. + * This can be used to mask out antennas which are not attached or should + * not be used for transmitting. If an antenna is not selected in this + * bitmap the hardware is not allowed to transmit on this antenna. + * + * Each bit represents one antenna, starting with antenna 1 at the first + * bit. Depending on which antennas are selected in the bitmap, 802.11n + * drivers can derive which chainmasks to use (if all antennas belonging to + * a particular chain are disabled this chain should be disabled) and if + * a chain has diversity antennas whether diversity should be used or not. + * HT capabilities (STBC, TX Beamforming, Antenna selection) can be + * derived from the available chains after applying the antenna mask. + * Non-802.11n drivers can derive wether to use diversity or not. + * Drivers may reject configurations or RX/TX mask combinations they cannot + * support by returning -EINVAL. + * + * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. + * This can be used to mask out antennas which are not attached or should + * not be used for receiving. If an antenna is not selected in this bitmap + * the hardware should not be configured to receive on this antenna. + * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. + * + * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS + * + * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be + * transmitted on another channel when the channel given doesn't match + * the current channel. If the current channel doesn't match and this + * flag isn't set, the frame will be rejected. This is also used as an + * nl80211 capability flag. + * + * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16) + * + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything between, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_PARAMS, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + NL80211_ATTR_BSS, + + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + + NL80211_ATTR_SUPPORTED_COMMANDS, + + NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, + + NL80211_ATTR_KEY_TYPE, + + NL80211_ATTR_MAX_SCAN_IE_LEN, + NL80211_ATTR_CIPHER_SUITES, + + NL80211_ATTR_FREQ_BEFORE, + NL80211_ATTR_FREQ_AFTER, + + NL80211_ATTR_FREQ_FIXED, + + + NL80211_ATTR_WIPHY_RETRY_SHORT, + NL80211_ATTR_WIPHY_RETRY_LONG, + NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + NL80211_ATTR_WIPHY_RTS_THRESHOLD, + + NL80211_ATTR_TIMED_OUT, + + NL80211_ATTR_USE_MFP, + + NL80211_ATTR_STA_FLAGS2, + + NL80211_ATTR_CONTROL_PORT, + + NL80211_ATTR_TESTDATA, + + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + + NL80211_ATTR_PREV_BSSID, + + NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, + + NL80211_ATTR_PID, + + NL80211_ATTR_4ADDR, + + NL80211_ATTR_SURVEY_INFO, + + NL80211_ATTR_PMKID, + NL80211_ATTR_MAX_NUM_PMKIDS, + + NL80211_ATTR_DURATION, + + NL80211_ATTR_COOKIE, + + NL80211_ATTR_WIPHY_COVERAGE_CLASS, + + NL80211_ATTR_TX_RATES, + + NL80211_ATTR_FRAME_MATCH, + + NL80211_ATTR_ACK, + + NL80211_ATTR_PS_STATE, + + NL80211_ATTR_CQM, + + NL80211_ATTR_LOCAL_STATE_CHANGE, + + NL80211_ATTR_AP_ISOLATE, + + NL80211_ATTR_WIPHY_TX_POWER_SETTING, + NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + + NL80211_ATTR_TX_FRAME_TYPES, + NL80211_ATTR_RX_FRAME_TYPES, + NL80211_ATTR_FRAME_TYPE, + + NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + + NL80211_ATTR_SUPPORT_IBSS_RSN, + + NL80211_ATTR_WIPHY_ANTENNA_TX, + NL80211_ATTR_WIPHY_ANTENNA_RX, + + NL80211_ATTR_MCAST_RATE, + + NL80211_ATTR_OFFCHANNEL_TX_OK, + + NL80211_ATTR_BSS_HT_OPMODE, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES +#define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 + +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_P2P_CLIENT: P2P client + * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @NUM_NL80211_IFTYPES: number of defined interface types + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_GO, + + /* keep last */ + NUM_NL80211_IFTYPES, + NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + * @NL80211_STA_FLAG_MAX: highest station flag number currently defined + * @__NL80211_STA_FLAG_AFTER_LAST: internal use + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * struct nl80211_sta_flag_update - station flags mask/set + * @mask: mask of station flags to set + * @set: which values to set them to + * + * Both mask and set contain bits as per &enum nl80211_sta_flags. + */ +struct nl80211_sta_flag_update { + __u32 mask; + __u32 set; +} __attribute__((packed)); + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_sta_info_txrate. + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, + NL80211_STA_INFO_TX_RETRIES, + NL80211_STA_INFO_TX_FAILED, + NL80211_STA_INFO_SIGNAL_AVG, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_SN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination + * @NL80211_MPATH_INFO_SN: destination sequence number + * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path + * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now + * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number + * currently defind + * @__NL80211_MPATH_INFO_AFTER_LAST: internal use + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_SN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined + * @__NL80211_BAND_ATTR_AFTER_LAST: internal use + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_frequency_attr - frequency attributes + * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is + * permitted on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, + NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number + * currently defined + * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure passed by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number + * currently defined + * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + +/** + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_CHANNEL_TIME, + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + + /* keep last */ + __NL80211_SURVEY_INFO_AFTER_LAST, + NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a + * source mesh point for path selection elements. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + NL80211_MESHCONF_HWMP_ROOTMODE, + NL80211_MESHCONF_ELEMENT_TTL, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin); + * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are + * from a Probe Response frame; otherwise they are from a Beacon frame. + * However, if the driver does not indicate the source of the IEs, these + * IEs may be from either frame subtype. + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @NL80211_BSS_STATUS: status, if this BSS is "used" + * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information + * elements from a Beacon frame (bin); not present if no Beacon frame has + * yet been received + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + NL80211_BSS_STATUS, + NL80211_BSS_SEEN_MS_AGO, + NL80211_BSS_BEACON_IES, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. + * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. + * + * The BSS status is a BSS attribute in scan dumps, which + * indicates the status the interface has wrt. this BSS. + */ +enum nl80211_bss_status { + NL80211_BSS_STATUS_AUTHENTICATED, + NL80211_BSS_STATUS_ASSOCIATED, + NL80211_BSS_STATUS_IBSS_JOINED, +}; + +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC +}; + +/** + * enum nl80211_key_type - Key Type + * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key + * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key + * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + * @NUM_NL80211_KEYTYPES: number of defined key types + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, + + NUM_NL80211_KEYTYPES +}; + +/** + * enum nl80211_mfp - Management frame protection state + * @NL80211_MFP_NO: Management frame protection not used + * @NL80211_MFP_REQUIRED: Management frame protection required + */ +enum nl80211_mfp { + NL80211_MFP_NO, + NL80211_MFP_REQUIRED, +}; + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + +/** + * enum nl80211_key_attributes - key attributes + * @__NL80211_KEY_INVALID: invalid + * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_KEY_IDX: key ID (u8, 0-3) + * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_KEY_DEFAULT: flag indicating default key + * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not + * specified the default depends on whether a MAC address was + * given with the command using the key or not (u32) + * @__NL80211_KEY_AFTER_LAST: internal + * @NL80211_KEY_MAX: highest key attribute + */ +enum nl80211_key_attributes { + __NL80211_KEY_INVALID, + NL80211_KEY_DATA, + NL80211_KEY_IDX, + NL80211_KEY_CIPHER, + NL80211_KEY_SEQ, + NL80211_KEY_DEFAULT, + NL80211_KEY_DEFAULT_MGMT, + NL80211_KEY_TYPE, + + /* keep last */ + __NL80211_KEY_AFTER_LAST, + NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +}; + +/** + * enum nl80211_tx_rate_attributes - TX rate set attributes + * @__NL80211_TXRATE_INVALID: invalid + * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection + * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with + * 1 = 500 kbps) but without the IE length restriction (at most + * %NL80211_MAX_SUPP_RATES in a single array). + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ: 2.4 GHz ISM band + * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, +}; + +enum nl80211_ps_state { + NL80211_PS_DISABLED, + NL80211_PS_ENABLED, +}; + +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many + * consecutive packets were not acknowledged by the peer + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + NL80211_ATTR_CQM_PKT_LOSS_EVENT, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + + +/** + * enum nl80211_tx_power_setting - TX power adjustment + * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power + * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter + * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter + */ +enum nl80211_tx_power_setting { + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_LIMITED, + NL80211_TX_POWER_FIXED, +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/src/tuning/runtime.cpp b/src/tuning/runtime.cpp new file mode 100644 index 0000000..cb62533 --- /dev/null +++ b/src/tuning/runtime.cpp @@ -0,0 +1,202 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include "unistd.h" +#include "runtime.h" +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> + +#include "../lib.h" +#include "../devices/runtime_pm.h" + +runtime_tunable::runtime_tunable(const char *path, const char *bus, const char *dev, const char *port) : tunable("", 0.4, _("Good"), _("Bad"), _("Unknown")) +{ + ifstream file; + sprintf(runtime_path, "%s/power/control", path); + + + sprintf(desc, _("Runtime PM for %s device %s"), bus, dev); + if (!device_has_runtime_pm(path)) + sprintf(desc, _("%s device %s has no runtime power management"), bus, dev); + + if (strcmp(bus, "pci") == 0) { + char filename[PATH_MAX]; + uint16_t vendor = 0, device = 0; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/vendor", bus, dev); + + file.open(filename, ios::in); + if (file) { + file >> hex >> vendor; + file.close(); + } + + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/device", bus, dev); + file.open(filename, ios::in); + if (file) { + file >> hex >> device; + file.close(); + } + + if (vendor && device) { + if (!device_has_runtime_pm(path)) + sprintf(desc, _("PCI Device %s has no runtime power management"), pci_id_to_name(vendor, device, filename, 4095)); + else + sprintf(desc, _("Runtime PM for PCI Device %s"), pci_id_to_name(vendor, device, filename, 4095)); + } + + if (string(path).find("ata") != string::npos) + sprintf(desc, _("Runtime PM for port %s of PCI device: %s"), port, pci_id_to_name(vendor, device, filename, 4095)); + + if (string(path).find("block") != string::npos) + sprintf(desc, _("Runtime PM for disk %s"), port); + + } + snprintf(toggle_good, sizeof(toggle_good), "echo 'auto' > '%s';", runtime_path); + snprintf(toggle_bad, sizeof(toggle_bad), "echo 'on' > '%s';", runtime_path); +} + +int runtime_tunable::good_bad(void) +{ + string content; + + content = read_sysfs_string(runtime_path); + + if (strcmp(content.c_str(), "auto") == 0) + return TUNE_GOOD; + + return TUNE_BAD; +} + +void runtime_tunable::toggle(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + write_sysfs(runtime_path, "on"); + return; + } + + write_sysfs(runtime_path, "auto"); +} + +const char *runtime_tunable::toggle_script(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + return toggle_bad; + } + + return toggle_good; +} + + +void add_runtime_tunables(const char *bus) +{ + struct dirent *entry; + DIR *dir; + char filename[PATH_MAX], port[PATH_MAX]; + int max_ports = 32, count=0; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/", bus); + dir = opendir(filename); + if (!dir) + return; + while (1) { + class runtime_tunable *runtime, *runtime_ahci_port, *runtime_ahci_disk; + + entry = readdir(dir); + + if (!entry) + break; + if (entry->d_name[0] == '.') + continue; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/power/control", bus, entry->d_name); + + if (access(filename, R_OK) != 0) + continue; + + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s", bus, entry->d_name); + + runtime = new class runtime_tunable(filename, bus, entry->d_name, NULL); + + if (!device_has_runtime_pm(filename)) + all_untunables.push_back(runtime); + else + all_tunables.push_back(runtime); + + for (int i=0; i < max_ports; i++) { + snprintf(port, sizeof(port), "ata%d", i); + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/%s/power/control", bus, entry->d_name, port); + + if (access(filename, R_OK) != 0) + continue; + + snprintf(filename, sizeof(filename), "/sys/bus/%s/devices/%s/%s", bus, entry->d_name, port); + runtime_ahci_port = new class runtime_tunable(filename, bus, entry->d_name, port); + + if (!device_has_runtime_pm(filename)) + all_untunables.push_back(runtime_ahci_port); + else + all_tunables.push_back(runtime_ahci_port); + } + + for (char blk = 'a'; blk <= 'z'; blk++) + { + if (count != 0) + break; + + snprintf(filename, sizeof(filename), "/sys/block/sd%c/device/power/control", blk); + + if (access(filename, R_OK) != 0) + continue; + + snprintf(port, sizeof(port), "sd%c", blk); + snprintf(filename, sizeof(filename), "/sys/block/%s/device", port); + runtime_ahci_disk = new class runtime_tunable(filename, bus, entry->d_name, port); + if (!device_has_runtime_pm(filename)) + all_untunables.push_back(runtime_ahci_disk); + else + all_tunables.push_back(runtime_ahci_disk); + } + count = 1; + + } + closedir(dir); +} diff --git a/src/tuning/runtime.h b/src/tuning/runtime.h new file mode 100644 index 0000000..84a8ef5 --- /dev/null +++ b/src/tuning/runtime.h @@ -0,0 +1,50 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_RUNTIME_TUNE_H +#define _INCLUDE_GUARD_RUNTIME_TUNE_H + +#include <vector> +#include <limits.h> + +#include "tunable.h" +using namespace std; + +class runtime_tunable : public tunable { + char runtime_path[PATH_MAX]; +public: + runtime_tunable(const char *runtime_path, const char *bus, const char *dev, const char *port); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_runtime_tunables(const char *bus); + + +#endif diff --git a/src/tuning/tunable.cpp b/src/tuning/tunable.cpp new file mode 100644 index 0000000..827b913 --- /dev/null +++ b/src/tuning/tunable.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include <string.h> +#include "../lib.h" + +vector<class tunable *> all_tunables; +vector<class tunable *> all_untunables; + + +tunable::tunable(const char *str, double _score, const char *good, const char *bad, const char *neutral) +{ + score = _score; + pt_strcpy(desc, str); + pt_strcpy(good_string, good); + pt_strcpy(bad_string, bad); + pt_strcpy(neutral_string, neutral); +} + + +tunable::tunable(void) +{ + score = 0; + desc[0] = 0; + pt_strcpy(good_string, _("Good")); + pt_strcpy(bad_string, _("Bad")); + pt_strcpy(neutral_string, _("Unknown")); +} diff --git a/src/tuning/tunable.h b/src/tuning/tunable.h new file mode 100644 index 0000000..3372378 --- /dev/null +++ b/src/tuning/tunable.h @@ -0,0 +1,81 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_TUNABLE_H +#define _INCLUDE_GUARD_TUNABLE_H + +#include <vector> + +#include "../lib.h" + +using namespace std; + +#define TUNE_GOOD 1 +#define TUNE_BAD -1 +#define TUNE_UNFIXABLE -2 +#define TUNE_UNKNOWN 0 +#define TUNE_NEUTRAL 0 + +class tunable { + + char good_string[128]; + char bad_string[128]; + char neutral_string[128]; +protected: + char toggle_good[4096]; + char toggle_bad[4096]; +public: + char desc[4096]; + double score; + + tunable(void); + tunable(const char *str, double _score, const char *good = "", const char *bad = "", const char *neutral =""); + virtual ~tunable() {}; + + virtual int good_bad(void) { return TUNE_NEUTRAL; } + + virtual char *result_string(void) + { + switch (good_bad()) { + case TUNE_GOOD: + return good_string; + case TUNE_BAD: + case TUNE_UNFIXABLE: + return bad_string; + } + return neutral_string; + } + + + virtual const char *description(void) { return desc; }; + + virtual void toggle(void) { }; + + virtual const char *toggle_script(void) { return NULL; } +}; + +extern vector<class tunable *> all_tunables; +extern vector<class tunable *> all_untunables; + +#endif diff --git a/src/tuning/tuning.cpp b/src/tuning/tuning.cpp new file mode 100644 index 0000000..bc34741 --- /dev/null +++ b/src/tuning/tuning.cpp @@ -0,0 +1,332 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include <algorithm> + +#include <stdio.h> +#include <string.h> +#include <ncurses.h> + + +#include "tuning.h" +#include "tuningi2c.h" +#include "tuningsysfs.h" +#include "tuningusb.h" +#include "runtime.h" +#include "bluetooth.h" +#include "ethernet.h" +#include "wifi.h" +#include "../display.h" +#include "../report/report.h" +#include "../report/report-maker.h" +#include "../report/report-data-html.h" +#include "../lib.h" + +static void sort_tunables(void); +static bool should_clear = false; + +class tuning_window *tune_window; + +class tuning_window: public tab_window { +public: + virtual void repaint(void); + virtual void cursor_enter(void); + virtual void expose(void); + virtual void window_refresh(void); +}; + +static void init_tuning(void) +{ + add_sysfs_tunable(_("Enable Audio codec power management"), "/sys/module/snd_hda_intel/parameters/power_save", "1"); + add_sysfs_tunable(_("NMI watchdog should be turned off"), "/proc/sys/kernel/nmi_watchdog", "0"); + add_sysfs_tunable(_("Power Aware CPU scheduler"), "/sys/devices/system/cpu/sched_mc_power_savings", "1"); + add_sysfs_tunable(_("VM writeback timeout"), "/proc/sys/vm/dirty_writeback_centisecs", "1500"); + add_sata_tunables(); + add_usb_tunables(); + add_runtime_tunables("pci"); + add_bt_tunable(); + add_wifi_tunables(); + add_i2c_tunables(); + + sort_tunables(); +} + +void initialize_tuning(void) +{ + class tuning_window *w; + + w = new tuning_window(); + create_tab("Tunables", _("Tunables"), w, _(" <ESC> Exit | <Enter> Toggle tunable | <r> Window refresh")); + + init_tuning(); + + w->cursor_max = all_tunables.size() - 1; + + if (tune_window) + delete tune_window; + + tune_window = w; +} + + + +static void __tuning_update_display(int cursor_pos) +{ + WINDOW *win; + unsigned int i; + + win = get_ncurses_win("Tunables"); + + if (!win) + return; + + if (should_clear) { + should_clear = false; + wclear(win); + } + + wmove(win, 2,0); + + for (i = 0; i < all_tunables.size(); i++) { + char res[128]; + char desc[4096]; + pt_strcpy(res, all_tunables[i]->result_string()); + pt_strcpy(desc, all_tunables[i]->description()); + while (strlen(res) < 12) + strcat(res, " "); + + while (strlen(desc) < 103) + strcat(desc, " "); + if ((int)i != cursor_pos) { + wattrset(win, A_NORMAL); + wprintw(win, " "); + } else { + wattrset(win, A_REVERSE); + wprintw(win, ">> "); + } + wprintw(win, "%s %s\n", _(res), _(desc)); + } +} + +void tuning_update_display(void) +{ + class tab_window *w; + + w = tab_windows["Tunables"]; + if (!w) + return; + w->repaint(); +} + +void tuning_window::repaint(void) +{ + __tuning_update_display(cursor_pos); +} + +void tuning_window::cursor_enter(void) +{ + class tunable *tun; + const char *toggle_script; + tun = all_tunables[cursor_pos]; + if (!tun) + return; + /** device will change its state so need to store toggle script before + * we toggle()*/ + toggle_script = tun->toggle_script(); + tun->toggle(); + ui_notify_user(">> %s\n", toggle_script); +} + +static bool tunables_sort(class tunable * i, class tunable * j) +{ + int i_g, j_g; + double d; + + i_g = i->good_bad(); + j_g = j->good_bad(); + + if (!equals(i_g, j_g)) + return i_g < j_g; + + d = i->score - j->score; + if (d < 0.0) + d = -d; + if (d > 0.0001) + return i->score > j->score; + + if (strcasecmp(i->description(), j->description()) == -1) + return true; + + return false; +} + +void tuning_window::window_refresh() +{ + clear_tuning(); + should_clear = true; + init_tuning(); +} + +static void sort_tunables(void) +{ + sort(all_tunables.begin(), all_tunables.end(), tunables_sort); +} + +void tuning_window::expose(void) +{ + cursor_pos = 0; + sort_tunables(); + repaint(); +} + +void report_show_tunables(void) +{ + unsigned int i; + /* three tables; bad, unfixable, good */ + sort_tunables(); + int idx, rows = 0, cols; + + /* First Table */ + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "tuning"); + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Set Table attributes, rows, and cols */ + table_attributes tune_table_css; + cols=2; + idx = cols; + + for (i = 0; i < all_tunables.size(); i++) { + int tgb; + tgb = all_tunables[i]->good_bad(); + if (tgb == TUNE_BAD) + rows+=1; + } + /* add section */ + report.add_div(&div_attr); + + if (rows > 0){ + rows= rows + 1; + init_tune_table_attr(&tune_table_css, rows, cols); + + /* Set array of data in row Major order */ + string *tunable_data = new string[cols * rows]; + + tunable_data[0]=__("Description"); + tunable_data[1]=__("Script"); + + for (i = 0; i < all_tunables.size(); i++) { + int gb; + gb = all_tunables[i]->good_bad(); + if (gb != TUNE_BAD) + continue; + tunable_data[idx]=string(all_tunables[i]->description()); + idx+=1; + tunable_data[idx]=string(all_tunables[i]->toggle_script()); + idx+=1; + } + + /* Report Output */ + report.add_title(&title_attr,__("Software Settings in Need of Tuning")); + report.add_table(tunable_data, &tune_table_css); + delete [] tunable_data; + } + + /* Second Table */ + /* Set Table attributes, rows, and cols */ + cols=1; + rows= all_untunables.size() + 1; + init_tune_table_attr(&tune_table_css, rows, cols); + + /* Set array of data in row Major order */ + string *untunable_data = new string[rows]; + untunable_data[0]=__("Description"); + + for (i = 0; i < all_untunables.size(); i++) + untunable_data[i+1]= string(all_untunables[i]->description()); + + /* Report Output */ + report.add_title(&title_attr,__("Untunable Software Issues")); + report.add_table(untunable_data, &tune_table_css); + delete [] untunable_data; + + /* Third Table */ + /* Set Table attributes, rows, and cols */ + rows = 1; + for (i = 0; i < all_tunables.size(); i++) { + int gb; + gb = all_tunables[i]->good_bad(); + if (gb != TUNE_GOOD) + continue; + rows+=1; + } + cols = 1; + init_tune_table_attr(&tune_table_css, rows, cols); + + /* Set array of data in row Major order */ + string *tuned_data = new string[rows]; + tuned_data[0]=__("Description"); + idx=cols; + for (i = 0; i < all_tunables.size(); i++) { + int gb; + gb = all_tunables[i]->good_bad(); + if (gb != TUNE_GOOD) + continue; + + tuned_data[idx]=string(all_tunables[i]->description()); + idx+=1; + } + /* Report Output */ + report.add_title(&title_attr,__("Optimal Tuned Software Settings")); + report.add_table(tuned_data, &tune_table_css); + report.end_div(); + delete [] tuned_data; +} + +void clear_tuning() +{ + for (size_t i = 0; i < all_tunables.size(); i++) { + delete all_tunables[i]; + } + all_tunables.clear(); + + for (size_t i = 0; i < all_untunables.size(); i++) { + delete all_untunables[i]; + } + all_untunables.clear(); +} + +void auto_toggle_tuning() +{ + for (unsigned int i = 0; i < all_tunables.size(); i++) { + if (all_tunables[i]->good_bad() == TUNE_BAD) + all_tunables[i]->toggle(); + } +} diff --git a/src/tuning/tuning.h b/src/tuning/tuning.h new file mode 100644 index 0000000..f70001b --- /dev/null +++ b/src/tuning/tuning.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_TUNING_H +#define _INCLUDE_GUARD_TUNING_H + +extern void initialize_tuning(void); +extern void tuning_update_display(void); +extern void report_show_tunables(void); +extern void clear_tuning(void); +extern void auto_toggle_tuning(void); +#endif diff --git a/src/tuning/tuningi2c.cpp b/src/tuning/tuningi2c.cpp new file mode 100644 index 0000000..b75e811 --- /dev/null +++ b/src/tuning/tuningi2c.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2015, 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. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + * Daniel Leung <daniel.leung@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include "unistd.h" +#include "tuningi2c.h" +#include <string.h> +#include <dirent.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <ctype.h> +#include <limits.h> + +#include "../lib.h" +#include "../devices/runtime_pm.h" + +i2c_tunable::i2c_tunable(const char *path, const char *name, bool is_adapter) : tunable("", 0.9, _("Good"), _("Bad"), _("Unknown")) +{ + ifstream file; + char filename[PATH_MAX]; + string devname; + + snprintf(filename, sizeof(filename), "%s/name", path); + file.open(filename, ios::in); + if (file) { + getline(file, devname); + file.close(); + } + + if (is_adapter) { + snprintf(i2c_path, sizeof(i2c_path), "%s/device/power/control", path); + snprintf(filename, sizeof(filename), "%s/device", path); + } else { + snprintf(i2c_path, sizeof(i2c_path), "%s/power/control", path); + snprintf(filename, sizeof(filename), "%s/device", path); + } + + if (device_has_runtime_pm(filename)) + snprintf(desc, sizeof(desc), _("Runtime PM for I2C %s %s (%s)"), (is_adapter ? _("Adapter") : _("Device")), name, (devname.empty() ? "" : devname.c_str())); + else + snprintf(desc, sizeof(desc), _("I2C %s %s has no runtime power management"), (is_adapter ? _("Adapter") : _("Device")), name); + + snprintf(toggle_good, sizeof(toggle_good), "echo 'auto' > '%s';", i2c_path); + snprintf(toggle_bad, sizeof(toggle_bad), "echo 'on' > '%s';", i2c_path); +} + +int i2c_tunable::good_bad(void) +{ + string content; + + content = read_sysfs_string(i2c_path); + + if (strcmp(content.c_str(), "auto") == 0) + return TUNE_GOOD; + + return TUNE_BAD; +} + +void i2c_tunable::toggle(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + write_sysfs(i2c_path, "on"); + return; + } + + write_sysfs(i2c_path, "auto"); +} + +const char *i2c_tunable::toggle_script(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + return toggle_bad; + } + + return toggle_good; +} + +static void add_i2c_callback(const char *d_name) +{ + class i2c_tunable *i2c; + char filename[PATH_MAX]; + bool is_adapter = false; + + snprintf(filename, PATH_MAX, "/sys/bus/i2c/devices/%s/new_device", d_name); + if (access(filename, W_OK) == 0) + is_adapter = true; + + snprintf(filename, PATH_MAX, "/sys/bus/i2c/devices/%s", d_name); + i2c = new class i2c_tunable(filename, d_name, is_adapter); + + if (is_adapter) + snprintf(filename, PATH_MAX, "/sys/bus/i2c/devices/%s/device", d_name); + else + snprintf(filename, PATH_MAX, "/sys/bus/i2c/devices/%s", d_name); + + if (device_has_runtime_pm(filename)) + all_tunables.push_back(i2c); + else + all_untunables.push_back(i2c); +} + +void add_i2c_tunables(void) +{ + process_directory("/sys/bus/i2c/devices/", add_i2c_callback); +} diff --git a/src/tuning/tuningi2c.h b/src/tuning/tuningi2c.h new file mode 100644 index 0000000..8fd8784 --- /dev/null +++ b/src/tuning/tuningi2c.h @@ -0,0 +1,46 @@ +/* + * Copyright 2015, 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. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + * Daniel Leung <daniel.leung@linux.intel.com> + */ + +#ifndef _INCLUDE_GUARD_I2C_TUNE_H +#define _INCLUDE_GUARD_I2C_TUNE_H + +#include <vector> +#include <limits.h> + +#include "tunable.h" + +using namespace std; + +class i2c_tunable : public tunable { + char i2c_path[PATH_MAX]; +public: + i2c_tunable(const char *path, const char *name, bool is_adapter); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_i2c_tunables(void); + + +#endif diff --git a/src/tuning/tuningsysfs.cpp b/src/tuning/tuningsysfs.cpp new file mode 100644 index 0000000..631e9fd --- /dev/null +++ b/src/tuning/tuningsysfs.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include "unistd.h" +#include "tuningsysfs.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> +#include <limits.h> + + +#include "../lib.h" + +sysfs_tunable::sysfs_tunable(const char *str, const char *_sysfs_path, const char *_target_content) : tunable(str, 1.0, _("Good"), _("Bad"), _("Unknown")) +{ + pt_strcpy(sysfs_path, _sysfs_path); + pt_strcpy(target_value, _target_content); + bad_value[0] = 0; + snprintf(toggle_good, sizeof(toggle_good), "echo '%s' > '%s';", target_value, sysfs_path); + snprintf(toggle_bad, sizeof(toggle_bad), "echo '%s' > '%s';", bad_value, sysfs_path); +} + +int sysfs_tunable::good_bad(void) +{ + char current_value[4096], *c; + ifstream file; + + file.open(sysfs_path, ios::in); + if (!file) + return TUNE_NEUTRAL; + file.getline(current_value, 4096); + file.close(); + + c = strchr(current_value, '\n'); + if (c) + *c = 0; + + if (strcmp(current_value, target_value) == 0) + return TUNE_GOOD; + + pt_strcpy(bad_value, current_value); + return TUNE_BAD; +} + +void sysfs_tunable::toggle(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + if (strlen(bad_value) > 0) + write_sysfs(sysfs_path, bad_value); + return; + } + + write_sysfs(sysfs_path, target_value); +} + +const char *sysfs_tunable::toggle_script(void) { + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + if (strlen(bad_value) > 0) + return toggle_bad; + return NULL; + } + + return toggle_good; +} + + +void add_sysfs_tunable(const char *str, const char *_sysfs_path, const char *_target_content) +{ + class sysfs_tunable *tunable; + + if (access(_sysfs_path, R_OK) != 0) + return; + + tunable = new class sysfs_tunable(str, _sysfs_path, _target_content); + + + all_tunables.push_back(tunable); +} + +static void add_sata_tunables_callback(const char *d_name) +{ + char filename[PATH_MAX]; + char msg[4096]; + + snprintf(filename, sizeof(filename), "/sys/class/scsi_host/%s/link_power_management_policy", d_name); + snprintf(msg, sizeof(msg), _("Enable SATA link power management for %s"), d_name); + add_sysfs_tunable(msg, filename, "med_power_with_dipm"); +} + +void add_sata_tunables(void) +{ + process_directory("/sys/class/scsi_host", add_sata_tunables_callback); +} diff --git a/src/tuning/tuningsysfs.h b/src/tuning/tuningsysfs.h new file mode 100644 index 0000000..57b9de7 --- /dev/null +++ b/src/tuning/tuningsysfs.h @@ -0,0 +1,53 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_SYSFS_TUNE_H +#define _INCLUDE_GUARD_SYSFS_TUNE_H + +#include <vector> +#include <limits.h> + +#include "tunable.h" + +using namespace std; + +class sysfs_tunable : public tunable { + char sysfs_path[PATH_MAX]; + char target_value[4096]; + char bad_value[4096]; +public: + sysfs_tunable(const char *str, const char *sysfs_path, const char *target_content); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_sysfs_tunable(const char *str, const char *_sysfs_path, const char *_target_content); +extern void add_sata_tunables(void); + +#endif diff --git a/src/tuning/tuningusb.cpp b/src/tuning/tuningusb.cpp new file mode 100644 index 0000000..3b7b2b1 --- /dev/null +++ b/src/tuning/tuningusb.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include "unistd.h" +#include "tuningusb.h" +#include <string.h> +#include <dirent.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <limits.h> + +#include "../lib.h" + +usb_tunable::usb_tunable(const char *path, const char *name) : tunable("", 0.9, _("Good"), _("Bad"), _("Unknown")) +{ + ifstream file; + char filename[PATH_MAX]; + char vendor[2048]; + char product[2048]; + string str1, str2; + snprintf(usb_path, sizeof(usb_path), "%s/power/control", path); + + vendor[0] = 0; + product[0] = 0; + + str1 = read_sysfs_string("%s/idVendor", path); + str2 = read_sysfs_string("%s/idProduct", path); + + snprintf(desc, sizeof(desc), _("Autosuspend for unknown USB device %s (%s:%s)"), name, str1.c_str(), str2.c_str()); + + snprintf(filename, sizeof(filename), "%s/manufacturer", path); + file.open(filename, ios::in); + if (file) { + file.getline(vendor, 2047); + if (strstr(vendor, "Linux ")) + vendor[0] = 0; + file.close(); + }; + snprintf(filename, sizeof(filename), "%s/product", path); + file.open(filename, ios::in); + if (file) { + file.getline(product, 2040); + file.close(); + }; + if (strlen(vendor) && strlen(product)) + snprintf(desc, sizeof(desc), _("Autosuspend for USB device %s [%s]"), product, vendor); + else if (strlen(product)) + snprintf(desc, sizeof(desc), _("Autosuspend for USB device %s [%s]"), product, name); + else if (strlen(vendor)) + snprintf(desc, sizeof(desc), _("Autosuspend for USB device %s [%s]"), vendor, name); + + snprintf(toggle_good, sizeof(toggle_good), "echo 'auto' > '%s';", usb_path); + snprintf(toggle_bad, sizeof(toggle_bad), "echo 'on' > '%s';", usb_path); +} + +int usb_tunable::good_bad(void) +{ + string content; + + content = read_sysfs_string(usb_path); + + if (strcmp(content.c_str(), "auto") == 0) + return TUNE_GOOD; + + return TUNE_BAD; +} + +void usb_tunable::toggle(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + write_sysfs(usb_path, "on"); + return; + } + + write_sysfs(usb_path, "auto"); +} + +const char *usb_tunable::toggle_script(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + return toggle_bad; + } + + return toggle_good; +} + +static void add_usb_callback(const char *d_name) +{ + class usb_tunable *usb; + char filename[PATH_MAX]; + DIR *dir; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/control", d_name); + if (access(filename, R_OK) != 0) + return; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/active_duration", d_name); + if (access(filename, R_OK)!=0) + return; + + /* every interface of this device should support autosuspend */ + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s", d_name); + if ((dir = opendir(filename))) { + struct dirent *entry; + while ((entry = readdir(dir))) { + /* dirname: <busnum>-<devnum>...:<config num>-<interface num> */ + if (!isdigit(entry->d_name[0])) + continue; + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/%s/supports_autosuspend", d_name, entry->d_name); + if (access(filename, R_OK) == 0 && read_sysfs(filename) == 0) + break; + } + closedir(dir); + if (entry) + return; + } + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s", d_name); + usb = new class usb_tunable(filename, d_name); + all_tunables.push_back(usb); +} + +void add_usb_tunables(void) +{ + process_directory("/sys/bus/usb/devices/", add_usb_callback); +} diff --git a/src/tuning/tuningusb.h b/src/tuning/tuningusb.h new file mode 100644 index 0000000..4e27e3a --- /dev/null +++ b/src/tuning/tuningusb.h @@ -0,0 +1,51 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_USB_TUNE_H +#define _INCLUDE_GUARD_USB_TUNE_H + +#include <vector> +#include <limits.h> + +#include "tunable.h" + +using namespace std; + +class usb_tunable : public tunable { + char usb_path[PATH_MAX]; +public: + usb_tunable(const char *usb_path, const char *path); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_usb_tunables(void); + + +#endif diff --git a/src/tuning/wifi.cpp b/src/tuning/wifi.cpp new file mode 100644 index 0000000..f7a91ec --- /dev/null +++ b/src/tuning/wifi.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ + +#include "tuning.h" +#include "tunable.h" +#include "unistd.h" +#include "wifi.h" +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <unistd.h> +#include <sys/types.h> +#include <dirent.h> + +#include "../lib.h" + +extern "C" { +#include "iw.h" +} + + +wifi_tunable::wifi_tunable(const char *_iface) : tunable("", 1.5, _("Good"), _("Bad"), _("Unknown")) +{ + pt_strcpy(iface, _iface); + sprintf(desc, _("Wireless Power Saving for interface %s"), iface); + + snprintf(toggle_good, sizeof(toggle_good), "iw dev %s set power_save on", iface); + snprintf(toggle_bad, sizeof(toggle_bad), "iw dev %s set power_save off", iface); +} + +int wifi_tunable::good_bad(void) +{ + if (get_wifi_power_saving(iface)) + return TUNE_GOOD; + + return TUNE_BAD; +} + +void wifi_tunable::toggle(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + set_wifi_power_saving(iface, 0); + return; + } + + set_wifi_power_saving(iface, 1); +} + +const char *wifi_tunable::toggle_script(void) +{ + int good; + good = good_bad(); + + if (good == TUNE_GOOD) { + return toggle_bad; + } + + return toggle_good; +} + +void add_wifi_tunables(void) +{ + struct dirent *entry; + DIR *dir; + char* wlan_name; + class wifi_tunable *wifi; + + + dir = opendir("/sys/class/net/"); + if (!dir) + return; + while (1) { + entry = readdir(dir); + if (!entry) + break; + wlan_name = strstr(entry->d_name, "wlan"); + if (wlan_name) { + wifi = new class wifi_tunable(wlan_name); + all_tunables.push_back(wifi); + } + + } + + closedir(dir); + +} diff --git a/src/tuning/wifi.h b/src/tuning/wifi.h new file mode 100644 index 0000000..50ca68c --- /dev/null +++ b/src/tuning/wifi.h @@ -0,0 +1,50 @@ +/* + * Copyright 2010, 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 + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan@linux.intel.com> + */ +#ifndef _INCLUDE_GUARD_WIFI_TUNE_H +#define _INCLUDE_GUARD_WIFI_TUNE_H + +#include <vector> + +#include "tunable.h" + +using namespace std; + +class wifi_tunable : public tunable { + char iface[4096]; +public: + wifi_tunable(const char *_iface); + + virtual int good_bad(void); + + virtual void toggle(void); + + virtual const char *toggle_script(void); + +}; + +extern void add_wifi_tunables(void); + + +#endif diff --git a/src/wakeup/waketab.cpp b/src/wakeup/waketab.cpp new file mode 100644 index 0000000..77d7567 --- /dev/null +++ b/src/wakeup/waketab.cpp @@ -0,0 +1,195 @@ +#include <algorithm> +#include <stdio.h> +#include <string.h> +#include <ncurses.h> +#include "wakeup.h" +#include <vector> +#include "../lib.h" +#include "../measurement/sysfs.h" +#include "../display.h" +#include "../report/report.h" +#include "../report/report-maker.h" +#include "../report/report-data-html.h" +#include "wakeup_ethernet.h" +#include "wakeup_usb.h" + +using namespace std; + +static bool should_clear = false; + +class wakeup_window *newtab_window; + +class wakeup_window: public tab_window { +public: + virtual void repaint(void); + virtual void cursor_enter(void); + virtual void expose(void); + virtual void window_refresh(void); +}; + +static void init_wakeup(void) +{ + add_ethernet_wakeup(); + add_usb_wakeup(); +} + +void initialize_wakeup(void) +{ + class wakeup_window *win; + + win = new wakeup_window(); + + create_tab("WakeUp", _("WakeUp"), win, _(" <ESC> Exit | <Enter> Toggle wakeup | <r> Window refresh")); + + init_wakeup(); + + win->cursor_max = wakeup_all.size() - 1; + + if (newtab_window) + delete newtab_window; + + newtab_window = win; +} + +static void __wakeup_update_display(int cursor_pos) +{ + WINDOW *win; + unsigned int i; + + win = get_ncurses_win("WakeUp"); + + if (!win) + return; + + if (should_clear) { + should_clear = false; + wclear(win); + } + + wmove(win, 1,0); + + for (i = 0; i < wakeup_all.size(); i++) { + char res[128]; + char desc[4096]; + pt_strcpy(res, wakeup_all[i]->wakeup_string()); + pt_strcpy(desc, wakeup_all[i]->description()); + while (strlen(res) < 12) + strcat(res, " "); + + while (strlen(desc) < 103) + strcat(desc, " "); + + if ((int)i != cursor_pos) { + wattrset(win, A_NORMAL); + wprintw(win, " "); + } else { + wattrset(win, A_REVERSE); + wprintw(win, ">> "); + } + wprintw(win, "%s %s\n", _(res), _(desc)); + } +} + +void wakeup_update_display(void) +{ + class tab_window *wt; + + wt = tab_windows["WakeUp"]; + if (!wt) + return; + wt->repaint(); +} + +void wakeup_window::repaint(void) +{ + __wakeup_update_display(cursor_pos); +} + +void wakeup_window::cursor_enter(void) +{ + class wakeup *wake; + const char *wakeup_toggle_script; + wake = wakeup_all[cursor_pos]; + if (!wake) + return; + wakeup_toggle_script = wake->wakeup_toggle_script(); + wake->wakeup_toggle(); + ui_notify_user(">> %s\n", wakeup_toggle_script); +} + +void report_show_wakeup(void) +{ + unsigned int i; + int idx, rows = 0, cols; + + /* div attr css_class and css_id */ + tag_attr div_attr; + init_div(&div_attr, "clear_block", "wakeup"); + + /* Set Title attributes */ + tag_attr title_attr; + init_title_attr(&title_attr); + + /* Set Table attributes, rows, and cols */ + table_attributes wakeup_table_css; + cols=2; + idx = cols; + + for (i = 0; i < wakeup_all.size(); i++) { + int tgb; + tgb = wakeup_all[i]->wakeup_value(); + if (tgb == WAKEUP_DISABLE) + rows+=1; + } + + /* add section */ + + report.add_div(&div_attr); + if (rows > 0){ + rows= rows + 1; + init_wakeup_table_attr(&wakeup_table_css, rows, cols); + + /* Set array of data in row Major order */ + string *wakeup_data = new string[cols * rows]; + + wakeup_data[0]=__("Description"); + wakeup_data[1]=__("Script"); + + for (i = 0; i < wakeup_all.size(); i++) { + int gb; + gb = wakeup_all[i]->wakeup_value(); + if (gb != WAKEUP_DISABLE) + continue; + wakeup_data[idx]=string(wakeup_all[i]->description()); + idx+=1; + wakeup_data[idx]=string(wakeup_all[i]->wakeup_toggle_script()); + idx+=1; + } + + /* Report Output */ + report.add_title(&title_attr,__("Wake status of the devices")); + report.add_table(wakeup_data, &wakeup_table_css); + delete [] wakeup_data; + } +} + +void wakeup_window::expose(void) +{ + cursor_pos = 0; + repaint(); +} + +void wakeup_window::window_refresh(void) +{ + clear_wakeup(); + should_clear = true; + init_wakeup(); +} + +void clear_wakeup() +{ + for (size_t i = 0; i < wakeup_all.size(); i++) { + delete wakeup_all[i]; + } + wakeup_all.clear(); +} diff --git a/src/wakeup/wakeup.cpp b/src/wakeup/wakeup.cpp new file mode 100644 index 0000000..9d83a9a --- /dev/null +++ b/src/wakeup/wakeup.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2018, 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 + * or just google for it. + * + * Authors: + * Gayatri Kammela <gayatri.kammela@intel.com> + */ + +#include <string.h> +#include <ncurses.h> +#include "wakeup.h" +#include <vector> +#include "../lib.h" + +using namespace std; + +vector<class wakeup *> wakeup_all; + +wakeup::wakeup(const char *str, double _score, const char *enable, const char *disable) +{ + score = _score; + pt_strcpy(desc, str); + pt_strcpy(wakeup_enable, enable); + pt_strcpy(wakeup_disable, disable); +} + +wakeup::wakeup(void) +{ + score = 0; + desc[0] = 0; + pt_strcpy(wakeup_enable, _("Enabled")); + pt_strcpy(wakeup_disable, _("Disabled")); + pt_strcpy(wakeup_idle, _("Unknown")); +} + diff --git a/src/wakeup/wakeup.h b/src/wakeup/wakeup.h new file mode 100644 index 0000000..13f99a3 --- /dev/null +++ b/src/wakeup/wakeup.h @@ -0,0 +1,81 @@ +/* + * Copyright 2018, 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 + * or just google for it. + * + * Authors: + * Gayatri Kammela <gayatri.kammela@intel.com> + */ +#ifndef _INCLUDE_GUARD_WAKEUP_H +#define _INCLUDE_GUARD_WAKEUP_H + +#include<vector> +#include <limits.h> + +using namespace std; + +#define WAKEUP_ENABLE 1 +#define WAKEUP_DISABLE 0 + +class wakeup { + char wakeup_enable[128]; + char wakeup_disable[128]; + char wakeup_idle[128]; +protected: + char toggle_enable[4096]; + char toggle_disable[4096]; +public: + char desc[4096]; + double score; + + wakeup(const char *str, double _score, const char *enable = "", const char *disable = ""); + wakeup(void); + + virtual ~wakeup () {}; + + virtual int wakeup_value(void) { return WAKEUP_DISABLE; } + + virtual char *wakeup_string(void) + { + switch (wakeup_value()) { + case WAKEUP_ENABLE: + return wakeup_enable; + case WAKEUP_DISABLE: + return wakeup_disable; + } + return wakeup_idle; + } + + + virtual const char *description(void) { return desc; }; + + virtual void wakeup_toggle(void) { }; + + virtual const char *wakeup_toggle_script(void) { return toggle_enable; } + +}; + +extern vector<class wakeup *> wakeup_all; + +extern void initialize_wakeup(void); +extern void wakeup_update_display(void); +extern void report_show_wakeup(void); +extern void clear_wakeup(void); + +#endif diff --git a/src/wakeup/wakeup_ethernet.cpp b/src/wakeup/wakeup_ethernet.cpp new file mode 100644 index 0000000..6ce1725 --- /dev/null +++ b/src/wakeup/wakeup_ethernet.cpp @@ -0,0 +1,111 @@ +;/* + * Copyright 2018, 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 + * or just google for it. + * + * Authors: + * Gayatri Kammela <gayatri.kammela@intel.com> + */ + +#include "wakeup.h" +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <sys/socket.h> +#include <errno.h> +#include <linux/types.h> +#include <net/if.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> + +#include <linux/ethtool.h> + +#include "../lib.h" +#include "wakeup_ethernet.h" + +ethernet_wakeup::ethernet_wakeup(const char *path, const char *iface) : wakeup("", 0.5, _("Enabled"), _("Disabled")) +{ + memset(interf, 0, sizeof(interf)); + pt_strcpy(interf, iface); + sprintf(desc, _("Wake-on-lan status for device %s"), iface); + snprintf(eth_path, sizeof(eth_path), "/sys/class/net/%s/device/power/wakeup", iface); + snprintf(toggle_enable, sizeof(toggle_enable), "echo 'enabled' > '%s';", eth_path); + snprintf(toggle_disable, sizeof(toggle_disable), "echo 'disabled' > '%s';", eth_path); +} + +int ethernet_wakeup::wakeup_value(void) +{ + string content; + + content = read_sysfs_string(eth_path); + + if (strcmp(content.c_str(), "enabled") == 0) + return WAKEUP_ENABLE; + + return WAKEUP_DISABLE; +} + +void ethernet_wakeup::wakeup_toggle(void) +{ + int enable; + enable = wakeup_value(); + + if (enable == WAKEUP_ENABLE) { + write_sysfs(eth_path, "disabled"); + return; + } + + write_sysfs(eth_path, "enabled"); +} + +const char *ethernet_wakeup::wakeup_toggle_script(void) +{ + int enable; + enable = wakeup_value(); + + if (enable == WAKEUP_ENABLE) { + return toggle_disable; + } + + return toggle_enable; +} + +void wakeup_eth_callback(const char *d_name) +{ + class ethernet_wakeup *eth; + char filename[PATH_MAX]; + + snprintf(filename, sizeof(filename), "/sys/class/net/%s/device/power/wakeup", d_name); + if (access(filename, R_OK) != 0) + return; + + snprintf(filename, sizeof(filename), "/sys/class/net/%s/device/power/wakeup", d_name); + eth = new class ethernet_wakeup(filename, d_name); + wakeup_all.push_back(eth); +} + +void add_ethernet_wakeup(void) +{ + process_directory("/sys/class/net/", wakeup_eth_callback); +} diff --git a/src/wakeup/wakeup_ethernet.h b/src/wakeup/wakeup_ethernet.h new file mode 100644 index 0000000..682bf95 --- /dev/null +++ b/src/wakeup/wakeup_ethernet.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018, 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 + * or just google for it. + * + * Authors: + * Gayatri Kammela <gayatri.kammela@intel.com> + */ +#ifndef _INCLUDE_GUARD_ETHERNET_WAKEUP_H +#define _INCLUDE_GUARD_ETHERNET_WAKEUP_H + +#include <vector> + +#include "wakeup.h" + +using namespace std; + +class ethernet_wakeup : public wakeup { + char eth_path[PATH_MAX]; +public: + char interf[4096]; + ethernet_wakeup(const char *eth_path, const char *iface); + + virtual int wakeup_value(void); + + virtual void wakeup_toggle(void); + + virtual const char *wakeup_toggle_script(void); + +}; + +extern void add_ethernet_wakeup(void); + +#endif diff --git a/src/wakeup/wakeup_usb.cpp b/src/wakeup/wakeup_usb.cpp new file mode 100644 index 0000000..e0e4567 --- /dev/null +++ b/src/wakeup/wakeup_usb.cpp @@ -0,0 +1,111 @@ +;/* + * Copyright 2018, 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 + * or just google for it. + * + * Authors: + * Gayatri Kammela <gayatri.kammela@intel.com> + */ + +#include "wakeup.h" +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <utility> +#include <iostream> +#include <fstream> +#include <sys/socket.h> +#include <errno.h> +#include <linux/types.h> +#include <net/if.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> + +#include <linux/ethtool.h> + +#include "../lib.h" +#include "wakeup_usb.h" + +usb_wakeup::usb_wakeup(const char *path, const char *iface) : wakeup("", 0.5, _("Enabled"), _("Disabled")) +{ + memset(interf, 0, sizeof(interf)); + pt_strcpy(interf, iface); + sprintf(desc, _("Wake status for USB device %s"), iface); + snprintf(usb_path, sizeof(usb_path), "/sys/bus/usb/devices/%s/power/wakeup", iface); + snprintf(toggle_enable, sizeof(toggle_enable), "echo 'enabled' > '%s';", usb_path); + snprintf(toggle_disable, sizeof(toggle_disable), "echo 'disabled' > '%s';", usb_path); +} + +int usb_wakeup::wakeup_value(void) +{ + string content; + + content = read_sysfs_string(usb_path); + + if (strcmp(content.c_str(), "enabled") == 0) + return WAKEUP_ENABLE; + + return WAKEUP_DISABLE; +} + +void usb_wakeup::wakeup_toggle(void) +{ + int enable; + enable = wakeup_value(); + + if (enable == WAKEUP_ENABLE) { + write_sysfs(usb_path, "disabled"); + return; + } + + write_sysfs(usb_path, "enabled"); +} + +const char *usb_wakeup::wakeup_toggle_script(void) +{ + int enable; + enable = wakeup_value(); + + if (enable == WAKEUP_ENABLE) { + return toggle_disable; + } + + return toggle_enable; +} + +void wakeup_usb_callback(const char *d_name) +{ + class usb_wakeup *usb; + char filename[PATH_MAX]; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/wakeup", d_name); + if (access(filename, R_OK) != 0) + return; + + snprintf(filename, sizeof(filename), "/sys/bus/usb/devices/%s/power/wakeup", d_name); + usb = new class usb_wakeup(filename, d_name); + wakeup_all.push_back(usb); +} + +void add_usb_wakeup(void) +{ + process_directory("/sys/bus/usb/devices/", wakeup_usb_callback); +} diff --git a/src/wakeup/wakeup_usb.h b/src/wakeup/wakeup_usb.h new file mode 100644 index 0000000..f7a1f7e --- /dev/null +++ b/src/wakeup/wakeup_usb.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018, 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 + * or just google for it. + * + * Authors: + * Gayatri Kammela <gayatri.kammela@intel.com> + */ +#ifndef _INCLUDE_GUARD_USB_WAKEUP_H +#define _INCLUDE_GUARD_USB_WAKEUP_H + +#include <vector> + +#include "wakeup.h" + +using namespace std; + +class usb_wakeup : public wakeup { + char usb_path[PATH_MAX]; +public: + char interf[4096]; + usb_wakeup(const char *usb_path, const char *iface); + + virtual int wakeup_value(void); + + virtual void wakeup_toggle(void); + + virtual const char *wakeup_toggle_script(void); + +}; + +extern void add_usb_wakeup(void); + +#endif |