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/abstract_cpu.cpp | |
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/abstract_cpu.cpp')
-rw-r--r-- | src/cpu/abstract_cpu.cpp | 535 |
1 files changed, 535 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(); +} |