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