• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &map;
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