• 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 "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