1800b99b8Sopenharmony_ci/*
2800b99b8Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3800b99b8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4800b99b8Sopenharmony_ci * you may not use this file except in compliance with the License.
5800b99b8Sopenharmony_ci * You may obtain a copy of the License at
6800b99b8Sopenharmony_ci *
7800b99b8Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8800b99b8Sopenharmony_ci *
9800b99b8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10800b99b8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11800b99b8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12800b99b8Sopenharmony_ci * See the License for the specific language governing permissions and
13800b99b8Sopenharmony_ci * limitations under the License.
14800b99b8Sopenharmony_ci */
15800b99b8Sopenharmony_ci#ifndef FP_UNWINDER_H
16800b99b8Sopenharmony_ci#define FP_UNWINDER_H
17800b99b8Sopenharmony_ci
18800b99b8Sopenharmony_ci#include <atomic>
19800b99b8Sopenharmony_ci#include <cinttypes>
20800b99b8Sopenharmony_ci#include <csignal>
21800b99b8Sopenharmony_ci#include <dlfcn.h>
22800b99b8Sopenharmony_ci#include <fcntl.h>
23800b99b8Sopenharmony_ci#include <memory>
24800b99b8Sopenharmony_ci#include <mutex>
25800b99b8Sopenharmony_ci#include <nocopyable.h>
26800b99b8Sopenharmony_ci#include <securec.h>
27800b99b8Sopenharmony_ci#include <sys/stat.h>
28800b99b8Sopenharmony_ci#include <sys/syscall.h>
29800b99b8Sopenharmony_ci#include <unistd.h>
30800b99b8Sopenharmony_ci#include "dfx_ark.h"
31800b99b8Sopenharmony_ci#include "dfx_define.h"
32800b99b8Sopenharmony_ci#include "stack_util.h"
33800b99b8Sopenharmony_ci
34800b99b8Sopenharmony_cinamespace OHOS {
35800b99b8Sopenharmony_cinamespace HiviewDFX {
36800b99b8Sopenharmony_ciclass FpUnwinder {
37800b99b8Sopenharmony_cipublic:
38800b99b8Sopenharmony_ci    static FpUnwinder* GetPtr()
39800b99b8Sopenharmony_ci    {
40800b99b8Sopenharmony_ci        static std::unique_ptr<FpUnwinder> ptr = nullptr;
41800b99b8Sopenharmony_ci        if (ptr == nullptr) {
42800b99b8Sopenharmony_ci            static std::once_flag flag;
43800b99b8Sopenharmony_ci            std::call_once(flag, [&] {
44800b99b8Sopenharmony_ci                ptr.reset(new FpUnwinder());
45800b99b8Sopenharmony_ci            });
46800b99b8Sopenharmony_ci        }
47800b99b8Sopenharmony_ci        return ptr.get();
48800b99b8Sopenharmony_ci    }
49800b99b8Sopenharmony_ci
50800b99b8Sopenharmony_ci    size_t Unwind(uintptr_t pc, uintptr_t fp, uintptr_t* pcs, size_t maxSize, size_t skipFrameNum = 0)
51800b99b8Sopenharmony_ci    {
52800b99b8Sopenharmony_ci        if (pcs == nullptr) {
53800b99b8Sopenharmony_ci            return 0;
54800b99b8Sopenharmony_ci        }
55800b99b8Sopenharmony_ci        if (gettid() == getpid()) {
56800b99b8Sopenharmony_ci            if (!GetMainStackRange(stackBottom_, stackTop_)) {
57800b99b8Sopenharmony_ci                return 0;
58800b99b8Sopenharmony_ci            }
59800b99b8Sopenharmony_ci        } else {
60800b99b8Sopenharmony_ci            if (!GetSelfStackRange(stackBottom_, stackTop_)) {
61800b99b8Sopenharmony_ci                return 0;
62800b99b8Sopenharmony_ci            }
63800b99b8Sopenharmony_ci        }
64800b99b8Sopenharmony_ci        uintptr_t firstFp = fp;
65800b99b8Sopenharmony_ci        size_t index = 0;
66800b99b8Sopenharmony_ci        MAYBE_UNUSED uintptr_t sp = 0;
67800b99b8Sopenharmony_ci        MAYBE_UNUSED bool isJsFrame = false;
68800b99b8Sopenharmony_ci        while ((index < maxSize) && (fp - firstFp < maxUnwindAddrRange_)) {
69800b99b8Sopenharmony_ci            if (index >= skipFrameNum) {
70800b99b8Sopenharmony_ci                pcs[index - skipFrameNum] = pc;
71800b99b8Sopenharmony_ci            }
72800b99b8Sopenharmony_ci            index++;
73800b99b8Sopenharmony_ci            uintptr_t prevFp = fp;
74800b99b8Sopenharmony_ci            if ((!ReadUptr(prevFp, fp)) ||
75800b99b8Sopenharmony_ci                (!ReadUptr(prevFp + sizeof(uintptr_t), pc))) {
76800b99b8Sopenharmony_ci                break;
77800b99b8Sopenharmony_ci            }
78800b99b8Sopenharmony_ci            if (fp <= prevFp || fp == 0) {
79800b99b8Sopenharmony_ci                break;
80800b99b8Sopenharmony_ci            }
81800b99b8Sopenharmony_ci        }
82800b99b8Sopenharmony_ci        return (index - skipFrameNum);
83800b99b8Sopenharmony_ci    }
84800b99b8Sopenharmony_ci
85800b99b8Sopenharmony_ci    NO_SANITIZE size_t UnwindSafe(uintptr_t pc, uintptr_t fp, uintptr_t* pcs, size_t maxSize, size_t skipFrameNum = 0)
86800b99b8Sopenharmony_ci    {
87800b99b8Sopenharmony_ci        if (pcs == nullptr) {
88800b99b8Sopenharmony_ci            return 0;
89800b99b8Sopenharmony_ci        }
90800b99b8Sopenharmony_ci        int32_t pfd[PIPE_NUM_SZ] = {-1, -1};
91800b99b8Sopenharmony_ci        if (syscall(SYS_pipe2, pfd, O_CLOEXEC | O_NONBLOCK) != 0) {
92800b99b8Sopenharmony_ci            return 0;
93800b99b8Sopenharmony_ci        }
94800b99b8Sopenharmony_ci        size_t index = 0;
95800b99b8Sopenharmony_ci        MAYBE_UNUSED uintptr_t sp = 0;
96800b99b8Sopenharmony_ci        MAYBE_UNUSED bool isJsFrame = false;
97800b99b8Sopenharmony_ci        while (index < maxSize) {
98800b99b8Sopenharmony_ci            if (index >= skipFrameNum) {
99800b99b8Sopenharmony_ci                pcs[index - skipFrameNum] = pc;
100800b99b8Sopenharmony_ci            }
101800b99b8Sopenharmony_ci            index++;
102800b99b8Sopenharmony_ci            uintptr_t prevFp = fp;
103800b99b8Sopenharmony_ci            if ((!ReadUptrSafe(prevFp, pfd[PIPE_WRITE], fp)) ||
104800b99b8Sopenharmony_ci                (!ReadUptrSafe(prevFp + sizeof(uintptr_t), pfd[PIPE_WRITE], pc))) {
105800b99b8Sopenharmony_ci                break;
106800b99b8Sopenharmony_ci            }
107800b99b8Sopenharmony_ci            pc = pc > 0x4 ? pc - 0x4 : pc; // adjust pc in Arm64 architecture
108800b99b8Sopenharmony_ci            if (fp <= prevFp || fp == 0) {
109800b99b8Sopenharmony_ci                break;
110800b99b8Sopenharmony_ci            }
111800b99b8Sopenharmony_ci        }
112800b99b8Sopenharmony_ci        syscall(SYS_close, pfd[PIPE_WRITE]);
113800b99b8Sopenharmony_ci        syscall(SYS_close, pfd[PIPE_READ]);
114800b99b8Sopenharmony_ci        return (index - skipFrameNum);
115800b99b8Sopenharmony_ci    }
116800b99b8Sopenharmony_ci
117800b99b8Sopenharmony_ci    static inline AT_ALWAYS_INLINE void GetPcFpRegs(void *regs)
118800b99b8Sopenharmony_ci    {
119800b99b8Sopenharmony_ci#if defined(__aarch64__)
120800b99b8Sopenharmony_ci        asm volatile(
121800b99b8Sopenharmony_ci        "1:\n"
122800b99b8Sopenharmony_ci        "adr x12, 1b\n"
123800b99b8Sopenharmony_ci        "stp x12, x29, [%[base], #0]\n"
124800b99b8Sopenharmony_ci        : [base] "+r"(regs)
125800b99b8Sopenharmony_ci        :
126800b99b8Sopenharmony_ci        : "x12", "memory");
127800b99b8Sopenharmony_ci#endif
128800b99b8Sopenharmony_ci    }
129800b99b8Sopenharmony_ci
130800b99b8Sopenharmony_ciprivate:
131800b99b8Sopenharmony_ci    FpUnwinder() = default;
132800b99b8Sopenharmony_ci    DISALLOW_COPY_AND_MOVE(FpUnwinder);
133800b99b8Sopenharmony_ci
134800b99b8Sopenharmony_ci    NO_SANITIZE bool ReadUptr(uintptr_t addr, uintptr_t& value)
135800b99b8Sopenharmony_ci    {
136800b99b8Sopenharmony_ci        if ((addr < stackBottom_) || (addr + sizeof(uintptr_t) >= stackTop_)) {
137800b99b8Sopenharmony_ci            return false;
138800b99b8Sopenharmony_ci        }
139800b99b8Sopenharmony_ci        value = *reinterpret_cast<uintptr_t *>(addr);
140800b99b8Sopenharmony_ci        return true;
141800b99b8Sopenharmony_ci    }
142800b99b8Sopenharmony_ci
143800b99b8Sopenharmony_ci    NO_SANITIZE bool ReadUptrSafe(uintptr_t addr, uint32_t fd, uintptr_t& value)
144800b99b8Sopenharmony_ci    {
145800b99b8Sopenharmony_ci        if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, fd, addr, sizeof(uintptr_t))) == -1) {
146800b99b8Sopenharmony_ci            return false;
147800b99b8Sopenharmony_ci        }
148800b99b8Sopenharmony_ci
149800b99b8Sopenharmony_ci        value = *reinterpret_cast<uintptr_t *>(addr);
150800b99b8Sopenharmony_ci        return true;
151800b99b8Sopenharmony_ci    }
152800b99b8Sopenharmony_ci
153800b99b8Sopenharmony_ciprivate:
154800b99b8Sopenharmony_ci    MAYBE_UNUSED const uintptr_t maxUnwindAddrRange_ = 16 * 1024; // 16: 16k
155800b99b8Sopenharmony_ci    uintptr_t stackBottom_ = 0;
156800b99b8Sopenharmony_ci    uintptr_t stackTop_ = 0;
157800b99b8Sopenharmony_ci    MAYBE_UNUSED uintptr_t arkMapStart_ = 0;
158800b99b8Sopenharmony_ci    MAYBE_UNUSED uintptr_t arkMapEnd_ = 0;
159800b99b8Sopenharmony_ci};
160800b99b8Sopenharmony_ci}
161800b99b8Sopenharmony_ci} // namespace OHOS
162800b99b8Sopenharmony_ci#endif