1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/trace_event/cpufreq_monitor_android.h"
6
7 #include <list>
8
9 #include <fcntl.h>
10
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_file.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/time.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace base {
20 namespace trace_event {
21
22 class TestTaskRunner final : public SingleThreadTaskRunner {
23 public:
PostDelayedTask(const Location & from_here,OnceClosure task,base::TimeDelta delay)24 bool PostDelayedTask(const Location& from_here,
25 OnceClosure task,
26 base::TimeDelta delay) override {
27 delayed_tasks_.push_back(std::make_pair(std::move(delay), std::move(task)));
28 return true;
29 }
30
PostNonNestableDelayedTask(const Location & from_here,OnceClosure task,base::TimeDelta delay)31 bool PostNonNestableDelayedTask(const Location& from_here,
32 OnceClosure task,
33 base::TimeDelta delay) override {
34 NOTREACHED();
35 return false;
36 }
37
RunsTasksInCurrentSequence() const38 bool RunsTasksInCurrentSequence() const override { return true; }
39
40 // Returns the delay in ms for this task if there was a task to be run,
41 // and -1 if there are no tasks in the queue.
RunNextTask()42 int64_t RunNextTask() {
43 if (delayed_tasks_.size() == 0)
44 return -1;
45 auto time_delta = delayed_tasks_.front().first;
46 std::move(delayed_tasks_.front().second).Run();
47 delayed_tasks_.pop_front();
48 return time_delta.InMilliseconds();
49 }
50
51 private:
~TestTaskRunner()52 ~TestTaskRunner() override {}
53
54 std::list<std::pair<base::TimeDelta, OnceClosure>> delayed_tasks_;
55 };
56
57 class TestDelegate : public CPUFreqMonitorDelegate {
58 public:
TestDelegate(const std::string & temp_dir_path)59 TestDelegate(const std::string& temp_dir_path)
60 : temp_dir_path_(temp_dir_path) {}
61
set_trace_category_enabled(bool enabled)62 void set_trace_category_enabled(bool enabled) {
63 trace_category_enabled_ = enabled;
64 }
65
set_cpu_ids(const std::vector<unsigned int> & cpu_ids)66 void set_cpu_ids(const std::vector<unsigned int>& cpu_ids) {
67 cpu_ids_ = cpu_ids;
68 }
69
set_kernel_max_cpu(unsigned int kernel_max_cpu)70 void set_kernel_max_cpu(unsigned int kernel_max_cpu) {
71 kernel_max_cpu_ = kernel_max_cpu;
72 }
73
recorded_freqs()74 const std::vector<std::pair<unsigned int, unsigned int>>& recorded_freqs() {
75 return recorded_freqs_;
76 }
77
78 // CPUFreqMonitorDelegate implementation:
GetCPUIds(std::vector<unsigned int> * ids) const79 void GetCPUIds(std::vector<unsigned int>* ids) const override {
80 // Use the test values if available.
81 if (cpu_ids_.size() > 0) {
82 *ids = cpu_ids_;
83 return;
84 }
85 // Otherwise fall back to the original function.
86 CPUFreqMonitorDelegate::GetCPUIds(ids);
87 }
88
RecordFrequency(unsigned int cpu_id,unsigned int freq)89 void RecordFrequency(unsigned int cpu_id, unsigned int freq) override {
90 recorded_freqs_.emplace_back(
91 std::pair<unsigned int, unsigned int>(cpu_id, freq));
92 }
93
IsTraceCategoryEnabled() const94 bool IsTraceCategoryEnabled() const override {
95 return trace_category_enabled_;
96 }
97
GetScalingCurFreqPathString(unsigned int cpu_id) const98 std::string GetScalingCurFreqPathString(unsigned int cpu_id) const override {
99 return base::StringPrintf("%s/scaling_cur_freq%d", temp_dir_path_.c_str(),
100 cpu_id);
101 }
102
GetRelatedCPUsPathString(unsigned int cpu_id) const103 std::string GetRelatedCPUsPathString(unsigned int cpu_id) const override {
104 return base::StringPrintf("%s/related_cpus%d", temp_dir_path_.c_str(),
105 cpu_id);
106 }
107
GetKernelMaxCPUs() const108 unsigned int GetKernelMaxCPUs() const override { return kernel_max_cpu_; }
109
110 protected:
CreateTaskRunner()111 scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner() override {
112 return base::WrapRefCounted(new TestTaskRunner());
113 }
114
115 private:
116 // Maps CPU ID to frequency.
117 std::vector<std::pair<unsigned int, unsigned int>> recorded_freqs_;
118
119 std::vector<unsigned int> cpu_ids_;
120
121 bool trace_category_enabled_ = true;
122 std::string temp_dir_path_;
123 unsigned int kernel_max_cpu_ = 0;
124 };
125
126 class CPUFreqMonitorTest : public testing::Test {
127 public:
CPUFreqMonitorTest()128 CPUFreqMonitorTest() : testing::Test() {}
129
SetUp()130 void SetUp() override {
131 temp_dir_ = std::make_unique<ScopedTempDir>();
132 ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
133
134 std::string base_path = temp_dir_->GetPath().value();
135 auto delegate = std::make_unique<TestDelegate>(base_path);
136 // Retain a pointer to the delegate since we're passing ownership to the
137 // monitor but we need to be able to modify it.
138 delegate_ = delegate.get();
139
140 // Can't use make_unique because it's a private constructor.
141 CPUFreqMonitor* monitor = new CPUFreqMonitor(std::move(delegate));
142 monitor_.reset(monitor);
143 }
144
TearDown()145 void TearDown() override {
146 monitor_.reset();
147 temp_dir_.reset();
148 }
149
CreateDefaultScalingCurFreqFiles(const std::vector<std::pair<unsigned int,unsigned int>> & frequencies)150 void CreateDefaultScalingCurFreqFiles(
151 const std::vector<std::pair<unsigned int, unsigned int>>& frequencies) {
152 for (auto& pair : frequencies) {
153 std::string file_path =
154 delegate_->GetScalingCurFreqPathString(pair.first);
155 std::string str_freq = base::StringPrintf("%d\n", pair.second);
156 base::WriteFile(base::FilePath(file_path), str_freq);
157 }
158 }
159
CreateRelatedCPUFiles(const std::vector<unsigned int> & clusters,const std::vector<std::string> & related_cpus)160 void CreateRelatedCPUFiles(const std::vector<unsigned int>& clusters,
161 const std::vector<std::string>& related_cpus) {
162 for (unsigned int i = 0; i < clusters.size(); i++) {
163 base::WriteFile(base::FilePath(delegate_->GetRelatedCPUsPathString(i)),
164 related_cpus[clusters[i]]);
165 }
166 }
167
InitBasicCPUInfo()168 void InitBasicCPUInfo() {
169 std::vector<std::pair<unsigned int, unsigned int>> frequencies = {
170 {0, 500}, {2, 1000}, {4, 800}, {6, 750},
171 };
172 std::vector<unsigned int> cpu_ids;
173 for (auto& pair : frequencies) {
174 cpu_ids.push_back(pair.first);
175 }
176 delegate()->set_cpu_ids(cpu_ids);
177
178 CreateDefaultScalingCurFreqFiles(frequencies);
179 }
180
GetOrCreateTaskRunner()181 TestTaskRunner* GetOrCreateTaskRunner() {
182 return static_cast<TestTaskRunner*>(
183 monitor_->GetOrCreateTaskRunner().get());
184 }
185
monitor()186 CPUFreqMonitor* monitor() { return monitor_.get(); }
temp_dir()187 ScopedTempDir* temp_dir() { return temp_dir_.get(); }
delegate()188 TestDelegate* delegate() { return delegate_; }
189
190 private:
191 scoped_refptr<TestTaskRunner> task_runner_;
192 std::unique_ptr<ScopedTempDir> temp_dir_;
193 std::unique_ptr<CPUFreqMonitor> monitor_;
194 raw_ptr<TestDelegate> delegate_;
195 };
196
TEST_F(CPUFreqMonitorTest,TestStart)197 TEST_F(CPUFreqMonitorTest, TestStart) {
198 InitBasicCPUInfo();
199 monitor()->Start();
200 ASSERT_TRUE(monitor()->IsEnabledForTesting());
201 }
202
TEST_F(CPUFreqMonitorTest,TestSample)203 TEST_F(CPUFreqMonitorTest, TestSample) {
204 // Vector of CPU ID to frequency.
205 std::vector<std::pair<unsigned int, unsigned int>> frequencies = {{0, 500},
206 {4, 1000}};
207 std::vector<unsigned int> cpu_ids;
208 for (auto& pair : frequencies) {
209 cpu_ids.push_back(pair.first);
210 }
211 delegate()->set_cpu_ids(cpu_ids);
212
213 // Build some files with CPU frequency info in it to sample.
214 std::vector<std::pair<unsigned int, base::ScopedFD>> fds;
215 for (auto& pair : frequencies) {
216 std::string file_path = base::StringPrintf(
217 "%s/temp%d", temp_dir()->GetPath().value().c_str(), pair.first);
218
219 // Uses raw file descriptors so we can build our ScopedFDs in the same loop.
220 int fd = open(file_path.c_str(), O_RDWR | O_CREAT | O_SYNC,
221 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
222 ASSERT_FALSE(fd == -1);
223
224 std::string str_freq = base::StringPrintf("%d\n", pair.second);
225 write(fd, str_freq.c_str(), str_freq.length());
226 fds.emplace_back(std::make_pair(pair.first, base::ScopedFD(fd)));
227 }
228
229 // This ensures we set it to enabled before sampling, otherwise the call to
230 // Sample() will end early.
231 CreateDefaultScalingCurFreqFiles(frequencies);
232 monitor()->Start();
233 ASSERT_TRUE(monitor()->IsEnabledForTesting());
234
235 // Ensure that we run our undelayed posted task for Sample.
236 ASSERT_EQ(GetOrCreateTaskRunner()->RunNextTask(), 0);
237 // Run the new delayed task so we sample again.
238 ASSERT_TRUE(GetOrCreateTaskRunner()->RunNextTask() ==
239 CPUFreqMonitor::kDefaultCPUFreqSampleIntervalMs);
240
241 // Ensure that the values that we recorded agree with the frequencies above.
242 auto recorded_freqs = delegate()->recorded_freqs();
243 ASSERT_EQ(recorded_freqs.size(), frequencies.size() * 2);
244 for (unsigned int i = 0; i < frequencies.size(); i++) {
245 auto freq_pair = frequencies[i];
246 // We sampled twice, so the recording pairs should be equal.
247 auto recorded_pair_1 = recorded_freqs[i];
248 auto recorded_pair_2 = recorded_freqs[i + 2];
249 ASSERT_EQ(freq_pair.first, recorded_pair_1.first);
250 ASSERT_EQ(freq_pair.second, recorded_pair_1.second);
251 ASSERT_EQ(freq_pair.first, recorded_pair_2.first);
252 ASSERT_EQ(freq_pair.second, recorded_pair_2.second);
253 }
254
255 // Test that calling Stop works, we shouldn't post any more tasks if Sample
256 // is called.
257 monitor()->Stop();
258 // Clear out the first Sample task that's on deck, then try again to make sure
259 // no new task was posted.
260 ASSERT_TRUE(GetOrCreateTaskRunner()->RunNextTask() ==
261 CPUFreqMonitor::kDefaultCPUFreqSampleIntervalMs);
262 ASSERT_EQ(GetOrCreateTaskRunner()->RunNextTask(), -1);
263 }
264
TEST_F(CPUFreqMonitorTest,TestStartFail_TraceCategoryDisabled)265 TEST_F(CPUFreqMonitorTest, TestStartFail_TraceCategoryDisabled) {
266 delegate()->set_trace_category_enabled(false);
267 CreateDefaultScalingCurFreqFiles({{0, 1000}});
268 monitor()->Start();
269 ASSERT_FALSE(monitor()->IsEnabledForTesting());
270 }
271
TEST_F(CPUFreqMonitorTest,TestStartFail_NoScalingCurFreqFiles)272 TEST_F(CPUFreqMonitorTest, TestStartFail_NoScalingCurFreqFiles) {
273 monitor()->Start();
274 ASSERT_FALSE(monitor()->IsEnabledForTesting());
275 }
276
TEST_F(CPUFreqMonitorTest,TestDelegate_GetCPUIds)277 TEST_F(CPUFreqMonitorTest, TestDelegate_GetCPUIds) {
278 delegate()->set_kernel_max_cpu(8);
279 std::vector<std::string> related_cpus = {"0 1 2 3\n", "4 5 6 7\n"};
280 std::vector<unsigned int> clusters = {0, 0, 0, 0, 1, 1, 1, 1};
281
282 CreateRelatedCPUFiles(clusters, related_cpus);
283
284 std::vector<unsigned int> cpu_ids;
285 delegate()->GetCPUIds(&cpu_ids);
286 EXPECT_EQ(cpu_ids.size(), 2U);
287 EXPECT_EQ(cpu_ids[0], 0U);
288 EXPECT_EQ(cpu_ids[1], 4U);
289 }
290
TEST_F(CPUFreqMonitorTest,TestDelegate_GetCPUIds_FailReadingFallback)291 TEST_F(CPUFreqMonitorTest, TestDelegate_GetCPUIds_FailReadingFallback) {
292 delegate()->set_kernel_max_cpu(8);
293
294 std::vector<unsigned int> cpu_ids;
295 delegate()->GetCPUIds(&cpu_ids);
296 EXPECT_EQ(cpu_ids.size(), 1U);
297 EXPECT_EQ(cpu_ids[0], 0U);
298 }
299
TEST_F(CPUFreqMonitorTest,TestMultipleStartStop)300 TEST_F(CPUFreqMonitorTest, TestMultipleStartStop) {
301 InitBasicCPUInfo();
302
303 monitor()->Start();
304 ASSERT_TRUE(monitor()->IsEnabledForTesting());
305 monitor()->Stop();
306 ASSERT_FALSE(monitor()->IsEnabledForTesting());
307
308 monitor()->Start();
309 ASSERT_TRUE(monitor()->IsEnabledForTesting());
310 monitor()->Stop();
311 ASSERT_FALSE(monitor()->IsEnabledForTesting());
312 }
313
TEST_F(CPUFreqMonitorTest,TestTraceLogEnableDisable)314 TEST_F(CPUFreqMonitorTest, TestTraceLogEnableDisable) {
315 InitBasicCPUInfo();
316
317 monitor()->OnTraceLogEnabled();
318 // OnTraceLogEnabled posts a task for Start.
319 GetOrCreateTaskRunner()->RunNextTask();
320 ASSERT_TRUE(monitor()->IsEnabledForTesting());
321 monitor()->OnTraceLogDisabled();
322 ASSERT_FALSE(monitor()->IsEnabledForTesting());
323 // We also need to clear out the task for Sample from the Start call.
324 GetOrCreateTaskRunner()->RunNextTask();
325
326 monitor()->OnTraceLogEnabled();
327 GetOrCreateTaskRunner()->RunNextTask();
328 ASSERT_TRUE(monitor()->IsEnabledForTesting());
329 monitor()->OnTraceLogDisabled();
330 ASSERT_FALSE(monitor()->IsEnabledForTesting());
331 }
332
333 } // namespace trace_event
334 } // namespace base
335