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 #include <string>
34
35 #include "perfmgr/EventNode.h"
36 #include "perfmgr/FileNode.h"
37 #include "perfmgr/PropertyNode.h"
38
39 namespace android {
40 namespace perfmgr {
41
42 namespace {
43 constexpr std::chrono::milliseconds kMilliSecondZero = std::chrono::milliseconds(0);
44 constexpr std::chrono::steady_clock::time_point kTimePointMax =
45 std::chrono::steady_clock::time_point::max();
46 } // namespace
47
48 using ::android::base::GetProperty;
49 using ::android::base::StringPrintf;
50
51 constexpr char kPowerHalTruncateProp[] = "vendor.powerhal.truncate";
52 constexpr std::string_view kConfigDebugPathProperty("vendor.powerhal.config.debug");
53 constexpr std::string_view kConfigProperty("vendor.powerhal.config");
54 constexpr std::string_view kConfigDefaultFileName("powerhint.json");
55 constexpr char kAdpfEventNodePath[] = "<AdpfConfig>:";
56
ValidateHint(const std::string & hint_type) const57 bool HintManager::ValidateHint(const std::string& hint_type) const {
58 if (nm_.get() == nullptr) {
59 LOG(ERROR) << "NodeLooperThread not present";
60 return false;
61 }
62 return IsHintSupported(hint_type);
63 }
64
IsHintSupported(const std::string & hint_type) const65 bool HintManager::IsHintSupported(const std::string& hint_type) const {
66 if (actions_.find(hint_type) == actions_.end()) {
67 LOG(DEBUG) << "Hint type not present in actions: " << hint_type;
68 return false;
69 }
70 return true;
71 }
72
IsHintEnabled(const std::string & hint_type) const73 bool HintManager::IsHintEnabled(const std::string &hint_type) const {
74 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
75 return actions_.at(hint_type).mask_requesters.empty();
76 }
77
InitHintStatus(const std::unique_ptr<HintManager> & hm)78 bool HintManager::InitHintStatus(const std::unique_ptr<HintManager> &hm) {
79 if (hm.get() == nullptr) {
80 return false;
81 }
82 for (auto &a : hm->actions_) {
83 // timeout_ms equaling kMilliSecondZero means forever until cancelling.
84 // As a result, if there's one NodeAction has timeout_ms of 0, we will store
85 // 0 instead of max. Also node actions could be empty, set to 0 in that case.
86 std::chrono::milliseconds timeout = kMilliSecondZero;
87 if (a.second.node_actions.size()) {
88 auto [min, max] =
89 std::minmax_element(a.second.node_actions.begin(), a.second.node_actions.end(),
90 [](const auto act1, const auto act2) {
91 return act1.timeout_ms < act2.timeout_ms;
92 });
93 timeout = min->timeout_ms == kMilliSecondZero ? kMilliSecondZero : max->timeout_ms;
94 }
95 a.second.status.reset(new HintStatus(timeout));
96 }
97 return true;
98 }
99
DoHintStatus(const std::string & hint_type,std::chrono::milliseconds timeout_ms)100 void HintManager::DoHintStatus(const std::string &hint_type, std::chrono::milliseconds timeout_ms) {
101 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
102 actions_.at(hint_type).status->stats.count.fetch_add(1);
103 auto now = std::chrono::steady_clock::now();
104 ATRACE_INT(("H:" + hint_type).c_str(), (timeout_ms == kMilliSecondZero)
105 ? std::numeric_limits<int>::max()
106 : timeout_ms.count());
107 ATRACE_NAME(("H:" + hint_type + ":" + std::to_string((timeout_ms == kMilliSecondZero)
108 ? std::numeric_limits<int>::max()
109 : timeout_ms.count())).c_str());
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 actions_.at(hint_type).status->end_time -
114 actions_.at(hint_type).status->start_time)
115 .count());
116 actions_.at(hint_type).status->start_time = now;
117 }
118 actions_.at(hint_type).status->end_time =
119 (timeout_ms == kMilliSecondZero) ? kTimePointMax : now + timeout_ms;
120 }
121
EndHintStatus(const std::string & hint_type)122 void HintManager::EndHintStatus(const std::string &hint_type) {
123 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
124 // Update HintStats if the hint ends earlier than expected end_time
125 auto now = std::chrono::steady_clock::now();
126 ATRACE_INT(("H:" + hint_type).c_str(), 0);
127 ATRACE_NAME(("H:" + hint_type + ":0").c_str());
128 if (now < actions_.at(hint_type).status->end_time) {
129 actions_.at(hint_type).status->stats.duration_ms.fetch_add(
130 std::chrono::duration_cast<std::chrono::milliseconds>(
131 now - actions_.at(hint_type).status->start_time)
132 .count());
133 actions_.at(hint_type).status->end_time = now;
134 }
135 }
136
DoHintAction(const std::string & hint_type)137 void HintManager::DoHintAction(const std::string &hint_type) {
138 for (auto &action : actions_.at(hint_type).hint_actions) {
139 if (!action.enable_property.empty() &&
140 !android::base::GetBoolProperty(action.enable_property, true)) {
141 // Disabled action based on its control property
142 continue;
143 }
144 switch (action.type) {
145 case HintActionType::DoHint:
146 DoHint(action.value);
147 break;
148 case HintActionType::EndHint:
149 EndHint(action.value);
150 break;
151 case HintActionType::MaskHint:
152 if (actions_.find(action.value) == actions_.end()) {
153 LOG(ERROR) << "Failed to find " << action.value << " action";
154 } else {
155 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
156 actions_.at(action.value).mask_requesters.insert(hint_type);
157 }
158 break;
159 default:
160 // should not reach here
161 LOG(ERROR) << "Invalid "
162 << static_cast<std::underlying_type<HintActionType>::type>(action.type)
163 << " type";
164 }
165 }
166 }
167
EndHintAction(const std::string & hint_type)168 void HintManager::EndHintAction(const std::string &hint_type) {
169 for (auto &action : actions_.at(hint_type).hint_actions) {
170 if (action.type == HintActionType::MaskHint &&
171 actions_.find(action.value) != actions_.end()) {
172 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
173 actions_.at(action.value).mask_requesters.erase(hint_type);
174 }
175 }
176 }
177
DoHint(const std::string & hint_type)178 bool HintManager::DoHint(const std::string& hint_type) {
179 LOG(VERBOSE) << "Do Powerhint: " << hint_type;
180 if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type) ||
181 !nm_->Request(actions_.at(hint_type).node_actions, hint_type)) {
182 return false;
183 }
184 DoHintStatus(hint_type, actions_.at(hint_type).status->max_timeout);
185 DoHintAction(hint_type);
186 return true;
187 }
188
DoHint(const std::string & hint_type,std::chrono::milliseconds timeout_ms_override)189 bool HintManager::DoHint(const std::string& hint_type,
190 std::chrono::milliseconds timeout_ms_override) {
191 LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
192 << timeout_ms_override.count() << "ms";
193 if (!ValidateHint(hint_type) || !IsHintEnabled(hint_type)) {
194 return false;
195 }
196 std::vector<NodeAction> actions_override = actions_.at(hint_type).node_actions;
197 for (auto& action : actions_override) {
198 action.timeout_ms = timeout_ms_override;
199 }
200 if (!nm_->Request(actions_override, hint_type)) {
201 return false;
202 }
203 DoHintStatus(hint_type, timeout_ms_override);
204 DoHintAction(hint_type);
205 return true;
206 }
207
EndHint(const std::string & hint_type)208 bool HintManager::EndHint(const std::string& hint_type) {
209 LOG(VERBOSE) << "End Powerhint: " << hint_type;
210 if (!ValidateHint(hint_type) || !nm_->Cancel(actions_.at(hint_type).node_actions, hint_type)) {
211 return false;
212 }
213 EndHintStatus(hint_type);
214 EndHintAction(hint_type);
215 return true;
216 }
217
IsRunning() const218 bool HintManager::IsRunning() const {
219 return (nm_.get() == nullptr) ? false : nm_->isRunning();
220 }
221
GetHints() const222 std::vector<std::string> HintManager::GetHints() const {
223 std::vector<std::string> hints;
224 for (auto const& action : actions_) {
225 hints.push_back(action.first);
226 }
227 return hints;
228 }
229
GetHintStats(const std::string & hint_type) const230 HintStats HintManager::GetHintStats(const std::string &hint_type) const {
231 HintStats hint_stats;
232 if (ValidateHint(hint_type)) {
233 std::lock_guard<std::mutex> lock(actions_.at(hint_type).hint_lock);
234 hint_stats.count =
235 actions_.at(hint_type).status->stats.count.load(std::memory_order_relaxed);
236 hint_stats.duration_ms =
237 actions_.at(hint_type).status->stats.duration_ms.load(std::memory_order_relaxed);
238 }
239 return hint_stats;
240 }
241
DumpToFd(int fd)242 void HintManager::DumpToFd(int fd) {
243 std::string header("========== Begin perfmgr nodes ==========\n");
244 if (!android::base::WriteStringToFd(header, fd)) {
245 LOG(ERROR) << "Failed to dump fd: " << fd;
246 }
247 nm_->DumpToFd(fd);
248 std::string footer("========== End perfmgr nodes ==========\n");
249 if (!android::base::WriteStringToFd(footer, fd)) {
250 LOG(ERROR) << "Failed to dump fd: " << fd;
251 }
252 header = "========== Begin perfmgr stats ==========\n"
253 "Hint Name\t"
254 "Counts\t"
255 "Duration\n";
256 if (!android::base::WriteStringToFd(header, fd)) {
257 LOG(ERROR) << "Failed to dump fd: " << fd;
258 }
259 std::string hint_stats_string;
260 std::vector<std::string> keys(GetHints());
261 std::sort(keys.begin(), keys.end());
262 for (const auto &ordered_key : keys) {
263 HintStats hint_stats(GetHintStats(ordered_key));
264 hint_stats_string += StringPrintf("%s\t%" PRIu32 "\t%" PRIu64 "\n", ordered_key.c_str(),
265 hint_stats.count, hint_stats.duration_ms);
266 }
267 if (!android::base::WriteStringToFd(hint_stats_string, fd)) {
268 LOG(ERROR) << "Failed to dump fd: " << fd;
269 }
270 footer = "========== End perfmgr stats ==========\n";
271 if (!android::base::WriteStringToFd(footer, fd)) {
272 LOG(ERROR) << "Failed to dump fd: " << fd;
273 }
274
275 // Dump current ADPF profiles
276 if (IsAdpfSupported()) {
277 header = "========== ADPF Tag Profile begin ==========\n";
278 if (!android::base::WriteStringToFd(header, fd)) {
279 LOG(ERROR) << "Failed to dump fd: " << fd;
280 }
281
282 header = "---- Default non-tagged adpf profile ----\n";
283 if (!android::base::WriteStringToFd(header, fd)) {
284 LOG(ERROR) << "Failed to dump fd: " << fd;
285 }
286 GetAdpfProfileFromDoHint()->dumpToFd(fd);
287
288 for (const auto &tag_profile : tag_profile_map_) {
289 header = StringPrintf("---- Tagged ADPF Profile: %s ----\n", tag_profile.first.c_str());
290 if (!android::base::WriteStringToFd(header, fd)) {
291 LOG(ERROR) << "Failed to dump fd: " << fd;
292 }
293 tag_profile.second->dumpToFd(fd);
294 }
295
296 footer = "========== ADPF Tag Profile end ==========\n";
297 if (!android::base::WriteStringToFd(footer, fd)) {
298 LOG(ERROR) << "Failed to dump fd: " << fd;
299 }
300 } else {
301 header = "========== IsAdpfSupported: No ===========\n";
302 if (!android::base::WriteStringToFd(header, fd)) {
303 LOG(ERROR) << "Failed to dump fd: " << fd;
304 }
305 }
306 fsync(fd);
307 }
308
Start()309 bool HintManager::Start() {
310 return nm_->Start();
311 }
312
313 std::unique_ptr<HintManager> HintManager::sInstance = nullptr;
314
Reload(bool start)315 void HintManager::Reload(bool start) {
316 std::string config_path = "/vendor/etc/";
317 if (android::base::GetBoolProperty(kConfigDebugPathProperty.data(), false)) {
318 config_path = "/data/vendor/etc/";
319 LOG(WARNING) << "Pixel Power HAL AIDL Service is using debug config from: " << config_path;
320 }
321 config_path.append(GetProperty(kConfigProperty.data(), kConfigDefaultFileName.data()));
322
323 LOG(INFO) << "Pixel Power HAL AIDL Service with Extension is starting with config: "
324 << config_path;
325 // Reload and start the HintManager
326 HintManager::GetFromJSON(config_path, start);
327 if (!sInstance) {
328 LOG(FATAL) << "Invalid config: " << config_path;
329 }
330 }
331
GetInstance()332 HintManager *HintManager::GetInstance() {
333 if (sInstance == nullptr) {
334 HintManager::Reload(false);
335 }
336 return sInstance.get();
337 }
338
ParseGpuSysfsNode(const std::string & json_doc)339 static std::optional<std::string> ParseGpuSysfsNode(const std::string &json_doc) {
340 Json::Value root;
341 Json::CharReaderBuilder builder;
342 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
343 std::string errorMessage;
344 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
345 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
346 return {};
347 }
348
349 if (root["GpuSysfsPath"].empty() || !root["GpuSysfsPath"].isString()) {
350 return {};
351 }
352 return {root["GpuSysfsPath"].asString()};
353 }
354
GetFromJSON(const std::string & config_path,bool start)355 HintManager *HintManager::GetFromJSON(const std::string &config_path, bool start) {
356 std::string json_doc;
357
358 if (!android::base::ReadFileToString(config_path, &json_doc)) {
359 LOG(ERROR) << "Failed to read JSON config from " << config_path;
360 return nullptr;
361 }
362
363 std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
364 if (nodes.empty()) {
365 LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
366 return nullptr;
367 }
368 std::vector<std::shared_ptr<AdpfConfig>> adpfs = HintManager::ParseAdpfConfigs(json_doc);
369 if (adpfs.empty()) {
370 LOG(INFO) << "No AdpfConfig section in the " << config_path;
371 }
372
373 std::unordered_map<std::string, Hint> actions = HintManager::ParseActions(json_doc, nodes);
374
375 // Parse ADPF Event Node
376 std::unordered_map<std::string, std::shared_ptr<AdpfConfig>> tag_adpfs;
377 LOG(VERBOSE) << "Parse ADPF Hint Event Table from all nodes.";
378 for (std::size_t i = 0; i < nodes.size(); ++i) {
379 const std::string &node_name = nodes[i]->GetName();
380 const std::string &node_path = nodes[i]->GetPath();
381 if (node_path.starts_with(kAdpfEventNodePath)) {
382 std::string tag = node_path.substr(strlen(kAdpfEventNodePath));
383 std::size_t index = nodes[i]->GetDefaultIndex();
384 std::string profile_name = nodes[i]->GetValues()[index];
385 for (std::size_t j = 0; j < adpfs.size(); ++j) {
386 if (adpfs[j]->mName == profile_name) {
387 tag_adpfs[tag] = adpfs[j];
388 LOG(INFO) << "[" << tag << ":" << node_name << "] set to '" << profile_name
389 << "'";
390 break;
391 }
392 }
393 if (!tag_adpfs[tag]) {
394 tag_adpfs[tag] = adpfs[0];
395 LOG(INFO) << "[" << tag << ":" << node_name << "] fallback to '" << adpfs[0]->mName
396 << "'";
397 }
398 }
399 }
400
401 if (actions.empty()) {
402 LOG(ERROR) << "Failed to parse Actions section from " << config_path;
403 return nullptr;
404 }
405
406 auto const gpu_sysfs_node = ParseGpuSysfsNode(json_doc);
407
408 sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
409 sInstance =
410 std::make_unique<HintManager>(std::move(nm), actions, adpfs, tag_adpfs, gpu_sysfs_node);
411
412 if (!HintManager::InitHintStatus(sInstance)) {
413 LOG(ERROR) << "Failed to initialize hint status";
414 return nullptr;
415 }
416
417 LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
418
419 if (start) {
420 sInstance->Start();
421 }
422
423 return HintManager::GetInstance();
424 }
425
ParseNodes(const std::string & json_doc)426 std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(const std::string &json_doc) {
427 // function starts
428 std::vector<std::unique_ptr<Node>> nodes_parsed;
429 std::set<std::string> nodes_name_parsed;
430 std::set<std::string> nodes_path_parsed;
431 Json::Value root;
432 Json::CharReaderBuilder builder;
433 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
434 std::string errorMessage;
435
436 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
437 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
438 return nodes_parsed;
439 }
440
441 Json::Value nodes = root["Nodes"];
442 for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
443 std::string name = nodes[i]["Name"].asString();
444 LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
445 if (name.empty()) {
446 LOG(ERROR) << "Failed to read "
447 << "Node[" << i << "]'s Name";
448 nodes_parsed.clear();
449 return nodes_parsed;
450 }
451
452 auto result = nodes_name_parsed.insert(name);
453 if (!result.second) {
454 LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
455 nodes_parsed.clear();
456 return nodes_parsed;
457 }
458
459 std::string path = nodes[i]["Path"].asString();
460 LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
461 if (path.empty()) {
462 LOG(ERROR) << "Failed to read "
463 << "Node[" << i << "]'s Path";
464 nodes_parsed.clear();
465 return nodes_parsed;
466 }
467
468 result = nodes_path_parsed.insert(path);
469 if (!result.second) {
470 LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
471 nodes_parsed.clear();
472 return nodes_parsed;
473 }
474
475 bool is_event_node = false;
476 bool is_file = false;
477 std::string node_type = nodes[i]["Type"].asString();
478 LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
479 if (node_type.empty()) {
480 is_file = true;
481 LOG(VERBOSE) << "Failed to read "
482 << "Node[" << i << "]'s Type, set to 'File' as default";
483 } else if (node_type == "Event") {
484 is_event_node = true;
485 } else if (node_type == "File") {
486 is_file = true;
487 } else if (node_type == "Property") {
488 is_file = false;
489 } else {
490 LOG(ERROR) << "Invalid Node[" << i
491 << "]'s Type: only File and Property supported.";
492 nodes_parsed.clear();
493 return nodes_parsed;
494 }
495
496 std::vector<RequestGroup> values_parsed;
497 std::set<std::string> values_set_parsed;
498 Json::Value values = nodes[i]["Values"];
499 for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
500 std::string value = values[j].asString();
501 LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
502 auto result = values_set_parsed.insert(value);
503 if (!result.second) {
504 LOG(ERROR) << "Duplicate value parsed in Node[" << i
505 << "]'s Value[" << j << "]";
506 nodes_parsed.clear();
507 return nodes_parsed;
508 }
509 if (is_file && value.empty()) {
510 LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j
511 << "]";
512 nodes_parsed.clear();
513 return nodes_parsed;
514 }
515 values_parsed.emplace_back(value);
516 }
517 if (values_parsed.size() < 1) {
518 LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
519 nodes_parsed.clear();
520 return nodes_parsed;
521 }
522
523 Json::UInt64 default_index = values_parsed.size() - 1;
524 if (nodes[i]["DefaultIndex"].empty() ||
525 !nodes[i]["DefaultIndex"].isUInt64()) {
526 LOG(INFO) << "Failed to read Node[" << i
527 << "]'s DefaultIndex, set to last index: "
528 << default_index;
529 } else {
530 default_index = nodes[i]["DefaultIndex"].asUInt64();
531 }
532 if (default_index > values_parsed.size() - 1) {
533 default_index = values_parsed.size() - 1;
534 LOG(ERROR) << "Node[" << i
535 << "]'s DefaultIndex out of bound, max value index: "
536 << default_index;
537 nodes_parsed.clear();
538 return nodes_parsed;
539 }
540 LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
541
542 bool reset = false;
543 if (nodes[i]["ResetOnInit"].empty() ||
544 !nodes[i]["ResetOnInit"].isBool()) {
545 LOG(INFO) << "Failed to read Node[" << i
546 << "]'s ResetOnInit, set to 'false'";
547 } else {
548 reset = nodes[i]["ResetOnInit"].asBool();
549 }
550 LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
551 << reset << std::noboolalpha;
552
553 if (is_event_node) {
554 auto update_callback = [](const std::string &name, const std::string &path,
555 const std::string &val) {
556 HintManager::GetInstance()->OnNodeUpdate(name, path, val);
557 };
558 nodes_parsed.emplace_back(std::make_unique<EventNode>(
559 name, path, values_parsed, static_cast<std::size_t>(default_index), reset,
560 update_callback));
561 } else if (is_file) {
562 bool truncate = android::base::GetBoolProperty(kPowerHalTruncateProp, true);
563 if (nodes[i]["Truncate"].empty() || !nodes[i]["Truncate"].isBool()) {
564 LOG(INFO) << "Failed to read Node[" << i << "]'s Truncate, set to 'true'";
565 } else {
566 truncate = nodes[i]["Truncate"].asBool();
567 }
568 LOG(VERBOSE) << "Node[" << i << "]'s Truncate: " << std::boolalpha << truncate
569 << std::noboolalpha;
570
571 bool hold_fd = false;
572 if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
573 LOG(INFO) << "Failed to read Node[" << i
574 << "]'s HoldFd, set to 'false'";
575 } else {
576 hold_fd = nodes[i]["HoldFd"].asBool();
577 }
578 LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
579 << hold_fd << std::noboolalpha;
580
581 bool write_only = false;
582 if (nodes[i]["WriteOnly"].empty() || !nodes[i]["WriteOnly"].isBool()) {
583 LOG(INFO) << "Failed to read Node[" << i
584 << "]'s WriteOnly, set to 'false'";
585 } else {
586 write_only = nodes[i]["WriteOnly"].asBool();
587 }
588 LOG(VERBOSE) << "Node[" << i << "]'s WriteOnly: " << std::boolalpha
589 << write_only << std::noboolalpha;
590
591 nodes_parsed.emplace_back(std::make_unique<FileNode>(
592 name, path, values_parsed, static_cast<std::size_t>(default_index), reset,
593 truncate, hold_fd, write_only));
594 } else {
595 nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
596 name, path, values_parsed, static_cast<std::size_t>(default_index), reset));
597 }
598 }
599 LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
600 return nodes_parsed;
601 }
602
ParseActions(const std::string & json_doc,const std::vector<std::unique_ptr<Node>> & nodes)603 std::unordered_map<std::string, Hint> HintManager::ParseActions(
604 const std::string &json_doc, const std::vector<std::unique_ptr<Node>> &nodes) {
605 // function starts
606 std::unordered_map<std::string, Hint> actions_parsed;
607 Json::Value root;
608 Json::CharReaderBuilder builder;
609 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
610 std::string errorMessage;
611
612 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
613 LOG(ERROR) << "Failed to parse JSON config";
614 return actions_parsed;
615 }
616
617 Json::Value actions = root["Actions"];
618 std::size_t total_parsed = 0;
619
620 std::map<std::string, std::size_t> nodes_index;
621 for (std::size_t i = 0; i < nodes.size(); ++i) {
622 nodes_index[nodes[i]->GetName()] = i;
623 }
624
625 for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
626 const std::string& hint_type = actions[i]["PowerHint"].asString();
627 LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
628 if (hint_type.empty()) {
629 LOG(ERROR) << "Failed to read "
630 << "Action[" << i << "]'s PowerHint";
631 actions_parsed.clear();
632 return actions_parsed;
633 }
634
635 HintActionType action_type = HintActionType::Node;
636 std::string type_string = actions[i]["Type"].asString();
637 std::string enable_property = actions[i]["EnableProperty"].asString();
638 LOG(VERBOSE) << "Action[" << i << "]'s Type: " << type_string;
639 if (type_string.empty()) {
640 LOG(VERBOSE) << "Failed to read "
641 << "Action[" << i << "]'s Type, set to 'Node' as default";
642 } else if (type_string == "DoHint") {
643 action_type = HintActionType::DoHint;
644 } else if (type_string == "EndHint") {
645 action_type = HintActionType::EndHint;
646 } else if (type_string == "MaskHint") {
647 action_type = HintActionType::MaskHint;
648 } else {
649 LOG(ERROR) << "Invalid Action[" << i << "]'s Type: " << type_string;
650 actions_parsed.clear();
651 return actions_parsed;
652 }
653 if (action_type == HintActionType::Node) {
654 std::string node_name = actions[i]["Node"].asString();
655 LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
656 std::size_t node_index;
657
658 if (nodes_index.find(node_name) == nodes_index.end()) {
659 LOG(ERROR) << "Failed to find "
660 << "Action[" << i << "]'s Node from Nodes section: [" << node_name
661 << "]";
662 actions_parsed.clear();
663 return actions_parsed;
664 }
665 node_index = nodes_index[node_name];
666
667 std::string value_name = actions[i]["Value"].asString();
668 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
669 std::size_t value_index = 0;
670
671 if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
672 LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
673 LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
674 << " is not defined in Node[" << node_name << "]";
675 actions_parsed.clear();
676 return actions_parsed;
677 }
678 LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
679
680 Json::UInt64 duration = 0;
681 if (actions[i]["Duration"].empty() || !actions[i]["Duration"].isUInt64()) {
682 LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
683 actions_parsed.clear();
684 return actions_parsed;
685 } else {
686 duration = actions[i]["Duration"].asUInt64();
687 }
688 LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
689
690 for (const auto &action : actions_parsed[hint_type].node_actions) {
691 if (action.node_index == node_index) {
692 LOG(ERROR)
693 << "Action[" << i
694 << "]'s NodeIndex is duplicated with another Action";
695 actions_parsed.clear();
696 return actions_parsed;
697 }
698 }
699 actions_parsed[hint_type].node_actions.emplace_back(
700 node_index, value_index, std::chrono::milliseconds(duration), enable_property);
701
702 } else {
703 const std::string &hint_value = actions[i]["Value"].asString();
704 LOG(VERBOSE) << "Action[" << i << "]'s Value: " << hint_value;
705 if (hint_value.empty()) {
706 LOG(ERROR) << "Failed to read "
707 << "Action[" << i << "]'s Value";
708 actions_parsed.clear();
709 return actions_parsed;
710 }
711 actions_parsed[hint_type].hint_actions.emplace_back(action_type, hint_value,
712 enable_property);
713 }
714
715 ++total_parsed;
716 }
717
718 LOG(INFO) << total_parsed << " actions parsed successfully";
719
720 for (const auto& action : actions_parsed) {
721 LOG(INFO) << "PowerHint " << action.first << " has " << action.second.node_actions.size()
722 << " node actions"
723 << ", and " << action.second.hint_actions.size() << " hint actions parsed";
724 }
725
726 return actions_parsed;
727 }
728
729 #define ADPF_PARSE(VARIABLE, ENTRY, TYPE) \
730 static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()), decltype(VARIABLE)>::value, \
731 "Parser type mismatch"); \
732 if (adpfs[i][ENTRY].empty() || !adpfs[i][ENTRY].is##TYPE()) { \
733 LOG(ERROR) << "Failed to read AdpfConfig[" << name << "][" ENTRY "]'s Values"; \
734 adpfs_parsed.clear(); \
735 return adpfs_parsed; \
736 } \
737 VARIABLE = adpfs[i][ENTRY].as##TYPE()
738
739 #define ADPF_PARSE_OPTIONAL(VARIABLE, ENTRY, TYPE) \
740 static_assert(std::is_same<decltype(adpfs[i][ENTRY].as##TYPE()), \
741 decltype(VARIABLE)::value_type>::value, \
742 "Parser type mismatch"); \
743 if (!adpfs[i][ENTRY].empty() && adpfs[i][ENTRY].is##TYPE()) { \
744 VARIABLE = adpfs[i][ENTRY].as##TYPE(); \
745 }
746
ParseAdpfConfigs(const std::string & json_doc)747 std::vector<std::shared_ptr<AdpfConfig>> HintManager::ParseAdpfConfigs(
748 const std::string &json_doc) {
749 // function starts
750 bool pidOn;
751 double pidPOver;
752 double pidPUnder;
753 double pidI;
754 double pidDOver;
755 double pidDUnder;
756 int64_t pidIInit;
757 int64_t pidIHighLimit;
758 int64_t pidILowLimit;
759 bool adpfUclamp;
760 uint32_t uclampMinInit;
761 uint32_t uclampMinHighLimit;
762 uint32_t uclampMinLowLimit;
763 uint64_t samplingWindowP;
764 uint64_t samplingWindowI;
765 uint64_t samplingWindowD;
766 double staleTimeFactor;
767 uint64_t reportingRate;
768 double targetTimeFactor;
769
770 std::vector<std::shared_ptr<AdpfConfig>> adpfs_parsed;
771 std::set<std::string> name_parsed;
772 Json::Value root;
773 Json::CharReaderBuilder builder;
774 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
775 std::string errorMessage;
776 if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
777 LOG(ERROR) << "Failed to parse JSON config: " << errorMessage;
778 return adpfs_parsed;
779 }
780 Json::Value adpfs = root["AdpfConfig"];
781 for (Json::Value::ArrayIndex i = 0; i < adpfs.size(); ++i) {
782 std::optional<bool> gpuBoost;
783 std::optional<uint64_t> gpuBoostCapacityMax;
784 uint64_t gpuCapacityLoadUpHeadroom = 0;
785 std::string name = adpfs[i]["Name"].asString();
786 LOG(VERBOSE) << "AdpfConfig[" << i << "]'s Name: " << name;
787 if (name.empty()) {
788 LOG(ERROR) << "Failed to read "
789 << "AdpfConfig[" << i << "]'s Name";
790 adpfs_parsed.clear();
791 return adpfs_parsed;
792 }
793 auto result = name_parsed.insert(name);
794 if (!result.second) {
795 LOG(ERROR) << "Duplicate AdpfConfig[" << i << "]'s Name";
796 adpfs_parsed.clear();
797 return adpfs_parsed;
798 }
799
800 // heuristic boost configs
801 std::optional<bool> heuristicBoostOn;
802 std::optional<uint32_t> hBoostModerateJankThreshold;
803 std::optional<double> hBoostOffMaxAvgDurRatio;
804 std::optional<double> hBoostSevereJankPidPu;
805 std::optional<uint32_t> hBoostSevereJankThreshold;
806 std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinCeilingRange;
807 std::optional<std::pair<uint32_t, uint32_t>> hBoostUclampMinFloorRange;
808 std::optional<double> jankCheckTimeFactor;
809 std::optional<uint32_t> lowFrameRateThreshold;
810 std::optional<uint32_t> maxRecordsNum;
811 std::optional<bool> heuristicRampup;
812 std::optional<uint32_t> defaultRampupMult;
813 std::optional<uint32_t> highRampupMult;
814
815 std::optional<uint32_t> uclampMinLoadUp;
816 std::optional<uint32_t> uclampMinLoadReset;
817 std::optional<int32_t> uclampMaxEfficientBase;
818 std::optional<int32_t> uclampMaxEfficientOffset;
819
820 ADPF_PARSE(pidOn, "PID_On", Bool);
821 ADPF_PARSE(pidPOver, "PID_Po", Double);
822 ADPF_PARSE(pidPUnder, "PID_Pu", Double);
823 ADPF_PARSE(pidI, "PID_I", Double);
824 ADPF_PARSE(pidIInit, "PID_I_Init", Int64);
825 ADPF_PARSE(pidIHighLimit, "PID_I_High", Int64);
826 ADPF_PARSE(pidILowLimit, "PID_I_Low", Int64);
827 ADPF_PARSE(pidDOver, "PID_Do", Double);
828 ADPF_PARSE(pidDUnder, "PID_Du", Double);
829 ADPF_PARSE(adpfUclamp, "UclampMin_On", Bool);
830 ADPF_PARSE(uclampMinInit, "UclampMin_Init", UInt);
831 ADPF_PARSE_OPTIONAL(uclampMinLoadUp, "UclampMin_LoadUp", UInt);
832 ADPF_PARSE_OPTIONAL(uclampMinLoadReset, "UclampMin_LoadReset", UInt);
833 ADPF_PARSE(uclampMinHighLimit, "UclampMin_High", UInt);
834 ADPF_PARSE(uclampMinLowLimit, "UclampMin_Low", UInt);
835 ADPF_PARSE(samplingWindowP, "SamplingWindow_P", UInt64);
836 ADPF_PARSE(samplingWindowI, "SamplingWindow_I", UInt64);
837 ADPF_PARSE(samplingWindowD, "SamplingWindow_D", UInt64);
838 ADPF_PARSE(staleTimeFactor, "StaleTimeFactor", Double);
839 ADPF_PARSE(reportingRate, "ReportingRateLimitNs", UInt64);
840 ADPF_PARSE(targetTimeFactor, "TargetTimeFactor", Double);
841 ADPF_PARSE_OPTIONAL(heuristicBoostOn, "HeuristicBoost_On", Bool);
842 ADPF_PARSE_OPTIONAL(hBoostModerateJankThreshold, "HBoostModerateJankThreshold", UInt);
843 ADPF_PARSE_OPTIONAL(hBoostOffMaxAvgDurRatio, "HBoostOffMaxAvgDurRatio", Double);
844 ADPF_PARSE_OPTIONAL(hBoostSevereJankPidPu, "HBoostSevereJankPidPu", Double);
845 ADPF_PARSE_OPTIONAL(hBoostSevereJankThreshold, "HBoostSevereJankThreshold", UInt);
846 ADPF_PARSE_OPTIONAL(jankCheckTimeFactor, "JankCheckTimeFactor", Double);
847 ADPF_PARSE_OPTIONAL(lowFrameRateThreshold, "LowFrameRateThreshold", UInt);
848 ADPF_PARSE_OPTIONAL(maxRecordsNum, "MaxRecordsNum", UInt);
849 ADPF_PARSE_OPTIONAL(heuristicRampup, "HeuristicRampup", Bool);
850 ADPF_PARSE_OPTIONAL(defaultRampupMult, "DefaultRampupMult", UInt);
851 ADPF_PARSE_OPTIONAL(highRampupMult, "HighRampupMult", UInt);
852 ADPF_PARSE_OPTIONAL(uclampMaxEfficientBase, "UclampMax_EfficientBase", Int);
853 ADPF_PARSE_OPTIONAL(uclampMaxEfficientOffset, "UclampMax_EfficientOffset", Int);
854
855 if (!adpfs[i]["GpuBoost"].empty() && adpfs[i]["GpuBoost"].isBool()) {
856 gpuBoost = adpfs[i]["GpuBoost"].asBool();
857 }
858 if (!adpfs[i]["GpuCapacityBoostMax"].empty() &&
859 adpfs[i]["GpuCapacityBoostMax"].isUInt64()) {
860 gpuBoostCapacityMax = adpfs[i]["GpuCapacityBoostMax"].asUInt64();
861 }
862 if (!adpfs[i]["GpuCapacityLoadUpHeadroom"].empty() &&
863 adpfs[i]["GpuCapacityLoadUpHeadroom"].isUInt64()) {
864 gpuCapacityLoadUpHeadroom = adpfs[i]["GpuCapacityLoadUpHeadroom"].asUInt64();
865 }
866
867 if (!adpfs[i]["HBoostUclampMinCeilingRange"].empty()) {
868 Json::Value ceilRange = adpfs[i]["HBoostUclampMinCeilingRange"];
869 if (ceilRange.size() == 2 && ceilRange[0].isUInt() && ceilRange[1].isUInt()) {
870 hBoostUclampMinCeilingRange =
871 std::make_pair(ceilRange[0].asUInt(), ceilRange[1].asUInt());
872 }
873 }
874
875 if (!adpfs[i]["HBoostUclampMinFloorRange"].empty()) {
876 Json::Value floorRange = adpfs[i]["HBoostUclampMinFloorRange"];
877 if (floorRange.size() == 2 && floorRange[0].isUInt() && floorRange[1].isUInt()) {
878 hBoostUclampMinFloorRange =
879 std::make_pair(floorRange[0].asUInt(), floorRange[1].asUInt());
880 }
881 }
882
883 // Check all the heuristic configurations are there if heuristic boost is going to
884 // be used.
885 if (heuristicBoostOn.has_value()) {
886 if (!hBoostModerateJankThreshold.has_value() || !hBoostOffMaxAvgDurRatio.has_value() ||
887 !hBoostSevereJankPidPu.has_value() || !hBoostSevereJankThreshold.has_value() ||
888 !hBoostUclampMinCeilingRange.has_value() ||
889 !hBoostUclampMinFloorRange.has_value() || !jankCheckTimeFactor.has_value() ||
890 !lowFrameRateThreshold.has_value() || !maxRecordsNum.has_value()) {
891 LOG(ERROR) << "Part of the heuristic boost configurations are missing!";
892 adpfs_parsed.clear();
893 return adpfs_parsed;
894 }
895
896 // check heuristic rampup configurations.
897 if (heuristicRampup.has_value() &&
898 (!defaultRampupMult.has_value() || !highRampupMult.has_value())) {
899 LOG(ERROR) << "Part of the heuristic rampup configurations are missing!";
900 adpfs_parsed.clear();
901 return adpfs_parsed;
902 }
903 }
904
905 if (uclampMaxEfficientBase.has_value() != uclampMaxEfficientBase.has_value()) {
906 LOG(ERROR) << "Part of the power efficiency configuration is missing!";
907 adpfs_parsed.clear();
908 return adpfs_parsed;
909 }
910
911 if (!uclampMinLoadUp.has_value()) {
912 uclampMinLoadUp = uclampMinHighLimit;
913 }
914 if (!uclampMinLoadReset.has_value()) {
915 uclampMinLoadReset = uclampMinHighLimit;
916 }
917
918 adpfs_parsed.emplace_back(std::make_shared<AdpfConfig>(
919 name, pidOn, pidPOver, pidPUnder, pidI, pidIInit, pidIHighLimit, pidILowLimit,
920 pidDOver, pidDUnder, adpfUclamp, uclampMinInit, uclampMinHighLimit,
921 uclampMinLowLimit, samplingWindowP, samplingWindowI, samplingWindowD, reportingRate,
922 targetTimeFactor, staleTimeFactor, gpuBoost, gpuBoostCapacityMax,
923 gpuCapacityLoadUpHeadroom, heuristicBoostOn, hBoostModerateJankThreshold,
924 hBoostOffMaxAvgDurRatio, hBoostSevereJankPidPu, hBoostSevereJankThreshold,
925 hBoostUclampMinCeilingRange, hBoostUclampMinFloorRange, jankCheckTimeFactor,
926 lowFrameRateThreshold, maxRecordsNum, heuristicRampup, defaultRampupMult,
927 highRampupMult, uclampMinLoadUp.value(), uclampMinLoadReset.value(),
928 uclampMaxEfficientBase, uclampMaxEfficientOffset));
929 }
930 LOG(INFO) << adpfs_parsed.size() << " AdpfConfigs parsed successfully";
931 return adpfs_parsed;
932 }
933
934 // TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
GetAdpfProfileFromDoHint() const935 std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfileFromDoHint() const {
936 if (adpfs_.empty())
937 return nullptr;
938 return adpfs_[adpf_index_];
939 }
940
941 // TODO(jimmyshiu@): Deprecated. Remove once all powerhint.json up-to-date.
SetAdpfProfileFromDoHint(const std::string & profile_name)942 bool HintManager::SetAdpfProfileFromDoHint(const std::string &profile_name) {
943 for (std::size_t i = 0; i < adpfs_.size(); ++i) {
944 if (adpfs_[i]->mName == profile_name) {
945 if (adpf_index_ != i) {
946 ATRACE_NAME(StringPrintf("%s %s:%s", __func__, adpfs_[adpf_index_]->mName.c_str(),
947 profile_name.c_str())
948 .c_str());
949 adpf_index_ = i;
950 }
951 return true;
952 }
953 }
954 return false;
955 }
956
IsAdpfSupported() const957 bool HintManager::IsAdpfSupported() const {
958 return !adpfs_.empty();
959 }
960
GetAdpfProfile(const std::string & tag) const961 std::shared_ptr<AdpfConfig> HintManager::GetAdpfProfile(const std::string &tag) const {
962 if (adpfs_.empty())
963 return nullptr;
964 if (tag_profile_map_.find(tag) == tag_profile_map_.end()) {
965 // TODO(jimmyshiu@): `return adpfs_[0]` once the GetAdpfProfileFromDoHint() retired.
966 return GetAdpfProfileFromDoHint();
967 }
968 return tag_profile_map_.at(tag);
969 }
970
SetAdpfProfile(const std::string & tag,const std::string & profile)971 bool HintManager::SetAdpfProfile(const std::string &tag, const std::string &profile) {
972 if (tag_profile_map_.find(tag) == tag_profile_map_.end()) {
973 LOG(WARNING) << "SetAdpfProfile('" << tag << "', " << profile << ") Invalidate Tag!!!";
974 return false;
975 }
976 if (tag_profile_map_[tag]->mName == profile) {
977 LOG(VERBOSE) << "SetAdpfProfile:(" << tag << ", " << profile << ") value not changed!";
978 return true;
979 }
980
981 bool updated = false;
982 for (std::size_t i = 0; i < adpfs_.size(); ++i) {
983 if (adpfs_[i]->mName == profile) {
984 LOG(DEBUG) << "SetAdpfProfile('" << tag << "', '" << profile << "') Done!";
985 tag_profile_map_[tag] = adpfs_[i];
986 updated = true;
987 break;
988 }
989 }
990 if (!updated) {
991 LOG(WARNING) << "SetAdpfProfile(" << tag << ") failed to find profile:'" << profile << "'";
992 }
993 return updated;
994 }
995
IsAdpfProfileSupported(const std::string & profile_name) const996 bool HintManager::IsAdpfProfileSupported(const std::string &profile_name) const {
997 for (std::size_t i = 0; i < adpfs_.size(); ++i) {
998 if (adpfs_[i]->mName == profile_name) {
999 return true;
1000 }
1001 }
1002 return false;
1003 }
1004
OnNodeUpdate(const std::string & name,const std::string & path,const std::string & value)1005 void HintManager::OnNodeUpdate(const std::string &name,
1006 __attribute__((unused)) const std::string &path,
1007 const std::string &value) {
1008 // Check if the node is to update ADPF.
1009 if (path.starts_with(kAdpfEventNodePath)) {
1010 std::string tag = path.substr(strlen(kAdpfEventNodePath));
1011 bool updated = SetAdpfProfile(tag, value);
1012 if (!updated) {
1013 LOG(DEBUG) << "OnNodeUpdate:[" << name << "] failed to update '" << value << "'";
1014 return;
1015 }
1016 auto &callback_list = tag_update_callback_list_[tag];
1017 for (const auto &callback : callback_list) {
1018 (*callback)(tag_profile_map_[tag]);
1019 }
1020 }
1021 }
1022
RegisterAdpfUpdateEvent(const std::string & tag,AdpfCallback * update_adpf_func)1023 void HintManager::RegisterAdpfUpdateEvent(const std::string &tag, AdpfCallback *update_adpf_func) {
1024 tag_update_callback_list_[tag].push_back(update_adpf_func);
1025 }
1026
UnregisterAdpfUpdateEvent(const std::string & tag,AdpfCallback * update_adpf_func)1027 void HintManager::UnregisterAdpfUpdateEvent(const std::string &tag,
1028 AdpfCallback *update_adpf_func) {
1029 auto &callback_list = tag_update_callback_list_[tag];
1030 // Use std::find to locate the function object
1031 auto it = std::find_if(
1032 callback_list.begin(), callback_list.end(),
1033 [update_adpf_func](const std::function<void(const std::shared_ptr<AdpfConfig>)> *func) {
1034 return func == update_adpf_func;
1035 });
1036 if (it != callback_list.end()) {
1037 // Erase the found function object
1038 callback_list.erase(it);
1039 }
1040 }
1041
gpu_sysfs_config_path() const1042 std::optional<std::string> HintManager::gpu_sysfs_config_path() const {
1043 return gpu_sysfs_config_path_;
1044 }
1045
1046 } // namespace perfmgr
1047 } // namespace android
1048