1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2021. 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 #ifndef HIPERF_CALLSTACK_H
16 #define HIPERF_CALLSTACK_H
17 
18 #include <map>
19 #include <string>
20 #include <unordered_map>
21 #include <vector>
22 
23 #include "hashlistpp.h"
24 #include "perf_event_record.h"
25 #include "unwinder.h"
26 #include "virtual_thread.h"
27 #include "hook_common.h"
28 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
29 using ADDR_TYPE = unw_word_t;
30 #else
31 using ADDR_TYPE = uintptr_t;
32 #endif
33 namespace OHOS {
34 namespace Developtools {
35 namespace NativeDaemon {
36 using namespace OHOS::HiviewDFX;
37 
38 struct UnwindInfo;
39 
40 class CallStack {
41 public:
42     CallStack();
43     ~CallStack();
44     bool UnwindCallStack(const VirtualThread &thread, u64 *regs, u64 regsNum,
45                          const u8 *stack, u64 stackSize, std::vector<CallFrame> &,
46                          size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE, int maxjsDepth = 0,
47                          bool jsReport = true);
48     size_t ExpendCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expendLimit = 1u);
49 
50 private:
51     uint64_t stackPoint_ = 0;
52     uint64_t stackEnd_ = 0;
53     u64 *regs_ = nullptr; // not const , be cause we will fix it for arm64 cpu in UpdateRegForABI
54     u64 regsNum_ = 0;
55     const u8 *stack_ = nullptr;
56     u64 stackSize_ = 0;
57 
58     void LogFrame(const std::string msg, const std::vector<CallFrame> &frames);
59     size_t ExpendCallStack(std::vector<CallFrame> &newCallFrames,
60                            const std::vector<CallFrame> &cachedCallFrames, size_t expendLimit);
61 
62     // we have a cache for all thread
63     std::map<pid_t, HashList<uint64_t, std::vector<CallFrame>>> cachedCallFramesMap_;
64     bool GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const;
65     ArchType arch_ = ArchType::ARCH_UNKNOWN;
66     using unwMemoryCache = std::unordered_map<ADDR_TYPE, ADDR_TYPE>;
67     std::unordered_map<pid_t, unwMemoryCache> porcessMemoryMap_;
68     static bool ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE addr,
69                                         ADDR_TYPE *data);
70 #if defined(HAVE_LIBUNWIND) && HAVE_LIBUNWIND
71     static const std::string GetUnwErrorName(int error);
72     static void dumpUDI(unw_dyn_info_t &di);
73     static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> mapmmap,
74                         const VirtualThread &thread);
75     static int FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
76                             int need_unwind_info, void *arg);
77     static int AccessMem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valuePoint,
78                          int writeOperation, void *arg);
79     static int AccessReg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valuePoint,
80                          int writeOperation, void *arg);
81     static void PutUnwindInfo(unw_addr_space_t as, unw_proc_info_t *pi, void *arg);
82     static int AccessFpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val,
83                            int writeOperation, void *arg);
84     static int GetDynInfoListAaddr(unw_addr_space_t as, unw_word_t *dil_vaddr, void *arg);
85     static int Resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg);
86     static int getProcName(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len,
87                            unw_word_t *offp, void *arg);
88     static int FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> mapmap,
89                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
90                                unw_proc_info_t *pi, int need_unwind_info, void *arg);
91     void UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callFrames, size_t maxStackLevel);
92     std::unordered_map<pid_t, unw_addr_space_t> unwindAddrSpaceMap_;
93 
94     using dsoUnwDynInfoMap = std::unordered_map<std::string, std::optional<unw_dyn_info_t>>;
95     std::unordered_map<pid_t, dsoUnwDynInfoMap> unwindTableInfoMap_;
96     unw_accessors_t accessors_ = {
97         .find_proc_info = FindProcInfo,
98         .put_unwind_info = PutUnwindInfo,
99         .get_dyn_info_list_addr = GetDynInfoListAaddr,
100         .access_mem = AccessMem,
101         .access_reg = AccessReg,
102         .access_fpreg = AccessFpreg,
103         .resume = Resume,
104         .get_proc_name = getProcName,
105     };
106     bool DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
107                   size_t maxStackLevel);
108 #endif
109 #if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
110 #ifdef target_cpu_arm64
111     static bool CheckAndStepArkFrame(const VirtualThread &thread, uintptr_t& pc, uintptr_t& fp, uintptr_t& sp);
112 #endif
113     bool DoUnwind2(const VirtualThread &thread, std::vector<CallFrame> &callStack, size_t maxStackLevel,
114                    int maxjsDepth = 0, bool jsReport = true);
115     static void DumpTableInfo(UnwindTableInfo &outTableInfo);
116     static int FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr,
117                                uintptr_t pc, UnwindTableInfo& outTableInfo);
118     static int FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg);
119     static int AccessMem2(uintptr_t addr, uintptr_t *val, void *arg);
120     static int GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg);
121 
122     // pid->unwinder(acc/regs/maps) cache
123     std::unordered_map<pid_t, std::shared_ptr<Unwinder>> pidUnwinder_;
124     // pid->elf->unwindtable cache
125     using DsoUnwindTableInfoMap = std::unordered_map<std::string, UnwindTableInfo>;
126     std::unordered_map<pid_t, DsoUnwindTableInfoMap> unwindTableInfoMap_;
127 
128     std::shared_ptr<UnwindAccessors> accessor_;
129 #endif
130 };
131 
132 struct UnwindInfo {
133     const VirtualThread &thread;
134     const u64 *regs;
135     size_t regNumber;
136     ArchType arch;
137     CallStack &callStack;
138 };
139 } // namespace NativeDaemon
140 } // namespace Developtools
141 } // namespace OHOS
142 #endif