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