1 /*
2 * Copyright (c) 2021 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 "Runtime"
16
17 #include "virtual_runtime.h"
18
19 #include <cinttypes>
20 #include <iostream>
21 #include <sstream>
22 #include <unistd.h>
23 #if !is_mingw
24 #include <sys/mman.h>
25 #endif
26
27 #include "register.h"
28 #include "symbols_file.h"
29 #include "utilities.h"
30
31 using namespace std::chrono;
32 namespace OHOS {
33 namespace Developtools {
34 namespace NativeDaemon {
35 namespace {
36 std::atomic<uint64_t> callStackErrCnt = 0;
37 constexpr uint32_t CALL_STACK_ERROR_TIMES = 10;
38 constexpr uint32_t SYMBOL_FILES_SIZE = 512;
39 }
40 // we unable to access 'swapper' from /proc/0/
ClearMaps()41 void VirtualRuntime::ClearMaps()
42 {
43 processMemMaps_.clear();
44 }
45
VirtualRuntime(const NativeHookConfig & hookConfig)46 VirtualRuntime::VirtualRuntime(const NativeHookConfig& hookConfig): hookConfig_(hookConfig)
47 {
48 symbolsFiles_.reserve(SYMBOL_FILES_SIZE);
49 if (!hookConfig_.offline_symbolization()) {
50 userSymbolCache_.reserve(USER_SYMBOL_CACHE_LIMIT);
51 }
52 }
53
~VirtualRuntime()54 VirtualRuntime::~VirtualRuntime()
55 {
56 HILOG_INFO(LOG_CORE, "%s:%d UserSymbolCache size = %zu", __func__, __LINE__, userSymbolCache_.size());
57 HILOG_INFO(LOG_CORE, "Total number of call stack errors: %" PRIu64 "", callStackErrCnt.load());
58 ClearMaps();
59 }
60
ReadThreadName(pid_t tid)61 std::string VirtualRuntime::ReadThreadName(pid_t tid)
62 {
63 std::string comm = ReadFileToString(StringPrintf("/proc/%d/comm", tid)).c_str();
64 comm.erase(std::remove(comm.begin(), comm.end(), '\r'), comm.end());
65 comm.erase(std::remove(comm.begin(), comm.end(), '\n'), comm.end());
66 return comm;
67 }
68
UpdateThread(pid_t pid,pid_t tid,const std::string name)69 VirtualThread &VirtualRuntime::UpdateThread(pid_t pid, pid_t tid, const std::string name)
70 {
71 #ifdef HIPERF_DEBUG_TIME
72 const auto startTime = steady_clock::now();
73 #endif
74 VirtualThread &thread = GetThread(pid, tid);
75 if (!name.empty()) {
76 thread.name_ = name;
77 }
78 #ifdef HIPERF_DEBUG_TIME
79 updateThreadTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
80 #endif
81 return thread;
82 }
83
CreateThread(pid_t pid,pid_t tid)84 VirtualThread &VirtualRuntime::CreateThread(pid_t pid, pid_t tid)
85 {
86 // make a new one
87 userSpaceThreadMap_.emplace(std::piecewise_construct, std::forward_as_tuple(tid),
88 std::forward_as_tuple(pid, tid, symbolsFiles_, this));
89 VirtualThread& thr = userSpaceThreadMap_.at(tid);
90 return thr;
91 }
92
GetThread(pid_t pid,pid_t tid)93 VirtualThread &VirtualRuntime::GetThread(pid_t pid, pid_t tid)
94 {
95 HLOGV("find thread %u:%u", pid, tid);
96 auto it = userSpaceThreadMap_.find(tid);
97 if (it == userSpaceThreadMap_.end()) {
98 // we also need thread
99 VirtualThread& thr = CreateThread(pid, tid);
100 return thr;
101 } else {
102 VirtualThread& thr = it->second;
103 return thr;
104 }
105 }
106
MakeCallFrame(Symbol & symbol,CallFrame & callFrame)107 void VirtualRuntime::MakeCallFrame(Symbol &symbol, CallFrame &callFrame)
108 {
109 callFrame.vaddrInFile_ = symbol.funcVaddr_;
110 callFrame.symbolName_ = symbol.symbolName_;
111 callFrame.symbolIndex_ = symbol.index_;
112 callFrame.filePath_ = symbol.module_.empty() ? symbol.comm_ : symbol.module_;
113 callFrame.symbolOffset_ = symbol.offset_;
114 callFrame.callFrameId_ = symbol.symbolId_;
115 callFrame.symbolNameId_ = symbol.symbolNameId_;
116 callFrame.filePathId_ = symbol.filePathId_;
117 if (symbol.funcVaddr_ != 0) {
118 callFrame.offset_ = symbol.funcVaddr_;
119 } else {
120 callFrame.offset_ = callFrame.ip_;
121 }
122 }
123
GetSymbolName(pid_t pid,pid_t tid,std::vector<CallFrame> & callFrames,int offset,bool first)124 bool VirtualRuntime::GetSymbolName(pid_t pid, pid_t tid, std::vector<CallFrame>& callFrames, int offset, bool first)
125 {
126 #ifdef HIPERF_DEBUG_TIME
127 const auto startTime = steady_clock::now();
128 #endif
129 // Symbolic the Call Stack
130 HLOGV("total %zu frames", callFrames.size());
131
132 perf_callchain_context perfCallchainContext = PERF_CONTEXT_MAX;
133 for (auto callFrameIt = callFrames.begin() + offset; callFrameIt != callFrames.end(); ++callFrameIt) {
134 auto &callFrame = callFrameIt.operator*();
135 if (callFrame.ip_ >= PERF_CONTEXT_MAX) {
136 // dont care, this is not issue.
137 HLOGV("%s", UpdatePerfContext(callFrame.ip_, perfCallchainContext).c_str());
138 continue;
139 }
140 auto symbol = GetSymbol(callFrame, pid, tid,
141 perfCallchainContext);
142 if (symbol.isValid()) {
143 MakeCallFrame(symbol, callFrame);
144 } else {
145 #ifdef TRY_UNWIND_TWICE
146 if (first) {
147 if (failedIPs_.find(callFrame.ip_) == failedIPs_.end()) {
148 return false;
149 } else {
150 callFrames.erase(callFrameIt, callFrames.end());
151 return true;
152 }
153 } else {
154 failedIPs_.insert(callFrame.ip_);
155 callFrames.erase(callFrameIt, callFrames.end());
156 return true;
157 }
158 #else
159 ++callStackErrCnt;
160 if (callStackErrCnt.load() % CALL_STACK_ERROR_TIMES == 0) {
161 HILOG_DEBUG(LOG_CORE, "number of call stack errors: %" PRIu64 "", callStackErrCnt.load());
162 }
163 callFrames.erase(callFrameIt, callFrames.end());
164 return true;
165 #endif
166 }
167 int index = callFrameIt - callFrames.begin();
168 HLOGV(" (%u)unwind symbol: %*s%s", index, index, "", callFrame.ToSymbolString().c_str());
169 }
170 #ifdef HIPERF_DEBUG_TIME
171 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
172 if (usedTime.count() != 0) {
173 HLOGV("cost %0.3f ms to symbolic ", usedTime.count() / MS_DUARTION);
174 }
175 symbolicRecordTimes_ += usedTime;
176 #endif
177 return true;
178 }
179
UpdateMaps(pid_t pid,pid_t tid)180 void VirtualRuntime::UpdateMaps(pid_t pid, pid_t tid)
181 {
182 auto &thread = UpdateThread(pid, tid);
183 if (thread.ParseMap(processMemMaps_, true)) {
184 HILOG_DEBUG(LOG_CORE, "voluntarily update maps succeed");
185 } else {
186 HILOG_DEBUG(LOG_CORE, "voluntarily update maps ignore");
187 }
188 }
189
UnwindStack(std::vector<u64> & regs,const u8 * stack_addr,int stack_size,pid_t pid,pid_t tid,std::vector<CallFrame> & callFrames,size_t maxStackLevel)190 bool VirtualRuntime::UnwindStack(std::vector<u64>& regs,
191 const u8* stack_addr,
192 int stack_size,
193 pid_t pid,
194 pid_t tid,
195 std::vector<CallFrame>& callFrames,
196 size_t maxStackLevel)
197 {
198 #ifdef HIPERF_DEBUG_TIME
199 const auto startTime = steady_clock::now();
200 #endif
201 // if we have userstack ?
202 int offset = 0;
203 auto &thread = UpdateThread(pid, tid);
204 if (stack_size > 0) {
205 callstack_.UnwindCallStack(thread, ®s[0], regs.size(), stack_addr, stack_size, callFrames, maxStackLevel);
206 if (callFrames.size() <= FILTER_STACK_DEPTH) {
207 callFrames.clear();
208 return false;
209 }
210 // Do not symbolize the first two frame, cause the two frame implement by tool itself
211 offset = FILTER_STACK_DEPTH;
212 #ifdef HIPERF_DEBUG_TIME
213 unwindCallStackTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
214 #endif
215 }
216 #ifdef HIPERF_DEBUG_TIME
217 unwindFromRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
218 #endif
219 if (hookConfig_.offline_symbolization()) {
220 return true;
221 }
222 if (!GetSymbolName(pid, tid, callFrames, offset, true)) {
223 #ifdef TRY_UNWIND_TWICE
224 HLOGD("clear and unwind one more time");
225 if (!thread.ParseMap(processMemMaps_, true)) {
226 GetSymbolName(pid, tid, callFrames, offset, false);
227 return false;
228 }
229 if (stack_size > 0) {
230 callFrames.clear();
231 callstack_.UnwindCallStack(thread, ®s[0], regs.size(), stack_addr,
232 stack_size, callFrames, maxStackLevel);
233 }
234 if (callFrames.size() <= FILTER_STACK_DEPTH) {
235 callFrames.clear();
236 return false;
237 }
238 if (!GetSymbolName(pid, tid, callFrames, offset, false)) {
239 return false;
240 }
241 #endif
242 }
243 return true;
244 }
245
IsSymbolExist(const std::string & fileName)246 bool VirtualRuntime::IsSymbolExist(const std::string& fileName)
247 {
248 if (symbolsFiles_.find(fileName) != symbolsFiles_.end()) {
249 HLOGV("already have '%s'", fileName.c_str());
250 return true;
251 }
252 return false;
253 }
254
DelSymbolFile(const std::string & fileName)255 void VirtualRuntime::DelSymbolFile(const std::string& fileName)
256 {
257 symbolsFiles_.erase(fileName);
258 }
259
UpdateSymbols(std::string fileName)260 void VirtualRuntime::UpdateSymbols(std::string fileName)
261 {
262 HLOGD("try to find symbols for file: %s", fileName.c_str());
263 #ifdef HIPERF_DEBUG_TIME
264 const auto startTime = steady_clock::now();
265 #endif
266 if (symbolsFiles_.find(fileName) != symbolsFiles_.end()) {
267 HLOGV("already have '%s'", fileName.c_str());
268 return;
269 }
270
271 // found it by name
272 auto symbolsFile = SymbolsFile::CreateSymbolsFile(fileName);
273
274 // set sybol path If it exists
275 if (symbolsPaths_.size() > 0) {
276 symbolsFile->setSymbolsFilePath(symbolsPaths_); // also load from search path
277 }
278 if (loadSymboleWhenNeeded_) {
279 // load it when we need it
280 symbolsFiles_[symbolsFile->filePath_] = std::move(symbolsFile);
281 } else if (symbolsFile->LoadSymbols()) {
282 symbolsFiles_[symbolsFile->filePath_] = std::move(symbolsFile);
283 } else {
284 HLOGW("symbols file for '%s' not found.", fileName.c_str());
285 }
286 #ifdef HIPERF_DEBUG_TIME
287 auto usedTime = duration_cast<microseconds>(steady_clock::now() - startTime);
288 if (usedTime.count() != 0) {
289 HLOGV("cost %0.3f ms to load '%s'", usedTime.count() / MS_DUARTION, fileName.c_str());
290 }
291 updateSymbolsTimes_ += usedTime;
292 #endif
293 }
294
GetKernelSymbol(uint64_t ip,const std::vector<MemMapItem> & memMaps,const VirtualThread & thread)295 const Symbol VirtualRuntime::GetKernelSymbol(uint64_t ip, const std::vector<MemMapItem> &memMaps,
296 const VirtualThread &thread)
297 {
298 Symbol vaddrSymbol(ip, thread.name_);
299 for (auto &map : memMaps) {
300 if (ip > map.begin_ && ip < map.end_) {
301 HLOGM("found addr 0x%" PRIx64 " in kernel map 0x%" PRIx64 " - 0x%" PRIx64 " from %s",
302 ip, map.begin_, map.end_, map.name_.c_str());
303 vaddrSymbol.module_ = map.name_;
304 // found symbols by file name
305 auto search = symbolsFiles_.find(map.name_);
306 if (search != symbolsFiles_.end()) {
307 auto& symbolsFile = search->second;
308 vaddrSymbol.fileVaddr_ =
309 symbolsFile->GetVaddrInSymbols(ip, map.begin_, map.pageoffset_);
310 HLOGV("found symbol vaddr 0x%" PRIx64 " for runtime vaddr 0x%" PRIx64
311 " at '%s'",
312 vaddrSymbol.fileVaddr_, ip, map.name_.c_str());
313 if (!symbolsFile->SymbolsLoaded()) {
314 symbolsFile->LoadSymbols();
315 }
316 Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
317 foundSymbols.taskVaddr_ = ip;
318 if (!foundSymbols.isValid()) {
319 HLOGW("addr 0x%" PRIx64 " vaddr 0x%" PRIx64 " NOT found in symbol file %s",
320 ip, vaddrSymbol.fileVaddr_, map.name_.c_str());
321 return vaddrSymbol;
322 } else {
323 return foundSymbols;
324 }
325 }
326 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
327 map.name_.c_str());
328 } else {
329 HLOGM("addr 0x%" PRIx64 " not in map 0x%" PRIx64 " - 0x%" PRIx64 " from %s", ip,
330 map.begin_, map.end_, map.name_.c_str());
331 }
332 }
333 return vaddrSymbol;
334 }
335
GetUserSymbol(uint64_t ip,const VirtualThread & thread)336 const Symbol VirtualRuntime::GetUserSymbol(uint64_t ip, const VirtualThread &thread)
337 {
338 Symbol vaddrSymbol(ip, thread.name_);
339 auto [curMemMaps, itemIndex] = FindMap(ip);
340 if (curMemMaps != nullptr) {
341 auto symbolsFilesIter = symbolsFiles_.find(curMemMaps->name_);
342 if (symbolsFilesIter != symbolsFiles_.end()) {
343 auto symbolsFile = symbolsFilesIter->second.get();
344 symbolsFile->LoadDebugInfo();
345 vaddrSymbol.fileVaddr_ =
346 symbolsFile->GetVaddrInSymbols(ip, curMemMaps->maps_[itemIndex].begin_,
347 curMemMaps->maps_[itemIndex].pageoffset_);
348 vaddrSymbol.module_ = curMemMaps->maps_[itemIndex].nameHold_;
349 vaddrSymbol.symbolName_ = vaddrSymbol.Name();
350 if (!symbolsFile->SymbolsLoaded()) {
351 symbolsFile->LoadSymbols();
352 }
353 Symbol foundSymbols = symbolsFile->GetSymbolWithVaddr(vaddrSymbol.fileVaddr_);
354 foundSymbols.taskVaddr_ = ip;
355 foundSymbols.symbolName_ = foundSymbols.Name();
356 if (!foundSymbols.isValid()) {
357 vaddrSymbol.filePathId_ = curMemMaps->filePathId_;
358 return vaddrSymbol;
359 } else {
360 foundSymbols.filePathId_ = curMemMaps->filePathId_;
361 return foundSymbols;
362 }
363 } else {
364 HLOGW("addr 0x%" PRIx64 " in map but NOT found the symbol file %s", ip,
365 curMemMaps->name_.c_str());
366 }
367 } else {
368 HLOGW("ReportVaddrMapMiss");
369 #ifdef HIPERF_DEBUG
370 thread.ReportVaddrMapMiss(ip);
371 #endif
372 }
373 return vaddrSymbol;
374 }
375
GetSymbolCache(uint64_t ip,Symbol & symbol,const VirtualThread & thread)376 bool VirtualRuntime::GetSymbolCache(uint64_t ip, Symbol &symbol, const VirtualThread &thread)
377 {
378 auto [curMemMaps, itemIndex] = FindMap(ip);
379 if (curMemMaps != nullptr) {
380 auto foundSymbolIter = userSymbolCache_.find(std::pair(ip, curMemMaps->filePathId_));
381 if (foundSymbolIter != userSymbolCache_.end()) {
382 symbol = foundSymbolIter->second;
383 return true;
384 }
385 }
386 return false;
387 }
388
UpdateSymbolCache(uint64_t ip,Symbol & symbol,HashList<uint64_t,Symbol> & cache)389 void VirtualRuntime::UpdateSymbolCache(uint64_t ip, Symbol &symbol,
390 HashList<uint64_t, Symbol> &cache)
391 {
392 // review change to LRU for memmory
393 HLOG_ASSERT_MESSAGE(cache.count(ip) == 0, "already have cached ip 0x%" PRIx64 "", ip);
394 cache[ip] = symbol;
395 }
396
GetSymbol(CallFrame & callFrame,pid_t pid,pid_t tid,const perf_callchain_context & context)397 const Symbol VirtualRuntime::GetSymbol(CallFrame& callFrame, pid_t pid, pid_t tid,
398 const perf_callchain_context &context)
399 {
400 HLOGM("try find tid %u ip 0x%" PRIx64 " in %zu symbolsFiles ", tid, callFrame.ip_, symbolsFiles_.size());
401 Symbol symbol;
402 if (GetSymbolCache(callFrame.ip_, symbol, GetThread(pid, tid))) {
403 return symbol;
404 }
405 if (context == PERF_CONTEXT_USER or (context == PERF_CONTEXT_MAX and !symbol.isValid())) {
406 // check userspace memmap
407 symbol = GetUserSymbol(callFrame.ip_, GetThread(pid, tid));
408 if (symbol.isValid()) {
409 HLOGM("GetUserSymbol valid tid = %d ip = 0x%" PRIx64 "", tid, callFrame.ip_);
410 symbol.symbolId_ = userSymbolCache_.size() + 1;
411 if (hookConfig_.string_compressed()) {
412 FillSymbolNameId(callFrame, symbol);
413 FillFileSet(callFrame, symbol);
414 }
415 callFrame.needReport_ |= CALL_FRAME_REPORT;
416 userSymbolCache_[std::pair(callFrame.ip_, symbol.filePathId_)] = symbol;
417 } else {
418 HLOGM("GetUserSymbol invalid!");
419 }
420 }
421
422 return symbol;
423 }
424
SetSymbolsPaths(const std::vector<std::string> & symbolsPaths)425 bool VirtualRuntime::SetSymbolsPaths(const std::vector<std::string> &symbolsPaths)
426 {
427 std::unique_ptr<SymbolsFile> symbolsFile = SymbolsFile::CreateSymbolsFile(SYMBOL_UNKNOW_FILE);
428 // we need check if the path is accessable
429 bool accessable = symbolsFile->setSymbolsFilePath(symbolsPaths);
430 if (accessable) {
431 symbolsPaths_ = symbolsPaths;
432 } else {
433 if (!symbolsPaths.empty()) {
434 printf("some symbols path unable access\n");
435 }
436 }
437 return accessable;
438 }
439
FillMapsCache(std::string & currentFileName,MemMapItem & memMapItem)440 void VirtualRuntime::FillMapsCache(std::string& currentFileName, MemMapItem& memMapItem)
441 {
442 if (currentFileName.compare(memMapItem.name_) != 0) {
443 currentFileName = memMapItem.name_;
444 soBegin_ = memMapItem.begin_;
445 mapsCache_[memMapItem.begin_] =
446 MemMaps(memMapItem.begin_, memMapItem.end_, memMapItem.pageoffset_,
447 memMapItem.type_, ++memMapFilePathId_, memMapItem.name_);
448 } else {
449 if (auto curMemMapsIter = mapsCache_.find(soBegin_);
450 curMemMapsIter != mapsCache_.end()) {
451 auto& curMemMaps = curMemMapsIter->second;
452 curMemMaps.soEnd_ = memMapItem.end_;
453 curMemMaps.maps_.back().end_ = memMapItem.begin_;
454 curMemMaps.maps_.emplace_back(memMapItem.begin_, memMapItem.end_, memMapItem.type_,
455 memMapItem.pageoffset_, curMemMaps.name_);
456 if (memMapItem.type_ & PROT_EXEC) {
457 offlineMapAddr_.push_back(soBegin_);
458 }
459 }
460 }
461 }
462
FillSymbolNameId(CallFrame & callFrame,Symbol & symbol)463 inline void VirtualRuntime::FillSymbolNameId(CallFrame& callFrame, Symbol& symbol)
464 {
465 auto itFuntion = functionMap_.find(std::string(symbol.symbolName_));
466 if (itFuntion != functionMap_.end()) {
467 symbol.symbolNameId_ = itFuntion->second;
468 } else {
469 symbol.symbolNameId_ = functionMap_.size() + 1;
470 functionMap_[std::string(symbol.symbolName_)] = symbol.symbolNameId_;
471 callFrame.needReport_ |= SYMBOL_NAME_ID_REPORT;
472 }
473 }
474
FillFileSet(CallFrame & callFrame,const Symbol & symbol)475 inline void VirtualRuntime::FillFileSet(CallFrame& callFrame, const Symbol& symbol)
476 {
477 auto itFile = fileSet_.find(symbol.filePathId_);
478 if (itFile == fileSet_.end()) {
479 callFrame.needReport_ |= FILE_PATH_ID_REPORT;
480 fileSet_.insert(symbol.filePathId_);
481 }
482 }
483
HandleMapInfo(uint64_t begin,uint64_t length,uint32_t flags,uint64_t offset,const std::string & filePath)484 void VirtualRuntime::HandleMapInfo(uint64_t begin, uint64_t length, uint32_t flags,
485 uint64_t offset, const std::string& filePath)
486 {
487 if (!(flags & MAP_FIXED) || mapsCache_.empty()) {
488 if (mapsCache_.find(begin) != mapsCache_.end()) {
489 return;
490 }
491 soBegin_ = begin;
492 mapsCache_[begin] = MemMaps(begin, begin + length, offset, flags, ++memMapFilePathId_, filePath);
493 UpdateSymbols(filePath);
494 } else {
495 auto curMemMapsIter = mapsCache_.find(soBegin_);
496 if (curMemMapsIter != mapsCache_.end()) {
497 auto& curMemMaps = curMemMapsIter->second;
498 curMemMaps.soEnd_ = begin + length;
499 curMemMaps.maps_.back().end_ = begin;
500 curMemMaps.maps_.emplace_back(begin, begin + length, (uint16_t)flags, offset, curMemMaps.name_);
501 if (flags & PROT_EXEC) {
502 offlineMapAddr_.push_back(soBegin_);
503 }
504 }
505 }
506 }
507
RemoveMaps(uint64_t addr)508 void VirtualRuntime::RemoveMaps(uint64_t addr)
509 {
510 mapsCache_.erase(addr);
511 }
512
FindMap(uint64_t addr)513 std::pair<MemMaps*, uint32_t> VirtualRuntime::FindMap(uint64_t addr)
514 {
515 auto iter = mapsCache_.upper_bound(addr);
516 if (iter == mapsCache_.begin()) {
517 // have map 2 3 4 5
518 // find 1 , will return 2 (index 0, begin elem)
519 // this same as not found any thins
520 return {nullptr, 0};
521 }
522
523 auto& curMemMaps = (--iter)->second;
524 if (addr >= curMemMaps.soBegin_ && addr < curMemMaps.soEnd_) {
525 for (auto curMemMapItem = curMemMaps.maps_.begin();
526 curMemMapItem != curMemMaps.maps_.end(); ++curMemMapItem) {
527 if (addr >= curMemMapItem->begin_ && addr < curMemMapItem->end_) {
528 return {&(curMemMaps), curMemMapItem - curMemMaps.maps_.begin()};
529 }
530 }
531 }
532 return {nullptr, 0};
533 }
534 } // namespace NativeDaemon
535 } // namespace Developtools
536 } // namespace OHOS