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