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,size_t size) const206 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, 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 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
297 begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
298 end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
299 break;
300 }
301 }
302 CreateMapItem(filename, begin, end - begin, 0);
303 }
304
ParseDevhostMap(pid_t devhost)305 void VirtualThread::ParseDevhostMap(pid_t devhost)
306 {
307 std::string mapPath = StringPrintf("/proc/%d/maps", devhost);
308 std::string mapContent = ReadFileToString(mapPath);
309 std::string filename;
310 if (mapContent.size() > 0) {
311 std::istringstream s(mapContent);
312 std::string line;
313 while (std::getline(s, line)) {
314 // 2fe40000-311e1000 r-xp 00000000 00:01 217 /lib/libdh-linux.so.5.10.97-oh
315 // 0 1 2 3 4 5
316 std::vector<std::string> mapTokens = StringSplit(line, " ");
317 if (mapTokens.size() < MMAP_LINE_MAX_TOKEN) {
318 continue;
319 }
320 HLOGM("map line: %s", line.c_str());
321
322 // 2fe40000-311e1000
323 constexpr const int mmapAddrRangeToken = 2;
324 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
325 if (addrRanges.size() != mmapAddrRangeToken) {
326 continue;
327 }
328 uint64_t begin, end, offset;
329 // 2fe40000 / 311e1000
330 try {
331 begin = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
332 end = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
333 offset = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
334 nullptr, NUMBER_FORMAT_HEX_BASE);
335 } catch (...) {
336 continue;
337 }
338
339 // --x-
340 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS ||
341 mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_PROT_EXEC_INDEX] != 'x') {
342 continue;
343 }
344
345 const std::string anonPrefix = "[anon:[";
346 const std::string anonPostfix = "]]";
347 filename = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
348 if (filename == "[shmm]") {
349 continue;
350 }
351 if (filename.find(anonPrefix) != std::string::npos) {
352 // '[anon:[liblinux/devhost.ko]]' to '/liblinux/devhost.ko'
353 filename = filename.substr(anonPrefix.size(),
354 filename.size() - anonPrefix.size() -
355 anonPostfix.size());
356 filename = "/" + filename;
357 } else if (filename.find(DEVHOST_LINUX_FILE_NAME) != std::string::npos) {
358 // '/lib/libdh-linux.so.5.10.97-oh' to '/lib/libdh-linux.so'
359 filename = DEVHOST_LINUX_FILE_NAME;
360 }
361 CreateMapItem(filename, begin, end - begin, offset);
362 }
363 }
364 SortMemMaps();
365 }
366
SortMemMaps()367 void VirtualThread::SortMemMaps()
368 {
369 for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
370 int targetPos = currPos - 1;
371 while (targetPos >= 0 && memMaps_[memMapsIndexs_[currPos]]->end < memMaps_[memMapsIndexs_[targetPos]]->end) {
372 --targetPos;
373 }
374 if (targetPos < currPos - 1) {
375 auto target = memMapsIndexs_[currPos];
376 for (int k = currPos - 1; k > targetPos; --k) {
377 memMapsIndexs_[k + 1] = memMapsIndexs_[k];
378 }
379 memMapsIndexs_[targetPos + 1] = target;
380 }
381 }
382 }
383
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset,uint32_t prot)384 std::shared_ptr<DfxMap> VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
385 uint64_t offset, uint32_t prot)
386 {
387 if (!OHOS::HiviewDFX::DfxMaps::IsLegalMapItem(filename)) {
388 return nullptr; // skip some memmap
389 }
390 std::shared_ptr<DfxMap> map = memMaps_.emplace_back(std::make_shared<DfxMap>(begin, begin + len, offset,
391 prot, filename));
392 memMapsIndexs_.emplace_back(memMaps_.size() >= 1 ? memMaps_.size() - 1 : 0);
393 HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
394 pid_, tid_, memMaps_.size(), map->name.c_str(), map->begin, map->end, map->offset);
395 SortMemMaps();
396 return map;
397 }
398 } // namespace HiPerf
399 } // namespace Developtools
400 } // namespace OHOS
401