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