• 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 #ifndef OHOS_HIVIEWDFX_CALLSTACK_H
17 #define OHOS_HIVIEWDFX_CALLSTACK_H
18 #ifdef 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 "dfx_types.h"
34 #include "hashlist.hpp"
35 #include "register.h"
36 
37 namespace OHOS {
38 #ifdef HIPERF_USE_CALLSTACK
39 namespace Developtools {
40 namespace HiPerf {
41 class SymbolsFile;
42 class VirtualThread;
43 class MemMapItem;
44 struct CallFrame;
45 } // namespace HiPerf
46 } // namespace Developtools
47 #endif // HIPERF_USE_CALLSTACK
48 
49 #ifdef NATIVEDAEMON_USE_CALLSTACK
50 namespace Developtools {
51 namespace NativeDaemon {
52 class SymbolsFile;
53 class VirtualThread;
54 class MemMaps;
55 class MemMapItem;
56 struct CallFrame;
57 } // namespace NativeDaemon
58 } // namespace Developtools
59 #endif // NATIVEDAEMON_USE_CALLSTACK
60 
61 namespace HiviewDFX {
62 #ifdef HIPERF_USE_CALLSTACK
63 using namespace OHOS::Developtools::HiPerf;
64 const size_t MAX_CALL_FRAME_UNWIND_SIZE = 256;
65 #endif // HIPERF_USE_CALLSTACK
66 
67 #ifdef NATIVEDAEMON_USE_CALLSTACK
68 using namespace OHOS::Developtools::NativeDaemon;
69 const size_t MAX_CALL_FRAME_UNWIND_SIZE = 102;
70 #endif // NATIVEDAEMON_USE_CALLSTACK
71 
72 const int MAX_CALL_FRAME_EXPAND_CYCLE = 10;
73 const size_t MAX_CALL_FRAME_EXPAND_CACHE_SIZE = 10;
74 
75 #ifdef HAVE_LIBUNWIND
76 struct UnwindInfo;
77 #endif
78 
79 class CallStack {
80 public:
81     CallStack();
82     ~CallStack();
83 
84     /**
85      * @brief unwind stack trace by thread context
86      *
87      * The current interface is currently in an intermediate state and is only available for hiperf and profiler.
88      *
89      * @param thread         Virtualthraed pointer
90      * @param abi32          Whether to get architecture type by abi32
91      * @param regs           Register address
92      * @param regsNum        Register number
93      * @param stack          Thread stack address
94      * @param stackSize      Thread stack size
95      * @param maxStackLevel  Max unwind stack trace size
96      * @return true          Unwind successfully
97      * @return false         Failed to unwind
98      */
99     bool UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum,
100                          const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack,
101                          size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE);
102 
103     /**
104      * @brief unwind stack trace by thread context
105      *
106      * The current interface is currently in an intermediate state and is only available for hiperf and profiler.
107      *
108      * @param thread         Virtualthraed pointer
109      * @param regs           Register address
110      * @param regsNum        Register number
111      * @param stack          Thread stack address
112      * @param stackSize      Thread stack size
113      * @param maxStackLevel  Max unwind stack trace size
114      * @return true          Unwind successfully
115      * @return false         Failed to unwind
116      */
117     bool UnwindCallStack(const VirtualThread &thread, u64 *regs, u64 regsNum,
118                          const u8 *stack, u64 stackSize, std::vector<CallFrame> &callStack,
119                          size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE);
120 
121     /**
122      * @brief Expand stack trace by input calling frames.
123      *
124      * The current interface is currently in an intermediate state and is only available for hiperf and profiler.
125      *
126      * @param tid           thread id
127      * @param callFrames    input callFrames
128      * @param expandLimit   expand stack trace limit
129      * @return * size_t     return the size of expanding stack trace
130      */
131     size_t ExpandCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expandLimit = 1u);
132 
133 private:
134 #ifdef HIPERF_USE_CALLSTACK
135     pid_t lastPid_ = -1;
136     unw_word_t lastAddr_ = 0;
137     unw_word_t lastData_ = 0;
138 #endif
139     uint64_t stackPoint_ = 0;
140     uint64_t stackEnd_ = 0;
141     u64 *regs_ = nullptr; // not const , be cause we will fix it for arm64 cpu in UpdateRegForABI
142     u64 regsNum_ = 0;
143     const u8 *stack_ = nullptr;
144     u64 stackSize_ = 0;
145 
146     void LogFrame(const std::string msg, const std::vector<CallFrame> &frames);
147     size_t DoExpandCallStack(std::vector<CallFrame> &newCallFrames,
148                              const std::vector<CallFrame> &cachedCallFrames, size_t expandLimit);
149 
150     // we have a cache for all thread
151     std::map<pid_t, HashList<uint64_t, std::vector<CallFrame>>> cachedCallFramesMap_;
152     bool GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const;
153     ArchType arch_ = ArchType::UNSUPPORT;
154 #ifdef HAVE_LIBUNWIND
155     static bool ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, unw_word_t addr,
156                                         unw_word_t *data);
157     static const std::string GetUnwErrorName(int error);
158     static void dumpUDI(unw_dyn_info_t &di);
159 #ifdef NATIVEDAEMON_USE_CALLSTACK
160     static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::pair<MemMaps*, uint32_t> curMemMapsPair,
161                         const VirtualThread &thread);
162     static int FindUnwindTable(SymbolsFile *symbolsFile, std::pair<MemMaps*, uint32_t> curMemMapsPair,
163                                UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
164                                unw_proc_info_t *pi, int need_unwind_info, void *arg);
165 #else
166     static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, const MemMapItem &mmap,
167                         const VirtualThread &thread);
168     static int FindUnwindTable(SymbolsFile *symbolsFile, const MemMapItem &mmap,
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 #endif // NATIVEDAEMON_USE_CALLSTACK
172     static int FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
173                             int need_unwind_info, void *arg);
174     static int AccessMem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valuePoint,
175                          int writeOperation, void *arg);
176     static int AccessReg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valuePoint,
177                          int writeOperation, void *arg);
178     static void PutUnwindInfo(unw_addr_space_t as, unw_proc_info_t *pi, void *arg);
179     static int AccessFpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val,
180                            int writeOperation, void *arg);
181     static int GetDynInfoListAaddr(unw_addr_space_t as, unw_word_t *dil_vaddr, void *arg);
182     static int Resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg);
183     static int getProcName(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len,
184                            unw_word_t *offp, void *arg);
185     void UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callFrames, size_t maxStackLevel);
186     std::unordered_map<pid_t, unw_addr_space_t> unwindAddrSpaceMap_;
187 
188     using dsoUnwDynInfoMap = std::unordered_map<std::string, std::optional<unw_dyn_info_t>>;
189     std::unordered_map<pid_t, dsoUnwDynInfoMap> unwindDynInfoMap_;
190 
191     using unwMemoryCache = std::unordered_map<unw_word_t, unw_word_t>;
192     std::unordered_map<pid_t, unwMemoryCache> porcessMemoryMap_;
193 
194     unw_accessors_t accessors_ = {
195         .find_proc_info = FindProcInfo,
196         .put_unwind_info = PutUnwindInfo,
197         .get_dyn_info_list_addr = GetDynInfoListAaddr,
198         .access_mem = AccessMem,
199         .access_reg = AccessReg,
200         .access_fpreg = AccessFpreg,
201         .resume = Resume,
202         .get_proc_name = getProcName,
203     };
204     bool DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack,
205                   size_t maxStackLevel);
206 #endif
207 };
208 
209 #ifdef HAVE_LIBUNWIND
210 struct UnwindInfo {
211     const VirtualThread &thread;
212     const u64 *regs;
213     size_t regNumber;
214     ArchType arch;
215     CallStack &callStack;
216 };
217 #endif
218 } // namespace HiviewDFX
219 } // namespace OHOS
220 #endif // OHOS_HIVIEWDFX_CALLSTACK_H
221