xref: /developtools/hiperf/include/callstack.h (revision 48f512ce)
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
39using ADDR_TYPE = unw_word_t;
40#else
41using ADDR_TYPE = uintptr_t;
42#endif
43
44namespace OHOS {
45namespace Developtools {
46namespace HiPerf {
47using namespace OHOS::HiviewDFX;
48const int MAX_CALL_FRAME_EXPAND_CYCLE = 10;
49const size_t MAX_CALL_FRAME_EXPAND_CACHE_SIZE = 10;
50const size_t MAX_CALL_FRAME_UNWIND_SIZE = 256;
51
52struct UnwindInfo;
53
54class CallStack {
55public:
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
63private:
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
150struct 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