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 "dfx_maps.h"
17 
18 #include <algorithm>
19 #include <cstring>
20 #include <securec.h>
21 #if is_mingw
22 #include "dfx_nonlinux_define.h"
23 #else
24 #include <sys/mman.h>
25 #endif
26 
27 #include "dfx_define.h"
28 #include "dfx_elf.h"
29 #include "dfx_log.h"
30 #include "dfx_trace_dlsym.h"
31 #include "string_printf.h"
32 #include "string_util.h"
33 
34 namespace OHOS {
35 namespace HiviewDFX {
36 namespace {
37 #undef LOG_DOMAIN
38 #undef LOG_TAG
39 #define LOG_DOMAIN 0xD002D11
40 #define LOG_TAG "DfxMaps"
41 
GetMapsFile(pid_t pid)42 inline const std::string GetMapsFile(pid_t pid)
43 {
44     std::string path = "";
45     if ((pid == 0) || (pid == getpid())) {
46         path = std::string(PROC_SELF_MAPS_PATH);
47     } else if (pid > 0) {
48         path = StringPrintf("/proc/%d/maps", (int)pid);
49     }
50     return path;
51 }
52 }
53 
Create(pid_t pid,bool crash)54 std::shared_ptr<DfxMaps> DfxMaps::Create(pid_t pid, bool crash)
55 {
56     std::string path = GetMapsFile(pid);
57     if (path == "") {
58         return nullptr;
59     }
60     auto dfxMaps = std::make_shared<DfxMaps>();
61     if (!crash) {
62         LOGU("Create maps(%s) with not crash, will only parse exec map", path.c_str());
63         dfxMaps->EnableOnlyExec(true);
64     }
65     if (dfxMaps->Parse(pid, path)) {
66         return dfxMaps;
67     }
68     return nullptr;
69 }
70 
Create(const pid_t pid,const std::string & path)71 std::shared_ptr<DfxMaps> DfxMaps::Create(const pid_t pid, const std::string& path)
72 {
73     auto dfxMaps = std::make_shared<DfxMaps>();
74     if (dfxMaps->Parse(pid, path)) {
75         return dfxMaps;
76     }
77     return nullptr;
78 }
79 
Create(const pid_t pid,std::vector<std::shared_ptr<DfxMap>> & maps,std::vector<int> & mapIndex)80 bool DfxMaps::Create(const pid_t pid, std::vector<std::shared_ptr<DfxMap>>& maps, std::vector<int>& mapIndex)
81 {
82     std::string path = GetMapsFile(pid);
83     if (path == "") {
84         return false;
85     }
86     auto dfxMaps = std::make_shared<DfxMaps>();
87     dfxMaps->EnableMapIndex(true);
88     if (!dfxMaps->Parse(pid, path)) {
89         return false;
90     }
91     maps = dfxMaps->GetMaps();
92     mapIndex = dfxMaps->GetMapIndexVec();
93     return true;
94 }
95 
Parse(const pid_t pid,const std::string & path)96 bool DfxMaps::Parse(const pid_t pid, const std::string& path)
97 {
98     DFX_TRACE_SCOPED_DLSYM("ParseMaps");
99     if ((pid < 0) || (path == "")) {
100         LOGE("param is error");
101         return false;
102     }
103 
104     FILE* fp = nullptr;
105     fp = fopen(path.c_str(), "r");
106     if (fp == nullptr) {
107         LOGE("Failed to open %s, err=%d", path.c_str(), errno);
108         return false;
109     }
110 
111     char mapBuf[PATH_LEN] = {0};
112     int fgetCount = 0;
113     while (fgets(mapBuf, sizeof(mapBuf), fp) != nullptr) {
114         fgetCount++;
115         auto map = std::make_shared<DfxMap>();
116         if (!map->Parse(mapBuf, sizeof(mapBuf))) {
117             LOGU("Failed to parse map: %s", mapBuf);
118             continue;
119         }
120 
121         FormatMapName(pid, map->name);
122         if (IsArkHapMapItem(map->name) || IsArkCodeMapItem(map->name)) {
123             AddMap(map, enableMapIndex_);
124             continue;
125         }
126         if (map->name == "[stack]") {
127             stackBottom_ = static_cast<uintptr_t>(map->begin);
128             stackTop_ = static_cast<uintptr_t>(map->end);
129         }
130         if (onlyExec_ && !map->IsMapExec()) {
131             continue;
132         }
133         if ((!enableMapIndex_) || IsLegalMapItem(map->name, false)) {
134             AddMap(map, enableMapIndex_);
135         }
136     }
137     (void)fclose(fp);
138     if (fgetCount == 0) {
139         LOGE("Failed to get maps(%s), err(%d).", path.c_str(), errno);
140         return false;
141     }
142     size_t mapsSize = GetMapsSize();
143     LOGU("parse maps(%s) completed, map size: (%zu), count: (%d)", path.c_str(), mapsSize, fgetCount);
144     return mapsSize > 0;
145 }
146 
FormatMapName(pid_t pid,std::string & mapName)147 void DfxMaps::FormatMapName(pid_t pid, std::string& mapName)
148 {
149     if (pid <= 0 || pid == getpid()) {
150         return;
151     }
152     // format sandbox file path, add '/proc/xxx/root' prefix
153     if (StartsWith(mapName, "/data/storage/")) {
154         mapName = "/proc/" + std::to_string(pid) + "/root" + mapName;
155     }
156 }
157 
UnFormatMapName(std::string & mapName)158 void DfxMaps::UnFormatMapName(std::string& mapName)
159 {
160     // unformat sandbox file path, drop '/proc/xxx/root' prefix
161     if (StartsWith(mapName, "/proc/")) {
162         auto startPos = mapName.find("/data/storage/");
163         if (startPos != std::string::npos) {
164             mapName = mapName.substr(startPos);
165         }
166     }
167 }
168 
IsArkHapMapItem(const std::string & name)169 bool DfxMaps::IsArkHapMapItem(const std::string& name)
170 {
171     if (name.empty()) {
172         return false;
173     }
174     if (EndsWith(name, ".hap") || EndsWith(name, ".hsp") || EndsWith(name, ".hqf")) {
175         return true;
176     }
177     return false;
178 }
179 
IsArkCodeMapItem(const std::string & name)180 bool DfxMaps::IsArkCodeMapItem(const std::string& name)
181 {
182     if (name.empty()) {
183         return false;
184     }
185     if (StartsWith(name, "[anon:ArkTS Code") || EndsWith(name, ".abc")) {
186         return true;
187     }
188     return false;
189 }
190 
IsLegalMapItem(const std::string & name,bool withArk)191 bool DfxMaps::IsLegalMapItem(const std::string& name, bool withArk)
192 {
193     // some special
194     if (withArk && (IsArkHapMapItem(name) || IsArkCodeMapItem(name))) {
195         return true;
196     }
197     if (EndsWith(name, "[vdso]") || EndsWith(name, "[shmm]")) {
198         return true;
199     }
200     if (name.empty() || name.find(':') != std::string::npos || name.front() == '[' ||
201         name.back() == ']' || std::strncmp(name.c_str(), "/dev/", sizeof("/dev/")) == 0 ||
202         std::strncmp(name.c_str(), "/memfd:", sizeof("/memfd:")) == 0 ||
203         std::strncmp(name.c_str(), "//anon", sizeof("//anon")) == 0 ||
204         EndsWith(name, ".ttf") || EndsWith(name, ".ai")) {
205         return false;
206     }
207     return true;
208 }
209 
AddMap(std::shared_ptr<DfxMap> map,bool enableMapIndex)210 void DfxMaps::AddMap(std::shared_ptr<DfxMap> map, bool enableMapIndex)
211 {
212     maps_.emplace_back(std::move(map));
213     if (enableMapIndex && !maps_.empty()) {
214         mapIndex_.emplace_back(maps_.size() - 1);
215     }
216 }
217 
FindMapByAddr(uintptr_t addr,std::shared_ptr<DfxMap> & map) const218 bool DfxMaps::FindMapByAddr(uintptr_t addr, std::shared_ptr<DfxMap>& map) const
219 {
220     if ((maps_.empty()) || (addr == 0x0)) {
221         return false;
222     }
223 
224     size_t first = 0;
225     size_t last = maps_.size();
226     while (first < last) {
227         size_t index = (first + last) / 2;
228         const auto& cur = maps_[index];
229         if (cur == nullptr) {
230             continue;
231         }
232         if (addr >= cur->begin && addr < cur->end) {
233             map = cur;
234             if (index > 0) {
235                 map->prevMap = maps_[index - 1];
236             }
237             return true;
238         } else if (addr < cur->begin) {
239             last = index;
240         } else {
241             first = index + 1;
242         }
243     }
244     return false;
245 }
246 
FindMapByFileInfo(std::string name,uint64_t offset,std::shared_ptr<DfxMap> & map) const247 bool DfxMaps::FindMapByFileInfo(std::string name, uint64_t offset, std::shared_ptr<DfxMap>& map) const
248 {
249     for (auto &iter : maps_) {
250         if (name != iter->name) {
251             continue;
252         }
253 
254         if (offset >= iter->offset && (offset - iter->offset) < (iter->end - iter->begin)) {
255             LOGI("Found name: %s, offset 0x%" PRIx64 " in map (%" PRIx64 "-%" PRIx64 " offset 0x%" PRIx64 ")",
256                 name.c_str(), offset, iter->begin, iter->end, iter->offset);
257             map = iter;
258             return true;
259         }
260     }
261     return false;
262 }
263 
FindMapsByName(std::string name,std::vector<std::shared_ptr<DfxMap>> & maps) const264 bool DfxMaps::FindMapsByName(std::string name, std::vector<std::shared_ptr<DfxMap>>& maps) const
265 {
266     if (maps_.empty()) {
267         return false;
268     }
269     for (auto &iter : maps_) {
270         if (EndsWith(iter->name, name)) {
271             maps.emplace_back(iter);
272         }
273     }
274     return (maps.size() > 0);
275 }
276 
Sort(bool less)277 void DfxMaps::Sort(bool less)
278 {
279     if (maps_.empty()) {
280         return;
281     }
282     if (less) {
283         std::sort(maps_.begin(), maps_.end(),
284             [](const std::shared_ptr<DfxMap>& a, const std::shared_ptr<DfxMap>& b) {
285             if (a == nullptr) {
286                 return false;
287             } else if (b == nullptr) {
288                 return true;
289             }
290             return a->begin < b->begin;
291         });
292     } else {
293         std::sort(maps_.begin(), maps_.end(),
294             [](const std::shared_ptr<DfxMap>& a, const std::shared_ptr<DfxMap>& b) {
295             if (a == nullptr) {
296                 return true;
297             } else if (b == nullptr) {
298                 return false;
299             }
300             return a->begin > b->begin;
301         });
302     }
303 }
304 
GetStackRange(uintptr_t & bottom,uintptr_t & top)305 bool DfxMaps::GetStackRange(uintptr_t& bottom, uintptr_t& top)
306 {
307     if (stackBottom_ == 0 || stackTop_ == 0) {
308         return false;
309     }
310     bottom = stackBottom_;
311     top = stackTop_;
312     return true;
313 }
314 
IsArkExecutedMap(uintptr_t addr)315 bool DfxMaps::IsArkExecutedMap(uintptr_t addr)
316 {
317     std::shared_ptr<DfxMap> map = nullptr;
318     if (!FindMapByAddr(addr, map)) {
319         LOGU("%s", "Not mapped map for current addr.");
320         return false;
321     }
322     return map->IsArkExecutable();
323 }
324 } // namespace HiviewDFX
325 } // namespace OHOS
326