123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/trace_event/cpufreq_monitor_android.h"
- #include <fcntl.h>
- #include "base/bind.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_file.h"
- #include "base/memory/scoped_refptr.h"
- #include "base/no_destructor.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_split.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/task_traits.h"
- #include "base/task/thread_pool.h"
- #include "base/trace_event/trace_event.h"
- namespace base {
- namespace trace_event {
- namespace {
- const size_t kNumBytesToReadForSampling = 32;
- constexpr const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("power");
- const char kEventTitle[] = "CPU Frequency";
- } // namespace
- CPUFreqMonitorDelegate::CPUFreqMonitorDelegate() {}
- std::string CPUFreqMonitorDelegate::GetScalingCurFreqPathString(
- unsigned int cpu_id) const {
- return base::StringPrintf(
- "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu_id);
- }
- bool CPUFreqMonitorDelegate::IsTraceCategoryEnabled() const {
- bool enabled;
- TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
- return enabled;
- }
- unsigned int CPUFreqMonitorDelegate::GetKernelMaxCPUs() const {
- std::string str;
- if (!base::ReadFileToString(
- base::FilePath("/sys/devices/system/cpu/kernel_max"), &str)) {
- // If we fail to read the kernel_max file, we just assume that CPU0 exists.
- return 0;
- }
- unsigned int kernel_max_cpu = 0;
- base::StringToUint(str, &kernel_max_cpu);
- return kernel_max_cpu;
- }
- std::string CPUFreqMonitorDelegate::GetRelatedCPUsPathString(
- unsigned int cpu_id) const {
- return base::StringPrintf(
- "/sys/devices/system/cpu/cpu%d/cpufreq/related_cpus", cpu_id);
- }
- void CPUFreqMonitorDelegate::GetCPUIds(std::vector<unsigned int>* ids) const {
- ids->clear();
- unsigned int kernel_max_cpu = GetKernelMaxCPUs();
- // CPUs related to one that's already marked for monitoring get set to "false"
- // so we don't needlessly monitor CPUs with redundant frequency information.
- char cpus_to_monitor[kernel_max_cpu + 1];
- std::memset(cpus_to_monitor, 1, kernel_max_cpu + 1);
- // Rule out the related CPUs for each one so we only end up with the CPUs
- // that are representative of the cluster.
- for (unsigned int i = 0; i <= kernel_max_cpu; i++) {
- if (!cpus_to_monitor[i])
- continue;
- std::string filename = GetRelatedCPUsPathString(i);
- std::string line;
- if (!base::ReadFileToString(base::FilePath(filename), &line))
- continue;
- // When reading the related_cpus file, we expected the format to be
- // something like "0 1 2 3" for CPU0-3 if they're all in one cluster.
- for (auto& str_piece :
- base::SplitString(line, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
- base::SplitResult::SPLIT_WANT_NONEMPTY)) {
- unsigned int cpu_id;
- if (base::StringToUint(str_piece, &cpu_id)) {
- if (cpu_id != i && cpu_id <= kernel_max_cpu)
- cpus_to_monitor[cpu_id] = 0;
- }
- }
- ids->push_back(i);
- }
- // If none of the files were readable, we assume CPU0 exists and fall back to
- // using that.
- if (ids->size() == 0)
- ids->push_back(0);
- }
- void CPUFreqMonitorDelegate::RecordFrequency(unsigned int cpu_id,
- unsigned int freq) {
- TRACE_COUNTER_ID1(kTraceCategory, kEventTitle, cpu_id, freq);
- }
- scoped_refptr<SingleThreadTaskRunner>
- CPUFreqMonitorDelegate::CreateTaskRunner() {
- return base::ThreadPool::CreateSingleThreadTaskRunner(
- {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
- base::TaskPriority::BEST_EFFORT},
- base::SingleThreadTaskRunnerThreadMode::SHARED);
- }
- CPUFreqMonitor::CPUFreqMonitor()
- : CPUFreqMonitor(std::make_unique<CPUFreqMonitorDelegate>()) {}
- CPUFreqMonitor::CPUFreqMonitor(std::unique_ptr<CPUFreqMonitorDelegate> delegate)
- : delegate_(std::move(delegate)) {}
- CPUFreqMonitor::~CPUFreqMonitor() {
- Stop();
- }
- // static
- CPUFreqMonitor* CPUFreqMonitor::GetInstance() {
- static base::NoDestructor<CPUFreqMonitor> instance;
- return instance.get();
- }
- void CPUFreqMonitor::OnTraceLogEnabled() {
- GetOrCreateTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&CPUFreqMonitor::Start, weak_ptr_factory_.GetWeakPtr()));
- }
- void CPUFreqMonitor::OnTraceLogDisabled() {
- Stop();
- }
- void CPUFreqMonitor::Start() {
- // It's the responsibility of the caller to ensure that Start/Stop are
- // synchronized. If Start/Stop are called asynchronously where this value
- // may be incorrect, we have bigger problems.
- if (is_enabled_.load(std::memory_order_relaxed) ||
- !delegate_->IsTraceCategoryEnabled()) {
- return;
- }
- std::vector<unsigned int> cpu_ids;
- delegate_->GetCPUIds(&cpu_ids);
- std::vector<std::pair<unsigned int, base::ScopedFD>> fds;
- for (unsigned int id : cpu_ids) {
- std::string fstr = delegate_->GetScalingCurFreqPathString(id);
- int fd = open(fstr.c_str(), O_RDONLY);
- if (fd == -1)
- continue;
- fds.emplace_back(std::make_pair(id, base::ScopedFD(fd)));
- }
- // We failed to read any scaling_cur_freq files, no point sampling nothing.
- if (fds.size() == 0)
- return;
- is_enabled_.store(true, std::memory_order_release);
- GetOrCreateTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&CPUFreqMonitor::Sample, weak_ptr_factory_.GetWeakPtr(),
- std::move(fds)));
- }
- void CPUFreqMonitor::Stop() {
- is_enabled_.store(false, std::memory_order_release);
- }
- void CPUFreqMonitor::Sample(
- std::vector<std::pair<unsigned int, base::ScopedFD>> fds) {
- // For the same reason as above we use relaxed ordering, because if this value
- // is in transition and we use acquire ordering then we'll never shut down our
- // original Sample tasks until the next Stop, so it's still the responsibility
- // of callers to sync Start/Stop.
- if (!is_enabled_.load(std::memory_order_relaxed))
- return;
- for (auto& id_fd : fds) {
- int fd = id_fd.second.get();
- unsigned int freq = 0;
- // If we have trouble reading data from the file for any reason we'll end up
- // reporting the frequency as nothing.
- lseek(fd, 0L, SEEK_SET);
- char data[kNumBytesToReadForSampling];
- ssize_t bytes_read = read(fd, data, kNumBytesToReadForSampling);
- if (bytes_read > 0) {
- if (static_cast<size_t>(bytes_read) < kNumBytesToReadForSampling)
- data[static_cast<size_t>(bytes_read)] = '\0';
- int ret = sscanf(data, "%d", &freq);
- if (ret == 0 || ret == std::char_traits<char>::eof())
- freq = 0;
- }
- delegate_->RecordFrequency(id_fd.first, freq);
- }
- GetOrCreateTaskRunner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&CPUFreqMonitor::Sample, weak_ptr_factory_.GetWeakPtr(),
- std::move(fds)),
- base::Milliseconds(kDefaultCPUFreqSampleIntervalMs));
- }
- bool CPUFreqMonitor::IsEnabledForTesting() {
- return is_enabled_.load(std::memory_order_acquire);
- }
- const scoped_refptr<SingleThreadTaskRunner>&
- CPUFreqMonitor::GetOrCreateTaskRunner() {
- if (!task_runner_)
- task_runner_ = delegate_->CreateTaskRunner();
- return task_runner_;
- }
- } // namespace trace_event
- } // namespace base
|