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
IsSorted() const33 bool VirtualThread::IsSorted() const
34 {
35 for (std::size_t index = 1; index < memMaps_.size(); ++index) {
36 if (memMaps_[index - 1].end_ > memMaps_[index].begin_) {
37 std::cout << "memMaps_ order error:\n"
38 << " " << memMaps_[index - 1].begin_ << "-" << memMaps_[index - 1].end_
39 << " " << memMaps_[index].begin_ << "-" << memMaps_[index].end_;
40 return false;
41 }
42 }
43 return true;
44 }
45 #endif
46
FindMapByAddr2(uint64_t addr) const47 const MemMapItem *VirtualThread::FindMapByAddr2(uint64_t addr) const
48 {
49 HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_.size());
50 if (memMaps_.size() == 0) {
51 return nullptr;
52 }
53 auto foundIt =
54 std::upper_bound(memMaps_.begin(), memMaps_.end(), addr, MemMapItem::ValueLessThan);
55 if (foundIt == memMaps_.begin()) {
56 // have map 2 3 4 5
57 // find 1 , will return 2 (index 0, begin elem)
58 // this same as not found any thins
59 } else {
60 // include memMaps_.end()
61 foundIt = std::prev(foundIt);
62 if (foundIt->Contain(addr)) {
63 HLOGM("found '%s' for vaddr 0x%016" PRIx64 "", foundIt->ToString().c_str(), addr);
64 return &(*foundIt);
65 }
66 }
67 return nullptr;
68 }
69
FindMapByAddr(uint64_t addr) const70 const MemMapItem *VirtualThread::FindMapByAddr(uint64_t addr) const
71 {
72 HLOGM("try found vaddr 0x%" PRIx64 " in maps %zu ", addr, memMaps_.size());
73 if (memMaps_.size() == 0) {
74 return nullptr;
75 }
76 if (memMaps_.front().begin_ > addr) {
77 return nullptr;
78 }
79 if (memMaps_.back().end_ <= addr) {
80 return nullptr;
81 }
82 constexpr int two {2};
83 std::size_t left {0};
84 std::size_t right {memMaps_.size()};
85 std::size_t mid = (right - left) / two + left;
86 while (left < right) {
87 if (addr < memMaps_[mid].end_) {
88 right = mid;
89 mid = (right - left) / two + left;
90 continue;
91 }
92 if (addr >= memMaps_[mid].end_) {
93 left = mid + 1;
94 mid = (right - left) / two + left;
95 continue;
96 }
97 }
98 if (addr >= memMaps_[left].begin_ and addr < memMaps_[left].end_) {
99 return &memMaps_[left];
100 }
101 return nullptr;
102 }
103
FindMapByFileInfo(const std::string name,uint64_t offset) const104 const MemMapItem *VirtualThread::FindMapByFileInfo(const std::string name, uint64_t offset) const
105 {
106 for (auto &map : memMaps_) {
107 if (name != map.name_) {
108 continue;
109 }
110 // check begin and length
111 if (offset >= map.pageoffset_ && (offset - map.pageoffset_) < (map.end_ - map.begin_)) {
112 HLOGMMM("found fileoffset 0x%" PRIx64 " in map (0x%" PRIx64 " - 0x%" PRIx64
113 " pageoffset 0x%" PRIx64 ") from %s",
114 offset, map.begin_, map.end_, map.pageoffset_, map.name_.c_str());
115 return ↦
116 }
117 }
118 HLOGM("NOT found offset 0x%" PRIx64 " in maps %zu ", offset, memMaps_.size());
119 return nullptr;
120 }
121
FindSymbolsFileByMap(const MemMapItem & inMap) const122 SymbolsFile *VirtualThread::FindSymbolsFileByMap(const MemMapItem &inMap) const
123 {
124 for (auto &symbolsFile : symbolsFiles_) {
125 if (symbolsFile->filePath_ == inMap.name_) {
126 HLOGM("found symbol for map '%s'", inMap.name_.c_str());
127 // LoadDebugInfo() could be failed when dump or report
128 if (symbolsFile->LoadDebugInfo() || !SymbolsFile::onRecording_) {
129 HLOGM("found symbol for map '%s'", inMap.name_.c_str());
130 return symbolsFile.get();
131 }
132 break;
133 }
134 }
135 #ifdef DEBUG_MISS_SYMBOL
136 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
137 missedSymbolFile_.end()) {
138 missedSymbolFile_.emplace_back(inMap.name_);
139 HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
140 for (auto &file : symbolsFiles_) {
141 HLOGW(" we have '%s'", file->filePath_.c_str());
142 }
143 }
144 #endif
145 return nullptr;
146 }
ReportVaddrMapMiss(uint64_t vaddr) const147 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
148 {
149 #ifdef HIPERF_DEBUG
150 if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
151 if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
152 missedRuntimeVaddr_.insert(vaddr);
153 HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
154 for (auto &map : memMaps_) {
155 HLOGV("map %s ", map.ToString().c_str());
156 }
157 }
158 }
159 #endif
160 }
161
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const162 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
163 {
164 const MemMapItem *map = FindMapByAddr(vaddr);
165 if (map != nullptr) {
166 // found symbols by file name
167 SymbolsFile *symbolsFile = FindSymbolsFileByMap(*map);
168 if (symbolsFile != nullptr) {
169 HLOGM("read vaddr from addr is 0x%" PRIx64 " at '%s'", vaddr - map->begin_,
170 map->name_.c_str());
171 if (size == symbolsFile->ReadRoMemory(map->FileOffsetFromAddr(vaddr), data, size)) {
172 return true;
173 } else {
174 return false;
175 }
176 } else {
177 HLOGW("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr,
178 map->name_.c_str());
179 }
180 } else {
181 #ifdef HIPERF_DEBUG
182 ReportVaddrMapMiss(vaddr);
183 #endif
184 }
185 return false;
186 }
187
IsLegalFileName(const std::string & fileName)188 bool VirtualThread::IsLegalFileName(const std::string &fileName)
189 {
190 // some special
191 if (fileName == "[vdso]") {
192 return true;
193 }
194 if (fileName.empty() or fileName.find(':') != std::string::npos or fileName.front() == '[' or
195 fileName.back() == ']' or std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
196 std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
197 std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0 or
198 StringEndsWith(fileName, ".ttf")) {
199 return false;
200 }
201 return true;
202 }
203
204 #if is_mingw
ParseMap()205 void VirtualThread::ParseMap()
206 {
207 // only linux support read maps in runtime
208 return;
209 }
210 #else
211 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
212 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
213 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
214 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
215 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
216 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
217 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
218
ParseMap()219 void VirtualThread::ParseMap()
220 {
221 std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
222 std::string mapContent = ReadFileToString(mapPath);
223 if (mapContent.size() > 0) {
224 std::istringstream s(mapContent);
225 std::string line;
226 while (std::getline(s, line)) {
227 HLOGM("map line: %s", line.c_str());
228 // b0023000-b0024000 r--p 00000000 b3:05 959 /system/lib/libdl.so
229 // 0 1 2 3 4 5
230 MemMapItem memMapItem;
231 std::vector<std::string> mapTokens = StringSplit(line, " ");
232
233 if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
234 // maybe file name is empty
235 continue;
236 }
237
238 // b0023000-b0024000
239 constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
240 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
241 if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
242 continue;
243 }
244
245 // b0023000 / b0024000
246 try {
247 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
248 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
249 } catch (...) {
250 // next line
251 continue;
252 }
253
254 constexpr const int MMAP_PROT_CHARS = 4;
255 int index = 0;
256 // rwxp
257 memMapItem.type_ = 0;
258 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
259 continue;
260 }
261 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
262 memMapItem.type_ |= PROT_READ;
263 }
264 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
265 memMapItem.type_ |= PROT_WRITE;
266 }
267 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
268 memMapItem.type_ |= PROT_EXEC;
269 }
270
271 if ((memMapItem.type_ & PROT_EXEC) or (memMapItem.type_ | PROT_READ)) {
272 /*
273 we need record the read hen exec map address
274 callstackk need r map to check the ehframe addrssss
275 Section Headers:
276 [Nr] Name Type Address Offset
277 Size EntSize Flags Link Info Align
278
279 [12] .eh_frame_hdr PROGBITS 00000000002929a0 000929a0
280 00000000000071fc 0000000000000000 A 0 0 4
281 [13] .eh_frame PROGBITS 0000000000299ba0 00099ba0
282 000000000002a8f4 0000000000000000 A 0 0 8
283 [14] .text PROGBITS 00000000002c5000 000c5000
284 00000000001caa4a 0000000000000000 AX 0 0 16
285
286 00200000-002c5000 r--p 00000000 08:02 46400311
287 002c5000-00490000 r-xp 000c5000 08:02 46400311
288 */
289 } else {
290 continue;
291 }
292
293 // MAP_PRIVATE or MAP_SHARED
294 constexpr const int MAP_FLAG_ATTR_INDEX = 3;
295 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
296 memMapItem.flags = MAP_PRIVATE;
297 } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
298 memMapItem.flags = MAP_SHARED;
299 }
300
301 try {
302 // 00000000
303 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
304 nullptr, NUMBER_FORMAT_HEX_BASE);
305
306 // major:minor
307 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
308
309 // b3:05
310 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
311 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
312
313 // 959
314 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
315 NUMBER_FORMAT_HEX_BASE);
316 } catch (...) {
317 // next line
318 continue;
319 }
320 if (memMapItem.major_ == 0) {
321 HLOGM("map line: exit %s", line.c_str());
322 continue;
323 }
324 // system/lib/libdl.so
325 if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
326 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
327 if (memMapItem.name_.find("/data/storage") == 0 && access(memMapItem.name_.c_str(), F_OK) != 0) {
328 memMapItem.name_ = "/proc/" + std::to_string(pid_) + "/root" + memMapItem.name_;
329 }
330 }
331 if (!IsLegalFileName(memMapItem.name_)) {
332 continue;
333 }
334 HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
335 memMaps_.emplace_back(std::move(memMapItem));
336 }
337 }
338 SortMemMaps();
339 }
340 #endif
341
SortMemMaps()342 void VirtualThread::SortMemMaps()
343 {
344 for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
345 int targetPos = currPos - 1;
346 while (targetPos >= 0 and memMaps_[currPos].end_ < memMaps_[targetPos].end_) {
347 --targetPos;
348 }
349 if (targetPos < currPos - 1) {
350 auto target = memMaps_[currPos];
351 for (int k = currPos - 1; k > targetPos; --k) {
352 memMaps_[k + 1] = memMaps_[k];
353 }
354 memMaps_[targetPos + 1] = target;
355 }
356 }
357 return;
358 }
359
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)360 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
361 uint64_t offset)
362 {
363 if (!IsLegalFileName(filename)) {
364 return; // skip some memmap
365 }
366 MemMapItem &map = memMaps_.emplace_back(begin, begin + len, offset, filename);
367 HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
368 pid_, tid_, memMaps_.size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
369 SortMemMaps();
370 }
371 } // namespace HiPerf
372 } // namespace Developtools
373 } // namespace OHOS
374