1 /*
2  * Copyright (c) 2024 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 FP_UNWINDER_H
16 #define FP_UNWINDER_H
17 
18 #include <atomic>
19 #include <cinttypes>
20 #include <csignal>
21 #include <dlfcn.h>
22 #include <fcntl.h>
23 #include <memory>
24 #include <mutex>
25 #include <nocopyable.h>
26 #include <securec.h>
27 #include <sys/stat.h>
28 #include <sys/syscall.h>
29 #include <unistd.h>
30 #include "dfx_ark.h"
31 #include "dfx_define.h"
32 #include "stack_util.h"
33 
34 namespace OHOS {
35 namespace HiviewDFX {
36 class FpUnwinder {
37 public:
GetPtr()38     static FpUnwinder* GetPtr()
39     {
40         static std::unique_ptr<FpUnwinder> ptr = nullptr;
41         if (ptr == nullptr) {
42             static std::once_flag flag;
43             std::call_once(flag, [&] {
44                 ptr.reset(new FpUnwinder());
45             });
46         }
47         return ptr.get();
48     }
49 
Unwind(uintptr_t pc, uintptr_t fp, uintptr_t* pcs, size_t maxSize, size_t skipFrameNum = 0)50     size_t Unwind(uintptr_t pc, uintptr_t fp, uintptr_t* pcs, size_t maxSize, size_t skipFrameNum = 0)
51     {
52         if (pcs == nullptr) {
53             return 0;
54         }
55         if (gettid() == getpid()) {
56             if (!GetMainStackRange(stackBottom_, stackTop_)) {
57                 return 0;
58             }
59         } else {
60             if (!GetSelfStackRange(stackBottom_, stackTop_)) {
61                 return 0;
62             }
63         }
64         uintptr_t firstFp = fp;
65         size_t index = 0;
66         MAYBE_UNUSED uintptr_t sp = 0;
67         MAYBE_UNUSED bool isJsFrame = false;
68         while ((index < maxSize) && (fp - firstFp < maxUnwindAddrRange_)) {
69             if (index >= skipFrameNum) {
70                 pcs[index - skipFrameNum] = pc;
71             }
72             index++;
73             uintptr_t prevFp = fp;
74             if ((!ReadUptr(prevFp, fp)) ||
75                 (!ReadUptr(prevFp + sizeof(uintptr_t), pc))) {
76                 break;
77             }
78             if (fp <= prevFp || fp == 0) {
79                 break;
80             }
81         }
82         return (index - skipFrameNum);
83     }
84 
UnwindSafe(uintptr_t pc, uintptr_t fp, uintptr_t* pcs, size_t maxSize, size_t skipFrameNum = 0)85     NO_SANITIZE size_t UnwindSafe(uintptr_t pc, uintptr_t fp, uintptr_t* pcs, size_t maxSize, size_t skipFrameNum = 0)
86     {
87         if (pcs == nullptr) {
88             return 0;
89         }
90         int32_t pfd[PIPE_NUM_SZ] = {-1, -1};
91         if (syscall(SYS_pipe2, pfd, O_CLOEXEC | O_NONBLOCK) != 0) {
92             return 0;
93         }
94         size_t index = 0;
95         MAYBE_UNUSED uintptr_t sp = 0;
96         MAYBE_UNUSED bool isJsFrame = false;
97         while (index < maxSize) {
98             if (index >= skipFrameNum) {
99                 pcs[index - skipFrameNum] = pc;
100             }
101             index++;
102             uintptr_t prevFp = fp;
103             if ((!ReadUptrSafe(prevFp, pfd[PIPE_WRITE], fp)) ||
104                 (!ReadUptrSafe(prevFp + sizeof(uintptr_t), pfd[PIPE_WRITE], pc))) {
105                 break;
106             }
107             pc = pc > 0x4 ? pc - 0x4 : pc; // adjust pc in Arm64 architecture
108             if (fp <= prevFp || fp == 0) {
109                 break;
110             }
111         }
112         syscall(SYS_close, pfd[PIPE_WRITE]);
113         syscall(SYS_close, pfd[PIPE_READ]);
114         return (index - skipFrameNum);
115     }
116 
GetPcFpRegs(void *regs)117     static inline AT_ALWAYS_INLINE void GetPcFpRegs(void *regs)
118     {
119 #if defined(__aarch64__)
120         asm volatile(
121         "1:\n"
122         "adr x12, 1b\n"
123         "stp x12, x29, [%[base], #0]\n"
124         : [base] "+r"(regs)
125         :
126         : "x12", "memory");
127 #endif
128     }
129 
130 private:
131     FpUnwinder() = default;
132     DISALLOW_COPY_AND_MOVE(FpUnwinder);
133 
ReadUptr(uintptr_t addr, uintptr_t& value)134     NO_SANITIZE bool ReadUptr(uintptr_t addr, uintptr_t& value)
135     {
136         if ((addr < stackBottom_) || (addr + sizeof(uintptr_t) >= stackTop_)) {
137             return false;
138         }
139         value = *reinterpret_cast<uintptr_t *>(addr);
140         return true;
141     }
142 
ReadUptrSafe(uintptr_t addr, uint32_t fd, uintptr_t& value)143     NO_SANITIZE bool ReadUptrSafe(uintptr_t addr, uint32_t fd, uintptr_t& value)
144     {
145         if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, fd, addr, sizeof(uintptr_t))) == -1) {
146             return false;
147         }
148 
149         value = *reinterpret_cast<uintptr_t *>(addr);
150         return true;
151     }
152 
153 private:
154     MAYBE_UNUSED const uintptr_t maxUnwindAddrRange_ = 16 * 1024; // 16: 16k
155     uintptr_t stackBottom_ = 0;
156     uintptr_t stackTop_ = 0;
157     MAYBE_UNUSED uintptr_t arkMapStart_ = 0;
158     MAYBE_UNUSED uintptr_t arkMapEnd_ = 0;
159 };
160 }
161 } // namespace OHOS
162 #endif