stacktrace_riscv-inl.inc (7269B)
1 // Copyright 2021 The Abseil Authors 2 // 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 // https://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 ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ 16 #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ 17 18 // Generate stack trace for riscv 19 20 #include <sys/ucontext.h> 21 22 #include "absl/base/config.h" 23 #include "absl/debugging/internal/addresses.h" 24 #if defined(__linux__) 25 #include <sys/mman.h> 26 #include <ucontext.h> 27 #include <unistd.h> 28 #endif 29 30 #include <atomic> 31 #include <cassert> 32 #include <cstdint> 33 #include <iostream> 34 #include <limits> 35 #include <utility> 36 37 #include "absl/base/attributes.h" 38 #include "absl/debugging/stacktrace.h" 39 40 static constexpr ptrdiff_t kUnknownFrameSize = 0; 41 42 // Compute the size of a stack frame in [low..high). We assume that low < high. 43 // Return size of kUnknownFrameSize. 44 template <typename T> 45 static inline ptrdiff_t ComputeStackFrameSize(const T *low, const T *high) { 46 const char *low_char_ptr = reinterpret_cast<const char *>(low); 47 const char *high_char_ptr = reinterpret_cast<const char *>(high); 48 return low < high ? static_cast<ptrdiff_t>(high_char_ptr - low_char_ptr) 49 : kUnknownFrameSize; 50 } 51 52 // Given a pointer to a stack frame, locate and return the calling stackframe, 53 // or return null if no stackframe can be found. Perform sanity checks (the 54 // strictness of which is controlled by the boolean parameter 55 // "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. 56 template <bool STRICT_UNWINDING, bool WITH_CONTEXT> 57 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 58 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 59 static void ** NextStackFrame(void **old_frame_pointer, const void *uc, 60 const std::pair<size_t, size_t> range) { 61 // . 62 // . 63 // . 64 // +-> +----------------+ 65 // | | return address | 66 // | | previous fp | 67 // | | ... | 68 // | +----------------+ <-+ 69 // | | return address | | 70 // +---|- previous fp | | 71 // | ... | | 72 // $fp ->|----------------+ | 73 // | return address | | 74 // | previous fp -|---+ 75 // $sp ->| ... | 76 // +----------------+ 77 void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); 78 uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer); 79 80 // The RISCV ELF psABI mandates that the stack pointer is always 16-byte 81 // aligned. 82 // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte 83 // alignment. 84 if (frame_pointer & 15) 85 return nullptr; 86 87 // If the new frame pointer matches the signal context, avoid terminating 88 // early to deal with alternate signal stacks. 89 if (WITH_CONTEXT) 90 if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc)) 91 // RISCV ELF psABI has the frame pointer at x8/fp/s0. 92 // -- RISCV psABI Table 18.2 93 if (ucv->uc_mcontext.__gregs[8] == frame_pointer) 94 return new_frame_pointer; 95 96 // Check frame size. In strict mode, we assume frames to be under 100,000 97 // bytes. In non-strict mode, we relax the limit to 1MB. 98 const ptrdiff_t max_size = STRICT_UNWINDING ? 100000 : 1000000; 99 const ptrdiff_t frame_size = 100 ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); 101 if (frame_size == kUnknownFrameSize) { 102 if (STRICT_UNWINDING) 103 return nullptr; 104 105 // In non-strict mode permit non-contiguous stacks (e.g. alternate signal 106 // frame handling). 107 if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || 108 reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) 109 return nullptr; 110 } 111 112 if (frame_size > max_size) 113 return nullptr; 114 115 return new_frame_pointer; 116 } 117 118 template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> 119 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 120 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 121 static int UnwindImpl(void **result, uintptr_t *frames, int *sizes, 122 int max_depth, int skip_count, const void *ucp, 123 int *min_dropped_frames) { 124 // The `frame_pointer` that is computed here points to the top of the frame. 125 // The two words preceding the address are the return address and the previous 126 // frame pointer. 127 #if defined(__GNUC__) 128 void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0)); 129 #else 130 #error reading stack pointer not yet supported on this platform 131 #endif 132 133 std::pair<size_t, size_t> stack = { 134 // assume that the first page is not the stack. 135 static_cast<size_t>(sysconf(_SC_PAGESIZE)), 136 std::numeric_limits<size_t>::max() - sizeof(void *) 137 }; 138 139 int n = 0; 140 void *return_address = nullptr; 141 while (frame_pointer && n < max_depth) { 142 return_address = frame_pointer[-1]; 143 144 // The absl::GetStackFrames routine is called when we are in some 145 // informational context (the failure signal handler for example). Use the 146 // non-strict unwinding rules to produce a stack trace that is as complete 147 // as possible (even if it contains a few bogus entries in some rare cases). 148 void **next_frame_pointer = 149 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, 150 stack); 151 152 if (skip_count > 0) { 153 skip_count--; 154 } else { 155 result[n] = return_address; 156 if (IS_STACK_FRAMES) { 157 // NextStackFrame() has already checked that frame size fits to int 158 if (frames != nullptr) { 159 frames[n] = 160 absl::debugging_internal::StripPointerMetadata(frame_pointer); 161 } 162 if (sizes != nullptr) { 163 sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); 164 } 165 } 166 n++; 167 } 168 169 frame_pointer = next_frame_pointer; 170 } 171 172 if (min_dropped_frames != nullptr) { 173 // Implementation detail: we clamp the max of frames we are willing to 174 // count, so as not to spend too much time in the loop below. 175 const int kMaxUnwind = 200; 176 int num_dropped_frames = 0; 177 for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { 178 if (skip_count > 0) { 179 skip_count--; 180 } else { 181 num_dropped_frames++; 182 } 183 frame_pointer = 184 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, 185 stack); 186 } 187 *min_dropped_frames = num_dropped_frames; 188 } 189 190 return n; 191 } 192 193 namespace absl { 194 ABSL_NAMESPACE_BEGIN 195 namespace debugging_internal { 196 bool StackTraceWorksForTest() { return true; } 197 } // namespace debugging_internal 198 ABSL_NAMESPACE_END 199 } // namespace absl 200 201 #endif