• 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             // LoadDebugInfo() could be failed when dump or report
128             if (symbolsFile->LoadDebugInfo() || !SymbolsFile::onRecording_) {
129                 HLOGM("found symbol for map '%s'", inMap.name_.c_str());
130                 return symbolsFile.get();
131             }
132             break;
133         }
134     }
135 #ifdef DEBUG_MISS_SYMBOL
136     if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
137         missedSymbolFile_.end()) {
138         missedSymbolFile_.emplace_back(inMap.name_);
139         HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
140         for (auto &file : symbolsFiles_) {
141             HLOGW(" we have '%s'", file->filePath_.c_str());
142         }
143     }
144 #endif
145     return nullptr;
146 }
ReportVaddrMapMiss(uint64_t vaddr) const147 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
148 {
149 #ifdef HIPERF_DEBUG
150     if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
151         if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
152             missedRuntimeVaddr_.insert(vaddr);
153             HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
154             for (auto &map : memMaps_) {
155                 HLOGV("map %s ", map.ToString().c_str());
156             }
157         }
158     }
159 #endif
160 }
161 
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const162 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
163 {
164     const MemMapItem *map = FindMapByAddr(vaddr);
165     if (map != nullptr) {
166         // found symbols by file name
167         SymbolsFile *symbolsFile = FindSymbolsFileByMap(*map);
168         if (symbolsFile != nullptr) {
169             HLOGM("read vaddr from addr is 0x%" PRIx64 " at '%s'", vaddr - map->begin_,
170                   map->name_.c_str());
171             if (size == symbolsFile->ReadRoMemory(map->FileOffsetFromAddr(vaddr), data, size)) {
172                 return true;
173             } else {
174                 return false;
175             }
176         } else {
177             HLOGW("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr,
178                   map->name_.c_str());
179         }
180     } else {
181 #ifdef HIPERF_DEBUG
182         ReportVaddrMapMiss(vaddr);
183 #endif
184     }
185     return false;
186 }
187 
IsLegalFileName(const std::string & fileName)188 bool VirtualThread::IsLegalFileName(const std::string &fileName)
189 {
190     // some special
191     if (fileName == "[vdso]") {
192         return true;
193     }
194     if (fileName.empty() or fileName.find(':') != std::string::npos or fileName.front() == '[' or
195         fileName.back() == ']' or std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
196         std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
197         std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0 or
198         StringEndsWith(fileName, ".ttf")) {
199         return false;
200     }
201     return true;
202 }
203 
204 #if is_mingw
ParseMap()205 void VirtualThread::ParseMap()
206 {
207     // only linux support read maps in runtime
208     return;
209 }
210 #else
211 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
212 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
213 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
214 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
215 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
216 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
217 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
218 
ParseMap()219 void VirtualThread::ParseMap()
220 {
221     std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
222     std::string mapContent = ReadFileToString(mapPath);
223     if (mapContent.size() > 0) {
224         std::istringstream s(mapContent);
225         std::string line;
226         while (std::getline(s, line)) {
227             HLOGM("map line: %s", line.c_str());
228             // b0023000-b0024000 r--p 00000000 b3:05 959        /system/lib/libdl.so
229             // 0                 1    2        3     4          5
230             MemMapItem memMapItem;
231             std::vector<std::string> mapTokens = StringSplit(line, " ");
232 
233             if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
234                 // maybe file name is empty
235                 continue;
236             }
237 
238             // b0023000-b0024000
239             constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
240             std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
241             if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
242                 continue;
243             }
244 
245             // b0023000 / b0024000
246             try {
247                 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
248                 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
249             } catch (...) {
250                 // next line
251                 continue;
252             }
253 
254             constexpr const int MMAP_PROT_CHARS = 4;
255             int index = 0;
256             // rwxp
257             memMapItem.type_ = 0;
258             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
259                 continue;
260             }
261             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
262                 memMapItem.type_ |= PROT_READ;
263             }
264             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
265                 memMapItem.type_ |= PROT_WRITE;
266             }
267             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
268                 memMapItem.type_ |= PROT_EXEC;
269             }
270 
271             if ((memMapItem.type_ & PROT_EXEC) or (memMapItem.type_ | PROT_READ)) {
272                 /*
273                 we need record the read hen exec map address
274                 callstackk need r map to check the ehframe addrssss
275                 Section Headers:
276                 [Nr] Name              Type             Address           Offset
277                     Size              EntSize          Flags  Link  Info  Align
278 
279                 [12] .eh_frame_hdr     PROGBITS         00000000002929a0  000929a0
280                     00000000000071fc  0000000000000000   A       0     0     4
281                 [13] .eh_frame         PROGBITS         0000000000299ba0  00099ba0
282                     000000000002a8f4  0000000000000000   A       0     0     8
283                 [14] .text             PROGBITS         00000000002c5000  000c5000
284                     00000000001caa4a  0000000000000000  AX       0     0     16
285 
286                 00200000-002c5000 r--p 00000000 08:02 46400311
287                 002c5000-00490000 r-xp 000c5000 08:02 46400311
288                 */
289             } else {
290                 continue;
291             }
292 
293             // MAP_PRIVATE or MAP_SHARED
294             constexpr const int MAP_FLAG_ATTR_INDEX = 3;
295             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
296                 memMapItem.flags = MAP_PRIVATE;
297             } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
298                 memMapItem.flags = MAP_SHARED;
299             }
300 
301             try {
302                 // 00000000
303                 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
304                                                      nullptr, NUMBER_FORMAT_HEX_BASE);
305 
306                 // major:minor
307                 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
308 
309                 // b3:05
310                 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
311                 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
312 
313                 // 959
314                 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
315                                                NUMBER_FORMAT_HEX_BASE);
316             } catch (...) {
317                 // next line
318                 continue;
319             }
320             if (memMapItem.major_ == 0) {
321                 HLOGM("map line: exit %s", line.c_str());
322                 continue;
323             }
324             // system/lib/libdl.so
325             if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
326                 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
327                 if (memMapItem.name_.find("/data/storage") == 0 && access(memMapItem.name_.c_str(), F_OK) != 0) {
328                     memMapItem.name_ = "/proc/" + std::to_string(pid_) + "/root" + memMapItem.name_;
329                 }
330             }
331             if (!IsLegalFileName(memMapItem.name_)) {
332                 continue;
333             }
334             HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
335             memMaps_.emplace_back(std::move(memMapItem));
336         }
337     }
338     SortMemMaps();
339 }
340 #endif
341 
SortMemMaps()342 void VirtualThread::SortMemMaps()
343 {
344     for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
345         int targetPos = currPos - 1;
346         while (targetPos >= 0 and memMaps_[currPos].end_ < memMaps_[targetPos].end_) {
347             --targetPos;
348         }
349         if (targetPos < currPos - 1) {
350             auto target = memMaps_[currPos];
351             for (int k = currPos - 1; k > targetPos; --k) {
352                 memMaps_[k + 1] = memMaps_[k];
353             }
354             memMaps_[targetPos + 1] = target;
355         }
356     }
357     return;
358 }
359 
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)360 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
361                                   uint64_t offset)
362 {
363     if (!IsLegalFileName(filename)) {
364         return; // skip some memmap
365     }
366     MemMapItem &map = memMaps_.emplace_back(begin, begin + len, offset, filename);
367     HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
368           pid_, tid_, memMaps_.size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
369     SortMemMaps();
370 }
371 } // namespace HiPerf
372 } // namespace Developtools
373 } // namespace OHOS
374