• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define HILOG_TAG "RuntimeThread"
16 
17 #include "virtual_thread.h"
18 
19 #include <cinttypes>
20 #include <iostream>
21 #include <sstream>
22 #if !is_mingw
23 #include <sys/mman.h>
24 #endif
25 
26 #include "symbols_file.h"
27 #include "utilities.h"
28 #include "virtual_runtime.h"
29 namespace OHOS {
30 namespace Developtools {
31 namespace HiPerf {
32 #ifdef DEBUG_TIME
33 
IsSorted() const34 bool VirtualThread::IsSorted() const
35 {
36     for (std::size_t index = 1; index < memMaps_.size(); ++index) {
37         if (memMaps_[memMapsIndexs_[index - 1]].end_ > memMaps_[memMapsIndexs_[index]].begin_) {
38             std::cout << "memMaps_ order error:\n"
39                       << "    " << memMaps_[memMapsIndexs_[index - 1]].begin_ << "-"
40                       << memMaps_[memMapsIndexs_[index - 1]].end_
41                       << "    " << memMaps_[memMapsIndexs_[index]].begin_ << "-"
42                       << memMaps_[memMapsIndexs_[index]].end_;
43             return false;
44         }
45     }
46     return true;
47 }
48 #endif
49 
FindMapByAddr2(uint64_t addr) const50 const MemMapItem *VirtualThread::FindMapByAddr2(uint64_t addr) const
51 {
52     HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_.size());
53     if (memMaps_.size() == 0) {
54         return nullptr;
55     }
56     auto foundIt =
57         std::upper_bound(memMaps_.begin(), memMaps_.end(), addr, MemMapItem::ValueLessThan);
58     if (foundIt == memMaps_.begin()) {
59         // have map 2 3 4 5
60         // find 1 , will return 2 (index 0, begin elem)
61         // this same as not found any thins
62     } else {
63         // include memMaps_.end()
64         foundIt = std::prev(foundIt);
65         if (foundIt->Contain(addr)) {
66             HLOGM("found '%s' for vaddr 0x%016" PRIx64 "", foundIt->ToString().c_str(), addr);
67             return &(*foundIt);
68         }
69     }
70     return nullptr;
71 }
72 
FindMapIndexByAddr(uint64_t addr) const73 int64_t VirtualThread::FindMapIndexByAddr(uint64_t addr) const
74 {
75     HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
76     const int64_t illegal = -1;
77     if (memMaps_.size() == 0) {
78         return illegal;
79     }
80     if (memMaps_[memMapsIndexs_[0]].begin_ > addr) {
81         return illegal;
82     }
83     if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() -  1]].end_ <= addr) {
84         return illegal;
85     }
86     constexpr int divisorNum {2};
87     std::size_t left {0};
88     std::size_t right {memMapsIndexs_.size()};
89     std::size_t mid = (right - left) / divisorNum + left;
90     while (left < right) {
91         if (addr < memMaps_[memMapsIndexs_[mid]].end_) {
92             right = mid;
93             mid = (right - left) / divisorNum + left;
94             continue;
95         }
96         if (addr >= memMaps_[memMapsIndexs_[mid]].end_) {
97             left = mid + 1;
98             mid = (right - left) / divisorNum + left;
99             continue;
100         }
101     }
102     if (addr >= memMaps_[memMapsIndexs_[left]].begin_ && addr < memMaps_[memMapsIndexs_[left]].end_) {
103         return static_cast<int64_t>(memMapsIndexs_[left]);
104     }
105     return illegal;
106 }
107 
FindMapByAddr(uint64_t addr) const108 const MemMapItem *VirtualThread::FindMapByAddr(uint64_t addr) const
109 {
110     HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
111         if (memMaps_.size() == 0) {
112         return nullptr;
113     }
114     if (memMaps_[memMapsIndexs_[0]].begin_ > addr) {
115         return nullptr;
116     }
117     if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() -  1]].end_ <= addr) {
118         return nullptr;
119     }
120     constexpr int divisorNum {2};
121     std::size_t left {0};
122     std::size_t right {memMapsIndexs_.size()};
123     std::size_t mid = (right - left) / divisorNum + left;
124     while (left < right) {
125         if (addr < memMaps_[memMapsIndexs_[mid]].end_) {
126             right = mid;
127             mid = (right - left) / divisorNum + left;
128             continue;
129         }
130         if (addr >= memMaps_[memMapsIndexs_[mid]].end_) {
131             left = mid + 1;
132             mid = (right - left) / divisorNum + left;
133             continue;
134         }
135     }
136     if (addr >= memMaps_[memMapsIndexs_[left]].begin_ && addr < memMaps_[memMapsIndexs_[left]].end_) {
137         return &memMaps_[memMapsIndexs_[left]];
138     }
139     return nullptr;
140 }
141 
FindMapByFileInfo(const std::string name,uint64_t offset) const142 const MemMapItem *VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
143 {
144     for (auto &map : memMaps_) {
145         if (name != map.name_) {
146             continue;
147         }
148         // check begin and length
149         if (offset >= map.pageoffset_ && (offset - map.pageoffset_) < (map.end_ - map.begin_)) {
150             HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
151                     " pageoffset 0x%" PRIx64 ")  from %s",
152                     offset, map.begin_, map.end_, map.pageoffset_, map.name_.c_str());
153             return &map;
154         }
155     }
156     HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size());
157     return nullptr;
158 }
159 
FindSymbolsFileByMap(const MemMapItem & inMap) const160 SymbolsFile *VirtualThread::FindSymbolsFileByMap(const MemMapItem &inMap) const
161 {
162     for (auto &symbolsFile : symbolsFiles_) {
163         if (symbolsFile->filePath_ == inMap.name_) {
164             HLOGM("found symbol for map '%s'", inMap.name_.c_str());
165             if (symbolsFile->LoadDebugInfo()) {
166                 HLOGM("found symbol for map '%s'", inMap.name_.c_str());
167                 return symbolsFile.get();
168             }
169             break;
170         }
171     }
172 
173 #ifdef DEBUG_MISS_SYMBOL
174     if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
175         missedSymbolFile_.end()) {
176         missedSymbolFile_.emplace_back(inMap.name_);
177         HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
178         for (const auto &file : symbolsFiles_) {
179             HLOGW(" we have '%s'", file->filePath_.c_str());
180         }
181     }
182 #endif
183     return nullptr;
184 }
ReportVaddrMapMiss(uint64_t vaddr) const185 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
186 {
187 #ifdef HIPERF_DEBUG
188     if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
189         if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
190             missedRuntimeVaddr_.insert(vaddr);
191             HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
192             for (auto &map : memMaps_) {
193                 HLOGV("map %s ", map.ToString().c_str());
194             }
195         }
196     }
197 #endif
198 }
199 
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const200 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
201 {
202     uint64_t pageIndex = vaddr >> 12;
203     uint64_t memMapIndex = -1;
204     const int64_t exceptRet = -1;
205     const uint64_t illegal = -1;
206     auto pageFile = vaddr4kPageCache_.find(pageIndex);
207     if (pageFile != vaddr4kPageCache_.end()) {
208         memMapIndex = pageFile->second;
209     } else {
210         int64_t retIndex = FindMapIndexByAddr(vaddr);
211         memMapIndex = static_cast<uint64_t>(retIndex);
212         // add to 4k page cache table
213         if (retIndex != exceptRet && memMapIndex < memMaps_.size()) {
214             const_cast<VirtualThread *>(this)->vaddr4kPageCache_[pageIndex] = memMapIndex;
215         }
216     }
217     if (memMapIndex != illegal) {
218         MemMapItem &map = memMaps_[memMapIndex];
219         if (map.symfile == nullptr) {
220             // find symbols by file name
221             map.symfile = FindSymbolsFileByMap(map);
222         }
223         if (map.symfile != nullptr) {
224             uint64_t foff = map.FileOffsetFromAddr(vaddr);
225             SymbolsFile *symFile = map.symfile;
226             if (size == symFile->ReadRoMemory(foff, data, size)) {
227                 return true;
228             } else {
229                 return false;
230             }
231         } else {
232             HLOGW("find addr %" PRIx64 "in map but not loaded symbole %s", vaddr,
233                   map.name_.c_str());
234         }
235     } else {
236 #ifdef HIPERF_DEBUG
237         ReportVaddrMapMiss(vaddr);
238 #endif
239     }
240     return false;
241 }
242 
IsLegalFileName(const std::string & fileName)243 bool VirtualThread::IsLegalFileName(const std::string &fileName)
244 {
245     // some special
246     if (fileName == "[vdso]") {
247         return true;
248     }
249     if (fileName.empty() or fileName.find(':') != std::string::npos or fileName.front() == '[' or
250         fileName.back() == ']' or std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
251         std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
252         std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0 or
253         StringEndsWith(fileName, ".ttf")) {
254         return false;
255     }
256     return true;
257 }
258 
259 #if is_mingw
ParseMap()260 void VirtualThread::ParseMap()
261 {
262     // only linux support read maps in runtime
263     return;
264 }
265 #else
266 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
267 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
268 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
269 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
270 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
271 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
272 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
273 
ParseMap()274 void VirtualThread::ParseMap()
275 {
276     std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
277     std::string mapContent = ReadFileToString(mapPath);
278     if (mapContent.size() > 0) {
279         std::istringstream s(mapContent);
280         std::string line;
281         while (std::getline(s, line)) {
282             HLOGM("map line: %s", line.c_str());
283             // b0023000-b0024000 r--p 00000000 b3:05 959        /system/lib/libdl.so
284             // 0                 1    2        3     4          5
285             MemMapItem memMapItem;
286             std::vector<std::string> mapTokens = StringSplit(line, " ");
287 
288             if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
289                 // maybe file name is empty
290                 continue;
291             }
292 
293             // b0023000-b0024000
294             constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
295             std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
296             if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
297                 continue;
298             }
299 
300             // b0023000 / b0024000
301             try {
302                 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
303                 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
304             } catch (...) {
305                 // next line
306                 continue;
307             }
308 
309             constexpr const int MMAP_PROT_CHARS = 4;
310             int index = 0;
311             // rwxp
312             memMapItem.type_ = 0;
313             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
314                 continue;
315             }
316             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
317                 memMapItem.type_ |= PROT_READ;
318             }
319             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
320                 memMapItem.type_ |= PROT_WRITE;
321             }
322             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
323                 memMapItem.type_ |= PROT_EXEC;
324             }
325 
326             if ((memMapItem.type_ & PROT_EXEC) or (memMapItem.type_ | PROT_READ)) {
327                 /*
328                 we need record the read hen exec map address
329                 callstackk need r map to check the ehframe addrssss
330                 Section Headers:
331                 [Nr] Name              Type             Address           Offset
332                     Size              EntSize          Flags  Link  Info  Align
333 
334                 [12] .eh_frame_hdr     PROGBITS         00000000002929a0  000929a0
335                     00000000000071fc  0000000000000000   A       0     0     4
336                 [13] .eh_frame         PROGBITS         0000000000299ba0  00099ba0
337                     000000000002a8f4  0000000000000000   A       0     0     8
338                 [14] .text             PROGBITS         00000000002c5000  000c5000
339                     00000000001caa4a  0000000000000000  AX       0     0     16
340 
341                 00200000-002c5000 r--p 00000000 08:02 46400311
342                 002c5000-00490000 r-xp 000c5000 08:02 46400311
343                 */
344             } else {
345                 continue;
346             }
347 
348             // MAP_PRIVATE or MAP_SHARED
349             constexpr const int MAP_FLAG_ATTR_INDEX = 3;
350             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
351                 memMapItem.flags = MAP_PRIVATE;
352             } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
353                 memMapItem.flags = MAP_SHARED;
354             }
355 
356             try {
357                 // 00000000
358                 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
359                                                      nullptr, NUMBER_FORMAT_HEX_BASE);
360 
361                 // major:minor
362                 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
363 
364                 // b3:05
365                 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
366                 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
367 
368                 // 959
369                 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
370                                                NUMBER_FORMAT_HEX_BASE);
371             } catch (...) {
372                 // next line
373                 continue;
374             }
375             if (memMapItem.major_ == 0) {
376                 HLOGM("map line: exit %s", line.c_str());
377                 continue;
378             }
379             // system/lib/libdl.so
380             if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
381                 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
382                 if (memMapItem.name_.find("/data/storage") == 0 && access(memMapItem.name_.c_str(), F_OK) != 0) {
383                     memMapItem.name_ = "/proc/" + std::to_string(pid_) + "/root" + memMapItem.name_;
384                 }
385             }
386             if (!IsLegalFileName(memMapItem.name_)) {
387                 continue;
388             }
389             HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
390             memMaps_.emplace_back(std::move(memMapItem));
391             memMapsIndexs_.emplace_back(memMaps_.size() - 1);
392         }
393     }
394     SortMemMaps();
395 }
396 #endif
397 
SortMemMaps()398 void VirtualThread::SortMemMaps()
399 {
400     for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
401         int targetPos = currPos - 1;
402         while (targetPos >= 0 and memMaps_[memMapsIndexs_[currPos]].end_ < memMaps_[memMapsIndexs_[targetPos]].end_) {
403             --targetPos;
404         }
405         if (targetPos < currPos - 1) {
406             auto target = memMapsIndexs_[currPos];
407             for (int k = currPos - 1; k > targetPos; --k) {
408                 memMapsIndexs_[k + 1] = memMapsIndexs_[k];
409             }
410             memMapsIndexs_[targetPos + 1] = target;
411         }
412     }
413     return;
414 }
415 
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)416 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
417                                   uint64_t offset)
418 {
419     if (!IsLegalFileName(filename)) {
420         return; // skip some memmap
421     }
422     MemMapItem &map = memMaps_.emplace_back(begin, begin + len, offset, filename);
423     memMapsIndexs_.emplace_back(memMaps_.size() - 1);
424     HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
425           pid_, tid_, memMaps_.size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
426     SortMemMaps();
427 }
428 } // namespace HiPerf
429 } // namespace Developtools
430 } // namespace OHOS
431