• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/traced/probes/common/cpu_freq_info.h"
18 
19 #include <set>
20 
21 #include "perfetto/ext/base/file_utils.h"
22 #include "perfetto/ext/base/string_splitter.h"
23 #include "perfetto/ext/base/string_utils.h"
24 
25 namespace perfetto {
26 
27 namespace {
28 
29 using CpuAndFreq = std::pair</* cpu */ uint32_t, /* freq */ uint32_t>;
30 
31 // Reads space-separated CPU frequencies from a sysfs file.
ReadAndAppendFreqs(std::set<CpuAndFreq> * freqs,uint32_t cpu_index,const std::string & sys_cpu_freqs)32 void ReadAndAppendFreqs(std::set<CpuAndFreq>* freqs,
33                         uint32_t cpu_index,
34                         const std::string& sys_cpu_freqs) {
35   base::StringSplitter entries(sys_cpu_freqs, ' ');
36   while (entries.Next()) {
37     auto freq = base::StringToUInt32(entries.cur_token());
38     if (freq.has_value())
39       freqs->insert({cpu_index, freq.value()});
40   }
41 }
42 
43 }  // namespace
44 
CpuFreqInfo(std::string cpu_dir_path)45 CpuFreqInfo::CpuFreqInfo(std::string cpu_dir_path) {
46   base::ScopedDir cpu_dir(opendir(cpu_dir_path.c_str()));
47   if (!cpu_dir) {
48     PERFETTO_PLOG("Failed to opendir(%s)", cpu_dir_path.c_str());
49     return;
50   }
51   // Accumulate cpu and freqs into a set to ensure stable order.
52   std::set<CpuAndFreq> freqs;
53   while (struct dirent* dir_ent = readdir(*cpu_dir)) {
54     if (dir_ent->d_type != DT_DIR)
55       continue;
56     std::string dir_name(dir_ent->d_name);
57     if (!base::StartsWith(dir_name, "cpu"))
58       continue;
59     auto maybe_cpu_index =
60         base::StringToUInt32(base::StripPrefix(dir_name, "cpu"));
61     // There are some directories (cpufreq, cpuidle) which should be skipped.
62     if (!maybe_cpu_index.has_value())
63       continue;
64     uint32_t cpu_index = maybe_cpu_index.value();
65     ReadAndAppendFreqs(
66         &freqs, cpu_index,
67         ReadFile(cpu_dir_path + "/cpu" + std::to_string(cpu_index) +
68                  "/cpufreq/scaling_available_frequencies"));
69     ReadAndAppendFreqs(
70         &freqs, cpu_index,
71         ReadFile(cpu_dir_path + "/cpu" + std::to_string(cpu_index) +
72                  "/cpufreq/scaling_boost_frequencies"));
73   }
74 
75   // Build index with guards.
76   uint32_t last_cpu = 0;
77   uint32_t index = 0;
78   frequencies_index_.push_back(0);
79   for (const auto& cpu_freq : freqs) {
80     frequencies_.push_back(cpu_freq.second);
81     if (cpu_freq.first != last_cpu)
82       frequencies_index_.push_back(index);
83     last_cpu = cpu_freq.first;
84     index++;
85   }
86   frequencies_.push_back(0);
87   frequencies_index_.push_back(index);
88 }
89 
90 CpuFreqInfo::~CpuFreqInfo() = default;
91 
GetFreqs(uint32_t cpu)92 CpuFreqInfo::Range CpuFreqInfo::GetFreqs(uint32_t cpu) {
93   if (cpu >= frequencies_index_.size() - 1) {
94     PERFETTO_DLOG("No frequencies for cpu%" PRIu32, cpu);
95     const uint32_t* end = frequencies_.data() + frequencies_.size();
96     return {end, end};
97   }
98   auto* start = &frequencies_[frequencies_index_[cpu]];
99   auto* end = &frequencies_[frequencies_index_[cpu + 1]];
100   return {start, end};
101 }
102 
GetCpuFreqIndex(uint32_t cpu,uint32_t freq)103 uint32_t CpuFreqInfo::GetCpuFreqIndex(uint32_t cpu, uint32_t freq) {
104   auto range = GetFreqs(cpu);
105   uint32_t index = 0;
106   for (const uint32_t* it = range.first; it != range.second; it++, index++) {
107     if (*it == freq) {
108       return static_cast<uint32_t>(frequencies_index_[cpu]) + index + 1;
109     }
110   }
111   return 0;
112 }
113 
ReadFile(std::string path)114 std::string CpuFreqInfo::ReadFile(std::string path) {
115   std::string contents;
116   if (!base::ReadFile(path, &contents))
117     return "";
118   return contents;
119 }
120 
121 }  // namespace perfetto
122