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 
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 
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             if (fp <= prevFp || fp == 0) {
108                 break;
109             }
110         }
111         syscall(SYS_close, pfd[PIPE_WRITE]);
112         syscall(SYS_close, pfd[PIPE_READ]);
113         return (index - skipFrameNum);
114     }
115 
GetPcFpRegs(void * regs)116     static inline AT_ALWAYS_INLINE void GetPcFpRegs(void *regs)
117     {
118 #if defined(__aarch64__)
119         asm volatile(
120         "1:\n"
121         "adr x12, 1b\n"
122         "stp x12, x29, [%[base], #0]\n"
123         : [base] "+r"(regs)
124         :
125         : "x12", "memory");
126 #endif
127     }
128 
129 private:
130     FpUnwinder() = default;
131     DISALLOW_COPY_AND_MOVE(FpUnwinder);
132 
ReadUptr(uintptr_t addr,uintptr_t & value)133     NO_SANITIZE bool ReadUptr(uintptr_t addr, uintptr_t& value)
134     {
135         if ((addr < stackBottom_) || (addr + sizeof(uintptr_t) >= stackTop_)) {
136             return false;
137         }
138         value = *reinterpret_cast<uintptr_t *>(addr);
139         return true;
140     }
141 
ReadUptrSafe(uintptr_t addr,uint32_t fd,uintptr_t & value)142     NO_SANITIZE bool ReadUptrSafe(uintptr_t addr, uint32_t fd, uintptr_t& value)
143     {
144         if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, fd, addr, sizeof(uintptr_t))) == -1) {
145             return false;
146         }
147 
148         value = *reinterpret_cast<uintptr_t *>(addr);
149         return true;
150     }
151 
152 private:
153     MAYBE_UNUSED const uintptr_t maxUnwindAddrRange_ = 16 * 1024; // 16: 16k
154     uintptr_t stackBottom_ = 0;
155     uintptr_t stackTop_ = 0;
156     MAYBE_UNUSED uintptr_t arkMapStart_ = 0;
157     MAYBE_UNUSED uintptr_t arkMapEnd_ = 0;
158 };
159 }
160 } // namespace OHOS
161 #endif