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