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