1 /*
2 * Copyright (c) 2021 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 NativeDaemon {
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 }
VirtualThread(pid_t pid,pid_t tid,const std::set<std::unique_ptr<SymbolsFile>,CCompareSymbolsFile> & symbolsFiles,VirtualRuntime * runtime,bool parseFlag)103 VirtualThread::VirtualThread(pid_t pid,
104 pid_t tid,
105 const std::set<std::unique_ptr<SymbolsFile>, CCompareSymbolsFile>& symbolsFiles,
106 VirtualRuntime* runtime,
107 bool parseFlag)
108 : pid_(pid), tid_(tid), symbolsFiles_(symbolsFiles), virtualruntime_(runtime)
109 {
110 memMaps_ = &virtualruntime_->processMemMaps_;
111 if (parseFlag) {
112 pthread_mutex_lock(&virtualruntime_->threadMemMapsLock_);
113 if (virtualruntime_->processMemMaps_.size() == 0) {
114 this->ParseMap(virtualruntime_->processMemMaps_);
115 }
116 pthread_mutex_unlock(&virtualruntime_->threadMemMapsLock_);
117 }
118
119 this->name_ = ReadThreadName(pid);
120 reg_nr = RegisterGetCount();
121 if (reg_nr <= 0) {
122 HLOGE("Getting register count failed");
123 reg_nr = 0;
124 user_regs = nullptr;
125 } else if (reg_nr != std::numeric_limits<size_t>::max()) {
126 user_regs = new (std::nothrow) u64[reg_nr];
127 if (!user_regs) {
128 HLOGM("new regs failed");
129 }
130 if (memset_s(user_regs, sizeof(u64) * reg_nr, 0, sizeof(u64) * reg_nr) != EOK) {
131 HLOGM("memset_s regs failed");
132 }
133 } else {
134 reg_nr = 0;
135 user_regs = nullptr;
136 }
137 HLOGM("%d %d map from parent size is %zu", pid, tid, memMaps_->size());
138 }
139
ReadThreadName(pid_t tid)140 std::string VirtualThread::ReadThreadName(pid_t tid)
141 {
142 std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
143 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
144 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
145 return comm;
146 }
147
FindMapByFileInfo(const std::string name,uint64_t offset) const148 const MemMapItem *VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
149 {
150 for (auto &map : *memMaps_) {
151 if (name != map.name_) {
152 continue;
153 }
154 // check begin and length
155 if (offset >= map.pageoffset_ && (offset - map.pageoffset_) < (map.end_ - map.begin_)) {
156 HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
157 " pageoffset 0x%" PRIx64 ") from %s",
158 offset, map.begin_, map.end_, map.pageoffset_, map.name_.c_str());
159 return ↦
160 }
161 }
162 HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_->size());
163 return nullptr;
164 }
165
FindSymbolsFileByMap(const MemMapItem & inMap) const166 SymbolsFile *VirtualThread::FindSymbolsFileByMap(const MemMapItem &inMap) const
167 {
168 for (auto &symbolsFile : symbolsFiles_) {
169 if (symbolsFile->filePath_ == inMap.name_) {
170 HLOGM("found symbol for map '%s'", inMap.name_.c_str());
171 symbolsFile->LoadDebugInfo();
172 return symbolsFile.get();
173 }
174 }
175 #ifdef DEBUG_MISS_SYMBOL
176 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
177 missedSymbolFile_.end()) {
178 missedSymbolFile_.emplace_back(inMap.name_);
179 HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
180 for (auto &file : symbolsFiles_) {
181 HLOGW(" we have '%s'", file->filePath_.c_str());
182 }
183 }
184 #endif
185 return nullptr;
186 }
ReportVaddrMapMiss(uint64_t vaddr) const187 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
188 {
189 #ifdef HIPERF_DEBUG
190 if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
191 if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
192 missedRuntimeVaddr_.insert(vaddr);
193 HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
194 for (auto &map : *memMaps_) {
195 HLOGV("map %s ", map.ToString().c_str());
196 }
197 }
198 }
199 #endif
200 }
201
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const202 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
203 {
204 const MemMapItem *map = FindMapByAddr(vaddr);
205 if (map != nullptr) {
206 // found symbols by file name
207 SymbolsFile *symbolsFile = FindSymbolsFileByMap(*map);
208 if (symbolsFile != nullptr) {
209 HLOGM("read vaddr from addr is 0x%" PRIx64 " at '%s'", vaddr - map->begin_,
210 map->name_.c_str());
211 if (size == symbolsFile->ReadRoMemory(map->FileOffsetFromAddr(vaddr), data, size)) {
212 return true;
213 } else {
214 return false;
215 }
216 } else {
217 HLOGW("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr,
218 map->name_.c_str());
219 }
220 } else {
221 #ifdef HIPERF_DEBUG
222 ReportVaddrMapMiss(vaddr);
223 #endif
224 }
225 return false;
226 }
227
IsLegalFileName(const std::string & fileName)228 bool VirtualThread::IsLegalFileName(const std::string &fileName)
229 {
230 // some specail
231 if (fileName == "[vdso]") {
232 return true;
233 }
234 if (fileName.empty() or fileName.find(':') != std::string::npos or fileName.front() == '[' or
235 fileName.back() == ']' or std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
236 std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
237 std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0 or
238 StringEndsWith(fileName, ".ttf")) {
239 return false;
240 }
241 return true;
242 }
243
244 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
245 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
246 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
247 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
248 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
249 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
250 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
251
ParseMap(std::vector<MemMapItem> & memMaps,bool update)252 bool VirtualThread::ParseMap(std::vector<MemMapItem>& memMaps, bool update)
253 {
254 std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
255 std::string mapContent = ReadFileToString(mapPath);
256 bool mapsAdded = !update;
257 std::set<std::string> addSymbolFile;
258 if (mapContent.size() > 0) {
259 std::istringstream s(mapContent);
260 std::string line;
261 while (std::getline(s, line)) {
262 HLOGM("map line: %s", line.c_str());
263 // b0023000-b0024000 r--p 00000000 b3:05 959 /system/lib/libdl.so
264 // 0 1 2 3 4 5
265 MemMapItem memMapItem;
266 std::vector<std::string> mapTokens = StringSplit(line, " ");
267
268 if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
269 // maybe file name is empty
270 continue;
271 }
272
273 // b0023000-b0024000
274 constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
275 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
276 if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
277 continue;
278 }
279
280 // b0023000 / b0024000
281 try {
282 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
283 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
284 } catch (...) {
285 // next line
286 continue;
287 }
288
289 constexpr const int MMAP_PROT_CHARS = 4;
290 int index = 0;
291 // rwxp
292 memMapItem.type_ = 0;
293 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
294 continue;
295 }
296 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
297 memMapItem.type_ |= PROT_READ;
298 }
299 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
300 memMapItem.type_ |= PROT_WRITE;
301 }
302 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
303 memMapItem.type_ |= PROT_EXEC;
304 }
305
306 if ((memMapItem.type_ & PROT_EXEC) or (memMapItem.type_ | PROT_READ)) {
307 /*
308 we need record the read hen exec map address
309 callstackk need r map to check the ehframe addrssss
310 Section Headers:
311 [Nr] Name Type Address Offset
312 Size EntSize Flags Link Info Align
313
314 [12] .eh_frame_hdr PROGBITS 00000000002929a0 000929a0
315 00000000000071fc 0000000000000000 A 0 0 4
316 [13] .eh_frame PROGBITS 0000000000299ba0 00099ba0
317 000000000002a8f4 0000000000000000 A 0 0 8
318 [14] .text PROGBITS 00000000002c5000 000c5000
319 00000000001caa4a 0000000000000000 AX 0 0 16
320
321 00200000-002c5000 r--p 00000000 08:02 46400311
322 002c5000-00490000 r-xp 000c5000 08:02 46400311
323 */
324 } else {
325 continue;
326 }
327
328 // MAP_PRIVATE or MAP_SHARED
329 constexpr const int MAP_FLAG_ATTR_INDEX = 3;
330 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
331 memMapItem.flags = MAP_PRIVATE;
332 } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
333 memMapItem.flags = MAP_SHARED;
334 }
335
336 try {
337 // 00000000
338 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
339 nullptr, NUMBER_FORMAT_HEX_BASE);
340
341 // major:minor
342 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
343
344 // b3:05
345 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
346 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
347
348 // 959
349 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
350 NUMBER_FORMAT_HEX_BASE);
351 } catch (...) {
352 // next line
353 continue;
354 }
355
356 if (memMapItem.major_ == 0) {
357 HLOGM("map line: exit %s", line.c_str());
358 continue;
359 }
360
361 // system/lib/libdl.so
362 if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
363 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
364 if (memMapItem.name_.find("/data/storage") == 0 && access(memMapItem.name_.c_str(), F_OK) != 0) {
365 memMapItem.name_ = "/proc/" + std::to_string(pid_) + "/root/" + memMapItem.name_;
366 }
367 }
368 if (!IsLegalFileName(memMapItem.name_)) {
369 continue;
370 }
371
372 memMapItem.nameHold_ = OHOS::Developtools::NativeDaemon::memHolder.HoldStringView(memMapItem.name_);
373 if (!update) {
374 memMaps.push_back(memMapItem);
375 virtualruntime_->UpdateSymbols(memMapItem.name_);
376 HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
377 } else if (!virtualruntime_->IsSymbolExist(memMapItem.name_)) {
378 mapsAdded = true;
379 memMaps.push_back(memMapItem);
380 addSymbolFile.emplace(memMapItem.name_);
381 HLOGD("%d %d memMap update '%s'", pid_, tid_, memMapItem.name_.c_str());
382 }
383 }
384 }
385
386 for (auto it : addSymbolFile) {
387 virtualruntime_->UpdateSymbols(it);
388 HLOGD("add symbol file %s", it.c_str());
389 }
390
391 if (mapsAdded) {
392 HILOG_INFO(LOG_CORE, "maps changed and need sort");
393 SortMemMaps();
394 } else {
395 HILOG_INFO(LOG_CORE, "maps no change");
396 return false;
397 }
398 return true;
399 }
400
SortMemMaps()401 void VirtualThread::SortMemMaps()
402 {
403 for (int currPos = 1; currPos < static_cast<int>(memMaps_->size()); ++currPos) {
404 int targetPos = currPos - 1;
405 while (targetPos >= 0 and (*memMaps_)[currPos].end_ < (*memMaps_)[targetPos].end_) {
406 --targetPos;
407 }
408 if (targetPos < currPos - 1) {
409 auto target = (*memMaps_)[currPos];
410 for (int k = currPos - 1; k > targetPos; --k) {
411 (*memMaps_)[k + 1] = (*memMaps_)[k];
412 }
413 (*memMaps_)[targetPos + 1] = target;
414 }
415 }
416 return;
417 }
418
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)419 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
420 uint64_t offset)
421 {
422 if (!IsLegalFileName(filename)) {
423 return; // skip some memmap
424 }
425 MemMapItem &map = memMaps_->emplace_back(begin, begin + len, offset, filename);
426 HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
427 pid_, tid_, memMaps_->size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
428 SortMemMaps();
429 }
430 } // namespace NativeDaemon
431 } // namespace Developtools
432 } // namespace OHOS