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 symbolsFile->LoadDebugInfo();
128 return symbolsFile.get();
129 }
130 }
131 #ifdef DEBUG_MISS_SYMBOL
132 if (find(missedSymbolFile_.begin(), missedSymbolFile_.end(), inMap.name_) ==
133 missedSymbolFile_.end()) {
134 missedSymbolFile_.emplace_back(inMap.name_);
135 HLOGW("NOT found symbol for map '%s'", inMap.name_.c_str());
136 for (auto &file : symbolsFiles_) {
137 HLOGW(" we have '%s'", file->filePath_.c_str());
138 }
139 }
140 #endif
141 return nullptr;
142 }
ReportVaddrMapMiss(uint64_t vaddr) const143 void VirtualThread::ReportVaddrMapMiss(uint64_t vaddr) const
144 {
145 #ifdef HIPERF_DEBUG
146 if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
147 if (missedRuntimeVaddr_.find(vaddr) == missedRuntimeVaddr_.end()) {
148 missedRuntimeVaddr_.insert(vaddr);
149 HLOGV("vaddr %" PRIx64 " not found in any map", vaddr);
150 for (auto &map : memMaps_) {
151 HLOGV("map %s ", map.ToString().c_str());
152 }
153 }
154 }
155 #endif
156 }
157
ReadRoMemory(uint64_t vaddr,uint8_t * data,size_t size) const158 bool VirtualThread::ReadRoMemory(uint64_t vaddr, uint8_t *data, size_t size) const
159 {
160 const MemMapItem *map = FindMapByAddr(vaddr);
161 if (map != nullptr) {
162 // found symbols by file name
163 SymbolsFile *symbolsFile = FindSymbolsFileByMap(*map);
164 if (symbolsFile != nullptr) {
165 HLOGM("read vaddr from addr is 0x%" PRIx64 " at '%s'", vaddr - map->begin_,
166 map->name_.c_str());
167 if (size == symbolsFile->ReadRoMemory(map->FileOffsetFromAddr(vaddr), data, size)) {
168 return true;
169 } else {
170 return false;
171 }
172 } else {
173 HLOGW("found addr %" PRIx64 " in map but not loaded symbole %s", vaddr,
174 map->name_.c_str());
175 }
176 } else {
177 #ifdef HIPERF_DEBUG
178 ReportVaddrMapMiss(vaddr);
179 #endif
180 }
181 return false;
182 }
183
IsLegalFileName(const std::string & fileName)184 bool VirtualThread::IsLegalFileName(const std::string &fileName)
185 {
186 // some special
187 if (fileName == "[vdso]") {
188 return true;
189 }
190 if (fileName.empty() or fileName.find(':') != std::string::npos or fileName.front() == '[' or
191 fileName.back() == ']' or std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or
192 std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or
193 std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0 or
194 StringEndsWith(fileName, ".ttf")) {
195 return false;
196 }
197 return true;
198 }
199
200 #if is_mingw
ParseMap()201 void VirtualThread::ParseMap()
202 {
203 // only linux support read maps in runtime
204 return;
205 }
206 #else
207 constexpr const int MMAP_LINE_MIN_TOKEN = 5;
208 constexpr const int MMAP_LINE_TOKEN_INDEX_FLAG = 1;
209 constexpr const int MMAP_LINE_TOKEN_INDEX_OFFSET = 2;
210 constexpr const int MMAP_LINE_TOKEN_INDEX_MM = 3;
211 constexpr const int MMAP_LINE_TOKEN_INDEX_INODE = 4;
212 constexpr const int MMAP_LINE_TOKEN_INDEX_NAME = 5;
213 constexpr const int MMAP_LINE_MAX_TOKEN = 6;
214
ParseMap()215 void VirtualThread::ParseMap()
216 {
217 std::string mapPath = StringPrintf("/proc/%d/maps", pid_);
218 std::string mapContent = ReadFileToString(mapPath);
219 if (mapContent.size() > 0) {
220 std::istringstream s(mapContent);
221 std::string line;
222 while (std::getline(s, line)) {
223 HLOGM("map line: %s", line.c_str());
224 // b0023000-b0024000 r--p 00000000 b3:05 959 /system/lib/libdl.so
225 // 0 1 2 3 4 5
226 MemMapItem memMapItem;
227 std::vector<std::string> mapTokens = StringSplit(line, " ");
228
229 if (mapTokens.size() < MMAP_LINE_MIN_TOKEN) {
230 // maybe file name is empty
231 continue;
232 }
233
234 // b0023000-b0024000
235 constexpr const int MMAP_ADDR_RANGE_TOKEN = 2;
236 std::vector<std::string> addrRanges = StringSplit(mapTokens[0], "-");
237 if (addrRanges.size() != MMAP_ADDR_RANGE_TOKEN) {
238 continue;
239 }
240
241 // b0023000 / b0024000
242 try {
243 memMapItem.begin_ = std::stoull(addrRanges[0], nullptr, NUMBER_FORMAT_HEX_BASE);
244 memMapItem.end_ = std::stoull(addrRanges[1], nullptr, NUMBER_FORMAT_HEX_BASE);
245 } catch (...) {
246 // next line
247 continue;
248 }
249
250 constexpr const int MMAP_PROT_CHARS = 4;
251 int index = 0;
252 // rwxp
253 memMapItem.type_ = 0;
254 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG].size() != MMAP_PROT_CHARS) {
255 continue;
256 }
257 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'r') {
258 memMapItem.type_ |= PROT_READ;
259 }
260 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'w') {
261 memMapItem.type_ |= PROT_WRITE;
262 }
263 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][index++] == 'x') {
264 memMapItem.type_ |= PROT_EXEC;
265 }
266
267 if ((memMapItem.type_ & PROT_EXEC) or (memMapItem.type_ == PROT_READ)) {
268 /*
269 we need record the read hen exec map address
270 callstackk need r map to check the ehframe addrssss
271 Section Headers:
272 [Nr] Name Type Address Offset
273 Size EntSize Flags Link Info Align
274
275 [12] .eh_frame_hdr PROGBITS 00000000002929a0 000929a0
276 00000000000071fc 0000000000000000 A 0 0 4
277 [13] .eh_frame PROGBITS 0000000000299ba0 00099ba0
278 000000000002a8f4 0000000000000000 A 0 0 8
279 [14] .text PROGBITS 00000000002c5000 000c5000
280 00000000001caa4a 0000000000000000 AX 0 0 16
281
282 00200000-002c5000 r--p 00000000 08:02 46400311
283 002c5000-00490000 r-xp 000c5000 08:02 46400311
284 */
285 } else {
286 continue;
287 }
288
289 // MAP_PRIVATE or MAP_SHARED
290 constexpr const int MAP_FLAG_ATTR_INDEX = 3;
291 if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 'p') {
292 memMapItem.flags = MAP_PRIVATE;
293 } else if (mapTokens[MMAP_LINE_TOKEN_INDEX_FLAG][MAP_FLAG_ATTR_INDEX] == 's') {
294 memMapItem.flags = MAP_SHARED;
295 }
296
297 try {
298 // 00000000
299 memMapItem.pageoffset_ = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_OFFSET],
300 nullptr, NUMBER_FORMAT_HEX_BASE);
301
302 // major:minor
303 std::vector<std::string> mm = StringSplit(mapTokens[MMAP_LINE_TOKEN_INDEX_MM], ":");
304
305 // b3:05
306 memMapItem.major_ = std::stoull(mm.at(0), nullptr, NUMBER_FORMAT_HEX_BASE);
307 memMapItem.minor_ = std::stoull(mm.at(1), nullptr, NUMBER_FORMAT_HEX_BASE);
308
309 // 959
310 memMapItem.inode = std::stoull(mapTokens[MMAP_LINE_TOKEN_INDEX_INODE], nullptr,
311 NUMBER_FORMAT_HEX_BASE);
312 } catch (...) {
313 // next line
314 continue;
315 }
316
317 // system/lib/libdl.so
318 if (mapTokens.size() == MMAP_LINE_MAX_TOKEN) {
319 memMapItem.name_ = mapTokens[MMAP_LINE_TOKEN_INDEX_NAME];
320 }
321 if (!IsLegalFileName(memMapItem.name_)) {
322 continue;
323 }
324 HLOGD("%d %d memMap add '%s'", pid_, tid_, memMapItem.name_.c_str());
325 memMaps_.emplace_back(std::move(memMapItem));
326 }
327 }
328 SortMemMaps();
329 }
330 #endif
331
SortMemMaps()332 void VirtualThread::SortMemMaps()
333 {
334 for (int currPos = 1; currPos < static_cast<int>(memMaps_.size()); ++currPos) {
335 int targetPos = currPos - 1;
336 while (targetPos >= 0 and memMaps_[currPos].end_ < memMaps_[targetPos].end_) {
337 --targetPos;
338 }
339 if (targetPos < currPos - 1) {
340 auto target = memMaps_[currPos];
341 for (int k = currPos - 1; k > targetPos; --k) {
342 memMaps_[k + 1] = memMaps_[k];
343 }
344 memMaps_[targetPos + 1] = target;
345 }
346 }
347 return;
348 }
349
CreateMapItem(const std::string filename,uint64_t begin,uint64_t len,uint64_t offset)350 void VirtualThread::CreateMapItem(const std::string filename, uint64_t begin, uint64_t len,
351 uint64_t offset)
352 {
353 if (!IsLegalFileName(filename)) {
354 return; // skip some memmap
355 }
356 MemMapItem &map = memMaps_.emplace_back(begin, begin + len, offset, filename);
357 HLOGD(" %u:%u create a new map(total %zu) at '%s' (0x%" PRIx64 "-0x%" PRIx64 ")@0x%" PRIx64 " ",
358 pid_, tid_, memMaps_.size(), map.name_.c_str(), map.begin_, map.end_, map.pageoffset_);
359 SortMemMaps();
360 }
361 } // namespace HiPerf
362 } // namespace Developtools
363 } // namespace OHOS