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