blob: 01373694d2574402314bc355ab371f83869843ca [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/traced/probes/common/cpu_freq_info.h"
#include <set>
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"
namespace perfetto {
namespace {
using CpuAndFreq = std::pair</* cpu */ uint32_t, /* freq */ uint32_t>;
// Reads space-separated CPU frequencies from a sysfs file.
void ReadAndAppendFreqs(std::set<CpuAndFreq>* freqs,
uint32_t cpu_index,
const std::string& sys_cpu_freqs) {
base::StringSplitter entries(sys_cpu_freqs, ' ');
while (entries.Next()) {
auto freq = base::StringToUInt32(entries.cur_token());
if (freq.has_value())
freqs->insert({cpu_index, freq.value()});
}
}
} // namespace
CpuFreqInfo::CpuFreqInfo(std::string cpu_dir_path) {
base::ScopedDir cpu_dir(opendir(cpu_dir_path.c_str()));
if (!cpu_dir) {
PERFETTO_PLOG("Failed to opendir(%s)", cpu_dir_path.c_str());
return;
}
// Accumulate cpu and freqs into a set to ensure stable order.
std::set<CpuAndFreq> freqs;
while (struct dirent* dir_ent = readdir(*cpu_dir)) {
if (dir_ent->d_type != DT_DIR)
continue;
std::string dir_name(dir_ent->d_name);
if (!base::StartsWith(dir_name, "cpu"))
continue;
auto maybe_cpu_index =
base::StringToUInt32(base::StripPrefix(dir_name, "cpu"));
// There are some directories (cpufreq, cpuidle) which should be skipped.
if (!maybe_cpu_index.has_value())
continue;
uint32_t cpu_index = maybe_cpu_index.value();
ReadAndAppendFreqs(
&freqs, cpu_index,
ReadFile(cpu_dir_path + "/cpu" + std::to_string(cpu_index) +
"/cpufreq/scaling_available_frequencies"));
ReadAndAppendFreqs(
&freqs, cpu_index,
ReadFile(cpu_dir_path + "/cpu" + std::to_string(cpu_index) +
"/cpufreq/scaling_boost_frequencies"));
}
// Build index with guards.
uint32_t last_cpu = 0;
uint32_t index = 0;
frequencies_index_.push_back(0);
for (const auto& cpu_freq : freqs) {
frequencies_.push_back(cpu_freq.second);
if (cpu_freq.first != last_cpu)
frequencies_index_.push_back(index);
last_cpu = cpu_freq.first;
index++;
}
frequencies_.push_back(0);
frequencies_index_.push_back(index);
}
CpuFreqInfo::~CpuFreqInfo() = default;
CpuFreqInfo::Range CpuFreqInfo::GetFreqs(uint32_t cpu) {
if (cpu >= frequencies_index_.size() - 1) {
PERFETTO_DLOG("No frequencies for cpu%" PRIu32, cpu);
const uint32_t* end = frequencies_.data() + frequencies_.size();
return {end, end};
}
auto* start = &frequencies_[frequencies_index_[cpu]];
auto* end = &frequencies_[frequencies_index_[cpu + 1]];
return {start, end};
}
uint32_t CpuFreqInfo::GetCpuFreqIndex(uint32_t cpu, uint32_t freq) {
auto range = GetFreqs(cpu);
uint32_t index = 0;
for (const uint32_t* it = range.first; it != range.second; it++, index++) {
if (*it == freq) {
return static_cast<uint32_t>(frequencies_index_[cpu]) + index + 1;
}
}
return 0;
}
std::string CpuFreqInfo::ReadFile(std::string path) {
std::string contents;
if (!base::ReadFile(path, &contents))
return "";
return contents;
}
} // namespace perfetto