• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
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 "CallStack"
16 
17 #include "call_stack.h"
18 #include "dfx_ark.h"
19 
20 #include <string>
21 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
22 #include <libunwind.h>
23 extern "C" {
24 #include <libunwind_i.h>
25 }
26 #endif
27 
28 #include "register.h"
29 #ifdef target_cpu_arm
30 // reg size is int (unw_word_t)
31 #define UNW_WORD_PFLAG "x"
32 #else
33 // reg size is long (unw_word_t)
34 #define UNW_WORD_PFLAG "zx"
35 #endif
36 namespace OHOS {
37 namespace Developtools {
38 namespace NativeDaemon {
ReadVirtualThreadMemory(UnwindInfo & unwindInfoPtr,ADDR_TYPE addr,ADDR_TYPE * data)39 bool CallStack::ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE addr,
40                                         ADDR_TYPE *data)
41 {
42     auto process = unwindInfoPtr.callStack.porcessMemoryMap_.find(unwindInfoPtr.thread.pid_);
43     if (process != unwindInfoPtr.callStack.porcessMemoryMap_.end()) {
44         auto memory = process->second.find(addr);
45         if (memory != process->second.end()) {
46             *data = memory->second;
47             return true;
48         }
49     }
50 
51     if (unwindInfoPtr.thread.ReadRoMemory(addr, reinterpret_cast<uint8_t *>(data), sizeof(ADDR_TYPE))) {
52         unwindInfoPtr.callStack.porcessMemoryMap_[unwindInfoPtr.thread.pid_][addr] = *data;
53         return true;
54     } else {
55         return false;
56     }
57 }
58 
59 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
60 const std::map<unw_error_t, const std::string> UNW_ERROR_MAP = {
61     {UNW_ESUCCESS, std::to_string(UNW_ESUCCESS)},
62     {UNW_EUNSPEC, std::to_string(UNW_EUNSPEC)},
63     {UNW_ENOMEM, std::to_string(UNW_ENOMEM)},
64     {UNW_EBADREG, std::to_string(UNW_EBADREG)},
65     {UNW_EREADONLYREG, std::to_string(UNW_EREADONLYREG)},
66     {UNW_ESTOPUNWIND, std::to_string(UNW_ESTOPUNWIND)},
67     {UNW_EINVALIDIP, std::to_string(UNW_EINVALIDIP)},
68     {UNW_EBADFRAME, std::to_string(UNW_EBADFRAME)},
69     {UNW_EINVAL, std::to_string(UNW_EINVAL)},
70     {UNW_EBADVERSION, std::to_string(UNW_EBADVERSION)},
71     {UNW_ENOINFO, std::to_string(UNW_ENOINFO)},
72 };
GetUnwErrorName(int error)73 const std::string CallStack::GetUnwErrorName(int error)
74 {
75     if (UNW_ERROR_MAP.count(static_cast<unw_error_t>(-error)) > 0) {
76         return UNW_ERROR_MAP.at(static_cast<unw_error_t>(-error));
77     } else {
78         return "UNKNOW_UNW_ERROR";
79     }
80 }
81 
dumpUDI(unw_dyn_info_t & di)82 void CallStack::dumpUDI(unw_dyn_info_t &di)
83 {
84     HLOGV("unwind_table info: ");
85     HLOGV(" di.start_ip:            0x%016" UNW_WORD_PFLAG "", di.start_ip);
86     HLOGV(" di.end_ip:              0x%016" UNW_WORD_PFLAG "", di.end_ip);
87     HLOGV(" di.u.rti.segbase:       0x%016" UNW_WORD_PFLAG "", di.u.rti.segbase);
88     HLOGV(" di.u.rti.table_data:    0x%016" UNW_WORD_PFLAG "", di.u.rti.table_data);
89     HLOGV(" di.u.rti.table_len:     0x%016" UNW_WORD_PFLAG "", di.u.rti.table_len);
90 }
91 
fillUDI(unw_dyn_info_t & di,SymbolsFile & symbolsFile,std::shared_ptr<DfxMap> map,const VirtualThread & thread)92 bool CallStack::fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> map,
93                         const VirtualThread &thread)
94 {
95     uint64_t fdeTableElfOffset = 0;
96     uint64_t fdeTableSize = 0;
97     uint64_t ehFrameHdrElfOffset = 0;
98     uint64_t SectionVaddr = 0;
99     uint64_t SectionSize = 0;
100     uint64_t SectionFileOffset = 0;
101     di.start_ip = map.begin;
102     di.end_ip = map.end;
103 #ifndef target_cpu_arm
104     if ((UNW_INFO_FORMAT_REMOTE_TABLE == di.format) &&
105         symbolsFile.GetHDRSectionInfo(ehFrameHdrElfOffset, fdeTableElfOffset, fdeTableSize)) {
106         /*
107             unw_word_t name_ptr;        // addr. of table name (e.g., library name)
108             unw_word_t segbase;         // segment base
109             unw_word_t table_len;       // must be a multiple of sizeof(unw_word_t)!
110             unw_word_t table_data;
111         */
112         /*
113             all the rti addr is offset of the elf file
114             begin - page offset = elf file base addr in vaddr user space
115             begin - page offset + elf offset = vaddr in real word.(for this thread)
116         */
117 
118         // segbase is file offset .
119         /*
120             00200000-00344000 r--p 00000000 08:02 46404365
121             00344000-005c4000 r-xp 00143000 08:02 46404365
122 
123             LOAD           0x00000000001439c0 0x00000000003449c0 0x00000000003449c0
124                             0x000000000027f3c0 0x000000000027f3c0  R E    0x1000
125 
126             GNU_EH_FRAME   0x00000000000f3248 0x00000000002f3248 0x00000000002f3248
127                             0x000000000000bb04 0x000000000000bb04  R      0x4
128 
129         */
130         auto ehFrameMap = thread.FindMapByFileInfo(map->name, ehFrameHdrElfOffset);
131         UNWIND_CHECK_NOTNULL(ehFrameMap, false, "no ehframe mmap found.");
132         di.u.rti.segbase = ehFrameMap->begin + ehFrameHdrElfOffset - ehFrameMap->offset;
133         di.u.rti.table_data = ehFrameMap->begin + fdeTableElfOffset - ehFrameMap->offset;
134         di.u.rti.table_len = fdeTableSize / sizeof(uintptr_t);
135 
136         HLOGV(" map pageoffset:         0x%016" PRIx64 "", map.offset);
137         HLOGV(" ehFrameHdrElfOffset:    0x%016" PRIx64 "", ehFrameHdrElfOffset);
138         HLOGV(" fdeTableElfOffset:      0x%016" PRIx64 "", fdeTableElfOffset);
139         HLOGV(" fdeTableSize:           0x%016" PRIx64 "", fdeTableSize);
140         return true;
141     }
142 #else
143     if ((UNW_INFO_FORMAT_ARM_EXIDX == di.format) &&
144         symbolsFile.GetSectionInfo(ARM_EXIDX, SectionVaddr, SectionSize, SectionFileOffset)) {
145         const MemMapItem *targetMmap = thread.FindMapByFileInfo(mmap.name_, SectionFileOffset);
146         UNWIND_CHECK_NOTNULL(targetMmap, false, "no debug mmap found.");
147         HLOGV(" begin: %" PRIx64 " offset:%" PRIx64 "", targetMmap->begin_,
148               targetMmap->pageoffset_);
149 
150         di.u.rti.table_data = targetMap->begin + SectionFileOffset - targetMap->offset;
151         di.u.rti.table_len = SectionSize;
152         HLOGV(" SectionName:           %s", std::string(ARM_EXIDX).c_str());
153         HLOGV(" SectionVaddrt:         0x%016" PRIx64 "", SectionVaddr);
154         HLOGV(" SectionFileOffset      0x%016" PRIx64 "", SectionFileOffset);
155         HLOGV(" SectionSize:           0x%016" PRIx64 "", SectionSize);
156 
157         // GetSectionInfo return true, but SectionVaddr || SectionSize is 0 ???
158         HLOG_ASSERT(SectionVaddr != 0 && SectionSize != 0);
159         return true;
160     }
161 #endif
162     return false;
163 }
164 
165 /*
166     https://www.nongnu.org/libunwind/man/libunwind-dynamic(3).html
167 */
FindUnwindTable(SymbolsFile * symbolsFile,std::shared_ptr<DfxMap> map,UnwindInfo * unwindInfoPtr,unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)168 int CallStack::FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map,
169                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
170                                unw_proc_info_t *pi, int need_unwind_info, void *arg)
171 {
172     HLOGV("try seach debug info at %s", symbolsFile->filePath_.c_str());
173     auto &dynInfoProcessMap = unwindInfoPtr->callStack.unwindTableInfoMap_;
174     // all the thread in same process have same map and symbols
175     if (dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) == dynInfoProcessMap.end()) {
176         dynInfoProcessMap.emplace(unwindInfoPtr->thread.pid_, dsoUnwDynInfoMap {});
177     }
178     dsoUnwDynInfoMap &dynFileMap = dynInfoProcessMap[unwindInfoPtr->thread.pid_];
179     // find use dso name as key
180     if (dynFileMap.find(symbolsFile->filePath_) == dynFileMap.end()) {
181         // we make a option empty value first
182         std::optional<unw_dyn_info_t> &odi = dynFileMap[symbolsFile->filePath_];
183 
184         unw_dyn_info_t newdi;
185         if (memset_s(&newdi, sizeof(unw_dyn_info_t), 0, sizeof(unw_dyn_info_t)) != EOK) {
186             HLOGE("memset_s failed");
187         }
188 #ifdef target_cpu_arm
189         // arm use .ARM.exidx , not use ehframe
190         newdi.format = UNW_INFO_FORMAT_ARM_EXIDX;
191 #else
192         // otherwise we use EH FRAME
193         newdi.format = UNW_INFO_FORMAT_REMOTE_TABLE;
194 #endif
195         if (fillUDI(newdi, *symbolsFile, map, unwindInfoPtr->thread)) {
196             dumpUDI(newdi);
197             odi = newdi;
198         }
199     }
200 
201     HLOG_ASSERT(dynInfoProcessMap.find(unwindInfoPtr->thread.pid_) != dynInfoProcessMap.end());
202     HLOG_ASSERT_MESSAGE(dynFileMap.find(symbolsFile->filePath_) != dynFileMap.end(), "%s",
203                         symbolsFile->filePath_.c_str());
204     std::optional<unw_dyn_info_t> &odi =
205         dynInfoProcessMap.at(unwindInfoPtr->thread.pid_).at(symbolsFile->filePath_);
206 
207     if (odi.has_value()) {
208         unw_dyn_info_t &di = odi.value();
209         /*
210             we dont use dwarf_search_unwind_table
211             because in arm it will search two function:
212             1 arm_search_unwind_table first
213             2 dwarf_search_unwind_table
214 
215             see libunwind_i.h for arm
216             define tdep_search_unwind_table UNW_OBJ(search_unwind_table)
217 
218         */
219         int ret = static_cast<unw_error_t>(
220             tdep_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg));
221 
222         HLOGM("search_unwind_table ret %d:%s", ret, GetUnwErrorName(ret).c_str());
223 
224         if (UNW_ESUCCESS != ret) {
225             if (UNW_ENOINFO != ret) {
226                 HLOGW("search_unwind_table ret error %d:%s", ret, GetUnwErrorName(ret).c_str());
227             }
228             return -UNW_EUNSPEC;
229         } else {
230             return UNW_ESUCCESS;
231         }
232     } else {
233         HLOGW("no debug info found for thread %d:%s", unwindInfoPtr->thread.tid_,
234               unwindInfoPtr->thread.name_.c_str());
235         return -UNW_EUNSPEC;
236     }
237 }
238 
FindProcInfo(unw_addr_space_t as,unw_word_t ip,unw_proc_info_t * pi,int need_unwind_info,void * arg)239 int CallStack::FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
240                             int need_unwind_info, void *arg)
241 {
242     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
243 
244     HLOGM("need_unwind_info ret %d ip %" UNW_WORD_PFLAG "", need_unwind_info, ip);
245     auto map = unwindInfoPtr->thread.FindMapByAddr(ip);
246     if (map != nullptr) {
247         SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(*map);
248         if (symbolsFile != nullptr) {
249             return FindUnwindTable(symbolsFile, map, unwindInfoPtr, as, ip, pi, need_unwind_info,
250                                    arg);
251         } else {
252             HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
253                   unwindInfoPtr->thread.name_.c_str());
254         }
255     } else {
256         HLOGE("ip 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", ip,
257               unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
258     }
259 
260     return -UNW_EUNSPEC;
261 }
262 
AccessMem(unw_addr_space_t as,unw_word_t addr,unw_word_t * valuePoint,int writeOperation,void * arg)263 int CallStack::AccessMem([[maybe_unused]] unw_addr_space_t as, unw_word_t addr,
264                          unw_word_t *valuePoint, int writeOperation, void *arg)
265 {
266     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
267     size_t stackOffset = 0;
268     *valuePoint = 0;
269     HLOGDUMMY("try access addr 0x%" UNW_WORD_PFLAG " ", addr);
270 
271     HLOG_ASSERT(writeOperation == 0);
272 
273     /* Check overflow. */
274     UNWIND_CHECK_TRUE(addr + sizeof(unw_word_t) >= addr, -UNW_EUNSPEC,
275                       "address overfolw at 0x%" UNW_WORD_PFLAG " increase 0x%zu",
276                       addr, sizeof(unw_word_t));
277 
278     if (addr < unwindInfoPtr->callStack.stackPoint_ or
279         addr + sizeof(unw_word_t) >= unwindInfoPtr->callStack.stackEnd_) {
280         if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, valuePoint)) {
281             HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *valuePoint);
282         } else {
283             HLOGW("access_mem addr failed, from mmap, ");
284             HLOGW("stack range 0x%" PRIx64 " -  0x%" PRIx64 "(0x%" PRIx64 ")",
285                   unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_,
286                   unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_);
287             return -UNW_EUNSPEC;
288         }
289     } else {
290         stackOffset = addr - unwindInfoPtr->callStack.stackPoint_;
291         *valuePoint = *(unw_word_t *)&unwindInfoPtr->callStack.stack_[stackOffset];
292         HLOGM("access_mem addr val %" UNW_WORD_PFLAG ", from stack offset %zu", *valuePoint, stackOffset);
293     }
294 
295     return UNW_ESUCCESS;
296 }
297 
AccessReg(unw_addr_space_t as,unw_regnum_t regnum,unw_word_t * valuePoint,int writeOperation,void * arg)298 int CallStack::AccessReg([[maybe_unused]] unw_addr_space_t as, unw_regnum_t regnum,
299                          unw_word_t *valuePoint, int writeOperation, void *arg)
300 {
301     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
302     uint64_t val;
303     size_t perfRegIndex = LibunwindRegIdToPerfReg(regnum);
304     if (perfRegIndex < PERF_REG_ARM64_X29) {
305         // libunwind not access other regs
306         HLOGE("access_reg not expected %d", regnum);
307     }
308     /* Don't support write, I suspect we don't need it. */
309     UNWIND_CHECK_TRUE(!writeOperation, -UNW_EINVAL, "access_reg %d", regnum);
310 
311     if (unwindInfoPtr->callStack.regsNum_ == 0) {
312         return -UNW_EUNSPEC;
313     }
314     UNWIND_CHECK_TRUE(
315         RegisterGetValue(val, unwindInfoPtr->callStack.regs_, perfRegIndex, unwindInfoPtr->callStack.regsNum_),
316         -UNW_EUNSPEC, "can't read reg %zu", perfRegIndex);
317     *valuePoint = (unw_word_t)val;
318     HLOGV("reg %d:%s, val 0x%" UNW_WORD_PFLAG "", regnum, RegisterGetName(perfRegIndex).c_str(), *valuePoint);
319     return UNW_ESUCCESS;
320 }
321 
PutUnwindInfo(unw_addr_space_t as,unw_proc_info_t * pi,void * arg)322 void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as,
323                               [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg)
324 {
325 }
326 
AccessFpreg(unw_addr_space_t as,unw_regnum_t num,unw_fpreg_t * val,int writeOperation,void * arg)327 int CallStack::AccessFpreg([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_regnum_t num,
328                            [[maybe_unused]] unw_fpreg_t *val, [[maybe_unused]] int writeOperation,
329                            [[maybe_unused]] void *arg)
330 {
331     return -UNW_EINVAL;
332 }
333 
GetDynInfoListAaddr(unw_addr_space_t as,unw_word_t * dil_vaddr,void * arg)334 int CallStack::GetDynInfoListAaddr([[maybe_unused]] unw_addr_space_t as,
335                                    [[maybe_unused]] unw_word_t *dil_vaddr,
336                                    [[maybe_unused]] void *arg)
337 {
338     return -UNW_ENOINFO;
339 }
340 
Resume(unw_addr_space_t as,unw_cursor_t * cu,void * arg)341 int CallStack::Resume([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_cursor_t *cu,
342                       [[maybe_unused]] void *arg)
343 {
344     return -UNW_EINVAL;
345 }
346 
getProcName(unw_addr_space_t as,unw_word_t addr,char * bufp,size_t buf_len,unw_word_t * offp,void * arg)347 int CallStack::getProcName([[maybe_unused]] unw_addr_space_t as, [[maybe_unused]] unw_word_t addr,
348                            [[maybe_unused]] char *bufp, [[maybe_unused]] size_t buf_len,
349                            [[maybe_unused]] unw_word_t *offp, [[maybe_unused]] void *arg)
350 {
351     return -UNW_EINVAL;
352 }
353 
UnwindStep(unw_cursor_t & c,std::vector<CallFrame> & callStack,size_t maxStackLevel)354 void CallStack::UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callStack, size_t maxStackLevel)
355 {
356     while (callStack.size() < maxStackLevel) {
357         int ret = unw_step(&c);
358         if (ret > 0) {
359             unw_word_t ip = 0;
360             unw_word_t sp = 0;
361             unw_get_reg(&c, UNW_REG_IP, &ip);
362             unw_get_reg(&c, UNW_REG_SP, &sp);
363 
364             if (ip == 0) {
365                 HLOGD("ip == 0 something is wrong. break");
366                 break;
367             }
368 
369             /*
370              * Decrement the IP for any non-activation frames.
371              * this is required to properly find the srcline
372              * for caller frames.
373              * See also the documentation for dwfl_frame_pc(),
374              * which this code tries to replicate.
375              */
376             if (unw_is_signal_frame(&c) <= 0) {
377                 --ip;
378             }
379             HLOGV("unwind:%zu: ip 0x%" UNW_WORD_PFLAG " sp 0x%" UNW_WORD_PFLAG "", callStack.size(),
380                   ip, sp);
381             if (callStack.back().ip_ == ip && callStack.back().sp_ == sp) {
382                 HLOGW("we found a same frame, stop here");
383                 break;
384             }
385             callStack.emplace_back(ip, sp);
386         } else {
387             HLOGV("no more frame step found. ret %d:%s", ret, GetUnwErrorName(ret).c_str());
388             break;
389         }
390     }
391 }
392 #endif
393 
GetIpSP(uint64_t & ip,uint64_t & sp,const u64 * regs,size_t regNum) const394 bool CallStack::GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const
395 {
396     if (regNum > 0) {
397         UNWIND_CHECK_TRUE(RegisterGetSPValue(sp, arch_, regs, regNum), false, "unable get sp");
398         UNWIND_CHECK_TRUE(RegisterGetIPValue(ip, arch_, regs, regNum), false, "unable get ip");
399         if (ip != 0) {
400             return true;
401         }
402     } else {
403         HLOGW("reg size is 0");
404         return false;
405     }
406     return false;
407 }
408 
409 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
DoUnwind(const VirtualThread & thread,std::vector<CallFrame> & callStack,size_t maxStackLevel)410 bool CallStack::DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
411                          size_t maxStackLevel)
412 {
413     unw_addr_space_t addr_space;
414     UnwindInfo unwindInfo = {
415         .thread = thread,
416         .callStack = *this,
417     };
418     unw_cursor_t c;
419     if (unwindAddrSpaceMap_.count(thread.tid_) == 0) {
420         addr_space = unw_create_addr_space(&accessors_, 0);
421         UNWIND_CHECK_TRUE(addr_space, false, "Can't create unwind vaddress space.");
422         unwindAddrSpaceMap_.emplace(thread.tid_, addr_space);
423         unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
424         unw_flush_cache(addr_space, 0, 0);
425     } else {
426         addr_space = unwindAddrSpaceMap_.at(thread.tid_);
427     }
428 
429     int ret = unw_init_remote(&c, addr_space, &unwindInfo);
430     if (ret) {
431         HLOGE("unwind error %d:%s see unw_error_t.", ret, GetUnwErrorName(ret).c_str());
432         return false;
433     } else {
434         UnwindStep(c, callStack, maxStackLevel);
435     }
436     return true;
437 }
438 #endif
439 
UnwindCallStack(const VirtualThread & thread,u64 * regs,u64 regsNum,const u8 * stack,u64 stackSize,std::vector<CallFrame> & callStack,size_t maxStackLevel,int maxjsDepth,bool jsReport)440 bool CallStack::UnwindCallStack(const VirtualThread &thread, u64 *regs, u64 regsNum,
441                                 const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack,
442                                 size_t maxStackLevel, int maxjsDepth, bool jsReport)
443 {
444     regs_ = regs;
445     regsNum_ = regsNum;
446     stack_ = stack;
447     stackSize_ = stackSize;
448 
449     arch_ = buildArchType;
450     UpdateRegForABI(arch_, regs_);
451     if (!RegisterGetSPValue(stackPoint_, arch_, regs_, regsNum_)) {
452         HLOGE("RegisterGetSPValue failed");
453         return false;
454     } else {
455         stackEnd_ = stackPoint_ + stackSize_;
456     }
457 
458     uint64_t ip = 0;
459     uint64_t sp = 0;
460     if (!GetIpSP(ip, sp, regs_, regsNum_)) {
461         HLOGW("unable get sp or sp , unable unwind");
462         return false;
463     } else {
464         if (ip != 0) {
465             HLOGV("unwind:%zu: ip 0x%" PRIx64 " sp 0x%" PRIx64 "", callStack.size(), ip, sp);
466             callStack.emplace_back(ip, sp);
467         }
468     }
469 
470     /*
471      * If we need more than one entry, do the DWARF
472      * unwind itself.
473      */
474     if (maxStackLevel - 1 > 0) {
475 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
476         return DoUnwind(thread, callStack, maxStackLevel);
477 #else
478         return DoUnwind2(thread, callStack, maxStackLevel, maxjsDepth, jsReport);
479 #endif
480     }
481     return true;
482 }
483 
484 /*
485 we should have CallStack cache for each thread
486 
487 0. A -> B -> C -> E -> F
488 1.           C -> E -> F
489 2.      B -> C
490 3. A -> B -> C
491 4.      B -> G -> H
492 5.      J -> C
493 
494 0 is our cache
495 1 2 3... is from record
496 
497 use expendLimit to setup how may frame match is needs
498 
499 */
500 #if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
501 
DoUnwind2(const VirtualThread & thread,std::vector<CallFrame> & callStack,size_t maxStackLevel,int maxjsDepth,bool jsReport)502 bool CallStack::DoUnwind2(const VirtualThread &thread, std::vector<CallFrame> &callStack,
503                           size_t maxStackLevel, int maxjsDepth, bool jsReport)
504 {
505     UnwindInfo unwindInfo = {
506         .thread = thread,
507         .callStack = *this,
508     };
509 
510     if (pidUnwinder_.count(thread.pid_) == 0) {
511         pidUnwinder_.emplace(thread.pid_, std::make_shared<Unwinder>(accessor_));
512     }
513     auto unwinder = pidUnwinder_[thread.pid_];
514 
515 #ifdef target_cpu_arm
516     std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm>();
517     std::vector<uintptr_t> tempRegs;
518     for (size_t i = 0; i < regsNum_; ++i) {
519         tempRegs.push_back((uintptr_t)regs_[i]);
520     }
521     regs->SetRegsData(tempRegs);
522 #else
523     std::shared_ptr<DfxRegs> regs = std::make_shared<DfxRegsArm64>();
524     regs->SetRegsData(reinterpret_cast<uintptr_t*>(regs_), regsNum_);
525 #endif
526 
527     unwinder->SetRegs(regs);
528 
529     unwinder->Unwind(&unwindInfo, maxStackLevel + maxjsDepth);
530     std::vector<DfxFrame> dfxcallStack;
531 
532     dfxcallStack = unwinder->GetFrames();
533     callStack.clear();
534     if (jsReport) {
535         for (auto& frame: dfxcallStack) {
536             HLOGD("pc 0x%" PRIx64 " sp 0x%" PRIx64 " isJsFrame = %d", frame.pc, frame.sp, frame.isJsFrame);
537             callStack.emplace_back(frame.pc, frame.sp, frame.isJsFrame);
538         }
539     } else {
540         for (auto& frame: dfxcallStack) {
541             if (frame.isJsFrame) {
542                 break;
543             }
544             HLOGD("pc 0x%" PRIx64 " sp 0x%" PRIx64 " isJsFrame = %d", frame.pc, frame.sp, frame.isJsFrame);
545             callStack.emplace_back(frame.pc, frame.sp, frame.isJsFrame);
546         }
547     }
548     return true;
549 }
550 
DumpTableInfo(UnwindTableInfo & outTableInfo)551 void CallStack::DumpTableInfo(UnwindTableInfo &outTableInfo)
552 {
553     HLOGV("unwind_table info: ");
554     HLOGV(" start_ip:            0x%016" UNW_WORD_PFLAG "", outTableInfo.startPc);
555     HLOGV(" end_ip:              0x%016" UNW_WORD_PFLAG "", outTableInfo.endPc);
556     HLOGV(" segbase:             0x%016" UNW_WORD_PFLAG "", outTableInfo.segbase);
557     HLOGV(" table_data:          0x%016" UNW_WORD_PFLAG "", outTableInfo.tableData);
558     HLOGV(" table_len:           0x%016" UNW_WORD_PFLAG "", outTableInfo.tableLen);
559 }
560 
FillUnwindTable(SymbolsFile * symbolsFile,std::shared_ptr<DfxMap> map,UnwindInfo * unwindInfoPtr,uintptr_t pc,UnwindTableInfo & outTableInfo)561 int CallStack::FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr,
562                                uintptr_t pc, UnwindTableInfo& outTableInfo)
563 {
564     HLOGM("try search debug info at %s", symbolsFile->filePath_.c_str());
565     auto &tableInfoMap = unwindInfoPtr->callStack.unwindTableInfoMap_;
566     // all the thread in same process have same mmap and symbols
567     if (tableInfoMap.find(unwindInfoPtr->thread.pid_) == tableInfoMap.end()) {
568         tableInfoMap.emplace(unwindInfoPtr->thread.pid_, DsoUnwindTableInfoMap {});
569     }
570     DsoUnwindTableInfoMap &unwTabMap = tableInfoMap[unwindInfoPtr->thread.pid_];
571     // find use dso name as key
572     if (unwTabMap.find(symbolsFile->filePath_) == unwTabMap.end()) {
573         UnwindTableInfo uti;
574         auto elf = symbolsFile->GetElfFile();
575         if (elf == nullptr) {
576             return -1;
577         }
578         if (elf->FindUnwindTableInfo(pc, map, uti) == 0) {
579             if (uti.format == -1) {
580                 HLOGV("parse unwind table failed.");
581                 return -1;
582             }
583             unwTabMap[symbolsFile->filePath_] = uti;
584             outTableInfo = unwTabMap[symbolsFile->filePath_];
585             DumpTableInfo(uti);
586             return 0;
587         } else {
588             HLOGV("FillUnwindTable failed");
589             return -1;
590         }
591     } else {
592         outTableInfo = unwTabMap[symbolsFile->filePath_];
593         return 0;
594     }
595     return -1;
596 }
597 
FindUnwindTable(uintptr_t pc,UnwindTableInfo & outTableInfo,void * arg)598 int CallStack::FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg)
599 {
600     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
601     auto map = unwindInfoPtr->thread.FindMapByAddr(pc);
602     if (map != nullptr) {
603         SymbolsFile *symbolsFile = unwindInfoPtr->thread.FindSymbolsFileByMap(map);
604         if (symbolsFile != nullptr) {
605             return FillUnwindTable(symbolsFile, map, unwindInfoPtr, pc, outTableInfo);
606         } else {
607             HLOGW("no symbols file found for thread %d:%s", unwindInfoPtr->thread.tid_,
608                   unwindInfoPtr->thread.name_.c_str());
609         }
610     } else {
611         HLOGE("pc 0x%016" UNW_WORD_PFLAG " not found in thread %d:%s", pc,
612               unwindInfoPtr->thread.tid_, unwindInfoPtr->thread.name_.c_str());
613     }
614     return -1;
615 }
616 
AccessMem2(uintptr_t addr,uintptr_t * val,void * arg)617 int CallStack::AccessMem2(uintptr_t addr, uintptr_t *val, void *arg)
618 {
619     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
620     *val = 0;
621 
622     /* Check overflow. */
623     if (addr + sizeof(uintptr_t) < addr) {
624         HLOGE("address overflow at 0x%" UNW_WORD_PFLAG " increase 0x%zu", addr, sizeof(uintptr_t));
625         return -1;
626     }
627 
628     if (addr < unwindInfoPtr->callStack.stackPoint_ or
629         addr + sizeof(uintptr_t) >= unwindInfoPtr->callStack.stackEnd_) {
630         if (ReadVirtualThreadMemory(*unwindInfoPtr, addr, val)) {
631             HLOGM("access_mem addr get val 0x%" UNW_WORD_PFLAG ", from mmap", *val);
632         } else {
633             HLOGW("access_mem mmap 0x%" PRIx64 " failed, STACK RANGE 0x%" PRIx64 "- 0x%" PRIx64 "(0x%" PRIx64 ")",
634                   (uint64_t)addr,
635                   unwindInfoPtr->callStack.stackPoint_, unwindInfoPtr->callStack.stackEnd_,
636                   unwindInfoPtr->callStack.stackEnd_ - unwindInfoPtr->callStack.stackPoint_);
637             return -1;
638         }
639     } else {
640         size_t stackOffset = addr - unwindInfoPtr->callStack.stackPoint_;
641         *val = *(uintptr_t *)&unwindInfoPtr->callStack.stack_[stackOffset];
642         HLOGM("access_mem addr %p val %" UNW_WORD_PFLAG ", from stack offset %zu",
643               reinterpret_cast<void *>(addr), *val, stackOffset);
644     }
645 
646     return 0;
647 }
648 
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map,void * arg)649 int CallStack::GetMapByPc([[maybe_unused]] uintptr_t pc, [[maybe_unused]] std::shared_ptr<DfxMap>& map,
650                           [[maybe_unused]] void *arg)
651 {
652     UnwindInfo *unwindInfoPtr = static_cast<UnwindInfo *>(arg);
653     map = unwindInfoPtr->thread.FindMapByAddr(pc);
654     if (map != nullptr) {
655         HLOGW("GetMapByPc success");
656         return 0;
657     }
658     HLOGW("GetMapByPc failed");
659     return -1;
660 }
661 #endif
662 
CallStack()663 CallStack::CallStack()
664 {
665 #if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
666     accessor_ = std::make_shared<OHOS::HiviewDFX::UnwindAccessors>();
667     accessor_->FindUnwindTable = &CallStack::FindUnwindTable;
668     accessor_->AccessMem = &CallStack::AccessMem2;
669     accessor_->AccessReg = nullptr;
670     accessor_->GetMapByPc = &CallStack::GetMapByPc;
671 #endif
672 }
673 
~CallStack()674 CallStack::~CallStack()
675 {
676 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
677     for (auto &pair : unwindAddrSpaceMap_) {
678         unw_destroy_addr_space(pair.second);
679     }
680 #endif
681 }
682 } // namespace NativeDaemon
683 } // namespace Developtools
684 } // namespace OHOS