• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 
16 #include "callstack.h"
17 
18 #include "callstack_log.h"
19 #include "mem_map_item.h"
20 #include "perf_event_record.h"
21 #include "symbols_file.h"
22 #include "virtual_thread.h"
23 
24 #include <string>
25 #ifdef HAVE_LIBUNWIND
26 #include <libunwind.h>
27 extern "C" {
28 #include <libunwind_i.h>
29 }
30 #endif
31 
32 #include "register.h"
33 #ifdef target_cpu_arm
34 // reg size is int (unw_word_t)
35 #define UNW_WORD_PFLAG "x"
36 #else
37 // reg size is long (unw_word_t)
38 #define UNW_WORD_PFLAG "zx"
39 #endif
40 namespace OHOS {
41 namespace HiviewDFX {
42 const std::string ARM_EXIDX = ".ARM.exidx";
43 
44 #ifdef HAVE_LIBUNWIND
45 const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = {
46     {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)},
47     {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)},
48     {UNW_ENOMEM, std::to_string(UNW_ENOMEM)},
49     {UNW_EBADREG, std::to_string(UNW_EBADREG)},
50     {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)},
51     {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)},
52     {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)},
53     {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)},
54     {UNW_EINVAL, std::to_string(UNW_EINVAL)},
55     {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)},
56     {UNW_ENOINFO, std::to_string(UNW_ENOINFO)},
57 };
GetUnwErrorName(int error)58 const std::string CallStack::GetUnwErrorName(int error)
59 {
60     if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) {
61         return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error));
62     } else {
63         return "UNKNOW_UNW_ERROR";
64     }
65 }
66 
dumpUDI(unw_dyn_info_t & di)67 void CallStack::dumpUDI(unw_dyn_info_t &di)
68 {
69     DFXLOG_DEBUG("unwind_table info: ");
70     DFXLOG_DEBUG(" di.start_ip:            0x%016" UNW_WORD_PFLAG "", di.start_ip);
71     DFXLOG_DEBUG(" di.end_ip:              0x%016" UNW_WORD_PFLAG "", di.end_ip);
72     DFXLOG_DEBUG(" di.u.rti.segbase:       0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase);
73     DFXLOG_DEBUG(" di.u.rti.table_data:    0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data);
74     DFXLOG_DEBUG(" di.u.rti.table_len:     0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len);
75 }
76 
77 #ifdef NATIVEDAEMON_USE_CALLSTACK
fillUDI(unw_dyn_info_t & di,SymbolsFile & symbolsFile,std::pair<MemMaps *,uint32_t> curMemMapsPair,const VirtualThread & thread)78 bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::pair<MemMaps*, uint32_t> curMemMapsPair,
79                         const VirtualThread &thread)
80 {
81     uint64_t fdeTableElfOffset = 0;
82     uint64_t fdeTableSize = 0;
83     uint64_t ehFrameHdrElfOffset = 0;
84     uint64_t SectionVaddr = 0;
85     uint64_t SectionSize = 0;
86     uint64_t SectionFileOffset = 0;
87     auto [curMemMaps, itemIndex] = curMemMapsPair;
88     di.start_ip = curMemMaps->maps_[itemIndex].begin_;
89     di.end_ip = curMemMaps->maps_[itemIndex].end_;
90 #ifndef __arm__
91     if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) &&
92         symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) {
93         /*
94             unw_word_t name_ptr;        // addr. of table name (e.g., library name)
95             unw_word_t segbase;         // segment base
96             unw_word_t table_len;       // must be a multiple of sizeof(unw_word_t)!
97             unw_word_t table_data;
98         */
99         /*
100             all the rti addr is offset of the elf file
101             begin - page offset = elf file base addr in vaddr user space
102             begin - page offset + elf offset = vaddr in real word.(for this thread)
103         */
104 
105         // segbase is file offset .
106         /*
107             00200000-00344000 r--p 00000000 08:02 46404365
108             00344000-005c4000 r-xp 00143000 08:02 46404365
109 
110             LOAD           0x00000000001439c0 0x00000000003449c0 0x00000000003449c0
111                             0x000000000027f3c0 0x000000000027f3c0  R E    0x1000
112 
113             GNU_EH_FRAME   0x00000000000f3248 0x00000000002f3248 0x00000000002f3248
114                             0x000000000000bb04 0x000000000000bb04  R      0x4
115 
116         */
117         const int32_t ehFrameMmapIndex = thread.FindMapByOffset(curMemMaps, ehFrameHdrElfOffset);
118         if (ehFrameMmapIndex == -1) {
119             DFXLOG_ERROR("no ehframe mmap found.");
120             return false;
121         }
122 
123         const auto& ehFrameMmap = curMemMaps->maps_[ehFrameMmapIndex];
124 
125         di.u.rti.segbase = ehFrameMmap.begin_ + ehFrameHdrElfOffset - ehFrameMmap.pageoffset_;
126         di.u.rti.table_data = ehFrameMmap.begin_ + fdeTableElfOffset - ehFrameMmap.pageoffset_;
127         di.u.rti.table_len = fdeTableSize / sizeof(unw_word_t);
128 
129         DFXLOG_DEBUG(" map pageoffset:         0x%016" PRIx64 "", curMemMaps->maps_[itemIndex].pageoffset_);
130         DFXLOG_DEBUG(" ehFrameHdrElfOffset:    0x%016" PRIx64 "", ehFrameHdrElfOffset);
131         DFXLOG_DEBUG(" fdeTableElfOffset:      0x%016" PRIx64 "", fdeTableElfOffset);
132         DFXLOG_DEBUG(" fdeTableSize:           0x%016" PRIx64 "", fdeTableSize);
133         return true;
134     } else {
135         DFXLOG_DEBUG("SymbolsFile::GetHDRSectionInfo() failed");
136     }
137 #else
138     if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) &&
139         symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) {
140         const int32_t targetIndex = thread.FindMapByOffset(curMemMaps, SectionFileOffset);
141         if (targetIndex == -1) {
142             DFXLOG_ERROR("no debug mmap found.");
143             return false;
144         }
145         const auto& targetMmap = curMemMaps->maps_[targetIndex];
146         DFXLOG_DEBUG(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMmap.begin_, targetMmap.pageoffset_);
147 
148         di.u.rti.table_data = targetMmap.begin_ + SectionFileOffset - targetMmap.pageoffset_;
149         di.u.rti.table_len = SectionSize;
150         DFXLOG_DEBUG(" SectionName:           %s", std::string(ARM_EXIDX).c_str());
151         DFXLOG_DEBUG(" SectionVaddrt:         0x%016" PRIx64 "", SectionVaddr);
152         DFXLOG_DEBUG(" SectionFileOffset      0x%016" PRIx64 "", SectionFileOffset);
153         DFXLOG_DEBUG(" SectionSize:           0x%016" PRIx64 "", SectionSize);
154 
155         // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ???
156         LOG_ASSERT(SectionVaddr != 0 && SectionSize != 0);
157         return true;
158     } else {
159         DFXLOG_DEBUG("SymbolsFile::GetSectionInfo() failed");
160     }
161 #endif
162     return false;
163 }
164 #else
fillUDI(unw_dyn_info_t & di,SymbolsFile & symbolsFile,const MemMapItem & mmap,const VirtualThread & thread)165 bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, const MemMapItem &mmap,
166                         const VirtualThread &thread)
167 {
168     uint64_t fdeTableElfOffset = 0;
169     uint64_t fdeTableSize = 0;
170     uint64_t ehFrameHdrElfOffset = 0;
171     uint64_t SectionVaddr = 0;
172     uint64_t SectionSize = 0;
173     uint64_t SectionFileOffset = 0;
174     di.start_ip = mmap.begin_;
175     di.end_ip = mmap.end_;
176 #ifndef __arm__
177     if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) &&
178         symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) {
179         /*
180             unw_word_t name_ptr;        // addr. of table name (e.g., library name)
181             unw_word_t segbase;         // segment base
182             unw_word_t table_len;       // must be a multiple of sizeof(unw_word_t)!
183             unw_word_t table_data;
184         */
185         /*
186             all the rti addr is offset of the elf file
187             begin - page offset = elf file base addr in vaddr user space
188             begin - page offset + elf offset = vaddr in real word.(for this thread)
189         */
190 
191         // segbase is file offset .
192         /*
193             00200000-00344000 r--p 00000000 08:02 46404365
194             00344000-005c4000 r-xp 00143000 08:02 46404365
195 
196             LOAD           0x00000000001439c0 0x00000000003449c0 0x00000000003449c0
197                             0x000000000027f3c0 0x000000000027f3c0  R E    0x1000
198 
199             GNU_EH_FRAME   0x00000000000f3248 0x00000000002f3248 0x00000000002f3248
200                             0x000000000000bb04 0x000000000000bb04  R      0x4
201 
202         */
203         const MemMapItem *ehFrameMmap = thread.FindMapByFileInfo(mmap.name_, ehFrameHdrElfOffset);
204 
205         if (ehFrameMmap == nullptr) {
206             DFXLOG_ERROR("no ehframe mmap found.");
207             return false;
208         }
209 
210         di.u.rti.segbase = ehFrameMmap->begin_ + ehFrameHdrElfOffset - ehFrameMmap->pageoffset_;
211         di.u.rti.table_data = ehFrameMmap->begin_ + fdeTableElfOffset - ehFrameMmap->pageoffset_;
212         di.u.rti.table_len = fdeTableSize / sizeof(unw_word_t);
213 
214         DFXLOG_DEBUG(" map pageoffset:         0x%016" PRIx64 "", mmap.pageoffset_);
215         DFXLOG_DEBUG(" ehFrameHdrElfOffset:    0x%016" PRIx64 "", ehFrameHdrElfOffset);
216         DFXLOG_DEBUG(" fdeTableElfOffset:      0x%016" PRIx64 "", fdeTableElfOffset);
217         DFXLOG_DEBUG(" fdeTableSize:           0x%016" PRIx64 "", fdeTableSize);
218         return true;
219     } else {
220         DFXLOG_DEBUG("SymbolsFile::GetHDRSectionInfo() failed");
221     }
222 #else
223     if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) &&
224         symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) {
225         const MemMapItem *targetMmap = thread.FindMapByFileInfo(mmap.name_, SectionFileOffset);
226         if (targetMmap == nullptr) {
227             DFXLOG_ERROR("no debug mmap found.");
228             return false;
229         }
230         DFXLOG_DEBUG(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMmap->begin_, targetMmap->pageoffset_);
231 
232         di.u.rti.table_data = targetMmap->begin_ + SectionFileOffset - targetMmap->pageoffset_;
233         di.u.rti.table_len = SectionSize;
234         DFXLOG_DEBUG(" SectionName:           %s", std::string(ARM_EXIDX).c_str());
235         DFXLOG_DEBUG(" SectionVaddrt:         0x%016" PRIx64 "", SectionVaddr);
236         DFXLOG_DEBUG(" SectionFileOffset      0x%016" PRIx64 "", SectionFileOffset);
237         DFXLOG_DEBUG(" SectionSize:           0x%016" PRIx64 "", SectionSize);
238 
239         // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ???
240         LOG_ASSERT(SectionVaddr != 0 && SectionSize != 0);
241         return true;
242     } else {
243         DFXLOG_DEBUG("SymbolsFile::GetSectionInfo() failed");
244     }
245 #endif
246     return false;
247 }
248 #endif // NATIVEDAEMON_USE_CALLSTACK
249 
250 /*
251     https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html
252 */
253 #ifdef NATIVEDAEMON_USE_CALLSTACK
FindUnwindTable(SymbolsFile * symbolsFile,std::pair<MemMaps *,uint32_t> curMemMapsPair,UnwindInfo * unwindInfoPtr,unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)254 int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, std::pair<MemMaps*, uint32_t> curMemMapsPair,
255                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
256                                unw_proc_info_t *pi, int need_unwind_info, void *arg)
257 #else
258 int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, const MemMapItem &mmap,
259                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
260                                unw_proc_info_t *pi, int need_unwind_info, void *arg)
261 #endif // NATIVEDAEMON_USE_CALLSTACK
262 {
263     DFXLOG_DEBUG("try search debug info at %s", symbolsFile->filePath_.c_str());
264     auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindDynInfoMap_;
265     // all the thread in same process have same mmap and symbols
266     if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) {
267         dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {});
268     }
269     dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_];
270     // find use dso name as key
271     if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) {
272         // we make a option empty value first
273         std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_];
274 
275         unw_dyn_info_t newdi;
276         if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) {
277             DFXLOG_ERROR("memset_s failed");
278             return -UNW_EUNSPEC;
279         }
280 #ifdef __arm__
281         // arm use .ARM.exidx , not use ehframe
282         newdi.format = UNW_INFO_FORMAT_ARM_EXIDX;
283 #else
284         // otherwise we use EH FRAME
285         newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE;
286 #endif
287 #ifdef NATIVEDAEMON_USE_CALLSTACK
288         if (fillUDI(newdi, *symbolsFile, curMemMapsPair, unwindInfoPtr->thread)) {
289 #else
290         if (fillUDI(newdi, *symbolsFile, mmap, unwindInfoPtr->thread)) {
291 #endif // NATIVEDAEMON_USE_CALLSTACK
292             dumpUDI(newdi);
293             odi = newdi;
294         } else {
295             DFXLOG_ERROR("fillUDI failed()");
296             return -UNW_EUNSPEC;
297         }
298     }
299 
300     LOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end());
301     LOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s",
302         symbolsFile->filePath_.c_str());
303     std::optional<unw_dyn_info_t> &odi =
304         dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_);
305 
306     if (odi.has_value()) {
307         unw_dyn_info_t &di = odi.value();
308         /*
309             we don't use dwarf_search_unwind_table
310             because in arm it will search two function:
311             1 arm_search_unwind_table first
312             2 dwarf_search_unwind_table
313 
314             see libunwind_i.h for arm
315             define tdep_search_unwind_table UNW_OBJ(search_unwind_table)
316 
317         */
318         int ret = static_cast<unw_error_t>(
319             tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg));
320 
321         DFXLOG_DEBUG("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str());
322 
323         if (UNW_ESUCCESS != ret) {
324             if (UNW_ENOINFO != ret) {
325                 DFXLOG_DEBUG("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str());
326             }
327             return -UNW_EUNSPEC;
328         } else {
329             return UNW_ESUCCESS;
330         }
331     } else {
332         DFXLOG_WARN("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_,
333             unwindInfoPtr->thread.name_.c_str());
334         return -UNW_EUNSPEC;
335     }
336 }
337 
338 int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
339                             int need_unwind_info, void *arg)
340 {
341     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
342 
343     DFXLOG_DEBUG("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip);
344 #ifdef NATIVEDAEMON_USE_CALLSTACK
345     auto curMemMapsPair = unwindInfoPtr->thread.FindMemMapsByAddr(ip);
346     auto [curMemMaps, itemIndex] = curMemMapsPair;
347     if (curMemMaps != nullptr) {
348         SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByName(curMemMaps->name_);
349         if (symbolsFile != nullptr) {
350             return FindUnwindTable(symbolsFile, curMemMapsPair, unwindInfoPtr, as, ip, pi,
351                                    need_unwind_info, arg);
352 #else
353     const MemMapItem *mmap = unwindInfoPtr->thread.FindMapByAddr(ip);
354     if (mmap != nullptr) {
355         SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(*mmap);
356         if (symbolsFile != nullptr) {
357             return FindUnwindTable(symbolsFile, *mmap, unwindInfoPtr, as, ip, pi, need_unwind_info, arg);
358 #endif // NATIVEDAEMON_USE_CALLSTACK
359         } else {
360             DFXLOG_WARN("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
361                 unwindInfoPtr->thread.name_.c_str());
362         }
363     } else {
364         DFXLOG_ERROR("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip,
365             unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
366     }
367 
368     return -UNW_EUNSPEC;
369 }
370 
371 bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, unw_word_t vaddr,
372                                         unw_word_t *data)
373 {
374 #if defined(HIPERF_USE_CALLSTACK)
375     if (__builtin_expect(unwindInfoPtr.thread.pid_ == unwindInfoPtr.callStack.lastPid_ &&
376         vaddr == unwindInfoPtr.callStack.lastAddr_, true)) {
377         *data = unwindInfoPtr.callStack.lastData_;
378         return true;
379     }
380 
381     if (unwindInfoPtr.thread.ReadRoMemory(vaddr, reinterpret_cast<uint8_t*>(data), sizeof(unw_word_t))) {
382         unwindInfoPtr.callStack.lastPid_ = unwindInfoPtr.thread.pid_;
383         unwindInfoPtr.callStack.lastAddr_ = vaddr;
384         unwindInfoPtr.callStack.lastData_ = *data;
385         return true;
386     } else {
387         unwindInfoPtr.callStack.lastPid_ = -1;
388         unwindInfoPtr.callStack.lastAddr_ = 0;
389         return false;
390     }
391 #elif defined(NATIVEDAEMON_USE_CALLSTACK)
392     auto process = unwindInfoPtr.callStack.porcessMemoryMap_.find(unwindInfoPtr.thread.pid_);
393     if (process != unwindInfoPtr.callStack.porcessMemoryMap_.end()) {
394         auto memory = process->second.find(vaddr);
395         if (memory != process->second.end()) {
396             *data = memory->second;
397             return true;
398         }
399     }
400 
401     if (unwindInfoPtr.thread.ReadRoMemory(vaddr, (uint8_t *)data, sizeof(unw_word_t))) {
402         unwindInfoPtr.callStack.porcessMemoryMap_[unwindInfoPtr.thread.pid_][vaddr] = *data;
403         return true;
404     } else {
405         return false;
406     }
407 #else
408 #error
409 #endif
410 }
411 
412 int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr,
413                          unw_word_t *valuePoint, int writeOperation, void *arg)
414 {
415     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
416     size_t stackOffset = 0;
417     *valuePoint = 0;
418 #ifdef NATIVEDAEMON_USE_CALLSTACK
419     DFXLOG_DEBUG("try access addr 0x%" UNW_WORD_PFLAG " ", addr);
420     LOG_ASSERT(writeOperation == 0);
421 #endif
422 
423     /* Check overflow. */
424     if (addr + sizeof(unw_word_t) < addr) {
425         DFXLOG_DEBUG("address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(unw_word_t));
426         return -UNW_EUNSPEC;
427     }
428 
429     if (addr < unwindInfoPtr->callStack.stackPoint_ or
430         addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) {
431         if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) {
432             DFXLOG_DEBUG("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *valuePoint);
433         } else {
434             DFXLOG_DEBUG("access_mem addr failed, from mmap, STACK RANGE 0x%" PRIx64 "- 0x%" PRIx64 "(0x%" PRIx64 ")",
435                   unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_,
436                   unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_);
437             return -UNW_EUNSPEC;
438         }
439     } else {
440         stackOffset = addr - unwindInfoPtr->callStack.stackPoint_;
441         *valuePoint = *(unw_word_t *)&unwindInfoPtr->callStack.stack_[stackOffset];
442         DFXLOG_DEBUG("access_mem addr val %" UNW_WORD_PFLAG ", from stack offset %zu", *valuePoint, stackOffset);
443     }
444 
445     return UNW_ESUCCESS;
446 }
447 
448 int CallStack::AccessReg([[maybe_unused]] unw_addr_space_t as, unw_regnum_t regnum,
449                          unw_word_t *valuePoint, int writeOperation, void *arg)
450 {
451     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
452     uint64_t val;
453     int perfRegIndex = LibunwindRegIdToPerfReg(regnum);
454 #if defined(HIPERF_USE_CALLSTACK)
455     if (perfRegIndex < 0) {
456         DFXLOG_DEBUG("can't read reg %d", perfRegIndex);
457         return perfRegIndex;
458     }
459 #elif defined(NATIVEDAEMON_USE_CALLSTACK)
460     if (perfRegIndex < PERF_REG_ARM64_X29) {
461         // libunwind not access other regs
462         DFXLOG_DEBUG("access_reg not expected %d", regnum);
463     }
464 #else
465 #error
466 #endif
467 
468     /* Don't support write, I suspect we don't need it. */
469     if (writeOperation) {
470         DFXLOG_DEBUG("access_reg %d", regnum);
471         return -UNW_EINVAL;
472     }
473 
474     if (unwindInfoPtr->callStack.regsNum_ == 0) {
475         return -UNW_EUNSPEC;
476     }
477 
478     if (!RegisterGetValue(val, unwindInfoPtr->callStack.regs_, perfRegIndex, unwindInfoPtr->callStack.regsNum_)) {
479         DFXLOG_DEBUG("can't read reg %zu", perfRegIndex);
480         return -UNW_EUNSPEC;
481     }
482 
483     *valuePoint = (unw_word_t)val;
484     DFXLOG_DEBUG("reg %d:%s, val 0x%" UNW_WORD_PFLAG "", regnum,
485         RegisterGetName(static_cast<size_t>(perfRegIndex)).c_str(), *valuePoint);
486     return UNW_ESUCCESS;
487 }
488 
489 void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as,
490                               [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg)
491 {
492 }
493 
494 int CallStack::AccessFpreg([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_regnum_t num,
495                            [[maybe_unused]] unw_fpreg_t *val, [[maybe_unused]] int writeOperation,
496                            [[maybe_unused]] void *arg)
497 {
498     return -UNW_EINVAL;
499 }
500 
501 int CallStack::GetDynInfoListAaddr([[maybe_unused]] unw_addr_space_t as,
502                                    [[maybe_unused]] unw_word_t *dil_vaddr,
503                                    [[maybe_unused]] void *arg)
504 {
505     return -UNW_ENOINFO;
506 }
507 
508 int CallStack::Resume([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_cursor_t *cu,
509                       [[maybe_unused]] void *arg)
510 {
511     return -UNW_EINVAL;
512 }
513 
514 int CallStack::getProcName([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_word_t addr,
515                            [[maybe_unused]] char *bufp, [[maybe_unused]] size_t buf_len,
516                            [[maybe_unused]] unw_word_t *offp, [[maybe_unused]] void *arg)
517 {
518     return -UNW_EINVAL;
519 }
520 
521 void CallStack::UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callStack, size_t maxStackLevel)
522 {
523     while (callStack.size() < maxStackLevel) {
524         int ret = unw_step(&c);
525         if (ret > 0) {
526             unw_word_t ip = 0;
527             unw_word_t sp = 0;
528             unw_get_reg(&c, UNW_REG_IP, &ip);
529             unw_get_reg(&c, UNW_REG_SP, &sp);
530 
531             if (ip == 0) {
532                 DFXLOG_DEBUG("ip == 0 something is wrong. break");
533                 break;
534             }
535 
536             /*
537              * Decrement the IP for any non-activation frames.
538              * this is required to properly find the srcline
539              * for caller frames.
540              * See also the documentation for dwfl_frame_pc(),
541              * which this code tries to replicate.
542              */
543             if (unw_is_signal_frame(&c) <= 0) {
544                 --ip;
545             }
546             DFXLOG_DEBUG("unwind:%zu: ip 0x%" UNW_WORD_PFLAG " sp 0x%" UNW_WORD_PFLAG "", callStack.size(), ip, sp);
547             if (callStack.back().ip_ == ip && callStack.back().sp_ == sp) {
548                 DFXLOG_DEBUG("we found a same frame, stop here");
549                 break;
550             }
551             callStack.emplace_back(ip, sp);
552         } else {
553             DFXLOG_DEBUG("no more frame step found. ret %d:%s", ret, GetUnwErrorName(ret).c_str());
554             break;
555         }
556     }
557 }
558 #endif
559 
560 bool CallStack::GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const
561 {
562     if (regNum > 0) {
563         if (!RegisterGetSPValue(sp, arch_, regs, regNum)) {
564             DFXLOG_WARN("unable get sp");
565             return false;
566         }
567         if (!RegisterGetIPValue(ip, arch_, regs, regNum)) {
568             DFXLOG_WARN("unable get ip");
569             return false;
570         }
571         if (ip != 0) {
572             return true;
573         }
574     } else {
575         DFXLOG_WARN("reg size is 0");
576         return false;
577     }
578     return false;
579 }
580 
581 #ifdef HAVE_LIBUNWIND
582 bool CallStack::DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
583                          size_t maxStackLevel)
584 {
585     unw_addr_space_t addr_space;
586     UnwindInfo unwindInfo = {
587         .thread = thread,
588         .callStack = *this,
589     };
590     unw_cursor_t c;
591     if (unwindAddrSpaceMap_.count(thread.tid_) == 0) {
592         addr_space = unw_create_addr_space(&accessors_, 0);
593         if (!addr_space) {
594             DFXLOG_ERROR("Can't create unwind vaddress space.");
595             return false;
596         }
597         unwindAddrSpaceMap_.emplace(thread.tid_, addr_space);
598         unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
599         unw_flush_cache(addr_space, 0, 0);
600     } else {
601         addr_space = unwindAddrSpaceMap_.at(thread.tid_);
602     }
603 
604     int ret = unw_init_remote(&c, addr_space, &unwindInfo);
605     if (ret) {
606         DFXLOG_ERROR("unwind error %d:%s see unw_error_t.", ret, GetUnwErrorName(ret).c_str());
607         return false;
608     } else {
609         UnwindStep(c, callStack, maxStackLevel);
610     }
611     return true;
612 }
613 #endif
614 
615 bool CallStack::UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum,
616                                 const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack,
617                                 size_t maxStackLevel)
618 {
619     regs_ = regs;
620     regsNum_ = regsNum;
621     stack_ = stack;
622     stackSize_ = stackSize;
623 
624     arch_ = GetArchTypeFromABI(abi32);
625     UpdateRegForABI(arch_, regs_);
626     if (!RegisterGetSPValue(stackPoint_, arch_, regs_, regsNum_)) {
627         DFXLOG_ERROR("RegisterGetSPValue failed");
628         return false;
629     } else {
630         stackEnd_ = stackPoint_ + stackSize_;
631     }
632 
633 #ifdef HAVE_LIBUNWIND
634     uint64_t ip;
635     uint64_t sp;
636     if (!GetIpSP(ip, sp, regs_, regsNum_)) {
637         DFXLOG_WARN("unable get sp or sp , unable unwind");
638         return false;
639     } else {
640         if (ip != 0) {
641             DFXLOG_DEBUG("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp);
642             callStack.emplace_back(ip, sp);
643         }
644     }
645 
646     /*
647      * If we need more than one entry, do the DWARF
648      * unwind itself.
649      */
650     if (maxStackLevel - 1 > 0) {
651         return DoUnwind(thread, callStack, maxStackLevel);
652     }
653 #endif
654     return true;
655 }
656 
657 bool CallStack::UnwindCallStack(const VirtualThread &thread, u64 *regs, u64 regsNum,
658                                 const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack,
659                                 size_t maxStackLevel)
660 {
661     regs_ = regs;
662     regsNum_ = regsNum;
663     stack_ = stack;
664     stackSize_ = stackSize;
665 
666     arch_ = buildArchType;
667     UpdateRegForABI(arch_, regs_);
668     if (!RegisterGetSPValue(stackPoint_, arch_, regs_, regsNum_)) {
669         DFXLOG_ERROR("RegisterGetSPValue failed");
670         return false;
671     } else {
672         stackEnd_ = stackPoint_ + stackSize_;
673     }
674 
675 #ifdef HAVE_LIBUNWIND
676     uint64_t ip;
677     uint64_t sp;
678     if (!GetIpSP(ip, sp, regs_, regsNum_)) {
679         DFXLOG_WARN("unable get sp or sp , unable unwind");
680         return false;
681     } else {
682         if (ip != 0) {
683             DFXLOG_DEBUG("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp);
684             callStack.emplace_back(ip, sp);
685         }
686     }
687 
688     /*
689      * If we need more than one entry, do the DWARF
690      * unwind itself.
691      */
692     if (maxStackLevel - 1 > 0) {
693         return DoUnwind(thread, callStack, maxStackLevel);
694     }
695 #endif
696     return true;
697 }
698 
699 void CallStack::LogFrame(const std::string msg, const std::vector<CallFrame> &frames)
700 {
701     DFXLOG_DEBUG("%s", msg.c_str());
702     int level = 0;
703     for (auto frame : frames) {
704         DFXLOG_DEBUG("%d:%s", level++, frame.ToString().c_str());
705     }
706 }
707 
708 /*
709 we should have CallStack cache for each thread
710 end                    begin
711 0. A -> B -> C -> E -> F
712 1.           C -> E -> F
713 2.      B -> C
714 3. A -> B -> C
715 4.      B -> G -> H
716 5.      J -> C
717 
718 0 is our cache
719 1 2 3... is from record
720 
721 use expandLimit to setup how may frame match is needs
722 
723 */
724 size_t CallStack::DoExpandCallStack(std::vector<CallFrame> &newCallFrames,
725                                     const std::vector<CallFrame> &cachedCallFrames,
726                                     size_t expandLimit)
727 {
728     int maxCycle = 0;
729 
730     if (expandLimit == 0 or newCallFrames.size() < expandLimit or
731         cachedCallFrames.size() < expandLimit) {
732         DFXLOG_DEBUG("expandLimit %zu not match new %zu cache %zu", expandLimit, newCallFrames.size(),
733             cachedCallFrames.size());
734         return 0; // size not enough
735     }
736 
737     // called (Stack Buttom) , this will NOT change when compare
738     // in case1 newIt -> C
739     // in case2 newIt -> B
740     const auto newIt = newCallFrames.end() - expandLimit;
741 
742     DFXLOG_DEBUG("try find new call chain bottom %s for limit %zu", newIt->ToString().c_str(), expandLimit);
743 
744     // first frame search, from called - > caller
745     // for case 2 it should found B
746     size_t distances = expandLimit - 1;
747     auto cachedIt = find(cachedCallFrames.begin(), cachedCallFrames.end(), *newIt);
748     if (cachedIt == cachedCallFrames.end()) {
749         DFXLOG_DEBUG("not found in first search");
750     }
751 
752     // cache frame found
753     while (std::distance(cachedIt, cachedCallFrames.end()) >= signed(expandLimit)) {
754         LOG_ASSERT_MESSAGE(maxCycle++ < MAX_CALL_FRAME_EXPAND_CYCLE, "MAX_UNWIND_CYCLE = %d reach",
755                            MAX_CALL_FRAME_EXPAND_CYCLE);
756 
757         if (std::equal(newIt, newIt + expandLimit, cachedIt)) {
758             DFXLOG_DEBUG("match %s + %zu", newIt->ToString().c_str(), expandLimit);
759             cachedIt += expandLimit; // in while we check the boundary safe
760             if (cachedIt == cachedCallFrames.end()) {
761                 // same but no more need expand
762                 break;
763             }
764 
765             // expand the frame and make some log ?
766             LogFrame("newCallStack:", newCallFrames);
767             LogFrame("cachedCallStack:", cachedCallFrames);
768 
769             newCallFrames.insert(newCallFrames.end(), cachedIt, cachedCallFrames.end());
770             auto expands = std::distance(cachedIt, cachedCallFrames.end());
771             DFXLOG_DEBUG("merge callstack increse to %zu (+%zd) ", newCallFrames.size(), expands);
772             // we done the deal
773             return expands;
774         } else {
775             // quick search next same farme again
776             cachedIt++;
777             if (cachedIt != cachedCallFrames.end()) {
778                 DFXLOG_DEBUG("search next");
779                 cachedIt = find(cachedIt, cachedCallFrames.end(), *newIt);
780             }
781         }
782     }
783     DFXLOG_DEBUG("cachedIt distance %zd , need %zd", std::distance(cachedCallFrames.begin(), cachedIt), distances);
784     return 0u; // nothing expand
785 }
786 
787 size_t CallStack::ExpandCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expandLimit)
788 {
789     size_t expand = 0u;
790     if (expandLimit == 0) {
791         return expand; // nothing need to do
792     } else if (callFrames.size() < expandLimit) {
793         DFXLOG_INFO("new callstack is too small, skip it");
794         return expand;
795     }
796     if (!cachedCallFramesMap_.count(tid)) {
797         cachedCallFramesMap_[tid].reserve(MAX_CALL_FRAME_EXPAND_CACHE_SIZE);
798     }
799     if (callFrames.size() >= 1u) {
800         // get top  (Earliest caller)
801         HashList<uint64_t, std::vector<CallFrame>> &cachedCallFrames = cachedCallFramesMap_[tid];
802         DFXLOG_DEBUG("find call stack frames in cache size %zu", cachedCallFrames.size());
803         // compare
804         using namespace std::rel_ops; // enable complement comparing operators
805         for (auto itr = cachedCallFrames.begin(); itr < cachedCallFrames.end(); ++itr) {
806             // each cached callstack
807             /*
808                 stack 2    1    0
809                 cache A -> B -> C
810                 new        B -> C
811                 check:
812                 1 if new B == cache C
813                 2 if new B == cache B
814                 3 if new C == new C (if limit > 0)
815                 4 insert A after B in new stack
816             */
817             const std::vector<CallFrame> &cachedCallStack = *itr;
818             if (cachedCallStack.size() < expandLimit) {
819                 DFXLOG_INFO("cache callstack is too small, skip it");
820                 continue; // check next
821             }
822             expand = DoExpandCallStack(callFrames, cachedCallStack, expandLimit);
823             if (expand > 0) {
824                 break;
825             }
826         }
827         // add new one in to cache cachedCallFrames.
828         // further optimization can be done by caching pointer which avoids copying
829         // vector
830         cachedCallFrames[callFrames[0].ip_] = callFrames;
831     }
832     DFXLOG_DEBUG("expand %zu", expand);
833     return expand;
834 }
835 
836 CallStack::CallStack() {}
837 
838 CallStack::~CallStack()
839 {
840 #ifdef HAVE_LIBUNWIND
841     for (auto &pair : unwindAddrSpaceMap_) {
842         unw_destroy_addr_space(pair.second);
843     }
844 #endif
845 }
846 } // namespace HiviewDFX
847 } // namespace OHOS
848