• 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 
33 static constexpr int MMAP_PROT_CHARS = 4;
34 static constexpr int MAP_PROT_EXEC_INDEX = 2;
35 
36 #ifdef DEBUG_TIME
37 
IsSorted() const38 bool VirtualThread::IsSorted() const
39 {
40     if (memMapsIndexs_.empty()) {
41         return true;
42     }
43     for (std::size_t index = 1; index < memMaps_.size(); ++index) {
44         if (memMaps_[memMapsIndexs_[index - 1]]->end > memMaps_[memMapsIndexs_[index]]->begin) {
45             std::cout << "memMaps_ order error:\n"
46                       << "    " << memMaps_[memMapsIndexs_[index - 1]]->begin << "-"
47                       << memMaps_[memMapsIndexs_[index - 1]]->end
48                       << "    " << memMaps_[memMapsIndexs_[index]]->begin << "-"
49                       << memMaps_[memMapsIndexs_[index]]->end;
50             return false;
51         }
52     }
53     return true;
54 }
55 #endif
56 
FindMapIndexByAddr(uint64_t addr) const57 int64_t VirtualThread::FindMapIndexByAddr(uint64_t addr) const
58 {
59     HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
60     const int64_t illegal = -1;
61     if (memMaps_.size() == 0) {
62         return illegal;
63     }
64     if (memMaps_[memMapsIndexs_[0]]->begin > addr) {
65         return illegal;
66     }
67     if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() >= 1 ? memMapsIndexs_.size() -  1 : 0]]->end <= addr) {
68         return illegal;
69     }
70     constexpr int divisorNum {2};
71     std::size_t left {0};
72     std::size_t right {memMapsIndexs_.size()};
73     std::size_t mid = (right - left) / divisorNum + left;
74     while (left < right) {
75         if (addr < memMaps_[memMapsIndexs_[mid]]->end) {
76             right = mid;
77             mid = (right - left) / divisorNum + left;
78             continue;
79         }
80         if (addr >= memMaps_[memMapsIndexs_[mid]]->end) {
81             left = mid + 1;
82             mid = (right - left) / divisorNum + left;
83             continue;
84         }
85     }
86     if (addr >= memMaps_[memMapsIndexs_[left]]->begin && addr < memMaps_[memMapsIndexs_[left]]->end) {
87         if (left > 0) {
88             memMaps_[memMapsIndexs_[left]]->prevMap = memMaps_[memMapsIndexs_[left - 1]];
89         }
90         return static_cast<int64_t>(memMapsIndexs_[left]);
91     }
92     return illegal;
93 }
94 
FindMapByAddr(uint64_t addr) const95 std::shared_ptr<DfxMap> VirtualThread::FindMapByAddr(uint64_t addr) const
96 {
97     HLOGM("try found vaddr 0x%" PRIx64 "in maps %zu", addr, memMaps_.size());
98     if (memMaps_.size() == 0) {
99         return nullptr;
100     }
101     if (memMaps_[memMapsIndexs_[0]]->begin > addr) {
102         return nullptr;
103     }
104     if (memMaps_[memMapsIndexs_[memMapsIndexs_.size() >= 1 ? memMapsIndexs_.size() - 1 : 0]]->end <= addr) {
105         return nullptr;
106     }
107     constexpr int divisorNum {2};
108     std::size_t left {0};
109     std::size_t right {memMapsIndexs_.size()};
110     std::size_t mid = (right - left) / divisorNum + left;
111     while (left < right) {
112         if (addr < memMaps_[memMapsIndexs_[mid]]->end) {
113             right = mid;
114             mid = (right - left) / divisorNum + left;
115             continue;
116         }
117         if (addr >= memMaps_[memMapsIndexs_[mid]]->end) {
118             left = mid + 1;
119             mid = (right - left) / divisorNum + left;
120             continue;
121         }
122     }
123     if (addr >= memMaps_[memMapsIndexs_[left]]->begin && addr < memMaps_[memMapsIndexs_[left]]->end) {
124         if (left > 0) {
125             memMaps_[memMapsIndexs_[left]]->prevMap = memMaps_[memMapsIndexs_[left - 1]];
126         }
127         return memMaps_[memMapsIndexs_[left]];
128     }
129     return nullptr;
130 }
131 
FindMapByFileInfo(const std::string name,uint64_t offset) const132 std::shared_ptr<DfxMap> VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
133 {
134     for (auto &map : memMaps_) {
135         if (name != map->name) {
136             continue;
137         }
138         // check begin and length
139         if (offset >= map->offset && (offset - map->offset) < (map->end - map->begin)) {
140             HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
141                     " pageoffset 0x%" PRIx64 ")  from %s",
142                     offset, map->begin, map->end, map->offset, map->name.c_str());
143             return map;
144         }
145     }
146     HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size());
147     return nullptr;
148 }
149 
FindSymbolsFileByMap(std::shared_ptr<DfxMap> map) const150 SymbolsFile *VirtualThread::FindSymbolsFileByMap(std::shared_ptr<DfxMap> map) const
151 {
152     if (map == nullptr) {
153         return nullptr;
154     }
155     if (map->symbolFileIndex != -1) {
156         // no need further operation
157         if (symbolsFiles_[map->symbolFileIndex]->LoadDebugInfo(map)) {
158             return symbolsFiles_[map->symbolFileIndex].get();
159         }
160     } else {
161         // add it to cache
162         for (size_t i = 0; i < symbolsFiles_.size(); ++i) {
163             if (symbolsFiles_[i]->filePath_ == map->name) {
164                 HLOGD("found symbol for map '%s'", map->name.c_str());
165                 map->symbolFileIndex = static_cast<int32_t>(i);
166                 if (symbolsFiles_[i]->LoadDebugInfo(map)) {
167                     return symbolsFiles_[i].get();
168                 }
169             }
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::logDisabled_) {
189         return;
190     }
191     if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
192         if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
193             missedRuntimeVaddr_.insert(vaddr);
194             HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
195             for (auto &map : memMaps_) {
196                 if (map == nullptr) {
197                     return;
198                 }
199                 HLOGV("map %s ", map->ToString().c_str());
200             }
201         }
202     }
203 #endif
204 }
205 
ReadRoMemory(uint64_t vaddr,uint8_t * data,const size_t size) const206 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, const size_t size) const
207 {
208     uint64_t pageIndex = vaddr >> 12;
209     uint64_t memMapIndex = -1;
210     const int64_t exceptRet = -1;
211     const uint64_t illegal = -1;
212     auto pageFile = vaddr4kPageCache_.find(pageIndex);
213     if (pageFile != vaddr4kPageCache_.end()) {
214         memMapIndex = pageFile->second;
215     } else {
216         int64_t retIndex = FindMapIndexByAddr(vaddr);
217         memMapIndex = static_cast<uint64_t>(retIndex);
218         // add to 4k page cache table
219         if (retIndex != exceptRet && memMapIndex < memMaps_.size()) {
220             const_cast<VirtualThread *>(this)->vaddr4kPageCache_[pageIndex] = memMapIndex;
221         }
222     }
223     if (memMapIndex != illegal) {
224         auto map = memMaps_[memMapIndex];
225         if (map != nullptr) {
226             if (map->elf == nullptr) {
227                 SymbolsFile* symFile = FindSymbolsFileByMap(map);
228                 if (symFile == nullptr) {
229                     return false;
230                 }
231                 map->elf = symFile->GetElfFile();
232             }
233             if (map->elf != nullptr) {
234                 // default base offset is zero
235                 uint64_t foff = vaddr - map->begin + map->offset - map->elf->GetBaseOffset();
236                 if (map->elf->Read(foff, data, size)) {
237                     return true;
238                 } else {
239                     return false;
240                 }
241             } else {
242                 HLOGW("find addr %" PRIx64 "in map but not loaded symbole %s", vaddr, map->name.c_str());
243             }
244         }
245     } else {
246         HLOGV("not found in any map");
247     }
248     return false;
249 }
250 
251 #if defined(is_mingw) && is_mingw
ParseMap()252 void VirtualThread::ParseMap()
253 {
254     // only linux support read maps in runtime
255     return;
256 }
257 #else
ParseMap()258 void VirtualThread::ParseMap()
259 {
260     if (!(OHOS::HiviewDFX::DfxMaps::Create(pid_, memMaps_, memMapsIndexs_))) {
261         HLOGE("VirtualThread Failed to Parse Map.");
262     }
263     SortMemMaps();
264 }
265 #endif
266 
FixHMBundleMap()267 void VirtualThread::FixHMBundleMap()
268 {
269     // fix bundle path in map
270     for (auto &map : memMaps_) {
271         NeedAdaptHMBundlePath(map->name, name_);
272     }
273 }
274 
275 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
276 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
277 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
278 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
ParseServiceMap(const std::string & filename)279 void VirtualThread::ParseServiceMap(const std::string &filename)
280 {
281     std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
282     std::string mapContent = ReadFileToString(mapPath);
283     uint64_t begin = 0;
284     uint64_t end = 0;
285     if (mapContent.size() == 0) {
286         HLOGW("Parse %s failed, content empty", mapPath.c_str());
287         return;
288     }
289     std::istringstream s(mapContent);
290     std::string line;
291     while (std::getline(s, line)) {
292         std::vector<std::string> mapTokens = StringSplit(line, " ");
293         if (mapTokens.size() == MMAP_LINE_MAX_TOKEN &&
294             mapTokens[MMAP_LINE_TOKEN_INDEX_NAME] == name_) {
295             HLOGM("map line: %s", line.c_str());
296             constexpr int mmapAddrRangeToken = 2;
297             std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
298             if (addrRanges.size() < mmapAddrRangeToken) {
299                 continue;
300             }
301             begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
302             end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
303             break;
304         }
305     }
306     CreateMapItem(filename, begin, end - begin, 0);
307 }
308 
ParseDevhostMap(const pid_t devhost)309 void VirtualThread::ParseDevhostMap(const pid_t devhost)
310 {
311     std::string mapPath = StringPrintf("/proc/%d/maps", devhost);
312     std::string mapContent = ReadFileToString(mapPath);
313     std::string filename;
314     if (mapContent.size() > 0) {
315         std::istringstream s(mapContent);
316         std::string line;
317         while (std::getline(s, line)) {
318             // 2fe40000-311e1000 r-xp 00000000 00:01 217 /lib/libdh-linux.so.5.10.97-oh
319             // 0                 1    2        3     4   5
320             std::vector<std::string> mapTokens = StringSplit(line, " ");
321             if (mapTokens.size() < MMAP_LINE_MAX_TOKEN) {
322                 continue;
323             }
324             HLOGM("map line: %s", line.c_str());
325 
326             // 2fe40000-311e1000
327             constexpr const int mmapAddrRangeToken = 2;
328             std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
329             if (addrRanges.size() != mmapAddrRangeToken) {
330                 continue;
331             }
332             uint64_t begin = 0;
333             uint64_t end = 0;
334             uint64_t offset = 0;
335             // 2fe40000 / 311e1000
336             try {
337                 begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
338                 end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
339                 offset = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
340                                      nullptr, NUMBER_FORMAT_HEX_BASE);
341             } catch (...) {
342                 continue;
343             }
344 
345             // --x-
346             if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS ||
347                 mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_PROT_EXEC_INDEX] != 'x') {
348                 continue;
349             }
350 
351             const std::string anonPrefix = "[anon:[";
352             const std::string anonPostfix = "]]";
353             filename = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
354             if (filename == "[shmm]") {
355                 continue;
356             }
357             if (filename.find(anonPrefix) != std::string::npos) {
358                 // '[anon:[liblinux/devhost.ko]]' to '/liblinux/devhost.ko'
359                 filename = filename.substr(anonPrefix.size(),
360                                            filename.size() - anonPrefix.size() -
361                                            anonPostfix.size());
362                 filename = "/" + filename;
363             } else if (filename.find(DEVHOST_LINUX_FILE_NAME) != std::string::npos) {
364                 // '/lib/libdh-linux.so.5.10.97-oh' to '/lib/libdh-linux.so'
365                 filename = DEVHOST_LINUX_FILE_NAME;
366             }
367             CreateMapItem(filename, begin, end - begin, offset);
368         }
369     }
370     SortMemMaps();
371 }
372 
SortMemMaps()373 void VirtualThread::SortMemMaps()
374 {
375     for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
376         int targetPos = currPos - 1;
377         while (targetPos >= 0 && memMaps_[memMapsIndexs_[currPos]]->end < memMaps_[memMapsIndexs_[targetPos]]->end) {
378             --targetPos;
379         }
380         if (targetPos < currPos - 1) {
381             auto target = memMapsIndexs_[currPos];
382             for (int k = currPos - 1; k > targetPos; --k) {
383                 memMapsIndexs_[k + 1] = memMapsIndexs_[k];
384             }
385             memMapsIndexs_[targetPos + 1] = target;
386         }
387     }
388 }
389 
CreateMapItem(const std::string & filename,uint64_t const begin,const uint64_t len,const uint64_t offset,const uint32_t prot)390 std::shared_ptr<DfxMap> VirtualThread::CreateMapItem(const std::string &filename, uint64_t const begin,
391                                                      const uint64_t len, const uint64_t offset,
392                                                      const uint32_t prot)
393 {
394     if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(filename)) {
395         return nullptr; // skip some memmap
396     }
397     std::shared_ptr<DfxMap> map = memMaps_.emplace_back(std::make_shared<DfxMap>(begin, begin + len, offset,
398         prot, filename));
399     memMapsIndexs_.emplace_back(memMaps_.size() >= 1 ? memMaps_.size() - 1 : 0);
400     HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
401           pid_, tid_, memMaps_.size(), map->name.c_str(), map->begin, map->end, map->offset);
402     SortMemMaps();
403     return map;
404 }
405 } // namespace HiPerf
406 } // namespace Developtools
407 } // namespace OHOS
408