• 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
IsSorted() const33 bool VirtualThread::IsSorted() const
34 {
35     for (std::size_t index = 1; index < memMaps_.size(); ++index) {
36         if (memMaps_[index - 1].end_ > memMaps_[index].begin_) {
37             std::cout << "memMaps_ order error:\n"
38                       << "    " << memMaps_[index - 1].begin_ << "-" << memMaps_[index - 1].end_
39                       << "    " << memMaps_[index].begin_ << "-" << memMaps_[index].end_;
40             return false;
41         }
42     }
43     return true;
44 }
45 #endif
46 
FindMapByAddr2(uint64_t addr) const47 const MemMapItem *VirtualThread::FindMapByAddr2(uint64_t addr) const
48 {
49     HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_.size());
50     if (memMaps_.size() == 0) {
51         return nullptr;
52     }
53     auto foundIt =
54         std::upper_bound(memMaps_.begin(), memMaps_.end(), addr, MemMapItem::ValueLessThan);
55     if (foundIt == memMaps_.begin()) {
56         // have map 2 3 4 5
57         // find 1 , will return 2 (index 0, begin elem)
58         // this same as not found any thins
59     } else {
60         // include memMaps_.end()
61         foundIt = std::prev(foundIt);
62         if (foundIt->Contain(addr)) {
63             HLOGM("found '%s' for vaddr 0x%016" PRIx64 "", foundIt->ToString().c_str(), addr);
64             return &(*foundIt);
65         }
66     }
67     return nullptr;
68 }
69 
FindMapByAddr(uint64_t addr) const70 const MemMapItem *VirtualThread::FindMapByAddr(uint64_t addr) const
71 {
72     HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_.size());
73     if (memMaps_.size() == 0) {
74         return nullptr;
75     }
76     if (memMaps_.front().begin_ > addr) {
77         return nullptr;
78     }
79     if (memMaps_.back().end_ <= addr) {
80         return nullptr;
81     }
82     constexpr int two {2};
83     std::size_t left {0};
84     std::size_t right {memMaps_.size()};
85     std::size_t mid = (right - left) / two + left;
86     while (left < right) {
87         if (addr < memMaps_[mid].end_) {
88             right = mid;
89             mid = (right - left) / two + left;
90             continue;
91         }
92         if (addr >= memMaps_[mid].end_) {
93             left = mid + 1;
94             mid = (right - left) / two + left;
95             continue;
96         }
97     }
98     if (addr >= memMaps_[left].begin_ and addr < memMaps_[left].end_) {
99         return &memMaps_[left];
100     }
101     return nullptr;
102 }
103 
FindMapByFileInfo(const std::string name,uint64_t offset) const104 const MemMapItem *VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
105 {
106     for (auto &map : memMaps_) {
107         if (name != map.name_) {
108             continue;
109         }
110         // check begin and length
111         if (offset >= map.pageoffset_ && (offset - map.pageoffset_) < (map.end_ - map.begin_)) {
112             HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
113                     " pageoffset 0x%" PRIx64 ")  from %s",
114                     offset, map.begin_, map.end_, map.pageoffset_, map.name_.c_str());
115             return &map;
116         }
117     }
118     HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size());
119     return nullptr;
120 }
121 
FindSymbolsFileByMap(const MemMapItem & inMap) const122 SymbolsFile *VirtualThread::FindSymbolsFileByMap(const MemMapItem &inMap) const
123 {
124     for (auto &symbolsFile : symbolsFiles_) {
125         if (symbolsFile->filePath_ == inMap.name_) {
126             HLOGM("found symbol for map '%s'", inMap.name_.c_str());
127             symbolsFile->LoadDebugInfo();
128             return symbolsFile.get();
129         }
130     }
131 #ifdef DEBUG_MISS_SYMBOL
132     if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
133         missedSymbolFile_.end()) {
134         missedSymbolFile_.emplace_back(inMap.name_);
135         HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
136         for (auto &file : symbolsFiles_) {
137             HLOGW(" we have '%s'", file->filePath_.c_str());
138         }
139     }
140 #endif
141     return nullptr;
142 }
ReportVaddrMapMiss(uint64_t vaddr) const143 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
144 {
145 #ifdef HIPERF_DEBUG
146     if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
147         if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
148             missedRuntimeVaddr_.insert(vaddr);
149             HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
150             for (auto &map : memMaps_) {
151                 HLOGV("map %s ", map.ToString().c_str());
152             }
153         }
154     }
155 #endif
156 }
157 
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const158 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
159 {
160     const MemMapItem *map = FindMapByAddr(vaddr);
161     if (map != nullptr) {
162         // found symbols by file name
163         SymbolsFile *symbolsFile = FindSymbolsFileByMap(*map);
164         if (symbolsFile != nullptr) {
165             HLOGM("read vaddr from addr is 0x%" PRIx64 " at '%s'", vaddr - map->begin_,
166                   map->name_.c_str());
167             if (size == symbolsFile->ReadRoMemory(map->FileOffsetFromAddr(vaddr), data, size)) {
168                 return true;
169             } else {
170                 return false;
171             }
172         } else {
173             HLOGW("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr,
174                   map->name_.c_str());
175         }
176     } else {
177 #ifdef HIPERF_DEBUG
178         ReportVaddrMapMiss(vaddr);
179 #endif
180     }
181     return false;
182 }
183 
IsLegalFileName(const std::string & fileName)184 bool VirtualThread::IsLegalFileName(const std::string &fileName)
185 {
186     // some special
187     if (fileName == "[vdso]") {
188         return true;
189     }
190     if (fileName.empty() or fileName.find(':') != std::string::npos or fileName.front() == '[' or
191         fileName.back() == ']' or std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
192         std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
193         std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0 or
194         StringEndsWith(fileName, ".ttf")) {
195         return false;
196     }
197     return true;
198 }
199 
200 #if is_mingw
ParseMap()201 void VirtualThread::ParseMap()
202 {
203     // only linux support read maps in runtime
204     return;
205 }
206 #else
207 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
208 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
209 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
210 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
211 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
212 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
213 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
214 
ParseMap()215 void VirtualThread::ParseMap()
216 {
217     std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
218     std::string mapContent = ReadFileToString(mapPath);
219     if (mapContent.size() > 0) {
220         std::istringstream s(mapContent);
221         std::string line;
222         while (std::getline(s, line)) {
223             HLOGM("map line: %s", line.c_str());
224             // b0023000-b0024000 r--p 00000000 b3:05 959        /system/lib/libdl.so
225             // 0                 1    2        3     4          5
226             MemMapItem memMapItem;
227             std::vector<std::string> mapTokens = StringSplit(line, " ");
228 
229             if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
230                 // maybe file name is empty
231                 continue;
232             }
233 
234             // b0023000-b0024000
235             constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
236             std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
237             if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
238                 continue;
239             }
240 
241             // b0023000 / b0024000
242             try {
243                 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
244                 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
245             } catch (...) {
246                 // next line
247                 continue;
248             }
249 
250             constexpr const int MMAP_PROT_CHARS = 4;
251             int index = 0;
252             // rwxp
253             memMapItem.type_ = 0;
254             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
255                 continue;
256             }
257             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
258                 memMapItem.type_ |= PROT_READ;
259             }
260             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
261                 memMapItem.type_ |= PROT_WRITE;
262             }
263             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
264                 memMapItem.type_ |= PROT_EXEC;
265             }
266 
267             if ((memMapItem.type_ & PROT_EXEC) or (memMapItem.type_ == PROT_READ)) {
268                 /*
269                 we need record the read hen exec map address
270                 callstackk need r map to check the ehframe addrssss
271                 Section Headers:
272                 [Nr] Name              Type             Address           Offset
273                     Size              EntSize          Flags  Link  Info  Align
274 
275                 [12] .eh_frame_hdr     PROGBITS         00000000002929a0  000929a0
276                     00000000000071fc  0000000000000000   A       0     0     4
277                 [13] .eh_frame         PROGBITS         0000000000299ba0  00099ba0
278                     000000000002a8f4  0000000000000000   A       0     0     8
279                 [14] .text             PROGBITS         00000000002c5000  000c5000
280                     00000000001caa4a  0000000000000000  AX       0     0     16
281 
282                 00200000-002c5000 r--p 00000000 08:02 46400311
283                 002c5000-00490000 r-xp 000c5000 08:02 46400311
284                 */
285             } else {
286                 continue;
287             }
288 
289             // MAP_PRIVATE or MAP_SHARED
290             constexpr const int MAP_FLAG_ATTR_INDEX = 3;
291             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
292                 memMapItem.flags = MAP_PRIVATE;
293             } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
294                 memMapItem.flags = MAP_SHARED;
295             }
296 
297             try {
298                 // 00000000
299                 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
300                                                      nullptr, NUMBER_FORMAT_HEX_BASE);
301 
302                 // major:minor
303                 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
304 
305                 // b3:05
306                 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
307                 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
308 
309                 // 959
310                 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
311                                                NUMBER_FORMAT_HEX_BASE);
312             } catch (...) {
313                 // next line
314                 continue;
315             }
316 
317             // system/lib/libdl.so
318             if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
319                 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
320             }
321             if (!IsLegalFileName(memMapItem.name_)) {
322                 continue;
323             }
324             HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
325             memMaps_.emplace_back(std::move(memMapItem));
326         }
327     }
328     SortMemMaps();
329 }
330 #endif
331 
SortMemMaps()332 void VirtualThread::SortMemMaps()
333 {
334     for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
335         int targetPos = currPos - 1;
336         while (targetPos >= 0 and memMaps_[currPos].end_ < memMaps_[targetPos].end_) {
337             --targetPos;
338         }
339         if (targetPos < currPos - 1) {
340             auto target = memMaps_[currPos];
341             for (int k = currPos - 1; k > targetPos; --k) {
342                 memMaps_[k + 1] = memMaps_[k];
343             }
344             memMaps_[targetPos + 1] = target;
345         }
346     }
347     return;
348 }
349 
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)350 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
351                                   uint64_t offset)
352 {
353     if (!IsLegalFileName(filename)) {
354         return; // skip some memmap
355     }
356     MemMapItem &map = memMaps_.emplace_back(begin, begin + len, offset, filename);
357     HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
358           pid_, tid_, memMaps_.size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
359     SortMemMaps();
360 }
361 } // namespace HiPerf
362 } // namespace Developtools
363 } // namespace OHOS