| // 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/atomicops.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/post_task.h" | 
 | #include "base/task/task_traits.h" | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "starboard/types.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | namespace trace_event { | 
 |  | 
 | namespace { | 
 |  | 
 | const size_t kNumBytesToReadForSampling = 32; | 
 | 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 >= 0 && 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::CreateSingleThreadTaskRunnerWithTraits( | 
 |       {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)), weak_ptr_factory_(this) { | 
 |   TRACE_EVENT_WARMUP_CATEGORY(kTraceCategory); | 
 | } | 
 |  | 
 | 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 (base::subtle::NoBarrier_Load(&is_enabled_) == 1 || | 
 |       !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; | 
 |  | 
 |   base::subtle::Release_Store(&is_enabled_, 1); | 
 |  | 
 |   GetOrCreateTaskRunner()->PostTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&CPUFreqMonitor::Sample, weak_ptr_factory_.GetWeakPtr(), | 
 |                      std::move(fds))); | 
 | } | 
 |  | 
 | void CPUFreqMonitor::Stop() { | 
 |   base::subtle::Release_Store(&is_enabled_, 0); | 
 | } | 
 |  | 
 | void CPUFreqMonitor::Sample( | 
 |     std::vector<std::pair<unsigned int, base::ScopedFD>> fds) { | 
 |   // For the same reason as above we use NoBarrier_Load, because if this value | 
 |   // is in transition and we use Acquire_Load 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 (base::subtle::NoBarrier_Load(&is_enabled_) == 0) | 
 |     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]; | 
 |  | 
 |     size_t bytes_read = read(fd, data, kNumBytesToReadForSampling); | 
 |     if (bytes_read > 0) { | 
 |       if (bytes_read < kNumBytesToReadForSampling) | 
 |         data[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::TimeDelta::FromMilliseconds(kDefaultCPUFreqSampleIntervalMs)); | 
 | } | 
 |  | 
 | bool CPUFreqMonitor::IsEnabledForTesting() { | 
 |   return base::subtle::Acquire_Load(&is_enabled_) == 1; | 
 | } | 
 |  | 
 | const scoped_refptr<SingleThreadTaskRunner>& | 
 | CPUFreqMonitor::GetOrCreateTaskRunner() { | 
 |   if (!task_runner_) | 
 |     task_runner_ = delegate_->CreateTaskRunner(); | 
 |   return task_runner_; | 
 | } | 
 |  | 
 | }  // namespace trace_event | 
 | }  // namespace base |