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