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/properties.h>
25 #include <android-base/stringprintf.h>
26 #include <inttypes.h>
27 #include <json/reader.h>
28 #include <json/value.h>
29 #include <utils/Trace.h>
30
31 #include <algorithm>
32 #include <set>
33
34 #include "perfmgr/FileNode.h"
35 #include "perfmgr/PropertyNode.h"
36
37 namespace android {
38 namespace perfmgr {
39
40 namespace {
41 constexpr std::chrono::milliseconds kMilliSecondZero = std::chrono::milliseconds(0);
42 constexpr std::chrono::steady_clock::time_point kTimePointMax =
43 std::chrono::steady_clock::time_point::max();
44 } // namespace
45
46 constexpr char kPowerHalTruncateProp[] = "vendor.powerhal.truncate";
47 constexpr std::string_view kConfigDebugPathProperty("vendor.powerhal.config.debug");
48 constexpr std::string_view kConfigProperty("vendor.powerhal.config");
49 constexpr std::string_view kConfigDefaultFileName("powerhint.json");
50
ValidateHint(const std::string & hint_type) const51 bool HintManager::ValidateHint(const std::string& hint_type) const {
52 if (nm_.get() == nullptr) {
53 LOG(ERROR) << "NodeLooperThread not present";
54 return false;
55 }
56 return IsHintSupported(hint_type);
57 }
58
IsHintSupported(const std::string & hint_type) const59 bool HintManager::IsHintSupported(const std::string& hint_type) const {
60 if (actions_.find(hint_type) == actions_.end()) {
61 LOG(DEBUG) << "Hint type not present in actions: " << hint_type;
62 return false;
63 }
64 return true;
65 }
66
IsHintEnabled(const std::string & hint_type) const67 bool HintManager::IsHintEnabled(const std::string &hint_type) const {
68 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
69 return actions_.at(hint_type).mask_requesters.empty();
70 }
71
InitHintStatus(const std::unique_ptr<HintManager> & hm)72 bool HintManager::InitHintStatus(const std::unique_ptr<HintManager> &hm) {
73 if (hm.get() == nullptr) {
74 return false;
75 }
76 for (auto &a : hm->actions_) {
77 // timeout_ms equaling kMilliSecondZero means forever until cancelling.
78 // As a result, if there's one NodeAction has timeout_ms of 0, we will store
79 // 0 instead of max. Also node actions could be empty, set to 0 in that case.
80 std::chrono::milliseconds timeout = kMilliSecondZero;
81 if (a.second.node_actions.size()) {
82 auto [min, max] =
83 std::minmax_element(a.second.node_actions.begin(), a.second.node_actions.end(),
84 [](const auto act1, const auto act2) {
85 return act1.timeout_ms < act2.timeout_ms;
86 });
87 timeout = min->timeout_ms == kMilliSecondZero ? kMilliSecondZero : max->timeout_ms;
88 }
89 a.second.status.reset(new HintStatus(timeout));
90 }
91 return true;
92 }
93
DoHintStatus(const std::string & hint_type,std::chrono::milliseconds timeout_ms)94 void HintManager::DoHintStatus(const std::string &hint_type, std::chrono::milliseconds timeout_ms) {
95 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
96 actions_.at(hint_type).status->stats.count.fetch_add(1);
97 auto now = std::chrono::steady_clock::now();
98 ATRACE_INT(hint_type.c_str(), (timeout_ms == kMilliSecondZero) ? std::numeric_limits<int>::max()
99 : timeout_ms.count());
100 if (now > actions_.at(hint_type).status->end_time) {
101 actions_.at(hint_type).status->stats.duration_ms.fetch_add(
102 std::chrono::duration_cast<std::chrono::milliseconds>(
103 actions_.at(hint_type).status->end_time -
104 actions_.at(hint_type).status->start_time)
105 .count());
106 actions_.at(hint_type).status->start_time = now;
107 }
108 actions_.at(hint_type).status->end_time =
109 (timeout_ms == kMilliSecondZero) ? kTimePointMax : now + timeout_ms;
110 }
111
EndHintStatus(const std::string & hint_type)112 void HintManager::EndHintStatus(const std::string &hint_type) {
113 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
114 // Update HintStats if the hint ends earlier than expected end_time
115 auto now = std::chrono::steady_clock::now();
116 ATRACE_INT(hint_type.c_str(), 0);
117 if (now < actions_.at(hint_type).status->end_time) {
118 actions_.at(hint_type).status->stats.duration_ms.fetch_add(
119 std::chrono::duration_cast<std::chrono::milliseconds>(
120 now - actions_.at(hint_type).status->start_time)
121 .count());
122 actions_.at(hint_type).status->end_time = now;
123 }
124 }
125
DoHintAction(const std::string & hint_type)126 void HintManager::DoHintAction(const std::string &hint_type) {
127 for (auto &action : actions_.at(hint_type).hint_actions) {
128 if (!action.enable_property.empty() &&
129 !android::base::GetBoolProperty(action.enable_property, true)) {
130 // Disabled action based on its control property
131 continue;
132 }
133 switch (action.type) {
134 case HintActionType::DoHint:
135 DoHint(action.value);
136 break;
137 case HintActionType::EndHint:
138 EndHint(action.value);
139 break;
140 case HintActionType::MaskHint:
141 if (actions_.find(action.value) == actions_.end()) {
142 LOG(ERROR) << "Failed to find " << action.value << " action";
143 } else {
144 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
145 actions_.at(action.value).mask_requesters.insert(hint_type);
146 }
147 break;
148 default:
149 // should not reach here
150 LOG(ERROR) << "Invalid "
151 << static_cast<std::underlying_type<HintActionType>::type>(action.type)
152 << " type";
153 }
154 }
155 }
156
EndHintAction(const std::string & hint_type)157 void HintManager::EndHintAction(const std::string &hint_type) {
158 for (auto &action : actions_.at(hint_type).hint_actions) {
159 if (action.type == HintActionType::MaskHint &&
160 actions_.find(action.value) != actions_.end()) {
161 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
162 actions_.at(action.value).mask_requesters.erase(hint_type);
163 }
164 }
165 }
166
DoHint(const std::string & hint_type)167 bool HintManager::DoHint(const std::string& hint_type) {
168 LOG(VERBOSE) << "Do Powerhint: " << hint_type;
169 if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type) ||
170 !nm_->Request(actions_.at(hint_type).node_actions, hint_type)) {
171 return false;
172 }
173 DoHintStatus(hint_type, actions_.at(hint_type).status->max_timeout);
174 DoHintAction(hint_type);
175 return true;
176 }
177
DoHint(const std::string & hint_type,std::chrono::milliseconds timeout_ms_override)178 bool HintManager::DoHint(const std::string& hint_type,
179 std::chrono::milliseconds timeout_ms_override) {
180 LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
181 << timeout_ms_override.count() << "ms";
182 if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type)) {
183 return false;
184 }
185 std::vector<NodeAction> actions_override = actions_.at(hint_type).node_actions;
186 for (auto& action : actions_override) {
187 action.timeout_ms = timeout_ms_override;
188 }
189 if (!nm_->Request(actions_override, hint_type)) {
190 return false;
191 }
192 DoHintStatus(hint_type, timeout_ms_override);
193 DoHintAction(hint_type);
194 return true;
195 }
196
EndHint(const std::string & hint_type)197 bool HintManager::EndHint(const std::string& hint_type) {
198 LOG(VERBOSE) << "End Powerhint: " << hint_type;
199 if (!ValidateHint(hint_type) || !nm_->Cancel(actions_.at(hint_type).node_actions, hint_type)) {
200 return false;
201 }
202 EndHintStatus(hint_type);
203 EndHintAction(hint_type);
204 return true;
205 }
206
IsRunning() const207 bool HintManager::IsRunning() const {
208 return (nm_.get() == nullptr) ? false : nm_->isRunning();
209 }
210
GetHints() const211 std::vector<std::string> HintManager::GetHints() const {
212 std::vector<std::string> hints;
213 for (auto const& action : actions_) {
214 hints.push_back(action.first);
215 }
216 return hints;
217 }
218
GetHintStats(const std::string & hint_type) const219 HintStats HintManager::GetHintStats(const std::string &hint_type) const {
220 HintStats hint_stats;
221 if (ValidateHint(hint_type)) {
222 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
223 hint_stats.count =
224 actions_.at(hint_type).status->stats.count.load(std::memory_order_relaxed);
225 hint_stats.duration_ms =
226 actions_.at(hint_type).status->stats.duration_ms.load(std::memory_order_relaxed);
227 }
228 return hint_stats;
229 }
230
DumpToFd(int fd)231 void HintManager::DumpToFd(int fd) {
232 std::string header("========== Begin perfmgr nodes ==========\n");
233 if (!android::base::WriteStringToFd(header, fd)) {
234 LOG(ERROR) << "Failed to dump fd: " << fd;
235 }
236 nm_->DumpToFd(fd);
237 std::string footer("========== End perfmgr nodes ==========\n");
238 if (!android::base::WriteStringToFd(footer, fd)) {
239 LOG(ERROR) << "Failed to dump fd: " << fd;
240 }
241 header = "========== Begin perfmgr stats ==========\n"
242 "Hint Name\t"
243 "Counts\t"
244 "Duration\n";
245 if (!android::base::WriteStringToFd(header, fd)) {
246 LOG(ERROR) << "Failed to dump fd: " << fd;
247 }
248 std::string hint_stats_string;
249 std::vector<std::string> keys(GetHints());
250 std::sort(keys.begin(), keys.end());
251 for (const auto &ordered_key : keys) {
252 HintStats hint_stats(GetHintStats(ordered_key));
253 hint_stats_string +=
254 android::base::StringPrintf("%s\t%" PRIu32 "\t%" PRIu64 "\n", ordered_key.c_str(),
255 hint_stats.count, hint_stats.duration_ms);
256 }
257 if (!android::base::WriteStringToFd(hint_stats_string, fd)) {
258 LOG(ERROR) << "Failed to dump fd: " << fd;
259 }
260 footer = "========== End perfmgr stats ==========\n";
261 if (!android::base::WriteStringToFd(footer, fd)) {
262 LOG(ERROR) << "Failed to dump fd: " << fd;
263 }
264
265 // Dump current ADPF profile
266 if (GetAdpfProfile()) {
267 header = "========== Begin current adpf profile ==========\n";
268 if (!android::base::WriteStringToFd(header, fd)) {
269 LOG(ERROR) << "Failed to dump fd: " << fd;
270 }
271 GetAdpfProfile()->dumpToFd(fd);
272 footer = "========== End current adpf profile ==========\n";
273 if (!android::base::WriteStringToFd(footer, fd)) {
274 LOG(ERROR) << "Failed to dump fd: " << fd;
275 }
276 }
277 fsync(fd);
278 }
279
Start()280 bool HintManager::Start() {
281 return nm_->Start();
282 }
283
284 std::unique_ptr<HintManager> HintManager::sInstance = nullptr;
285
Reload(bool start)286 void HintManager::Reload(bool start) {
287 std::string config_path = "/vendor/etc/";
288 if (android::base::GetBoolProperty(kConfigDebugPathProperty.data(), false)) {
289 config_path = "/data/vendor/etc/";
290 LOG(WARNING) << "Pixel Power HAL AIDL Service is using debug config from: " << config_path;
291 }
292 config_path.append(
293 android::base::GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()));
294
295 LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is starting with config: "
296 << config_path;
297 // Reload and start the HintManager
298 HintManager::GetFromJSON(config_path, start);
299 if (!sInstance) {
300 LOG(FATAL) << "Invalid config: " << config_path;
301 }
302 }
303
GetInstance()304 HintManager *HintManager::GetInstance() {
305 if (sInstance == nullptr) {
306 HintManager::Reload(false);
307 }
308 return sInstance.get();
309 }
310
ParseGpuSysfsNode(const std::string & json_doc)311 static std::optional<std::string> ParseGpuSysfsNode(const std::string &json_doc) {
312 Json::Value root;
313 Json::CharReaderBuilder builder;
314 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
315 std::string errorMessage;
316 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
317 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
318 return {};
319 }
320
321 if (root["GpuSysfsPath"].empty() || !root["GpuSysfsPath"].isString()) {
322 return {};
323 }
324 return {root["GpuSysfsPath"].asString()};
325 }
326
GetFromJSON(const std::string & config_path,bool start)327 HintManager *HintManager::GetFromJSON(const std::string &config_path, bool start) {
328 std::string json_doc;
329
330 if (!android::base::ReadFileToString(config_path, &json_doc)) {
331 LOG(ERROR) << "Failed to read JSON config from " << config_path;
332 return nullptr;
333 }
334
335 std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
336 if (nodes.empty()) {
337 LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
338 return nullptr;
339 }
340 std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
341 if (adpfs.empty()) {
342 LOG(INFO) << "No AdpfConfig section in the " << config_path;
343 }
344
345 std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc, nodes);
346
347 if (actions.empty()) {
348 LOG(ERROR) << "Failed to parse Actions section from " << config_path;
349 return nullptr;
350 }
351
352 auto const gpu_sysfs_node = ParseGpuSysfsNode(json_doc);
353
354 sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
355 sInstance = std::make_unique<HintManager>(std::move(nm), actions, adpfs, gpu_sysfs_node);
356
357 if (!HintManager::InitHintStatus(sInstance)) {
358 LOG(ERROR) << "Failed to initialize hint status";
359 return nullptr;
360 }
361
362 LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
363
364 if (start) {
365 sInstance->Start();
366 }
367
368 return HintManager::GetInstance();
369 }
370
ParseNodes(const std::string & json_doc)371 std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
372 const std::string& json_doc) {
373 // function starts
374 std::vector<std::unique_ptr<Node>> nodes_parsed;
375 std::set<std::string> nodes_name_parsed;
376 std::set<std::string> nodes_path_parsed;
377 Json::Value root;
378 Json::CharReaderBuilder builder;
379 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
380 std::string errorMessage;
381
382 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
383 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
384 return nodes_parsed;
385 }
386
387 Json::Value nodes = root["Nodes"];
388 for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
389 std::string name = nodes[i]["Name"].asString();
390 LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
391 if (name.empty()) {
392 LOG(ERROR) << "Failed to read "
393 << "Node[" << i << "]'s Name";
394 nodes_parsed.clear();
395 return nodes_parsed;
396 }
397
398 auto result = nodes_name_parsed.insert(name);
399 if (!result.second) {
400 LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
401 nodes_parsed.clear();
402 return nodes_parsed;
403 }
404
405 std::string path = nodes[i]["Path"].asString();
406 LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
407 if (path.empty()) {
408 LOG(ERROR) << "Failed to read "
409 << "Node[" << i << "]'s Path";
410 nodes_parsed.clear();
411 return nodes_parsed;
412 }
413
414 result = nodes_path_parsed.insert(path);
415 if (!result.second) {
416 LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
417 nodes_parsed.clear();
418 return nodes_parsed;
419 }
420
421 bool is_file = true;
422 std::string node_type = nodes[i]["Type"].asString();
423 LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
424 if (node_type.empty()) {
425 LOG(VERBOSE) << "Failed to read "
426 << "Node[" << i << "]'s Type, set to 'File' as default";
427 } else if (node_type == "File") {
428 is_file = true;
429 } else if (node_type == "Property") {
430 is_file = false;
431 } else {
432 LOG(ERROR) << "Invalid Node[" << i
433 << "]'s Type: only File and Property supported.";
434 nodes_parsed.clear();
435 return nodes_parsed;
436 }
437
438 std::vector<RequestGroup> values_parsed;
439 std::set<std::string> values_set_parsed;
440 Json::Value values = nodes[i]["Values"];
441 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
442 std::string value = values[j].asString();
443 LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
444 auto result = values_set_parsed.insert(value);
445 if (!result.second) {
446 LOG(ERROR) << "Duplicate value parsed in Node[" << i
447 << "]'s Value[" << j << "]";
448 nodes_parsed.clear();
449 return nodes_parsed;
450 }
451 if (is_file && value.empty()) {
452 LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
453 << "]";
454 nodes_parsed.clear();
455 return nodes_parsed;
456 }
457 values_parsed.emplace_back(value);
458 }
459 if (values_parsed.size() < 1) {
460 LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
461 nodes_parsed.clear();
462 return nodes_parsed;
463 }
464
465 Json::UInt64 default_index = values_parsed.size() - 1;
466 if (nodes[i]["DefaultIndex"].empty() ||
467 !nodes[i]["DefaultIndex"].isUInt64()) {
468 LOG(INFO) << "Failed to read Node[" << i
469 << "]'s DefaultIndex, set to last index: "
470 << default_index;
471 } else {
472 default_index = nodes[i]["DefaultIndex"].asUInt64();
473 }
474 if (default_index > values_parsed.size() - 1) {
475 default_index = values_parsed.size() - 1;
476 LOG(ERROR) << "Node[" << i
477 << "]'s DefaultIndex out of bound, max value index: "
478 << default_index;
479 nodes_parsed.clear();
480 return nodes_parsed;
481 }
482 LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
483
484 bool reset = false;
485 if (nodes[i]["ResetOnInit"].empty() ||
486 !nodes[i]["ResetOnInit"].isBool()) {
487 LOG(INFO) << "Failed to read Node[" << i
488 << "]'s ResetOnInit, set to 'false'";
489 } else {
490 reset = nodes[i]["ResetOnInit"].asBool();
491 }
492 LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
493 << reset << std::noboolalpha;
494
495 if (is_file) {
496 bool truncate = android::base::GetBoolProperty(kPowerHalTruncateProp, true);
497 if (nodes[i]["Truncate"].empty() || !nodes[i]["Truncate"].isBool()) {
498 LOG(INFO) << "Failed to read Node[" << i << "]'s Truncate, set to 'true'";
499 } else {
500 truncate = nodes[i]["Truncate"].asBool();
501 }
502 LOG(VERBOSE) << "Node[" << i << "]'s Truncate: " << std::boolalpha << truncate
503 << std::noboolalpha;
504
505 bool hold_fd = false;
506 if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
507 LOG(INFO) << "Failed to read Node[" << i
508 << "]'s HoldFd, set to 'false'";
509 } else {
510 hold_fd = nodes[i]["HoldFd"].asBool();
511 }
512 LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
513 << hold_fd << std::noboolalpha;
514
515 bool write_only = false;
516 if (nodes[i]["WriteOnly"].empty() || !nodes[i]["WriteOnly"].isBool()) {
517 LOG(INFO) << "Failed to read Node[" << i
518 << "]'s WriteOnly, set to 'false'";
519 } else {
520 write_only = nodes[i]["WriteOnly"].asBool();
521 }
522 LOG(VERBOSE) << "Node[" << i << "]'s WriteOnly: " << std::boolalpha
523 << write_only << std::noboolalpha;
524
525 nodes_parsed.emplace_back(std::make_unique<FileNode>(
526 name, path, values_parsed, static_cast<std::size_t>(default_index), reset,
527 truncate, hold_fd, write_only));
528 } else {
529 nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
530 name, path, values_parsed,
531 static_cast<std::size_t>(default_index), reset));
532 }
533 }
534 LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
535 return nodes_parsed;
536 }
537
ParseActions(const std::string & json_doc,const std::vector<std::unique_ptr<Node>> & nodes)538 std::unordered_map<std::string, Hint> HintManager::ParseActions(
539 const std::string &json_doc, const std::vector<std::unique_ptr<Node>> &nodes) {
540 // function starts
541 std::unordered_map<std::string, Hint> actions_parsed;
542 Json::Value root;
543 Json::CharReaderBuilder builder;
544 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
545 std::string errorMessage;
546
547 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
548 LOG(ERROR) << "Failed to parse JSON config";
549 return actions_parsed;
550 }
551
552 Json::Value actions = root["Actions"];
553 std::size_t total_parsed = 0;
554
555 std::map<std::string, std::size_t> nodes_index;
556 for (std::size_t i = 0; i < nodes.size(); ++i) {
557 nodes_index[nodes[i]->GetName()] = i;
558 }
559
560 for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
561 const std::string& hint_type = actions[i]["PowerHint"].asString();
562 LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
563 if (hint_type.empty()) {
564 LOG(ERROR) << "Failed to read "
565 << "Action[" << i << "]'s PowerHint";
566 actions_parsed.clear();
567 return actions_parsed;
568 }
569
570 HintActionType action_type = HintActionType::Node;
571 std::string type_string = actions[i]["Type"].asString();
572 std::string enable_property = actions[i]["EnableProperty"].asString();
573 LOG(VERBOSE) << "Action[" << i << "]'s Type: " << type_string;
574 if (type_string.empty()) {
575 LOG(VERBOSE) << "Failed to read "
576 << "Action[" << i << "]'s Type, set to 'Node' as default";
577 } else if (type_string == "DoHint") {
578 action_type = HintActionType::DoHint;
579 } else if (type_string == "EndHint") {
580 action_type = HintActionType::EndHint;
581 } else if (type_string == "MaskHint") {
582 action_type = HintActionType::MaskHint;
583 } else {
584 LOG(ERROR) << "Invalid Action[" << i << "]'s Type: " << type_string;
585 actions_parsed.clear();
586 return actions_parsed;
587 }
588 if (action_type == HintActionType::Node) {
589 std::string node_name = actions[i]["Node"].asString();
590 LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
591 std::size_t node_index;
592
593 if (nodes_index.find(node_name) == nodes_index.end()) {
594 LOG(ERROR) << "Failed to find "
595 << "Action[" << i << "]'s Node from Nodes section: [" << node_name
596 << "]";
597 actions_parsed.clear();
598 return actions_parsed;
599 }
600 node_index = nodes_index[node_name];
601
602 std::string value_name = actions[i]["Value"].asString();
603 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
604 std::size_t value_index = 0;
605
606 if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
607 LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
608 LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
609 << " is not defined in Node[" << node_name << "]";
610 actions_parsed.clear();
611 return actions_parsed;
612 }
613 LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
614
615 Json::UInt64 duration = 0;
616 if (actions[i]["Duration"].empty() || !actions[i]["Duration"].isUInt64()) {
617 LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
618 actions_parsed.clear();
619 return actions_parsed;
620 } else {
621 duration = actions[i]["Duration"].asUInt64();
622 }
623 LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
624
625 for (const auto &action : actions_parsed[hint_type].node_actions) {
626 if (action.node_index == node_index) {
627 LOG(ERROR)
628 << "Action[" << i
629 << "]'s NodeIndex is duplicated with another Action";
630 actions_parsed.clear();
631 return actions_parsed;
632 }
633 }
634 actions_parsed[hint_type].node_actions.emplace_back(
635 node_index, value_index, std::chrono::milliseconds(duration), enable_property);
636
637 } else {
638 const std::string &hint_value = actions[i]["Value"].asString();
639 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << hint_value;
640 if (hint_value.empty()) {
641 LOG(ERROR) << "Failed to read "
642 << "Action[" << i << "]'s Value";
643 actions_parsed.clear();
644 return actions_parsed;
645 }
646 actions_parsed[hint_type].hint_actions.emplace_back(action_type, hint_value,
647 enable_property);
648 }
649
650 ++total_parsed;
651 }
652
653 LOG(INFO) << total_parsed << " actions parsed successfully";
654
655 for (const auto& action : actions_parsed) {
656 LOG(INFO) << "PowerHint " << action.first << " has " << action.second.node_actions.size()
657 << " node actions"
658 << ", and " << action.second.hint_actions.size() << " hint actions parsed";
659 }
660
661 return actions_parsed;
662 }
663
664 #define ADPF_PARSE(VARIABLE, ENTRY, TYPE) \
665 static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()), decltype(VARIABLE)>::value, \
666 "Parser type mismatch"); \
667 if (adpfs[i][ENTRY].empty() || !adpfs[i][ENTRY].is##TYPE()) { \
668 LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][" ENTRY "]'s Values"; \
669 adpfs_parsed.clear(); \
670 return adpfs_parsed; \
671 } \
672 VARIABLE = adpfs[i][ENTRY].as##TYPE()
673
674 #define ADPF_PARSE_OPTIONAL(VARIABLE, ENTRY, TYPE) \
675 static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()), \
676 decltype(VARIABLE)::value_type>::value, \
677 "Parser type mismatch"); \
678 if (!adpfs[i][ENTRY].empty() && adpfs[i][ENTRY].is##TYPE()) { \
679 VARIABLE = adpfs[i][ENTRY].as##TYPE(); \
680 }
681
ParseAdpfConfigs(const std::string & json_doc)682 std::vector<std::shared_ptr<AdpfConfig>> HintManager::ParseAdpfConfigs(
683 const std::string &json_doc) {
684 // function starts
685 bool pidOn;
686 double pidPOver;
687 double pidPUnder;
688 double pidI;
689 double pidDOver;
690 double pidDUnder;
691 int64_t pidIInit;
692 int64_t pidIHighLimit;
693 int64_t pidILowLimit;
694 bool adpfUclamp;
695 uint32_t uclampMinInit;
696 uint32_t uclampMinHighLimit;
697 uint32_t uclampMinLowLimit;
698 uint64_t samplingWindowP;
699 uint64_t samplingWindowI;
700 uint64_t samplingWindowD;
701 double staleTimeFactor;
702 uint64_t reportingRate;
703 double targetTimeFactor;
704
705 std::vector<std::shared_ptr<AdpfConfig>> adpfs_parsed;
706 std::set<std::string> name_parsed;
707 Json::Value root;
708 Json::CharReaderBuilder builder;
709 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
710 std::string errorMessage;
711 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
712 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
713 return adpfs_parsed;
714 }
715 Json::Value adpfs = root["AdpfConfig"];
716 for (Json::Value::ArrayIndex i = 0; i < adpfs.size(); ++i) {
717 std::optional<bool> gpuBoost;
718 std::optional<uint64_t> gpuBoostCapacityMax;
719 uint64_t gpuCapacityLoadUpHeadroom = 0;
720 std::string name = adpfs[i]["Name"].asString();
721 LOG(VERBOSE) << "AdpfConfig[" << i << "]'s Name: " << name;
722 if (name.empty()) {
723 LOG(ERROR) << "Failed to read "
724 << "AdpfConfig[" << i << "]'s Name";
725 adpfs_parsed.clear();
726 return adpfs_parsed;
727 }
728 auto result = name_parsed.insert(name);
729 if (!result.second) {
730 LOG(ERROR) << "Duplicate AdpfConfig[" << i << "]'s Name";
731 adpfs_parsed.clear();
732 return adpfs_parsed;
733 }
734
735 // heuristic boost configs
736 std::optional<bool> heuristicBoostOn;
737 std::optional<uint32_t> hBoostOnMissedCycles;
738 std::optional<double> hBoostOffMaxAvgRatio;
739 std::optional<uint32_t> hBoostOffMissedCycles;
740 std::optional<double> hBoostPidPuFactor;
741 std::optional<uint32_t> hBoostUclampMin;
742 std::optional<double> jankCheckTimeFactor;
743 std::optional<uint32_t> lowFrameRateThreshold;
744 std::optional<uint32_t> maxRecordsNum;
745
746 std::optional<uint32_t> uclampMinLoadUp;
747 std::optional<uint32_t> uclampMinLoadReset;
748 std::optional<int32_t> uclampMaxEfficientBase;
749 std::optional<int32_t> uclampMaxEfficientOffset;
750
751 ADPF_PARSE(pidOn, "PID_On", Bool);
752 ADPF_PARSE(pidPOver, "PID_Po", Double);
753 ADPF_PARSE(pidPUnder, "PID_Pu", Double);
754 ADPF_PARSE(pidI, "PID_I", Double);
755 ADPF_PARSE(pidIInit, "PID_I_Init", Int64);
756 ADPF_PARSE(pidIHighLimit, "PID_I_High", Int64);
757 ADPF_PARSE(pidILowLimit, "PID_I_Low", Int64);
758 ADPF_PARSE(pidDOver, "PID_Do", Double);
759 ADPF_PARSE(pidDUnder, "PID_Du", Double);
760 ADPF_PARSE(adpfUclamp, "UclampMin_On", Bool);
761 ADPF_PARSE(uclampMinInit, "UclampMin_Init", UInt);
762 ADPF_PARSE_OPTIONAL(uclampMinLoadUp, "UclampMin_LoadUp", UInt);
763 ADPF_PARSE_OPTIONAL(uclampMinLoadReset, "UclampMin_LoadReset", UInt);
764 ADPF_PARSE(uclampMinHighLimit, "UclampMin_High", UInt);
765 ADPF_PARSE(uclampMinLowLimit, "UclampMin_Low", UInt);
766 ADPF_PARSE(samplingWindowP, "SamplingWindow_P", UInt64);
767 ADPF_PARSE(samplingWindowI, "SamplingWindow_I", UInt64);
768 ADPF_PARSE(samplingWindowD, "SamplingWindow_D", UInt64);
769 ADPF_PARSE(staleTimeFactor, "StaleTimeFactor", Double);
770 ADPF_PARSE(reportingRate, "ReportingRateLimitNs", UInt64);
771 ADPF_PARSE(targetTimeFactor, "TargetTimeFactor", Double);
772 ADPF_PARSE_OPTIONAL(heuristicBoostOn, "HeuristicBoost_On", Bool);
773 ADPF_PARSE_OPTIONAL(hBoostOnMissedCycles, "HBoostOnMissedCycles", UInt);
774 ADPF_PARSE_OPTIONAL(hBoostOffMaxAvgRatio, "HBoostOffMaxAvgRatio", Double);
775 ADPF_PARSE_OPTIONAL(hBoostOffMissedCycles, "HBoostOffMissedCycles", UInt);
776 ADPF_PARSE_OPTIONAL(hBoostPidPuFactor, "HBoostPidPuFactor", Double);
777 ADPF_PARSE_OPTIONAL(hBoostUclampMin, "HBoostUclampMin", UInt);
778 ADPF_PARSE_OPTIONAL(jankCheckTimeFactor, "JankCheckTimeFactor", Double);
779 ADPF_PARSE_OPTIONAL(lowFrameRateThreshold, "LowFrameRateThreshold", UInt);
780 ADPF_PARSE_OPTIONAL(maxRecordsNum, "MaxRecordsNum", UInt);
781 ADPF_PARSE_OPTIONAL(uclampMaxEfficientBase, "UclampMax_EfficientBase", Int);
782 ADPF_PARSE_OPTIONAL(uclampMaxEfficientOffset, "UclampMax_EfficientOffset", Int);
783
784 if (!adpfs[i]["GpuBoost"].empty() && adpfs[i]["GpuBoost"].isBool()) {
785 gpuBoost = adpfs[i]["GpuBoost"].asBool();
786 }
787 if (!adpfs[i]["GpuCapacityBoostMax"].empty() &&
788 adpfs[i]["GpuCapacityBoostMax"].isUInt64()) {
789 gpuBoostCapacityMax = adpfs[i]["GpuCapacityBoostMax"].asUInt64();
790 }
791 if (!adpfs[i]["GpuCapacityLoadUpHeadroom"].empty() &&
792 adpfs[i]["GpuCapacityLoadUpHeadroom"].isUInt64()) {
793 gpuCapacityLoadUpHeadroom = adpfs[i]["GpuCapacityLoadUpHeadroom"].asUInt64();
794 }
795
796 // Check all the heuristic configurations are there if heuristic boost is going to
797 // be used.
798 if (heuristicBoostOn.has_value()) {
799 if (!hBoostOnMissedCycles.has_value() || !hBoostOffMaxAvgRatio.has_value() ||
800 !hBoostOffMissedCycles.has_value() || !hBoostPidPuFactor.has_value() ||
801 !hBoostUclampMin.has_value() || !jankCheckTimeFactor.has_value() ||
802 !lowFrameRateThreshold.has_value() || !maxRecordsNum.has_value()) {
803 LOG(ERROR) << "Part of the heuristic boost configurations are missing!";
804 adpfs_parsed.clear();
805 return adpfs_parsed;
806 }
807 }
808
809 if (uclampMaxEfficientBase.has_value() != uclampMaxEfficientBase.has_value()) {
810 LOG(ERROR) << "Part of the power efficiency configuration is missing!";
811 adpfs_parsed.clear();
812 return adpfs_parsed;
813 }
814
815 if (!uclampMinLoadUp.has_value()) {
816 uclampMinLoadUp = uclampMinHighLimit;
817 }
818 if (!uclampMinLoadReset.has_value()) {
819 uclampMinLoadReset = uclampMinHighLimit;
820 }
821
822 adpfs_parsed.emplace_back(std::make_shared<AdpfConfig>(
823 name, pidOn, pidPOver, pidPUnder, pidI, pidIInit, pidIHighLimit, pidILowLimit,
824 pidDOver, pidDUnder, adpfUclamp, uclampMinInit, uclampMinHighLimit,
825 uclampMinLowLimit, samplingWindowP, samplingWindowI, samplingWindowD, reportingRate,
826 targetTimeFactor, staleTimeFactor, gpuBoost, gpuBoostCapacityMax,
827 gpuCapacityLoadUpHeadroom, heuristicBoostOn, hBoostOnMissedCycles,
828 hBoostOffMaxAvgRatio, hBoostOffMissedCycles, hBoostPidPuFactor, hBoostUclampMin,
829 jankCheckTimeFactor, lowFrameRateThreshold, maxRecordsNum, uclampMinLoadUp.value(),
830 uclampMinLoadReset.value(), uclampMaxEfficientBase, uclampMaxEfficientOffset));
831 }
832 LOG(INFO) << adpfs_parsed.size() << " AdpfConfigs parsed successfully";
833 return adpfs_parsed;
834 }
835
GetAdpfProfile() const836 std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfile() const {
837 if (adpfs_.empty())
838 return nullptr;
839 return adpfs_[adpf_index_];
840 }
841
SetAdpfProfile(const std::string & profile_name)842 bool HintManager::SetAdpfProfile(const std::string &profile_name) {
843 for (std::size_t i = 0; i < adpfs_.size(); ++i) {
844 if (adpfs_[i]->mName == profile_name) {
845 adpf_index_ = i;
846 return true;
847 }
848 }
849 return false;
850 }
851
IsAdpfProfileSupported(const std::string & profile_name) const852 bool HintManager::IsAdpfProfileSupported(const std::string &profile_name) const {
853 for (std::size_t i = 0; i < adpfs_.size(); ++i) {
854 if (adpfs_[i]->mName == profile_name) {
855 return true;
856 }
857 }
858 return false;
859 }
860
gpu_sysfs_config_path() const861 std::optional<std::string> HintManager::gpu_sysfs_config_path() const {
862 return gpu_sysfs_config_path_;
863 }
864
865 } // namespace perfmgr
866 } // namespace android
867