diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:00:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:00:20 +0000 |
commit | fcb4cb5c3d0fec0fede160d565134d553d783fb2 (patch) | |
tree | 7be42535554ca6badc1847d83ef123f4dc3c5506 /src/cpu | |
parent | Initial commit. (diff) | |
download | powertop-upstream.tar.xz powertop-upstream.zip |
Adding upstream version 2.15.upstream/2.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cpu')
-rw-r--r-- | src/cpu/abstract_cpu.cpp | 535 | ||||
-rw-r--r-- | src/cpu/cpu.cpp | 1075 | ||||
-rw-r--r-- | src/cpu/cpu.h | 234 | ||||
-rw-r--r-- | src/cpu/cpu_core.cpp | 100 | ||||
-rw-r--r-- | src/cpu/cpu_linux.cpp | 350 | ||||
-rw-r--r-- | src/cpu/cpu_package.cpp | 113 | ||||
-rw-r--r-- | src/cpu/cpu_rapl_device.cpp | 74 | ||||
-rw-r--r-- | src/cpu/cpu_rapl_device.h | 57 | ||||
-rw-r--r-- | src/cpu/cpudevice.cpp | 86 | ||||
-rw-r--r-- | src/cpu/cpudevice.h | 63 | ||||
-rw-r--r-- | src/cpu/dram_rapl_device.cpp | 75 | ||||
-rw-r--r-- | src/cpu/dram_rapl_device.h | 57 | ||||
-rw-r--r-- | src/cpu/intel_cpus.cpp | 757 | ||||
-rw-r--r-- | src/cpu/intel_cpus.h | 180 | ||||
-rw-r--r-- | src/cpu/intel_gpu.cpp | 122 | ||||
-rw-r--r-- | src/cpu/rapl/rapl_interface.cpp | 699 | ||||
-rw-r--r-- | src/cpu/rapl/rapl_interface.h | 91 |
17 files changed, 4668 insertions, 0 deletions
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 |