/* * 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 */ #include #include #include #include #include #include #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 "". 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(); }