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>
23extern "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
36namespace OHOS {
37namespace Developtools {
38namespace NativeDaemon {
39bool 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
60const 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};
73const 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
82void 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
92bool 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*/
168int 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
239int 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
263int 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
298int 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
322void CallStack::PutUnwindInfo([[maybe_unused]] unw_addr_space_t as,
323                              [[maybe_unused]] unw_proc_info_t *pi, [[maybe_unused]] void *arg)
324{
325}
326
327int 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
334int 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
341int 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
347int 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
354void 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
394bool 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
410bool 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
440bool 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/*
485we should have CallStack cache for each thread
486
4870. A -> B -> C -> E -> F
4881.           C -> E -> F
4892.      B -> C
4903. A -> B -> C
4914.      B -> G -> H
4925.      J -> C
493
4940 is our cache
4951 2 3... is from record
496
497use expendLimit to setup how may frame match is needs
498
499*/
500#if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
501
502bool 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
551void 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
561int 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
598int 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
617int 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
649int 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
663CallStack::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
674CallStack::~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