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