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