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 ↦
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