• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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