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