• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifndef HIPERF_CALLSTACK_H
16 #define HIPERF_CALLSTACK_H
17 
18 #if HAVE_LIBUNWIND
19 // for libunwind.h empty struct has size 0 in c, size 1 in c++
20 #define UNW_EMPTY_STRUCT uint8_t unused;
21 #include <libunwind.h>
22 #endif
23 
24 #include <map>
25 #include <optional>
26 #include <string>
27 #include <vector>
28 
29 #if !is_mingw
30 #include <sys/mman.h>
31 #endif
32 
33 #include "hashlist.hpp"
34 #include "register.h"
35 #include "utilities.h"
36 #include "unwinder.h"
37 #include "virtual_thread.h"
38 
39 #if HAVE_LIBUNWIND
40 using ADDR_TYPE = unw_word_t;
41 #else
42 using ADDR_TYPE = uintptr_t;
43 #endif
44 
45 namespace OHOS {
46 namespace Developtools {
47 namespace HiPerf {
48 using namespace OHOS::HiviewDFX;
49 const int MAX_CALL_FRAME_EXPAND_CYCLE = 10;
50 const size_t MAX_CALL_FRAME_EXPAND_CACHE_SIZE = 10;
51 const size_t MAX_CALL_FRAME_UNWIND_SIZE = 256;
52 
53 struct UnwindInfo;
54 
55 class CallStack {
56 public:
57     CallStack();
58     ~CallStack();
59     bool UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum,
60                          const u8 *stack, u64 stackSize, std::vector<CallFrame> &,
61                          size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE);
62     size_t ExpandCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expandLimit = 1u);
63 
64 private:
65     pid_t lastPid_ = -1;
66     ADDR_TYPE lastAddr_ = 0;
67     ADDR_TYPE lastData_ = 0;
68     uint64_t stackPoint_ = 0;
69     uint64_t stackEnd_ = 0;
70     u64 *regs_ = nullptr; // not const , be cause we will fix it for arm64 cpu in UpdateRegForABI
71     u64 regsNum_ = 0;
72     const u8 *stack_ = nullptr;
73     u64 stackSize_ = 0;
74 
75     void LogFrame(const std::string msg, const std::vector<CallFrame> &frames);
76     size_t DoExpandCallStack(std::vector<CallFrame> &newCallFrames,
77                            const std::vector<CallFrame> &cachedCallFrames, size_t expandLimit);
78 
79     // we have a cache for all thread
80     std::map<pid_t, HashList<uint64_t, std::vector<CallFrame>>> cachedCallFramesMap_;
81     bool GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const;
82     ArchType arch_ = ArchType::ARCH_UNKNOWN;
83 
84     static bool ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE addr, ADDR_TYPE *data);
85 #if HAVE_LIBUNWIND
86     static const std::string GetUnwErrorName(int error);
87     static void dumpUDI(unw_dyn_info_t &di);
88     static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> map,
89                         const VirtualThread &thread);
90     static int FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
91                             int need_unwind_info, void *arg);
92     static int AccessMem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valuePoint,
93                          int writeOperation, void *arg);
94     static int AccessReg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valuePoint,
95                          int writeOperation, void *arg);
96     static void PutUnwindInfo(unw_addr_space_t as, unw_proc_info_t *pi, void *arg);
97     static int AccessFpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val,
98                            int writeOperation, void *arg);
99     static int GetDynInfoListAaddr(unw_addr_space_t as, unw_word_t *dil_vaddr, void *arg);
100     static int Resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg);
101     static int getProcName(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len,
102                            unw_word_t *offp, void *arg);
103     static int FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map,
104                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
105                                unw_proc_info_t *pi, int need_unwind_info, void *arg);
106     void UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callFrames, size_t maxStackLevel);
107     std::unordered_map<pid_t, unw_addr_space_t> unwindAddrSpaceMap_;
108 
109     using dsoUnwDynInfoMap = std::unordered_map<std::string, std::optional<unw_dyn_info_t>>;
110     std::unordered_map<pid_t, dsoUnwDynInfoMap> unwindTableInfoMap_;
111 
112     using unwMemoryCache = std::unordered_map<unw_word_t, unw_word_t>;
113     std::unordered_map<pid_t, unwMemoryCache> porcessMemoryMap_;
114 
115     unw_accessors_t accessors_ = {
116         .find_proc_info = FindProcInfo,
117         .put_unwind_info = PutUnwindInfo,
118         .get_dyn_info_list_addr = GetDynInfoListAaddr,
119         .access_mem = AccessMem,
120         .access_reg = AccessReg,
121         .access_fpreg = AccessFpreg,
122         .resume = Resume,
123         .get_proc_name = getProcName,
124     };
125     bool DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
126                   size_t maxStackLevel);
127 #endif
128 #if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
129 #ifdef target_cpu_arm64
130     static bool CheckAndStepArkFrame(const VirtualThread &thread, uintptr_t& pc, uintptr_t& fp, uintptr_t& sp);
131 #endif
132     bool DoUnwind2(const VirtualThread &thread, std::vector<CallFrame> &callStack, size_t maxStackLevel);
133     static void DumpTableInfo(UnwindTableInfo &outTableInfo);
134     static int FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr,
135                                uintptr_t pc, UnwindTableInfo& outTableInfo);
136     static int FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg);
137     static int AccessMem2(uintptr_t addr, uintptr_t *val, void *arg);
138 
139     // pid->unwinder(acc/regs/maps) cache
140     std::unordered_map<pid_t, std::shared_ptr<Unwinder>> pidUnwinder_;
141     // pid->elf->unwindtable cache
142     using DsoUnwindTableInfoMap = std::unordered_map<std::string, UnwindTableInfo>;
143     std::unordered_map<pid_t, DsoUnwindTableInfoMap> unwindTableInfoMap_;
144 
145     std::shared_ptr<UnwindAccessors> accessor_;
146 #endif
147 
148     FRIEND_TEST(CallStackTest, ExpendCallStackFullCache);
149     FRIEND_TEST(CallStackTest, LibUnwindEmptyFunc);
150     FRIEND_TEST(CallStackTest, GetUnwErrorName);
151 };
152 
153 struct UnwindInfo {
154     const VirtualThread &thread;
155     const u64 *regs;
156     size_t regNumber;
157     ArchType arch;
158     CallStack &callStack;
159 };
160 } // namespace HiPerf
161 } // namespace Developtools
162 } // namespace OHOS
163 #endif // HIPERF_CALLSTACK_H
164