• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specic language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <algorithm>
18 #include <thread>
19 
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/test_utils.h>
23 
24 #include <gtest/gtest.h>
25 
26 #include "perfmgr/HintManager.h"
27 
28 namespace android {
29 namespace perfmgr {
30 
31 using namespace std::chrono_literals;
32 
33 constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
34 
35 // JSON_CONFIG
36 // {
37 //     "Nodes": [
38 //         {
39 //             "Name": "CPUCluster0MinFreq",
40 //             "Path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq",
41 //             "Values": [
42 //                 "1512000",
43 //                 "1134000",
44 //                 "384000"
45 //             ],
46 //             "DefaultIndex": 2,
47 //             "ResetOnInit": true
48 //         },
49 //         {
50 //             "Name": "CPUCluster1MinFreq",
51 //             "Path": "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq",
52 //             "Values": [
53 //                 "1512000",
54 //                 "1134000",
55 //                 "384000"
56 //             ],
57 //             "HoldFd": true
58 //         }
59 //     ],
60 //     "Actions": [
61 //         {
62 //             "PowerHint": "INTERACTION",
63 //             "Node": "CPUCluster1MinFreq",
64 //             "Value": "1134000",
65 //             "Duration": 800
66 //         },
67 //         {
68 //             "PowerHint": "LAUNCH",
69 //             "Node": "CPUCluster0MinFreq",
70 //             "Value": "1134000",
71 //             "Duration": 500
72 //         },
73 //         {
74 //             "PowerHint": "LAUNCH",
75 //             "Node": "CPUCluster1MinFreq",
76 //             "Value": "1512000",
77 //             "Duration": 2000
78 //         }
79 //     ]
80 // }
81 constexpr char kJSON_RAW[] =
82     "{\"Nodes\":[{\"Name\":\"CPUCluster0MinFreq\",\"Path\":\"/sys/devices/"
83     "system/cpu/cpu0/cpufreq/"
84     "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
85     "\"DefaultIndex\":2,\"ResetOnInit\":true},{\"Name\":\"CPUCluster1MinFreq\","
86     "\"Path\":\"/sys/devices/system/cpu/cpu4/cpufreq/"
87     "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
88     "\"HoldFd\":true}],\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":"
89     "\"CPUCluster1MinFreq\",\"Value\":\"1134000\",\"Duration\":800},{"
90     "\"PowerHint\":"
91     "\"LAUNCH\",\"Node\":\"CPUCluster0MinFreq\",\"Value\":\"1134000\","
92     "\"Duration\":"
93     "500},{\"PowerHint\":\"LAUNCH\",\"Node\":\"CPUCluster1MinFreq\","
94     "\"Value\":\"1512000\",\"Duration\":2000}]}";
95 
96 class HintManagerTest : public ::testing::Test, public HintManager {
97   protected:
HintManagerTest()98     HintManagerTest()
99         : HintManager(nullptr,
100                       std::map<std::string, std::vector<NodeAction>>{}) {
101         android::base::SetMinimumLogSeverity(android::base::VERBOSE);
102     }
103 
SetUp()104     virtual void SetUp() {
105         // Set up dummy nodes
106         std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
107         nodes_.emplace_back(
108             new Node("n0", tf->path,
109                      {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, false));
110         files_.emplace_back(std::move(tf));
111         tf = std::make_unique<TemporaryFile>();
112         nodes_.emplace_back(
113             new Node("n1", tf->path,
114                      {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, true));
115         files_.emplace_back(std::move(tf));
116         nm_ = new NodeLooperThread(std::move(nodes_));
117         // Set up dummy actions
118         // "INTERACTION"
119         // Node0, value1, 800ms
120         // Node1, value1, forever
121         // "LAUNCH"
122         // Node0, value0, forever
123         // Node1, value0, 400ms
124         actions_ = std::map<std::string, std::vector<NodeAction>>{
125             {"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}}},
126             {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}}}};
127 
128         // Prepare dummy files to replace the nodes' path in example json_doc
129         files_.emplace_back(std::make_unique<TemporaryFile>());
130         files_.emplace_back(std::make_unique<TemporaryFile>());
131         // replace filepath
132         json_doc_ = kJSON_RAW;
133         std::string from =
134             "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq";
135         size_t start_pos = json_doc_.find(from);
136         json_doc_.replace(start_pos, from.length(), files_[0 + 2]->path);
137         from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq";
138         start_pos = json_doc_.find(from);
139         json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
140     }
141 
TearDown()142     virtual void TearDown() {
143         actions_.clear();
144         nodes_.clear();
145         files_.clear();
146         nm_ = nullptr;
147     }
148     sp<NodeLooperThread> nm_;
149     std::map<std::string, std::vector<NodeAction>> actions_;
150     std::vector<std::unique_ptr<Node>> nodes_;
151     std::vector<std::unique_ptr<TemporaryFile>> files_;
152     std::string json_doc_;
153 };
154 
_VerifyPathValue(const std::string & path,const std::string & value)155 static inline void _VerifyPathValue(const std::string& path,
156                                     const std::string& value) {
157     std::string s;
158     EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
159     EXPECT_EQ(value, s);
160 }
161 
162 // Test GetHints
TEST_F(HintManagerTest,GetHintsTest)163 TEST_F(HintManagerTest, GetHintsTest) {
164     HintManager hm(nm_, actions_);
165     std::vector<std::string> hints = hm.GetHints();
166     EXPECT_TRUE(hm.IsRunning());
167     EXPECT_EQ(2u, hints.size());
168     EXPECT_NE(std::find(hints.begin(), hints.end(), "INTERACTION"), hints.end());
169     EXPECT_NE(std::find(hints.begin(), hints.end(), "LAUNCH"), hints.end());
170 }
171 
172 // Test initialization of default values
TEST_F(HintManagerTest,HintInitDefaultTest)173 TEST_F(HintManagerTest, HintInitDefaultTest) {
174     HintManager hm(nm_, actions_);
175     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
176     EXPECT_TRUE(hm.IsRunning());
177     _VerifyPathValue(files_[0]->path, "");
178     _VerifyPathValue(files_[1]->path, "n1_value2");
179 }
180 
181 // Test hint/cancel/expire
TEST_F(HintManagerTest,HintTest)182 TEST_F(HintManagerTest, HintTest) {
183     HintManager hm(nm_, actions_);
184     EXPECT_TRUE(hm.IsRunning());
185     EXPECT_TRUE(hm.DoHint("INTERACTION"));
186     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
187     _VerifyPathValue(files_[0]->path, "n0_value1");
188     _VerifyPathValue(files_[1]->path, "n1_value1");
189     // this won't change the expire time of INTERACTION hint
190     EXPECT_TRUE(hm.DoHint("INTERACTION", 200ms));
191     // now place new hint
192     EXPECT_TRUE(hm.DoHint("LAUNCH"));
193     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
194     _VerifyPathValue(files_[0]->path, "n0_value0");
195     _VerifyPathValue(files_[1]->path, "n1_value0");
196     EXPECT_TRUE(hm.DoHint("LAUNCH", 500ms));
197     // no"LAUNCH" node1 not expired
198     std::this_thread::sleep_for(400ms);
199     _VerifyPathValue(files_[0]->path, "n0_value0");
200     _VerifyPathValue(files_[1]->path, "n1_value0");
201     // "LAUNCH" node1 expired
202     std::this_thread::sleep_for(100ms + kSLEEP_TOLERANCE_MS);
203     _VerifyPathValue(files_[0]->path, "n0_value0");
204     _VerifyPathValue(files_[1]->path, "n1_value1");
205     EXPECT_TRUE(hm.EndHint("LAUNCH"));
206     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
207     // "LAUNCH" canceled
208     _VerifyPathValue(files_[0]->path, "n0_value1");
209     _VerifyPathValue(files_[1]->path, "n1_value1");
210     std::this_thread::sleep_for(200ms);
211     // "INTERACTION" node0 expired
212     _VerifyPathValue(files_[0]->path, "n0_value2");
213     _VerifyPathValue(files_[1]->path, "n1_value1");
214     EXPECT_TRUE(hm.EndHint("INTERACTION"));
215     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
216     // "INTERACTION" canceled
217     _VerifyPathValue(files_[0]->path, "n0_value2");
218     _VerifyPathValue(files_[1]->path, "n1_value2");
219 }
220 
221 // Test parsing nodes with duplicate name
TEST_F(HintManagerTest,ParseNodesTest)222 TEST_F(HintManagerTest, ParseNodesTest) {
223     std::vector<std::unique_ptr<Node>> nodes =
224         HintManager::ParseNodes(json_doc_);
225     EXPECT_EQ(2u, nodes.size());
226     EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
227     EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
228     EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
229     EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
230     EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
231     EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
232     EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
233     EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
234     EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
235     EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
236     EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
237     EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
238     EXPECT_TRUE(nodes[0]->GetResetOnInit());
239     EXPECT_FALSE(nodes[1]->GetResetOnInit());
240     EXPECT_FALSE(nodes[0]->GetHoldFd());
241     EXPECT_TRUE(nodes[1]->GetHoldFd());
242 }
243 
244 // Test parsing actions
TEST_F(HintManagerTest,ParseNodesDuplicateNameTest)245 TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
246     std::string from = "CPUCluster0MinFreq";
247     size_t start_pos = json_doc_.find(from);
248     json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
249     std::vector<std::unique_ptr<Node>> nodes =
250         HintManager::ParseNodes(json_doc_);
251     EXPECT_EQ(0u, nodes.size());
252 }
253 
254 // Test parsing nodes with duplicate path
TEST_F(HintManagerTest,ParseNodesDuplicatePathTest)255 TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) {
256     std::string from = files_[0 + 2]->path;
257     size_t start_pos = json_doc_.find(from);
258     json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
259     std::vector<std::unique_ptr<Node>> nodes =
260         HintManager::ParseNodes(json_doc_);
261     EXPECT_EQ(0u, nodes.size());
262 }
263 
264 // Test parsing invalid json for nodes
TEST_F(HintManagerTest,ParseBadNodesTest)265 TEST_F(HintManagerTest, ParseBadNodesTest) {
266     std::vector<std::unique_ptr<Node>> nodes =
267         HintManager::ParseNodes("invalid json");
268     EXPECT_EQ(0u, nodes.size());
269     nodes = HintManager::ParseNodes(
270         "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
271         "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}");
272     EXPECT_EQ(0u, nodes.size());
273 }
274 
275 // Test parsing actions
TEST_F(HintManagerTest,ParseActionsTest)276 TEST_F(HintManagerTest, ParseActionsTest) {
277     std::vector<std::unique_ptr<Node>> nodes =
278         HintManager::ParseNodes(json_doc_);
279     std::map<std::string, std::vector<NodeAction>> actions =
280         HintManager::ParseActions(json_doc_, nodes);
281     EXPECT_EQ(2u, actions.size());
282     EXPECT_EQ(1u, actions["INTERACTION"].size());
283 
284     EXPECT_EQ(1u, actions["INTERACTION"][0].node_index);
285     EXPECT_EQ(1u, actions["INTERACTION"][0].value_index);
286     EXPECT_EQ(std::chrono::milliseconds(800).count(),
287               actions["INTERACTION"][0].timeout_ms.count());
288 
289     EXPECT_EQ(2u, actions["LAUNCH"].size());
290 
291     EXPECT_EQ(0u, actions["LAUNCH"][0].node_index);
292     EXPECT_EQ(1u, actions["LAUNCH"][0].value_index);
293     EXPECT_EQ(std::chrono::milliseconds(500).count(),
294               actions["LAUNCH"][0].timeout_ms.count());
295 
296     EXPECT_EQ(1u, actions["LAUNCH"][1].node_index);
297     EXPECT_EQ(0u, actions["LAUNCH"][1].value_index);
298     EXPECT_EQ(std::chrono::milliseconds(2000).count(),
299               actions["LAUNCH"][1].timeout_ms.count());
300 }
301 
302 // Test parsing actions with duplicate node
TEST_F(HintManagerTest,ParseActionDuplicateNodeTest)303 TEST_F(HintManagerTest, ParseActionDuplicateNodeTest) {
304     std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
305     size_t start_pos = json_doc_.find(from);
306     json_doc_.replace(start_pos, from.length(),
307                       "\"Node\": \"CPUCluster1MinFreq\"");
308     std::vector<std::unique_ptr<Node>> nodes =
309         HintManager::ParseNodes(json_doc_);
310     EXPECT_EQ(2u, nodes.size());
311     std::map<std::string, std::vector<NodeAction>> actions =
312         HintManager::ParseActions(json_doc_, nodes);
313     EXPECT_EQ(0u, actions.size());
314 }
315 
316 // Test parsing invalid json for actions
TEST_F(HintManagerTest,ParseBadActionsTest)317 TEST_F(HintManagerTest, ParseBadActionsTest) {
318     std::vector<std::unique_ptr<Node>> nodes =
319         HintManager::ParseNodes(json_doc_);
320     std::map<std::string, std::vector<NodeAction>> actions =
321         HintManager::ParseActions("invalid json", nodes);
322     EXPECT_EQ(0u, actions.size());
323     actions = HintManager::ParseActions(
324         "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
325         "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}",
326         nodes);
327     EXPECT_EQ(0u, actions.size());
328 }
329 
330 // Test hint/cancel/expire with json config
TEST_F(HintManagerTest,GetFromJSONTest)331 TEST_F(HintManagerTest, GetFromJSONTest) {
332     TemporaryFile json_file;
333     ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path))
334         << strerror(errno);
335     std::unique_ptr<HintManager> hm = HintManager::GetFromJSON(json_file.path);
336     EXPECT_NE(nullptr, hm.get());
337     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
338     EXPECT_TRUE(hm->IsRunning());
339     // Initial default value on Node0
340     _VerifyPathValue(files_[0 + 2]->path, "384000");
341     _VerifyPathValue(files_[1 + 2]->path, "");
342     // Do INTERACTION
343     EXPECT_TRUE(hm->DoHint("INTERACTION"));
344     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
345     _VerifyPathValue(files_[0 + 2]->path, "384000");
346     _VerifyPathValue(files_[1 + 2]->path, "1134000");
347     // Do LAUNCH
348     EXPECT_TRUE(hm->DoHint("LAUNCH"));
349     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
350     _VerifyPathValue(files_[0 + 2]->path, "1134000");
351     _VerifyPathValue(files_[1 + 2]->path, "1512000");
352     std::this_thread::sleep_for(500ms);
353     // "LAUNCH" node0 expired
354     _VerifyPathValue(files_[0 + 2]->path, "384000");
355     _VerifyPathValue(files_[1 + 2]->path, "1512000");
356     EXPECT_TRUE(hm->EndHint("LAUNCH"));
357     std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
358     // "LAUNCH" canceled
359     _VerifyPathValue(files_[0 + 2]->path, "384000");
360     _VerifyPathValue(files_[1 + 2]->path, "1134000");
361     std::this_thread::sleep_for(300ms);
362     // "INTERACTION" node1 expired
363     _VerifyPathValue(files_[0 + 2]->path, "384000");
364     _VerifyPathValue(files_[1 + 2]->path, "384000");
365 }
366 
367 }  // namespace perfmgr
368 }  // namespace android
369