1 // Copyright 2018 The Chromium Authors. All rights reserved.
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/strings/stringprintf.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace base {
18 namespace trace_event {
19
20 class TestTaskRunner : public SingleThreadTaskRunner {
21 public:
PostDelayedTask(const Location & from_here,OnceClosure task,base::TimeDelta delay)22 bool PostDelayedTask(const Location& from_here,
23 OnceClosure task,
24 base::TimeDelta delay) override {
25 delayed_tasks_.push_back(std::make_pair(std::move(delay), std::move(task)));
26 return true;
27 }
28
PostNonNestableDelayedTask(const Location & from_here,OnceClosure task,base::TimeDelta delay)29 bool PostNonNestableDelayedTask(const Location& from_here,
30 OnceClosure task,
31 base::TimeDelta delay) override {
32 NOTREACHED();
33 return false;
34 }
35
RunsTasksInCurrentSequence() const36 bool RunsTasksInCurrentSequence() const override { return true; }
37
38 // Returns the delay in ms for this task if there was a task to be run,
39 // and -1 if there are no tasks in the queue.
RunNextTask()40 int64_t RunNextTask() {
41 if (delayed_tasks_.size() == 0)
42 return -1;
43 auto time_delta = delayed_tasks_.front().first;
44 std::move(delayed_tasks_.front().second).Run();
45 delayed_tasks_.pop_front();
46 return time_delta.InMilliseconds();
47 }
48
49 private:
~TestTaskRunner()50 ~TestTaskRunner() final {}
51
52 std::list<std::pair<base::TimeDelta, OnceClosure>> delayed_tasks_;
53 };
54
55 class TestDelegate : public CPUFreqMonitorDelegate {
56 public:
TestDelegate(const std::string & temp_dir_path)57 TestDelegate(const std::string& temp_dir_path)
58 : temp_dir_path_(temp_dir_path) {}
59
set_trace_category_enabled(bool enabled)60 void set_trace_category_enabled(bool enabled) {
61 trace_category_enabled_ = enabled;
62 }
63
set_cpu_ids(const std::vector<unsigned int> & cpu_ids)64 void set_cpu_ids(const std::vector<unsigned int>& cpu_ids) {
65 cpu_ids_ = cpu_ids;
66 }
67
set_kernel_max_cpu(unsigned int kernel_max_cpu)68 void set_kernel_max_cpu(unsigned int kernel_max_cpu) {
69 kernel_max_cpu_ = kernel_max_cpu;
70 }
71
recorded_freqs()72 const std::vector<std::pair<unsigned int, unsigned int>>& recorded_freqs() {
73 return recorded_freqs_;
74 }
75
76 // CPUFreqMonitorDelegate implementation:
GetCPUIds(std::vector<unsigned int> * ids) const77 void GetCPUIds(std::vector<unsigned int>* ids) const override {
78 // Use the test values if available.
79 if (cpu_ids_.size() > 0) {
80 *ids = cpu_ids_;
81 return;
82 }
83 // Otherwise fall back to the original function.
84 CPUFreqMonitorDelegate::GetCPUIds(ids);
85 }
86
RecordFrequency(unsigned int cpu_id,unsigned int freq)87 void RecordFrequency(unsigned int cpu_id, unsigned int freq) override {
88 recorded_freqs_.emplace_back(
89 std::pair<unsigned int, unsigned int>(cpu_id, freq));
90 }
91
IsTraceCategoryEnabled() const92 bool IsTraceCategoryEnabled() const override {
93 return trace_category_enabled_;
94 }
95
GetScalingCurFreqPathString(unsigned int cpu_id) const96 std::string GetScalingCurFreqPathString(unsigned int cpu_id) const override {
97 return base::StringPrintf("%s/scaling_cur_freq%d", temp_dir_path_.c_str(),
98 cpu_id);
99 }
100
GetRelatedCPUsPathString(unsigned int cpu_id) const101 std::string GetRelatedCPUsPathString(unsigned int cpu_id) const override {
102 return base::StringPrintf("%s/related_cpus%d", temp_dir_path_.c_str(),
103 cpu_id);
104 }
105
GetKernelMaxCPUs() const106 unsigned int GetKernelMaxCPUs() const override { return kernel_max_cpu_; }
107
108 protected:
CreateTaskRunner()109 scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner() override {
110 return base::WrapRefCounted(new TestTaskRunner());
111 }
112
113 private:
114 // Maps CPU ID to frequency.
115 std::vector<std::pair<unsigned int, unsigned int>> recorded_freqs_;
116
117 std::vector<unsigned int> cpu_ids_;
118
119 bool trace_category_enabled_ = true;
120 std::string temp_dir_path_;
121 unsigned int kernel_max_cpu_ = 0;
122 };
123
124 class CPUFreqMonitorTest : public testing::Test {
125 public:
CPUFreqMonitorTest()126 CPUFreqMonitorTest() : testing::Test() {}
127
SetUp()128 void SetUp() override {
129 temp_dir_ = std::make_unique<ScopedTempDir>();
130 ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
131
132 std::string base_path = temp_dir_->GetPath().value();
133 auto delegate = std::make_unique<TestDelegate>(base_path);
134 // Retain a pointer to the delegate since we're passing ownership to the
135 // monitor but we need to be able to modify it.
136 delegate_ = delegate.get();
137
138 // Can't use make_unique because it's a private constructor.
139 CPUFreqMonitor* monitor = new CPUFreqMonitor(std::move(delegate));
140 monitor_.reset(monitor);
141 }
142
TearDown()143 void TearDown() override {
144 monitor_.reset();
145 temp_dir_.reset();
146 }
147
CreateDefaultScalingCurFreqFiles(const std::vector<std::pair<unsigned int,unsigned int>> & frequencies)148 void CreateDefaultScalingCurFreqFiles(
149 const std::vector<std::pair<unsigned int, unsigned int>>& frequencies) {
150 for (auto& pair : frequencies) {
151 std::string file_path =
152 delegate_->GetScalingCurFreqPathString(pair.first);
153 std::string str_freq = base::StringPrintf("%d\n", pair.second);
154 base::WriteFile(base::FilePath(file_path), str_freq.c_str(),
155 str_freq.length());
156 }
157 }
158
CreateRelatedCPUFiles(const std::vector<unsigned int> & clusters,const std::vector<std::string> & related_cpus)159 void CreateRelatedCPUFiles(const std::vector<unsigned int>& clusters,
160 const std::vector<std::string>& related_cpus) {
161 for (unsigned int i = 0; i < clusters.size(); i++) {
162 base::WriteFile(base::FilePath(delegate_->GetRelatedCPUsPathString(i)),
163 related_cpus[clusters[i]].c_str(),
164 related_cpus[clusters[i]].length());
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 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