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 #define LOG_TAG "libperfmgr"
18
19 #include "perfmgr/HintManager.h"
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <json/reader.h>
24 #include <json/value.h>
25
26 #include <algorithm>
27 #include <set>
28
29 #include "perfmgr/FileNode.h"
30 #include "perfmgr/PropertyNode.h"
31
32 namespace android {
33 namespace perfmgr {
34
ValidateHint(const std::string & hint_type) const35 bool HintManager::ValidateHint(const std::string& hint_type) const {
36 if (nm_.get() == nullptr) {
37 LOG(ERROR) << "NodeLooperThread not present";
38 return false;
39 }
40 return IsHintSupported(hint_type);
41 }
42
IsHintSupported(const std::string & hint_type) const43 bool HintManager::IsHintSupported(const std::string& hint_type) const {
44 if (actions_.find(hint_type) == actions_.end()) {
45 LOG(INFO) << "Hint type not present in actions: " << hint_type;
46 return false;
47 }
48 return true;
49 }
50
DoHint(const std::string & hint_type)51 bool HintManager::DoHint(const std::string& hint_type) {
52 LOG(VERBOSE) << "Do Powerhint: " << hint_type;
53 return ValidateHint(hint_type)
54 ? nm_->Request(actions_.at(hint_type), hint_type)
55 : false;
56 }
57
DoHint(const std::string & hint_type,std::chrono::milliseconds timeout_ms_override)58 bool HintManager::DoHint(const std::string& hint_type,
59 std::chrono::milliseconds timeout_ms_override) {
60 LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
61 << timeout_ms_override.count() << "ms";
62 if (!ValidateHint(hint_type)) {
63 return false;
64 }
65 std::vector<NodeAction> actions_override = actions_.at(hint_type);
66 for (auto& action : actions_override) {
67 action.timeout_ms = timeout_ms_override;
68 }
69 return nm_->Request(actions_override, hint_type);
70 }
71
EndHint(const std::string & hint_type)72 bool HintManager::EndHint(const std::string& hint_type) {
73 LOG(VERBOSE) << "End Powerhint: " << hint_type;
74 return ValidateHint(hint_type)
75 ? nm_->Cancel(actions_.at(hint_type), hint_type)
76 : false;
77 }
78
IsRunning() const79 bool HintManager::IsRunning() const {
80 return (nm_.get() == nullptr) ? false : nm_->isRunning();
81 }
82
GetHints() const83 std::vector<std::string> HintManager::GetHints() const {
84 std::vector<std::string> hints;
85 for (auto const& action : actions_) {
86 hints.push_back(action.first);
87 }
88 return hints;
89 }
90
DumpToFd(int fd)91 void HintManager::DumpToFd(int fd) {
92 std::string header(
93 "========== Begin perfmgr nodes ==========\n"
94 "Node Name\t"
95 "Node Path\t"
96 "Current Index\t"
97 "Current Value\n");
98 if (!android::base::WriteStringToFd(header, fd)) {
99 LOG(ERROR) << "Failed to dump fd: " << fd;
100 }
101 nm_->DumpToFd(fd);
102 std::string footer("========== End perfmgr nodes ==========\n");
103 if (!android::base::WriteStringToFd(footer, fd)) {
104 LOG(ERROR) << "Failed to dump fd: " << fd;
105 }
106 fsync(fd);
107 }
108
Start()109 bool HintManager::Start() {
110 return nm_->Start();
111 }
112
GetFromJSON(const std::string & config_path,bool start)113 std::unique_ptr<HintManager> HintManager::GetFromJSON(
114 const std::string& config_path, bool start) {
115 std::string json_doc;
116
117 if (!android::base::ReadFileToString(config_path, &json_doc)) {
118 LOG(ERROR) << "Failed to read JSON config from " << config_path;
119 return nullptr;
120 }
121
122 std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
123 if (nodes.empty()) {
124 LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
125 return nullptr;
126 }
127 std::map<std::string, std::vector<NodeAction>> actions =
128 HintManager::ParseActions(json_doc, nodes);
129
130 if (actions.empty()) {
131 LOG(ERROR) << "Failed to parse Actions section from " << config_path;
132 return nullptr;
133 }
134
135 sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
136 std::unique_ptr<HintManager> hm =
137 std::make_unique<HintManager>(std::move(nm), actions);
138
139 LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
140
141 if (start) {
142 hm->Start();
143 }
144 return hm;
145 }
146
ParseNodes(const std::string & json_doc)147 std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
148 const std::string& json_doc) {
149 // function starts
150 std::vector<std::unique_ptr<Node>> nodes_parsed;
151 std::set<std::string> nodes_name_parsed;
152 std::set<std::string> nodes_path_parsed;
153 Json::Value root;
154 Json::Reader reader;
155
156 if (!reader.parse(json_doc, root)) {
157 LOG(ERROR) << "Failed to parse JSON config";
158 return nodes_parsed;
159 }
160
161 Json::Value nodes = root["Nodes"];
162 for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
163 std::string name = nodes[i]["Name"].asString();
164 LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
165 if (name.empty()) {
166 LOG(ERROR) << "Failed to read "
167 << "Node[" << i << "]'s Name";
168 nodes_parsed.clear();
169 return nodes_parsed;
170 }
171
172 auto result = nodes_name_parsed.insert(name);
173 if (!result.second) {
174 LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
175 nodes_parsed.clear();
176 return nodes_parsed;
177 }
178
179 std::string path = nodes[i]["Path"].asString();
180 LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
181 if (path.empty()) {
182 LOG(ERROR) << "Failed to read "
183 << "Node[" << i << "]'s Path";
184 nodes_parsed.clear();
185 return nodes_parsed;
186 }
187
188 result = nodes_path_parsed.insert(path);
189 if (!result.second) {
190 LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
191 nodes_parsed.clear();
192 return nodes_parsed;
193 }
194
195 bool is_file = true;
196 std::string node_type = nodes[i]["Type"].asString();
197 LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
198 if (node_type.empty()) {
199 LOG(ERROR) << "Failed to read "
200 << "Node[" << i << "]'s Type, set to 'File' as default";
201 } else if (node_type == "File") {
202 is_file = true;
203 } else if (node_type == "Property") {
204 is_file = false;
205 } else {
206 LOG(ERROR) << "Invalid Node[" << i
207 << "]'s Type: only File and Property supported.";
208 nodes_parsed.clear();
209 return nodes_parsed;
210 }
211
212 std::vector<RequestGroup> values_parsed;
213 std::set<std::string> values_set_parsed;
214 Json::Value values = nodes[i]["Values"];
215 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
216 std::string value = values[j].asString();
217 LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
218 auto result = values_set_parsed.insert(value);
219 if (!result.second) {
220 LOG(ERROR) << "Duplicate value parsed in Node[" << i
221 << "]'s Value[" << j << "]";
222 nodes_parsed.clear();
223 return nodes_parsed;
224 }
225 if (is_file && value.empty()) {
226 LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
227 << "]";
228 nodes_parsed.clear();
229 return nodes_parsed;
230 }
231 values_parsed.emplace_back(value);
232 }
233 if (values_parsed.size() < 1) {
234 LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
235 nodes_parsed.clear();
236 return nodes_parsed;
237 }
238
239 Json::UInt64 default_index = values_parsed.size() - 1;
240 if (nodes[i]["DefaultIndex"].empty() ||
241 !nodes[i]["DefaultIndex"].isUInt64()) {
242 LOG(INFO) << "Failed to read Node[" << i
243 << "]'s DefaultIndex, set to last index: "
244 << default_index;
245 } else {
246 default_index = nodes[i]["DefaultIndex"].asUInt64();
247 }
248 if (default_index > values_parsed.size() - 1) {
249 default_index = values_parsed.size() - 1;
250 LOG(ERROR) << "Node[" << i
251 << "]'s DefaultIndex out of bound, max value index: "
252 << default_index;
253 nodes_parsed.clear();
254 return nodes_parsed;
255 }
256 LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
257
258 bool reset = false;
259 if (nodes[i]["ResetOnInit"].empty() ||
260 !nodes[i]["ResetOnInit"].isBool()) {
261 LOG(INFO) << "Failed to read Node[" << i
262 << "]'s ResetOnInit, set to 'false'";
263 } else {
264 reset = nodes[i]["ResetOnInit"].asBool();
265 }
266 LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
267 << reset << std::noboolalpha;
268
269 if (is_file) {
270 bool hold_fd = false;
271 if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
272 LOG(INFO) << "Failed to read Node[" << i
273 << "]'s HoldFd, set to 'false'";
274 } else {
275 hold_fd = nodes[i]["HoldFd"].asBool();
276 }
277 LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
278 << hold_fd << std::noboolalpha;
279
280 nodes_parsed.emplace_back(std::make_unique<FileNode>(
281 name, path, values_parsed,
282 static_cast<std::size_t>(default_index), reset, hold_fd));
283 } else {
284 nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
285 name, path, values_parsed,
286 static_cast<std::size_t>(default_index), reset));
287 }
288 }
289 LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
290 return nodes_parsed;
291 }
292
ParseActions(const std::string & json_doc,const std::vector<std::unique_ptr<Node>> & nodes)293 std::map<std::string, std::vector<NodeAction>> HintManager::ParseActions(
294 const std::string& json_doc,
295 const std::vector<std::unique_ptr<Node>>& nodes) {
296 // function starts
297 std::map<std::string, std::vector<NodeAction>> actions_parsed;
298 Json::Value root;
299 Json::Reader reader;
300
301 if (!reader.parse(json_doc, root)) {
302 LOG(ERROR) << "Failed to parse JSON config";
303 return actions_parsed;
304 }
305
306 Json::Value actions = root["Actions"];
307 std::size_t total_parsed = 0;
308
309 std::map<std::string, std::size_t> nodes_index;
310 for (std::size_t i = 0; i < nodes.size(); ++i) {
311 nodes_index[nodes[i]->GetName()] = i;
312 }
313
314 for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
315 const std::string& hint_type = actions[i]["PowerHint"].asString();
316 LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
317 if (hint_type.empty()) {
318 LOG(ERROR) << "Failed to read "
319 << "Action[" << i << "]'s PowerHint";
320 actions_parsed.clear();
321 return actions_parsed;
322 }
323
324 std::string node_name = actions[i]["Node"].asString();
325 LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
326 std::size_t node_index;
327
328 if (nodes_index.find(node_name) == nodes_index.end()) {
329 LOG(ERROR) << "Failed to find "
330 << "Action[" << i << "]'s Node from Nodes section: ["
331 << node_name << "]";
332 actions_parsed.clear();
333 return actions_parsed;
334 }
335 node_index = nodes_index[node_name];
336
337 std::string value_name = actions[i]["Value"].asString();
338 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
339 std::size_t value_index = 0;
340
341 if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
342 LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
343 LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
344 << " is not defined in Node[" << node_name << "]";
345 actions_parsed.clear();
346 return actions_parsed;
347 }
348 LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
349
350 Json::UInt64 duration = 0;
351 if (actions[i]["Duration"].empty() ||
352 !actions[i]["Duration"].isUInt64()) {
353 LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
354 actions_parsed.clear();
355 return actions_parsed;
356 } else {
357 duration = actions[i]["Duration"].asUInt64();
358 }
359 LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
360
361 if (actions_parsed.find(hint_type) == actions_parsed.end()) {
362 actions_parsed[hint_type] = std::vector<NodeAction>{
363 {node_index, value_index, std::chrono::milliseconds(duration)}};
364 } else {
365 for (const auto& action : actions_parsed[hint_type]) {
366 if (action.node_index == node_index) {
367 LOG(ERROR)
368 << "Action[" << i
369 << "]'s NodeIndex is duplicated with another Action";
370 actions_parsed.clear();
371 return actions_parsed;
372 }
373 }
374 actions_parsed[hint_type].emplace_back(
375 node_index, value_index, std::chrono::milliseconds(duration));
376 }
377
378 ++total_parsed;
379 }
380
381 LOG(INFO) << total_parsed << " Actions parsed successfully";
382
383 for (const auto& action : actions_parsed) {
384 LOG(INFO) << "PowerHint " << action.first << " has "
385 << action.second.size() << " actions parsed";
386 }
387
388 return actions_parsed;
389 }
390
391 } // namespace perfmgr
392 } // namespace android
393