blob: 16bb65f26dfa25b60712a2f35717f0a0a06cca67 [file] [log] [blame]
Wei Wangf72cfad2017-10-26 22:41:03 -07001/*
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>
Wei Wangea78eca2017-12-27 18:18:45 -080021#include <android-base/logging.h>
Wei Wangf72cfad2017-10-26 22:41:03 -070022#include <android-base/test_utils.h>
23
24#include <gtest/gtest.h>
25
26#include "perfmgr/HintManager.h"
27
28namespace android {
29namespace perfmgr {
30
31using namespace std::chrono_literals;
32
33constexpr 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// "ValueIndex": 1,
65// "Duration": 800
66// },
67// {
68// "PowerHint": "LAUNCH",
69// "Node": "CPUCluster0MinFreq",
70// "ValueIndex": 1,
71// "Duration": 500
72// },
73// {
74// "PowerHint": "LAUNCH",
75// "Node": "CPUCluster1MinFreq",
76// "ValueIndex": 0,
77// "Duration": 2000
78// }
79// ]
80// }
81constexpr 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\",\"ValueIndex\":1,\"Duration\":800},{\"PowerHint\":"
90 "\"LAUNCH\",\"Node\":\"CPUCluster0MinFreq\",\"ValueIndex\":1,\"Duration\":"
91 "500},{\"PowerHint\":\"LAUNCH\",\"Node\":\"CPUCluster1MinFreq\","
92 "\"ValueIndex\":0,\"Duration\":2000}]}";
93
94class HintManagerTest : public ::testing::Test, public HintManager {
95 protected:
96 HintManagerTest()
97 : HintManager(nullptr,
Wei Wangea78eca2017-12-27 18:18:45 -080098 std::map<std::string, std::vector<NodeAction>>{}) {
99 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
100 }
Wei Wangf72cfad2017-10-26 22:41:03 -0700101
102 virtual void SetUp() {
103 // Set up dummy nodes
104 std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
105 nodes_.emplace_back(
106 new Node("n0", tf->path,
107 {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, false));
108 files_.emplace_back(std::move(tf));
109 tf = std::make_unique<TemporaryFile>();
110 nodes_.emplace_back(
111 new Node("n1", tf->path,
112 {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, true));
113 files_.emplace_back(std::move(tf));
114 nm_ = new NodeLooperThread(std::move(nodes_));
115 // Set up dummy actions
116 // "INTERACTION"
117 // Node0, value1, 800ms
118 // Node1, value1, forever
119 // "LAUNCH"
120 // Node0, value0, forever
121 // Node1, value0, 400ms
122 actions_ = std::map<std::string, std::vector<NodeAction>>{
123 {"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}}},
124 {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}}}};
125
126 // Prepare dummy files to replace the nodes' path in example json_doc
127 files_.emplace_back(std::make_unique<TemporaryFile>());
128 files_.emplace_back(std::make_unique<TemporaryFile>());
129 // replace filepath
130 json_doc_ = kJSON_RAW;
131 std::string from =
132 "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq";
133 size_t start_pos = json_doc_.find(from);
134 json_doc_.replace(start_pos, from.length(), files_[0 + 2]->path);
135 from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq";
136 start_pos = json_doc_.find(from);
137 json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
138 }
139
140 virtual void TearDown() {
141 actions_.clear();
142 nodes_.clear();
143 files_.clear();
144 nm_ = nullptr;
145 }
146 sp<NodeLooperThread> nm_;
147 std::map<std::string, std::vector<NodeAction>> actions_;
148 std::vector<std::unique_ptr<Node>> nodes_;
149 std::vector<std::unique_ptr<TemporaryFile>> files_;
150 std::string json_doc_;
151};
152
153static inline void _VerifyPathValue(std::string path, std::string value) {
154 std::string s;
155 EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
156 EXPECT_EQ(value, s);
157}
158
Wei Wang90fc4d42018-01-25 14:45:33 -0800159// Test GetHints
160TEST_F(HintManagerTest, GetHintsTest) {
161 HintManager hm(nm_, actions_);
162 std::vector<std::string> hints = hm.GetHints();
163 EXPECT_TRUE(hm.IsRunning());
164 EXPECT_EQ(2u, hints.size());
165 EXPECT_NE(std::find(hints.begin(), hints.end(), "INTERACTION"), hints.end());
166 EXPECT_NE(std::find(hints.begin(), hints.end(), "LAUNCH"), hints.end());
167}
168
Wei Wangf72cfad2017-10-26 22:41:03 -0700169// Test initialization of default values
170TEST_F(HintManagerTest, HintInitDefaultTest) {
171 HintManager hm(nm_, actions_);
172 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
173 EXPECT_TRUE(hm.IsRunning());
174 _VerifyPathValue(files_[0]->path, "");
175 _VerifyPathValue(files_[1]->path, "n1_value2");
176}
177
178// Test hint/cancel/expire
179TEST_F(HintManagerTest, HintTest) {
180 HintManager hm(nm_, actions_);
181 EXPECT_TRUE(hm.IsRunning());
182 EXPECT_TRUE(hm.DoHint("INTERACTION"));
183 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
184 _VerifyPathValue(files_[0]->path, "n0_value1");
185 _VerifyPathValue(files_[1]->path, "n1_value1");
186 // this won't change the expire time of INTERACTION hint
187 EXPECT_TRUE(hm.DoHint("INTERACTION", 200ms));
188 // now place new hint
189 EXPECT_TRUE(hm.DoHint("LAUNCH"));
190 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
191 _VerifyPathValue(files_[0]->path, "n0_value0");
192 _VerifyPathValue(files_[1]->path, "n1_value0");
193 EXPECT_TRUE(hm.DoHint("LAUNCH", 500ms));
194 // no"LAUNCH" node1 not expired
195 std::this_thread::sleep_for(400ms);
196 _VerifyPathValue(files_[0]->path, "n0_value0");
197 _VerifyPathValue(files_[1]->path, "n1_value0");
198 // "LAUNCH" node1 expired
199 std::this_thread::sleep_for(100ms + kSLEEP_TOLERANCE_MS);
200 _VerifyPathValue(files_[0]->path, "n0_value0");
201 _VerifyPathValue(files_[1]->path, "n1_value1");
202 EXPECT_TRUE(hm.EndHint("LAUNCH"));
203 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
204 // "LAUNCH" canceled
205 _VerifyPathValue(files_[0]->path, "n0_value1");
206 _VerifyPathValue(files_[1]->path, "n1_value1");
207 std::this_thread::sleep_for(200ms);
208 // "INTERACTION" node0 expired
209 _VerifyPathValue(files_[0]->path, "n0_value2");
210 _VerifyPathValue(files_[1]->path, "n1_value1");
211 EXPECT_TRUE(hm.EndHint("INTERACTION"));
212 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
213 // "INTERACTION" canceled
214 _VerifyPathValue(files_[0]->path, "n0_value2");
215 _VerifyPathValue(files_[1]->path, "n1_value2");
216}
217
218// Test parsing nodes with duplicate name
219TEST_F(HintManagerTest, ParseNodesTest) {
220 std::vector<std::unique_ptr<Node>> nodes =
221 HintManager::ParseNodes(json_doc_);
222 EXPECT_EQ(2u, nodes.size());
223 EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
224 EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
225 EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
226 EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
227 EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
228 EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
229 EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
230 EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
231 EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
232 EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
233 EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
234 EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
235 EXPECT_TRUE(nodes[0]->GetResetOnInit());
236 EXPECT_FALSE(nodes[1]->GetResetOnInit());
237 EXPECT_FALSE(nodes[0]->GetHoldFd());
238 EXPECT_TRUE(nodes[1]->GetHoldFd());
239}
240
241// Test parsing actions
242TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
243 std::string from = "CPUCluster0MinFreq";
244 size_t start_pos = json_doc_.find(from);
245 json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
246 std::vector<std::unique_ptr<Node>> nodes =
247 HintManager::ParseNodes(json_doc_);
248 EXPECT_EQ(0u, nodes.size());
249}
250
251// Test parsing nodes with duplicate path
252TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) {
253 std::string from = files_[0 + 2]->path;
254 size_t start_pos = json_doc_.find(from);
255 json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
256 std::vector<std::unique_ptr<Node>> nodes =
257 HintManager::ParseNodes(json_doc_);
258 EXPECT_EQ(0u, nodes.size());
259}
260
261// Test parsing invalid json for nodes
262TEST_F(HintManagerTest, ParseBadNodesTest) {
263 std::vector<std::unique_ptr<Node>> nodes =
264 HintManager::ParseNodes("invalid json");
265 EXPECT_EQ(0u, nodes.size());
266 nodes = HintManager::ParseNodes(
267 "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
268 "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}");
269 EXPECT_EQ(0u, nodes.size());
270}
271
272// Test parsing actions
273TEST_F(HintManagerTest, ParseActionsTest) {
274 std::vector<std::unique_ptr<Node>> nodes =
275 HintManager::ParseNodes(json_doc_);
276 std::map<std::string, std::vector<NodeAction>> actions =
277 HintManager::ParseActions(json_doc_, nodes);
278 EXPECT_EQ(2u, actions.size());
279 EXPECT_EQ(1u, actions["INTERACTION"].size());
280
281 EXPECT_EQ(1u, actions["INTERACTION"][0].node_index);
282 EXPECT_EQ(1u, actions["INTERACTION"][0].value_index);
283 EXPECT_EQ(std::chrono::milliseconds(800).count(),
284 actions["INTERACTION"][0].timeout_ms.count());
285
286 EXPECT_EQ(2u, actions["LAUNCH"].size());
287
288 EXPECT_EQ(0u, actions["LAUNCH"][0].node_index);
289 EXPECT_EQ(1u, actions["LAUNCH"][0].value_index);
290 EXPECT_EQ(std::chrono::milliseconds(500).count(),
291 actions["LAUNCH"][0].timeout_ms.count());
292
293 EXPECT_EQ(1u, actions["LAUNCH"][1].node_index);
294 EXPECT_EQ(0u, actions["LAUNCH"][1].value_index);
295 EXPECT_EQ(std::chrono::milliseconds(2000).count(),
296 actions["LAUNCH"][1].timeout_ms.count());
297}
298
Wei Wangabb9e8f2018-01-11 23:10:01 -0800299// Test parsing actions with duplicate node
300TEST_F(HintManagerTest, ParseActionDuplicateNodeTest) {
301 std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
302 size_t start_pos = json_doc_.find(from);
303 json_doc_.replace(start_pos, from.length(),
304 "\"Node\": \"CPUCluster1MinFreq\"");
305 std::vector<std::unique_ptr<Node>> nodes =
306 HintManager::ParseNodes(json_doc_);
307 EXPECT_EQ(2u, nodes.size());
308 std::map<std::string, std::vector<NodeAction>> actions =
309 HintManager::ParseActions(json_doc_, nodes);
310 EXPECT_EQ(0u, actions.size());
311}
312
Wei Wangf72cfad2017-10-26 22:41:03 -0700313// Test parsing invalid json for actions
314TEST_F(HintManagerTest, ParseBadActionsTest) {
315 std::vector<std::unique_ptr<Node>> nodes =
316 HintManager::ParseNodes(json_doc_);
317 std::map<std::string, std::vector<NodeAction>> actions =
318 HintManager::ParseActions("invalid json", nodes);
319 EXPECT_EQ(0u, actions.size());
320 actions = HintManager::ParseActions(
321 "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
322 "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}",
323 nodes);
324 EXPECT_EQ(0u, actions.size());
325}
326
327// Test hint/cancel/expire with json config
328TEST_F(HintManagerTest, GetFromJSONTest) {
329 TemporaryFile json_file;
330 ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path))
331 << strerror(errno);
332 std::unique_ptr<HintManager> hm = HintManager::GetFromJSON(json_file.path);
333 EXPECT_NE(nullptr, hm.get());
334 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
335 EXPECT_TRUE(hm->IsRunning());
336 // Initial default value on Node0
337 _VerifyPathValue(files_[0 + 2]->path, "384000");
338 _VerifyPathValue(files_[1 + 2]->path, "");
339 // Do INTERACTION
340 EXPECT_TRUE(hm->DoHint("INTERACTION"));
341 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
342 _VerifyPathValue(files_[0 + 2]->path, "384000");
343 _VerifyPathValue(files_[1 + 2]->path, "1134000");
344 // Do LAUNCH
345 EXPECT_TRUE(hm->DoHint("LAUNCH"));
346 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
347 _VerifyPathValue(files_[0 + 2]->path, "1134000");
348 _VerifyPathValue(files_[1 + 2]->path, "1512000");
349 std::this_thread::sleep_for(500ms);
350 // "LAUNCH" node0 expired
351 _VerifyPathValue(files_[0 + 2]->path, "384000");
352 _VerifyPathValue(files_[1 + 2]->path, "1512000");
353 EXPECT_TRUE(hm->EndHint("LAUNCH"));
354 std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
355 // "LAUNCH" canceled
356 _VerifyPathValue(files_[0 + 2]->path, "384000");
357 _VerifyPathValue(files_[1 + 2]->path, "1134000");
358 std::this_thread::sleep_for(300ms);
359 // "INTERACTION" node1 expired
360 _VerifyPathValue(files_[0 + 2]->path, "384000");
361 _VerifyPathValue(files_[1 + 2]->path, "384000");
362}
363
364} // namespace perfmgr
365} // namespace android