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 "virtual_thread.h" 37 38 namespace OHOS { 39 namespace Developtools { 40 namespace HiPerf { 41 const int MAX_CALL_FRAME_EXPAND_CYCLE = 10; 42 const size_t MAX_CALL_FRAME_EXPAND_CACHE_SIZE = 10; 43 const size_t MAX_CALL_FRAME_UNWIND_SIZE = 256; 44 // if ip is 0 , 1 both not useful 45 const uint64_t BAD_IP_ADDRESS = 2; 46 47 #if HAVE_LIBUNWIND 48 struct UnwindInfo; 49 #endif 50 51 class CallStack { 52 public: 53 CallStack(); 54 ~CallStack(); 55 bool UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum, 56 const u8 *stack, u64 stackSize, std::vector<CallFrame> &, 57 size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE); 58 size_t ExpandCallStack(pid_t tid, std::vector<CallFrame> &callFrames, size_t expandLimit = 1u); 59 60 private: 61 uint64_t stackPoint_ = 0; 62 uint64_t stackEnd_ = 0; 63 u64 *regs_ = nullptr; // not const , be cause we will fix it for arm64 cpu in UpdateRegForABI 64 u64 regsNum_ = 0; 65 const u8 *stack_ = nullptr; 66 u64 stackSize_ = 0; 67 68 void LogFrame(const std::string msg, const std::vector<CallFrame> &frames); 69 size_t DoExpandCallStack(std::vector<CallFrame> &newCallFrames, 70 const std::vector<CallFrame> &cachedCallFrames, size_t expandLimit); 71 72 // we have a cache for all thread 73 std::map<pid_t, HashList<uint64_t, std::vector<CallFrame>>> cachedCallFramesMap_; 74 bool GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const; 75 ArchType arch_ = ArchType::UNSUPPORT; 76 #if HAVE_LIBUNWIND 77 static bool ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, unw_word_t addr, 78 unw_word_t *data); 79 static const std::string GetUnwErrorName(int error); 80 static void dumpUDI(unw_dyn_info_t &di); 81 static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, const MemMapItem &mmap, 82 const VirtualThread &thread); 83 static int FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, 84 int need_unwind_info, void *arg); 85 static int AccessMem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valuePoint, 86 int writeOperation, void *arg); 87 static int AccessReg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valuePoint, 88 int writeOperation, void *arg); 89 static void PutUnwindInfo(unw_addr_space_t as, unw_proc_info_t *pi, void *arg); 90 static int AccessFpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val, 91 int writeOperation, void *arg); 92 static int GetDynInfoListAaddr(unw_addr_space_t as, unw_word_t *dil_vaddr, void *arg); 93 static int Resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg); 94 static int getProcName(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, 95 unw_word_t *offp, void *arg); 96 static int FindUnwindTable(SymbolsFile *symbolsFile, const MemMapItem &mmap, 97 UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip, 98 unw_proc_info_t *pi, int need_unwind_info, void *arg); 99 void UnwindStep(unw_cursor_t &c, std::vector<CallFrame> &callFrames, size_t maxStackLevel); 100 std::unordered_map<pid_t, unw_addr_space_t> unwindAddrSpaceMap_; 101 102 using dsoUnwDynInfoMap = std::unordered_map<std::string, std::optional<unw_dyn_info_t>>; 103 std::unordered_map<pid_t, dsoUnwDynInfoMap> unwindDynInfoMap_; 104 105 using unwMemoryCache = std::unordered_map<unw_word_t, unw_word_t>; 106 std::unordered_map<pid_t, unwMemoryCache> porcessMemoryMap_; 107 108 unw_accessors_t accessors_ = { 109 .find_proc_info = FindProcInfo, 110 .put_unwind_info = PutUnwindInfo, 111 .get_dyn_info_list_addr = GetDynInfoListAaddr, 112 .access_mem = AccessMem, 113 .access_reg = AccessReg, 114 .access_fpreg = AccessFpreg, 115 .resume = Resume, 116 .get_proc_name = getProcName, 117 }; 118 bool DoUnwind(const VirtualThread &thread, std::vector<CallFrame> &callStack, 119 size_t maxStackLevel); 120 #endif 121 122 FRIEND_TEST(CallStackTest, ExpendCallStackFullCache); 123 FRIEND_TEST(CallStackTest, LibUnwindEmptyFunc); 124 FRIEND_TEST(CallStackTest, GetUnwErrorName); 125 }; 126 127 #if HAVE_LIBUNWIND 128 struct UnwindInfo { 129 const VirtualThread &thread; 130 const u64 *regs; 131 size_t regNumber; 132 ArchType arch; 133 CallStack &callStack; 134 }; 135 #endif 136 } // namespace HiPerf 137 } // namespace Developtools 138 } // namespace OHOS 139 #endif // HIPERF_CALLSTACK_H 140