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