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