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 <android-base/stringprintf.h>
24 #include <json/reader.h>
25 #include <json/value.h>
26
27 #include <inttypes.h>
28 #include <algorithm>
29 #include <set>
30
31 #include "perfmgr/FileNode.h"
32 #include "perfmgr/PropertyNode.h"
33
34 namespace android {
35 namespace perfmgr {
36
37 namespace {
38 constexpr std::chrono::milliseconds kMilliSecondZero = std::chrono::milliseconds(0);
39 constexpr std::chrono::steady_clock::time_point kTimePointMax =
40 std::chrono::steady_clock::time_point::max();
41 } // namespace
42
ValidateHint(const std::string & hint_type) const43 bool HintManager::ValidateHint(const std::string& hint_type) const {
44 if (nm_.get() == nullptr) {
45 LOG(ERROR) << "NodeLooperThread not present";
46 return false;
47 }
48 return IsHintSupported(hint_type);
49 }
50
IsHintSupported(const std::string & hint_type) const51 bool HintManager::IsHintSupported(const std::string& hint_type) const {
52 if (actions_.find(hint_type) == actions_.end()) {
53 LOG(INFO) << "Hint type not present in actions: " << hint_type;
54 return false;
55 }
56 return true;
57 }
58
IsHintEnabled(const std::string & hint_type) const59 bool HintManager::IsHintEnabled(const std::string &hint_type) const {
60 return actions_.at(hint_type).enabled;
61 }
62
InitHintStatus(const std::unique_ptr<HintManager> & hm)63 bool HintManager::InitHintStatus(const std::unique_ptr<HintManager> &hm) {
64 if (hm.get() == nullptr) {
65 return false;
66 }
67 for (auto &a : hm->actions_) {
68 // timeout_ms equaling kMilliSecondZero means forever until cancelling.
69 // As a result, if there's one NodeAction has timeout_ms of 0, we will store
70 // 0 instead of max. Also node actions could be empty, set to 0 in that case.
71 std::chrono::milliseconds timeout = kMilliSecondZero;
72 if (a.second.node_actions.size()) {
73 auto [min, max] =
74 std::minmax_element(a.second.node_actions.begin(), a.second.node_actions.end(),
75 [](const auto act1, const auto act2) {
76 return act1.timeout_ms < act2.timeout_ms;
77 });
78 timeout = min->timeout_ms == kMilliSecondZero ? kMilliSecondZero : max->timeout_ms;
79 }
80 a.second.status.reset(new HintStatus(timeout));
81 }
82 return true;
83 }
84
DoHintStatus(const std::string & hint_type,std::chrono::milliseconds timeout_ms)85 void HintManager::DoHintStatus(const std::string &hint_type, std::chrono::milliseconds timeout_ms) {
86 std::lock_guard<std::mutex> lock(actions_.at(hint_type).status->mutex);
87 actions_.at(hint_type).status->stats.count.fetch_add(1);
88 auto now = std::chrono::steady_clock::now();
89 if (now > actions_.at(hint_type).status->end_time) {
90 actions_.at(hint_type).status->stats.duration_ms.fetch_add(
91 std::chrono::duration_cast<std::chrono::milliseconds>(
92 actions_.at(hint_type).status->end_time -
93 actions_.at(hint_type).status->start_time)
94 .count());
95 actions_.at(hint_type).status->start_time = now;
96 }
97 actions_.at(hint_type).status->end_time =
98 (timeout_ms == kMilliSecondZero) ? kTimePointMax : now + timeout_ms;
99 }
100
EndHintStatus(const std::string & hint_type)101 void HintManager::EndHintStatus(const std::string &hint_type) {
102 std::lock_guard<std::mutex> lock(actions_.at(hint_type).status->mutex);
103 // Update HintStats if the hint ends earlier than expected end_time
104 auto now = std::chrono::steady_clock::now();
105 if (now < actions_.at(hint_type).status->end_time) {
106 actions_.at(hint_type).status->stats.duration_ms.fetch_add(
107 std::chrono::duration_cast<std::chrono::milliseconds>(
108 now - actions_.at(hint_type).status->start_time)
109 .count());
110 actions_.at(hint_type).status->end_time = now;
111 }
112 }
113
DoHintAction(const std::string & hint_type)114 void HintManager::DoHintAction(const std::string &hint_type) {
115 for (auto &action : actions_.at(hint_type).hint_actions) {
116 switch (action.type) {
117 case HintActionType::DoHint:
118 // TODO: add parse logic to prevent circular hints.
119 DoHint(action.value);
120 break;
121 case HintActionType::EndHint:
122 EndHint(action.value);
123 break;
124 case HintActionType::MaskHint:
125 if (actions_.find(action.value) == actions_.end()) {
126 LOG(ERROR) << "Failed to find " << action.value << " action";
127 } else {
128 actions_.at(action.value).enabled = false;
129 }
130 break;
131 default:
132 // should not reach here
133 LOG(ERROR) << "Invalid "
134 << static_cast<std::underlying_type<HintActionType>::type>(action.type)
135 << " type";
136 }
137 }
138 }
139
EndHintAction(const std::string & hint_type)140 void HintManager::EndHintAction(const std::string &hint_type) {
141 for (auto &action : actions_.at(hint_type).hint_actions) {
142 if (action.type == HintActionType::MaskHint &&
143 actions_.find(action.value) != actions_.end()) {
144 actions_.at(action.value).enabled = true;
145 }
146 }
147 }
148
DoHint(const std::string & hint_type)149 bool HintManager::DoHint(const std::string& hint_type) {
150 LOG(VERBOSE) << "Do Powerhint: " << hint_type;
151 if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type) ||
152 !nm_->Request(actions_.at(hint_type).node_actions, hint_type)) {
153 return false;
154 }
155 DoHintStatus(hint_type, actions_.at(hint_type).status->max_timeout);
156 DoHintAction(hint_type);
157 return true;
158 }
159
DoHint(const std::string & hint_type,std::chrono::milliseconds timeout_ms_override)160 bool HintManager::DoHint(const std::string& hint_type,
161 std::chrono::milliseconds timeout_ms_override) {
162 LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
163 << timeout_ms_override.count() << "ms";
164 if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type)) {
165 return false;
166 }
167 std::vector<NodeAction> actions_override = actions_.at(hint_type).node_actions;
168 for (auto& action : actions_override) {
169 action.timeout_ms = timeout_ms_override;
170 }
171 if (!nm_->Request(actions_override, hint_type)) {
172 return false;
173 }
174 DoHintStatus(hint_type, timeout_ms_override);
175 DoHintAction(hint_type);
176 return true;
177 }
178
EndHint(const std::string & hint_type)179 bool HintManager::EndHint(const std::string& hint_type) {
180 LOG(VERBOSE) << "End Powerhint: " << hint_type;
181 if (!ValidateHint(hint_type) || !nm_->Cancel(actions_.at(hint_type).node_actions, hint_type)) {
182 return false;
183 }
184 EndHintStatus(hint_type);
185 EndHintAction(hint_type);
186 return true;
187 }
188
IsRunning() const189 bool HintManager::IsRunning() const {
190 return (nm_.get() == nullptr) ? false : nm_->isRunning();
191 }
192
GetHints() const193 std::vector<std::string> HintManager::GetHints() const {
194 std::vector<std::string> hints;
195 for (auto const& action : actions_) {
196 hints.push_back(action.first);
197 }
198 return hints;
199 }
200
GetHintStats(const std::string & hint_type) const201 HintStats HintManager::GetHintStats(const std::string &hint_type) const {
202 HintStats hint_stats;
203 if (ValidateHint(hint_type)) {
204 hint_stats.count =
205 actions_.at(hint_type).status->stats.count.load(std::memory_order_relaxed);
206 hint_stats.duration_ms =
207 actions_.at(hint_type).status->stats.duration_ms.load(std::memory_order_relaxed);
208 }
209 return hint_stats;
210 }
211
DumpToFd(int fd)212 void HintManager::DumpToFd(int fd) {
213 std::string header(
214 "========== Begin perfmgr nodes ==========\n"
215 "Node Name\t"
216 "Node Path\t"
217 "Current Index\t"
218 "Current Value\n");
219 if (!android::base::WriteStringToFd(header, fd)) {
220 LOG(ERROR) << "Failed to dump fd: " << fd;
221 }
222 nm_->DumpToFd(fd);
223 std::string footer("========== End perfmgr nodes ==========\n");
224 if (!android::base::WriteStringToFd(footer, fd)) {
225 LOG(ERROR) << "Failed to dump fd: " << fd;
226 }
227 header = "========== Begin perfmgr stats ==========\n"
228 "Hint Name\t"
229 "Counts\t"
230 "Duration\n";
231 if (!android::base::WriteStringToFd(header, fd)) {
232 LOG(ERROR) << "Failed to dump fd: " << fd;
233 }
234 std::string hint_stats_string;
235 std::vector<std::string> keys(GetHints());
236 std::sort(keys.begin(), keys.end());
237 for (const auto &ordered_key : keys) {
238 HintStats hint_stats(GetHintStats(ordered_key));
239 hint_stats_string +=
240 android::base::StringPrintf("%s\t%" PRIu32 "\t%" PRIu64 "\n", ordered_key.c_str(),
241 hint_stats.count, hint_stats.duration_ms);
242 }
243 if (!android::base::WriteStringToFd(hint_stats_string, fd)) {
244 LOG(ERROR) << "Failed to dump fd: " << fd;
245 }
246 footer = "========== End perfmgr stats ==========\n";
247 if (!android::base::WriteStringToFd(footer, fd)) {
248 LOG(ERROR) << "Failed to dump fd: " << fd;
249 }
250 fsync(fd);
251 }
252
Start()253 bool HintManager::Start() {
254 return nm_->Start();
255 }
256
GetFromJSON(const std::string & config_path,bool start)257 std::unique_ptr<HintManager> HintManager::GetFromJSON(
258 const std::string& config_path, bool start) {
259 std::string json_doc;
260
261 if (!android::base::ReadFileToString(config_path, &json_doc)) {
262 LOG(ERROR) << "Failed to read JSON config from " << config_path;
263 return nullptr;
264 }
265
266 std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
267 if (nodes.empty()) {
268 LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
269 return nullptr;
270 }
271 std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc, nodes);
272
273 if (actions.empty()) {
274 LOG(ERROR) << "Failed to parse Actions section from " << config_path;
275 return nullptr;
276 }
277
278 sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
279 std::unique_ptr<HintManager> hm =
280 std::make_unique<HintManager>(std::move(nm), actions);
281
282 if (!HintManager::InitHintStatus(hm)) {
283 LOG(ERROR) << "Failed to initialize hint status";
284 return nullptr;
285 }
286
287 LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
288
289 if (start) {
290 hm->Start();
291 }
292 return hm;
293 }
294
ParseNodes(const std::string & json_doc)295 std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
296 const std::string& json_doc) {
297 // function starts
298 std::vector<std::unique_ptr<Node>> nodes_parsed;
299 std::set<std::string> nodes_name_parsed;
300 std::set<std::string> nodes_path_parsed;
301 Json::Value root;
302 Json::CharReaderBuilder builder;
303 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
304 std::string errorMessage;
305
306 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
307 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
308 return nodes_parsed;
309 }
310
311 Json::Value nodes = root["Nodes"];
312 for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
313 std::string name = nodes[i]["Name"].asString();
314 LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
315 if (name.empty()) {
316 LOG(ERROR) << "Failed to read "
317 << "Node[" << i << "]'s Name";
318 nodes_parsed.clear();
319 return nodes_parsed;
320 }
321
322 auto result = nodes_name_parsed.insert(name);
323 if (!result.second) {
324 LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
325 nodes_parsed.clear();
326 return nodes_parsed;
327 }
328
329 std::string path = nodes[i]["Path"].asString();
330 LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
331 if (path.empty()) {
332 LOG(ERROR) << "Failed to read "
333 << "Node[" << i << "]'s Path";
334 nodes_parsed.clear();
335 return nodes_parsed;
336 }
337
338 result = nodes_path_parsed.insert(path);
339 if (!result.second) {
340 LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
341 nodes_parsed.clear();
342 return nodes_parsed;
343 }
344
345 bool is_file = true;
346 std::string node_type = nodes[i]["Type"].asString();
347 LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
348 if (node_type.empty()) {
349 LOG(VERBOSE) << "Failed to read "
350 << "Node[" << i << "]'s Type, set to 'File' as default";
351 } else if (node_type == "File") {
352 is_file = true;
353 } else if (node_type == "Property") {
354 is_file = false;
355 } else {
356 LOG(ERROR) << "Invalid Node[" << i
357 << "]'s Type: only File and Property supported.";
358 nodes_parsed.clear();
359 return nodes_parsed;
360 }
361
362 std::vector<RequestGroup> values_parsed;
363 std::set<std::string> values_set_parsed;
364 Json::Value values = nodes[i]["Values"];
365 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
366 std::string value = values[j].asString();
367 LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
368 auto result = values_set_parsed.insert(value);
369 if (!result.second) {
370 LOG(ERROR) << "Duplicate value parsed in Node[" << i
371 << "]'s Value[" << j << "]";
372 nodes_parsed.clear();
373 return nodes_parsed;
374 }
375 if (is_file && value.empty()) {
376 LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
377 << "]";
378 nodes_parsed.clear();
379 return nodes_parsed;
380 }
381 values_parsed.emplace_back(value);
382 }
383 if (values_parsed.size() < 1) {
384 LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
385 nodes_parsed.clear();
386 return nodes_parsed;
387 }
388
389 Json::UInt64 default_index = values_parsed.size() - 1;
390 if (nodes[i]["DefaultIndex"].empty() ||
391 !nodes[i]["DefaultIndex"].isUInt64()) {
392 LOG(INFO) << "Failed to read Node[" << i
393 << "]'s DefaultIndex, set to last index: "
394 << default_index;
395 } else {
396 default_index = nodes[i]["DefaultIndex"].asUInt64();
397 }
398 if (default_index > values_parsed.size() - 1) {
399 default_index = values_parsed.size() - 1;
400 LOG(ERROR) << "Node[" << i
401 << "]'s DefaultIndex out of bound, max value index: "
402 << default_index;
403 nodes_parsed.clear();
404 return nodes_parsed;
405 }
406 LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
407
408 bool reset = false;
409 if (nodes[i]["ResetOnInit"].empty() ||
410 !nodes[i]["ResetOnInit"].isBool()) {
411 LOG(INFO) << "Failed to read Node[" << i
412 << "]'s ResetOnInit, set to 'false'";
413 } else {
414 reset = nodes[i]["ResetOnInit"].asBool();
415 }
416 LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
417 << reset << std::noboolalpha;
418
419 if (is_file) {
420 bool hold_fd = false;
421 if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
422 LOG(INFO) << "Failed to read Node[" << i
423 << "]'s HoldFd, set to 'false'";
424 } else {
425 hold_fd = nodes[i]["HoldFd"].asBool();
426 }
427 LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
428 << hold_fd << std::noboolalpha;
429
430 nodes_parsed.emplace_back(std::make_unique<FileNode>(
431 name, path, values_parsed,
432 static_cast<std::size_t>(default_index), reset, hold_fd));
433 } else {
434 nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
435 name, path, values_parsed,
436 static_cast<std::size_t>(default_index), reset));
437 }
438 }
439 LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
440 return nodes_parsed;
441 }
442
ParseActions(const std::string & json_doc,const std::vector<std::unique_ptr<Node>> & nodes)443 std::unordered_map<std::string, Hint> HintManager::ParseActions(
444 const std::string &json_doc, const std::vector<std::unique_ptr<Node>> &nodes) {
445 // function starts
446 std::unordered_map<std::string, Hint> actions_parsed;
447 Json::Value root;
448 Json::CharReaderBuilder builder;
449 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
450 std::string errorMessage;
451
452 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
453 LOG(ERROR) << "Failed to parse JSON config";
454 return actions_parsed;
455 }
456
457 Json::Value actions = root["Actions"];
458 std::size_t total_parsed = 0;
459
460 std::map<std::string, std::size_t> nodes_index;
461 for (std::size_t i = 0; i < nodes.size(); ++i) {
462 nodes_index[nodes[i]->GetName()] = i;
463 }
464
465 for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
466 const std::string& hint_type = actions[i]["PowerHint"].asString();
467 LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
468 if (hint_type.empty()) {
469 LOG(ERROR) << "Failed to read "
470 << "Action[" << i << "]'s PowerHint";
471 actions_parsed.clear();
472 return actions_parsed;
473 }
474
475 HintActionType action_type = HintActionType::Node;
476 std::string type_string = actions[i]["Type"].asString();
477 LOG(VERBOSE) << "Action[" << i << "]'s Type: " << type_string;
478 if (type_string.empty()) {
479 LOG(VERBOSE) << "Failed to read "
480 << "Action[" << i << "]'s Type, set to 'Node' as default";
481 } else if (type_string == "DoHint") {
482 action_type = HintActionType::DoHint;
483 } else if (type_string == "EndHint") {
484 action_type = HintActionType::EndHint;
485 } else if (type_string == "MaskHint") {
486 action_type = HintActionType::MaskHint;
487 } else {
488 LOG(ERROR) << "Invalid Action[" << i << "]'s Type: " << type_string;
489 actions_parsed.clear();
490 return actions_parsed;
491 }
492 if (action_type == HintActionType::Node) {
493 std::string node_name = actions[i]["Node"].asString();
494 LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
495 std::size_t node_index;
496
497 if (nodes_index.find(node_name) == nodes_index.end()) {
498 LOG(ERROR) << "Failed to find "
499 << "Action[" << i << "]'s Node from Nodes section: [" << node_name
500 << "]";
501 actions_parsed.clear();
502 return actions_parsed;
503 }
504 node_index = nodes_index[node_name];
505
506 std::string value_name = actions[i]["Value"].asString();
507 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
508 std::size_t value_index = 0;
509
510 if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
511 LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
512 LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
513 << " is not defined in Node[" << node_name << "]";
514 actions_parsed.clear();
515 return actions_parsed;
516 }
517 LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
518
519 Json::UInt64 duration = 0;
520 if (actions[i]["Duration"].empty() || !actions[i]["Duration"].isUInt64()) {
521 LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
522 actions_parsed.clear();
523 return actions_parsed;
524 } else {
525 duration = actions[i]["Duration"].asUInt64();
526 }
527 LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
528
529 for (const auto &action : actions_parsed[hint_type].node_actions) {
530 if (action.node_index == node_index) {
531 LOG(ERROR)
532 << "Action[" << i
533 << "]'s NodeIndex is duplicated with another Action";
534 actions_parsed.clear();
535 return actions_parsed;
536 }
537 }
538 actions_parsed[hint_type].node_actions.emplace_back(
539 node_index, value_index, std::chrono::milliseconds(duration));
540
541 } else {
542 const std::string &hint_value = actions[i]["Value"].asString();
543 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << hint_value;
544 if (hint_value.empty()) {
545 LOG(ERROR) << "Failed to read "
546 << "Action[" << i << "]'s Value";
547 actions_parsed.clear();
548 return actions_parsed;
549 }
550 actions_parsed[hint_type].hint_actions.emplace_back(action_type, hint_value);
551 }
552
553 ++total_parsed;
554 }
555
556 LOG(INFO) << total_parsed << " actions parsed successfully";
557
558 for (const auto& action : actions_parsed) {
559 LOG(INFO) << "PowerHint " << action.first << " has " << action.second.node_actions.size()
560 << " node actions"
561 << ", and " << action.second.hint_actions.size() << " hint actions parsed";
562 }
563
564 return actions_parsed;
565 }
566
567 } // namespace perfmgr
568 } // namespace android
569