1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "pgo.h"
17 #include "os/filesystem.h"
18 #include "utils/logger.h"
19 
20 namespace panda::panda_file::pgo {
21 
22 /* static */
GetNameInfo(const std::unique_ptr<BaseItem> &item)23 std::string ProfileOptimizer::GetNameInfo(const std::unique_ptr<BaseItem> &item)
24 {
25     std::string identity;
26     if (item->GetName() == CLASS_ITEM) {
27         identity = static_cast<ClassItem *>(item.get())->GetNameItem()->GetData();
28         ASSERT(identity.find('L') == 0);                        // the first character must be 'L'
29         // must end with ";\0",2 indicates the end mark of the count
30         ASSERT(identity.find(";\0") == identity.length() - 2);
31         // remove 'L' and ";\0",3 indicates the number of characters that need to be removed
32         identity = identity.substr(1, identity.length() - 3);
33         std::replace(identity.begin(), identity.end(), '/', '.');
34     } else if (item->GetName() == STRING_ITEM) {
35         identity = static_cast<StringItem *>(item.get())->GetData();
36         ASSERT(identity.find('\0') == identity.length() - 1);  // must end with '\0'
37         identity.pop_back();                                   // remove '\0'
38     } else {
39         UNREACHABLE();
40     }
41     return identity;
42 }
43 
MarkProfileItem(std::unique_ptr<BaseItem> &item, bool set_pgo) const44 void ProfileOptimizer::MarkProfileItem(std::unique_ptr<BaseItem> &item, bool set_pgo) const
45 {
46     auto inc = static_cast<uint32_t>(set_pgo);
47     if (item->GetName() == CLASS_ITEM) {
48         item->SetPGORank(PGO_CLASS_DEFAULT_COUNT + inc);
49     } else if (item->GetName() == STRING_ITEM) {
50         item->SetPGORank(PGO_STRING_DEFAULT_COUNT + inc);
51     } else if (item->GetName() == CODE_ITEM) {
52         item->SetPGORank(PGO_CODE_DEFAULT_COUNT + inc);
53     } else {
54         UNREACHABLE();
55     }
56 }
57 
ParseProfileData()58 bool ProfileOptimizer::ParseProfileData()
59 {
60     std::string path = os::GetAbsolutePath(profile_file_path_);
61     if (path == "") {
62         LOG(ERROR, PANDAFILE) << "failed to resolve profile file path: " << profile_file_path_;
63         return false;
64     }
65 
66     std::ifstream file;
67     file.open(path, std::ios::in);
68     if (!file.is_open()) {
69         LOG(ERROR, PANDAFILE) << "failed to open pgo files: " << profile_file_path_;
70         return false;
71     }
72     std::string str_line;
73     while (std::getline(file, str_line)) {
74         if (str_line.empty()) {
75             continue;
76         }
77         auto comma_pos = str_line.find(':');
78         if (comma_pos == std::string::npos) {
79             continue;
80         }
81         auto item_type = str_line.substr(0, comma_pos);
82         auto str = str_line.substr(comma_pos + 1);
83         profile_data_.emplace_back(item_type, str);
84     }
85 
86     return true;
87 }
88 
cmp(const std::unique_ptr<BaseItem> &item1, const std::unique_ptr<BaseItem> &item2)89 static bool cmp(const std::unique_ptr<BaseItem> &item1, const std::unique_ptr<BaseItem> &item2)
90 {
91     if (item1->GetPGORank() == item2->GetPGORank()) {
92         return item1->GetOriginalRank() < item2->GetOriginalRank();
93     }
94     if ((item1->GetName() != CODE_ITEM && item2->GetName() != CODE_ITEM) ||
95         (item1->GetName() == CODE_ITEM && item2->GetName() == CODE_ITEM)) {
96         return item1->GetPGORank() > item2->GetPGORank();
97     }
98     // code items will depends on the layout of string and literal item, so put it on the end
99     return item1->GetName() != CODE_ITEM;
100 }
101 
ProfileGuidedRelayout(std::list<std::unique_ptr<BaseItem>> &items)102 void ProfileOptimizer::ProfileGuidedRelayout(std::list<std::unique_ptr<BaseItem>> &items)
103 {
104     ParseProfileData();
105     uint32_t original_rank = 0;
106     for (auto &item : items) {
107         item->SetOriginalRank(original_rank++);
108         if (!item->NeedsEmit()) {
109             continue;
110         }
111         auto type_name = item->GetName();
112         if (type_name != CLASS_ITEM && type_name != STRING_ITEM && type_name != CODE_ITEM) {
113             continue;
114         }
115 
116         MarkProfileItem(item, false);
117 
118         auto finder = [&item](const std::pair<std::string, std::string> &p) {
119             if (p.first != item->GetName()) {
120                 return false;
121             }
122             if (item->GetName() != CODE_ITEM) {
123                 return p.second == GetNameInfo(item);
124             }
125             // CodeItem can be shared between multiple methods, so we need to check all these methods
126             auto method_names = static_cast<CodeItem *>(item.get())->GetMethodNames();
127             return std::find(method_names.begin(), method_names.end(), p.second) != method_names.end();
128         };
129         if (std::find_if(profile_data_.begin(), profile_data_.end(), finder) != profile_data_.end()) {
130             MarkProfileItem(item, true);
131         }
132     }
133 
134     items.sort(cmp);
135 }
136 
137 }  // namespace panda::panda_file::pgo
138