1 /**
2  * Copyright (c) 2021-2024 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 ark::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() - 2U);  // must end with ";\0"
29         identity = identity.substr(1, identity.length() - 3U);   // 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 setPgo) const41 void ProfileOptimizer::MarkProfileItem(std::unique_ptr<BaseItem> &item, bool setPgo) const
42 {
43     auto inc = static_cast<uint32_t>(setPgo);
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(profileFilePath_, std::ios::in);
59     if (!file.is_open()) {
60         LOG(ERROR, PANDAFILE) << "failed to open pgo files: " << profileFilePath_;
61         return false;
62     }
63     std::string strLine;
64     while (std::getline(file, strLine)) {
65         if (strLine.empty()) {
66             continue;
67         }
68         auto commaPos = strLine.find(':');
69         if (commaPos == std::string::npos) {
70             continue;
71         }
72         auto itemType = strLine.substr(0, commaPos);
73         auto str = strLine.substr(commaPos + 1);
74         profileData_.emplace_back(itemType, 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 originalRank = 0;
97     for (auto &item : items) {
98         item->SetOriginalRank(originalRank++);
99         if (!item->NeedsEmit()) {
100             continue;
101         }
102         auto typeName = item->GetName();
103         if (typeName != CLASS_ITEM && typeName != STRING_ITEM && typeName != 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 methodNames = static_cast<CodeItem *>(item.get())->GetMethodNames();
118             return std::find(methodNames.begin(), methodNames.end(), p.second) != methodNames.end();
119         };
120         if (std::find_if(profileData_.begin(), profileData_.end(), finder) != profileData_.end()) {
121             MarkProfileItem(item, true);
122         }
123     }
124 
125     items.sort(Cmp);
126 }
127 
128 }  // namespace ark::panda_file::pgo
129