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