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