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
18 namespace panda::panda_file::pgo {
19
20 /* static */
GetNameInfo(const std::unique_ptr<BaseItem> & item)21 std::string ProfileOptimizer::GetNameInfo(const std::unique_ptr<BaseItem> &item)
22 {
23 std::string identity;
24 if (item->GetName() == CLASS_ITEM) {
25 identity = static_cast<ClassItem *>(item.get())->GetNameItem()->GetData();
26 ASSERT(identity.find('L') == 0); // the first character must be 'L'
27 // must end with ";\0",2 indicates the end mark of the count
28 ASSERT(identity.find(";\0") == identity.length() - 2);
29 // remove 'L' and ";\0",3 indicates the number of characters that need to be removed
30 identity = identity.substr(1, identity.length() - 3);
31 std::replace(identity.begin(), identity.end(), '/', '.');
32 } else if (item->GetName() == STRING_ITEM) {
33 identity = static_cast<StringItem *>(item.get())->GetData();
34 ASSERT(identity.find('\0') == identity.length() - 1); // must end with '\0'
35 identity.pop_back(); // remove '\0'
36 } else {
37 UNREACHABLE();
38 }
39 return identity;
40 }
41
MarkProfileItem(std::unique_ptr<BaseItem> & item,bool set_pgo) const42 void ProfileOptimizer::MarkProfileItem(std::unique_ptr<BaseItem> &item, bool set_pgo) const
43 {
44 auto inc = static_cast<uint32_t>(set_pgo);
45 if (item->GetName() == CLASS_ITEM) {
46 item->SetPGORank(PGO_CLASS_DEFAULT_COUNT + inc);
47 } else if (item->GetName() == STRING_ITEM) {
48 item->SetPGORank(PGO_STRING_DEFAULT_COUNT + inc);
49 } else if (item->GetName() == CODE_ITEM) {
50 item->SetPGORank(PGO_CODE_DEFAULT_COUNT + inc);
51 } else {
52 UNREACHABLE();
53 }
54 }
55
ParseProfileData()56 bool ProfileOptimizer::ParseProfileData()
57 {
58 std::string path = os::GetAbsolutePath(profile_file_path_);
59 if (path == "") {
60 LOG(ERROR, PANDAFILE) << "failed to resolve profile file path: " << profile_file_path_;
61 return false;
62 }
63
64 std::ifstream file;
65 file.open(path, std::ios::in);
66 if (!file.is_open()) {
67 LOG(ERROR, PANDAFILE) << "failed to open pgo files: " << profile_file_path_;
68 return false;
69 }
70 std::string str_line;
71 while (std::getline(file, str_line)) {
72 if (str_line.empty()) {
73 continue;
74 }
75 auto comma_pos = str_line.find(':');
76 if (comma_pos == std::string::npos) {
77 continue;
78 }
79 auto item_type = str_line.substr(0, comma_pos);
80 auto str = str_line.substr(comma_pos + 1);
81 profile_data_.emplace_back(item_type, str);
82 }
83
84 return true;
85 }
86
cmp(const std::unique_ptr<BaseItem> & item1,const std::unique_ptr<BaseItem> & item2)87 static bool cmp(const std::unique_ptr<BaseItem> &item1, const std::unique_ptr<BaseItem> &item2)
88 {
89 if (item1->GetPGORank() == item2->GetPGORank()) {
90 return item1->GetOriginalRank() < item2->GetOriginalRank();
91 }
92 if ((item1->GetName() != CODE_ITEM && item2->GetName() != CODE_ITEM) ||
93 (item1->GetName() == CODE_ITEM && item2->GetName() == CODE_ITEM)) {
94 return item1->GetPGORank() > item2->GetPGORank();
95 }
96 // code items will depends on the layout of string and literal item, so put it on the end
97 return item1->GetName() != CODE_ITEM;
98 }
99
ProfileGuidedRelayout(std::list<std::unique_ptr<BaseItem>> & items)100 void ProfileOptimizer::ProfileGuidedRelayout(std::list<std::unique_ptr<BaseItem>> &items)
101 {
102 ParseProfileData();
103 uint32_t original_rank = 0;
104 for (auto &item : items) {
105 item->SetOriginalRank(original_rank++);
106 if (!item->NeedsEmit()) {
107 continue;
108 }
109 auto type_name = item->GetName();
110 if (type_name != CLASS_ITEM && type_name != STRING_ITEM && type_name != CODE_ITEM) {
111 continue;
112 }
113
114 MarkProfileItem(item, false);
115
116 auto finder = [&item](const std::pair<std::string, std::string> &p) {
117 if (p.first != item->GetName()) {
118 return false;
119 }
120 if (item->GetName() != CODE_ITEM) {
121 return p.second == GetNameInfo(item);
122 }
123 // CodeItem can be shared between multiple methods, so we need to check all these methods
124 auto method_names = static_cast<CodeItem *>(item.get())->GetMethodNames();
125 return std::find(method_names.begin(), method_names.end(), p.second) != method_names.end();
126 };
127 if (std::find_if(profile_data_.begin(), profile_data_.end(), finder) != profile_data_.end()) {
128 MarkProfileItem(item, true);
129 }
130 }
131
132 items.sort(cmp);
133 }
134
135 } // namespace panda::panda_file::pgo
136