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