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