• 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 <unistd.h>
20 
21 #include <set>
22 
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/base/string_splitter.h"
25 #include "perfetto/ext/base/string_utils.h"
26 
27 namespace perfetto {
28 
29 namespace {
30 
31 using CpuAndFreq = std::pair</* cpu */ uint32_t, /* freq */ uint32_t>;
32 
33 // Reads space-separated CPU frequencies from a sysfs file.
ReadAndAppendFreqs(std::set<CpuAndFreq> * freqs,uint32_t cpu_index,const std::string & sys_cpu_freqs)34 void ReadAndAppendFreqs(std::set<CpuAndFreq>* freqs,
35                         uint32_t cpu_index,
36                         const std::string& sys_cpu_freqs) {
37   base::StringSplitter entries(sys_cpu_freqs, ' ');
38   while (entries.Next()) {
39     auto freq = base::StringToUInt32(entries.cur_token());
40     if (freq.has_value())
41       freqs->insert({cpu_index, freq.value()});
42   }
43 }
44 
45 }  // namespace
46 
CpuFreqInfo(std::string sysfs_cpu_path)47 CpuFreqInfo::CpuFreqInfo(std::string sysfs_cpu_path)
48     : sysfs_cpu_path_{sysfs_cpu_path} {
49   base::ScopedDir cpu_dir(opendir(sysfs_cpu_path_.c_str()));
50   if (!cpu_dir) {
51     PERFETTO_PLOG("Failed to opendir(%s)", sysfs_cpu_path_.c_str());
52     return;
53   }
54   // Accumulate cpu and freqs into a set to ensure stable order.
55   std::set<CpuAndFreq> freqs;
56   while (struct dirent* dir_ent = readdir(*cpu_dir)) {
57     if (dir_ent->d_type != DT_DIR)
58       continue;
59     std::string dir_name(dir_ent->d_name);
60     if (!base::StartsWith(dir_name, "cpu"))
61       continue;
62     auto maybe_cpu_index =
63         base::StringToUInt32(base::StripPrefix(dir_name, "cpu"));
64     // There are some directories (cpufreq, cpuidle) which should be skipped.
65     if (!maybe_cpu_index.has_value())
66       continue;
67     uint32_t cpu_index = maybe_cpu_index.value();
68     ReadAndAppendFreqs(
69         &freqs, cpu_index,
70         ReadFile(sysfs_cpu_path_ + "/cpu" + std::to_string(cpu_index) +
71                  "/cpufreq/scaling_available_frequencies"));
72     ReadAndAppendFreqs(
73         &freqs, cpu_index,
74         ReadFile(sysfs_cpu_path_ + "/cpu" + std::to_string(cpu_index) +
75                  "/cpufreq/scaling_boost_frequencies"));
76   }
77 
78   // Build index with guards.
79   uint32_t last_cpu = 0;
80   uint32_t index = 0;
81   frequencies_index_.push_back(0);
82   for (const auto& cpu_freq : freqs) {
83     frequencies_.push_back(cpu_freq.second);
84     if (cpu_freq.first != last_cpu)
85       frequencies_index_.push_back(index);
86     last_cpu = cpu_freq.first;
87     index++;
88   }
89   frequencies_.push_back(0);
90   frequencies_index_.push_back(index);
91 }
92 
93 CpuFreqInfo::~CpuFreqInfo() = default;
94 
GetFreqs(uint32_t cpu)95 CpuFreqInfo::Range CpuFreqInfo::GetFreqs(uint32_t cpu) {
96   if (cpu >= frequencies_index_.size() - 1) {
97     PERFETTO_DLOG("No frequencies for cpu%" PRIu32, cpu);
98     const uint32_t* end = frequencies_.data() + frequencies_.size();
99     return {end, end};
100   }
101   auto* start = &frequencies_[frequencies_index_[cpu]];
102   auto* end = &frequencies_[frequencies_index_[cpu + 1]];
103   return {start, end};
104 }
105 
GetCpuFreqIndex(uint32_t cpu,uint32_t freq)106 uint32_t CpuFreqInfo::GetCpuFreqIndex(uint32_t cpu, uint32_t freq) {
107   auto range = GetFreqs(cpu);
108   uint32_t index = 0;
109   for (const uint32_t* it = range.first; it != range.second; it++, index++) {
110     if (*it == freq) {
111       return static_cast<uint32_t>(frequencies_index_[cpu]) + index + 1;
112     }
113   }
114   return 0;
115 }
116 
ReadFile(std::string path)117 std::string CpuFreqInfo::ReadFile(std::string path) {
118   std::string contents;
119   if (!base::ReadFile(path, &contents))
120     return "";
121   return contents;
122 }
123 
ReadCpuCurrFreq()124 const std::vector<uint32_t>& CpuFreqInfo::ReadCpuCurrFreq() {
125   // Check if capacity of cpu_curr_freq_ is enough for all CPUs
126   auto num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
127   if (cpu_curr_freq_.size() < num_cpus)
128     cpu_curr_freq_.resize(num_cpus);
129 
130   for (uint32_t i = 0; i < cpu_curr_freq_.size(); i++) {
131     // Read CPU current frequency. Set 0 for offline/disabled cpus.
132     std::string buf(ReadFile(sysfs_cpu_path_ + "/cpu" + std::to_string(i) +
133                              "/cpufreq/scaling_cur_freq"));
134     if (buf.empty()) {
135       cpu_curr_freq_[i] = 0;
136       continue;
137     }
138     auto freq = base::StringToUInt32(base::StripSuffix(buf, "\n"));
139     if (!freq.has_value()) {
140       cpu_curr_freq_[i] = 0;
141       continue;
142     }
143     cpu_curr_freq_[i] = freq.value();
144   }
145   return cpu_curr_freq_;
146 }
147 
148 }  // namespace perfetto
149