148f512ceSopenharmony_ci/*
248f512ceSopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
348f512ceSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
448f512ceSopenharmony_ci * you may not use this file except in compliance with the License.
548f512ceSopenharmony_ci * You may obtain a copy of the License at
648f512ceSopenharmony_ci *
748f512ceSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
848f512ceSopenharmony_ci *
948f512ceSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1048f512ceSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1148f512ceSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1248f512ceSopenharmony_ci * See the License for the specific language governing permissions and
1348f512ceSopenharmony_ci * limitations under the License.
1448f512ceSopenharmony_ci */
1548f512ceSopenharmony_ci#ifndef HIPERF_CALLSTACK_H
1648f512ceSopenharmony_ci#define HIPERF_CALLSTACK_H
1748f512ceSopenharmony_ci
1848f512ceSopenharmony_ci#if HAVE_LIBUNWIND
1948f512ceSopenharmony_ci// for libunwind.h empty struct has size 0 in c, size 1 in c++
2048f512ceSopenharmony_ci#include <libunwind.h>
2148f512ceSopenharmony_ci#endif
2248f512ceSopenharmony_ci
2348f512ceSopenharmony_ci#include <map>
2448f512ceSopenharmony_ci#include <optional>
2548f512ceSopenharmony_ci#include <string>
2648f512ceSopenharmony_ci#include <vector>
2748f512ceSopenharmony_ci
2848f512ceSopenharmony_ci#if !is_mingw
2948f512ceSopenharmony_ci#include <sys/mman.h>
3048f512ceSopenharmony_ci#endif
3148f512ceSopenharmony_ci
3248f512ceSopenharmony_ci#include "hashlist.h"
3348f512ceSopenharmony_ci#include "register.h"
3448f512ceSopenharmony_ci#include "utilities.h"
3548f512ceSopenharmony_ci#include "unwinder.h"
3648f512ceSopenharmony_ci#include "virtual_thread.h"
3748f512ceSopenharmony_ci
3848f512ceSopenharmony_ci#if HAVE_LIBUNWIND
3948f512ceSopenharmony_ciusing ADDR_TYPE = unw_word_t;
4048f512ceSopenharmony_ci#else
4148f512ceSopenharmony_ciusing ADDR_TYPE = uintptr_t;
4248f512ceSopenharmony_ci#endif
4348f512ceSopenharmony_ci
4448f512ceSopenharmony_cinamespace OHOS {
4548f512ceSopenharmony_cinamespace Developtools {
4648f512ceSopenharmony_cinamespace HiPerf {
4748f512ceSopenharmony_ciusing namespace OHOS::HiviewDFX;
4848f512ceSopenharmony_ciconst int MAX_CALL_FRAME_EXPAND_CYCLE = 10;
4948f512ceSopenharmony_ciconst size_t MAX_CALL_FRAME_EXPAND_CACHE_SIZE = 10;
5048f512ceSopenharmony_ciconst size_t MAX_CALL_FRAME_UNWIND_SIZE = 256;
5148f512ceSopenharmony_ci
5248f512ceSopenharmony_cistruct UnwindInfo;
5348f512ceSopenharmony_ci
5448f512ceSopenharmony_ciclass CallStack {
5548f512ceSopenharmony_cipublic:
5648f512ceSopenharmony_ci    CallStack();
5748f512ceSopenharmony_ci    ~CallStack();
5848f512ceSopenharmony_ci    bool UnwindCallStack(const VirtualThread &thread, bool abi32, u64 *regs, u64 regsNum,
5948f512ceSopenharmony_ci                         const u8 *stack, u64 stackSize, std::vector<DfxFrame> &,
6048f512ceSopenharmony_ci                         size_t maxStackLevel = MAX_CALL_FRAME_UNWIND_SIZE);
6148f512ceSopenharmony_ci    size_t ExpandCallStack(pid_t tid, std::vector<DfxFrame> &callFrames, size_t expandLimit = 1u);
6248f512ceSopenharmony_ci
6348f512ceSopenharmony_ciprivate:
6448f512ceSopenharmony_ci    pid_t lastPid_ = -1;
6548f512ceSopenharmony_ci    ADDR_TYPE lastAddr_ = 0;
6648f512ceSopenharmony_ci    ADDR_TYPE lastData_ = 0;
6748f512ceSopenharmony_ci    uint64_t stackPoint_ = 0;
6848f512ceSopenharmony_ci    uint64_t stackEnd_ = 0;
6948f512ceSopenharmony_ci    u64 *regs_ = nullptr; // not const , be cause we will fix it for arm64 cpu in UpdateRegForABI
7048f512ceSopenharmony_ci    u64 regsNum_ = 0;
7148f512ceSopenharmony_ci    const u8 *stack_ = nullptr;
7248f512ceSopenharmony_ci    u64 stackSize_ = 0;
7348f512ceSopenharmony_ci
7448f512ceSopenharmony_ci    void LogFrame(const std::string msg, const std::vector<DfxFrame> &frames);
7548f512ceSopenharmony_ci    size_t DoExpandCallStack(std::vector<DfxFrame> &newCallFrames,
7648f512ceSopenharmony_ci                           const std::vector<DfxFrame> &cachedCallFrames, size_t expandLimit);
7748f512ceSopenharmony_ci
7848f512ceSopenharmony_ci    // we have a cache for all thread
7948f512ceSopenharmony_ci    std::map<pid_t, HashList<uint64_t, std::vector<DfxFrame>>> cachedCallFramesMap_;
8048f512ceSopenharmony_ci    bool GetIpSP(uint64_t &ip, uint64_t &sp, const u64 *regs, size_t regNum) const;
8148f512ceSopenharmony_ci    ArchType arch_ = ArchType::ARCH_UNKNOWN;
8248f512ceSopenharmony_ci
8348f512ceSopenharmony_ci    static bool ReadVirtualThreadMemory(UnwindInfo &unwindInfoPtr, ADDR_TYPE addr, ADDR_TYPE *data);
8448f512ceSopenharmony_ci#if HAVE_LIBUNWIND
8548f512ceSopenharmony_ci    static const std::string GetUnwErrorName(int error);
8648f512ceSopenharmony_ci    static void dumpUDI(unw_dyn_info_t &di);
8748f512ceSopenharmony_ci    static bool fillUDI(unw_dyn_info_t &di, SymbolsFile &symbolsFile, std::shared_ptr<DfxMap> map,
8848f512ceSopenharmony_ci                        const VirtualThread &thread);
8948f512ceSopenharmony_ci    static int FindProcInfo(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
9048f512ceSopenharmony_ci                            int need_unwind_info, void *arg);
9148f512ceSopenharmony_ci    static int AccessMem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valuePoint,
9248f512ceSopenharmony_ci                         int writeOperation, void *arg);
9348f512ceSopenharmony_ci    static int AccessReg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valuePoint,
9448f512ceSopenharmony_ci                         int writeOperation, void *arg);
9548f512ceSopenharmony_ci    static void PutUnwindInfo(unw_addr_space_t as, unw_proc_info_t *pi, void *arg);
9648f512ceSopenharmony_ci    static int AccessFpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val,
9748f512ceSopenharmony_ci                           int writeOperation, void *arg);
9848f512ceSopenharmony_ci    static int GetDynInfoListAaddr(unw_addr_space_t as, unw_word_t *dil_vaddr, void *arg);
9948f512ceSopenharmony_ci    static int Resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg);
10048f512ceSopenharmony_ci    static int getProcName(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len,
10148f512ceSopenharmony_ci                           unw_word_t *offp, void *arg);
10248f512ceSopenharmony_ci    static int FindUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map,
10348f512ceSopenharmony_ci                               UnwindInfo *unwindInfoPtr, unw_addr_space_t as, unw_word_t ip,
10448f512ceSopenharmony_ci                               unw_proc_info_t *pi, int need_unwind_info, void *arg);
10548f512ceSopenharmony_ci    void UnwindStep(unw_cursor_t &c, std::vector<DfxFrame> &callFrames, size_t maxStackLevel);
10648f512ceSopenharmony_ci    std::unordered_map<pid_t, unw_addr_space_t> unwindAddrSpaceMap_;
10748f512ceSopenharmony_ci
10848f512ceSopenharmony_ci    using dsoUnwDynInfoMap = std::unordered_map<std::string, std::optional<unw_dyn_info_t>>;
10948f512ceSopenharmony_ci    std::unordered_map<pid_t, dsoUnwDynInfoMap> unwindTableInfoMap_;
11048f512ceSopenharmony_ci
11148f512ceSopenharmony_ci    using unwMemoryCache = std::unordered_map<unw_word_t, unw_word_t>;
11248f512ceSopenharmony_ci    std::unordered_map<pid_t, unwMemoryCache> porcessMemoryMap_;
11348f512ceSopenharmony_ci
11448f512ceSopenharmony_ci    unw_accessors_t accessors_ = {
11548f512ceSopenharmony_ci        .find_proc_info = FindProcInfo,
11648f512ceSopenharmony_ci        .put_unwind_info = PutUnwindInfo,
11748f512ceSopenharmony_ci        .get_dyn_info_list_addr = GetDynInfoListAaddr,
11848f512ceSopenharmony_ci        .access_mem = AccessMem,
11948f512ceSopenharmony_ci        .access_reg = AccessReg,
12048f512ceSopenharmony_ci        .access_fpreg = AccessFpreg,
12148f512ceSopenharmony_ci        .resume = Resume,
12248f512ceSopenharmony_ci        .get_proc_name = getProcName,
12348f512ceSopenharmony_ci    };
12448f512ceSopenharmony_ci    bool DoUnwind(const VirtualThread &thread, std::vector<DfxFrame> &callStack,
12548f512ceSopenharmony_ci                  size_t maxStackLevel);
12648f512ceSopenharmony_ci#endif
12748f512ceSopenharmony_ci#if defined(HAVE_LIBUNWINDER) && HAVE_LIBUNWINDER
12848f512ceSopenharmony_ci    bool DoUnwind2(const VirtualThread &thread, std::vector<DfxFrame> &callStack, size_t maxStackLevel);
12948f512ceSopenharmony_ci    static void DumpTableInfo(UnwindTableInfo &outTableInfo);
13048f512ceSopenharmony_ci    static int FillUnwindTable(SymbolsFile *symbolsFile, std::shared_ptr<DfxMap> map, UnwindInfo *unwindInfoPtr,
13148f512ceSopenharmony_ci                               uintptr_t pc, UnwindTableInfo& outTableInfo);
13248f512ceSopenharmony_ci    static int FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg);
13348f512ceSopenharmony_ci    static int AccessMem2(uintptr_t addr, uintptr_t *val, void *arg);
13448f512ceSopenharmony_ci    static int GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg);
13548f512ceSopenharmony_ci
13648f512ceSopenharmony_ci    // pid->unwinder(acc/regs/maps) cache
13748f512ceSopenharmony_ci    std::unordered_map<pid_t, std::shared_ptr<Unwinder>> pidUnwinder_;
13848f512ceSopenharmony_ci    // pid->elf->unwindtable cache
13948f512ceSopenharmony_ci    using DsoUnwindTableInfoMap = std::unordered_map<std::string, UnwindTableInfo>;
14048f512ceSopenharmony_ci    std::unordered_map<pid_t, DsoUnwindTableInfoMap> unwindTableInfoMap_;
14148f512ceSopenharmony_ci
14248f512ceSopenharmony_ci    std::shared_ptr<UnwindAccessors> accessor_;
14348f512ceSopenharmony_ci#endif
14448f512ceSopenharmony_ci
14548f512ceSopenharmony_ci    FRIEND_TEST(CallStackTest, ExpendCallStackFullCache);
14648f512ceSopenharmony_ci    FRIEND_TEST(CallStackTest, LibUnwindEmptyFunc);
14748f512ceSopenharmony_ci    FRIEND_TEST(CallStackTest, GetUnwErrorName);
14848f512ceSopenharmony_ci};
14948f512ceSopenharmony_ci
15048f512ceSopenharmony_cistruct UnwindInfo {
15148f512ceSopenharmony_ci    const VirtualThread &thread;
15248f512ceSopenharmony_ci    const u64 *regs;
15348f512ceSopenharmony_ci    size_t regNumber;
15448f512ceSopenharmony_ci    ArchType arch;
15548f512ceSopenharmony_ci    CallStack &callStack;
15648f512ceSopenharmony_ci};
15748f512ceSopenharmony_ci} // namespace HiPerf
15848f512ceSopenharmony_ci} // namespace Developtools
15948f512ceSopenharmony_ci} // namespace OHOS
16048f512ceSopenharmony_ci#endif // HIPERF_CALLSTACK_H
161