Warning, /include/absl/debugging/internal/stacktrace_riscv-inl.inc is written in an unsupported language. File is not indexed.
0001 // Copyright 2021 The Abseil Authors
0002 //
0003 // Licensed under the Apache License, Version 2.0 (the "License");
0004 // you may not use this file except in compliance with the License.
0005 // You may obtain a copy of the License at
0006 //
0007 // https://www.apache.org/licenses/LICENSE-2.0
0008 //
0009 // Unless required by applicable law or agreed to in writing, software
0010 // distributed under the License is distributed on an "AS IS" BASIS,
0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 // See the License for the specific language governing permissions and
0013 // limitations under the License.
0014
0015 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
0016 #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_
0017
0018 // Generate stack trace for riscv
0019
0020 #include <sys/ucontext.h>
0021
0022 #include "absl/base/config.h"
0023 #if defined(__linux__)
0024 #include <sys/mman.h>
0025 #include <ucontext.h>
0026 #include <unistd.h>
0027 #endif
0028
0029 #include <atomic>
0030 #include <cassert>
0031 #include <cstdint>
0032 #include <iostream>
0033 #include <limits>
0034 #include <utility>
0035
0036 #include "absl/base/attributes.h"
0037 #include "absl/debugging/stacktrace.h"
0038
0039 static const uintptr_t kUnknownFrameSize = 0;
0040
0041 // Compute the size of a stack frame in [low..high). We assume that low < high.
0042 // Return size of kUnknownFrameSize.
0043 template <typename T>
0044 static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) {
0045 const char *low_char_ptr = reinterpret_cast<const char *>(low);
0046 const char *high_char_ptr = reinterpret_cast<const char *>(high);
0047 return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
0048 }
0049
0050 // Given a pointer to a stack frame, locate and return the calling stackframe,
0051 // or return null if no stackframe can be found. Perform sanity checks (the
0052 // strictness of which is controlled by the boolean parameter
0053 // "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
0054 template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
0055 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
0056 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
0057 static void ** NextStackFrame(void **old_frame_pointer, const void *uc,
0058 const std::pair<size_t, size_t> range) {
0059 // .
0060 // .
0061 // .
0062 // +-> +----------------+
0063 // | | return address |
0064 // | | previous fp |
0065 // | | ... |
0066 // | +----------------+ <-+
0067 // | | return address | |
0068 // +---|- previous fp | |
0069 // | ... | |
0070 // $fp ->|----------------+ |
0071 // | return address | |
0072 // | previous fp -|---+
0073 // $sp ->| ... |
0074 // +----------------+
0075 void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]);
0076 uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer);
0077
0078 // The RISCV ELF psABI mandates that the stack pointer is always 16-byte
0079 // aligned.
0080 // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte
0081 // alignment.
0082 if (frame_pointer & 15)
0083 return nullptr;
0084
0085 // If the new frame pointer matches the signal context, avoid terminating
0086 // early to deal with alternate signal stacks.
0087 if (WITH_CONTEXT)
0088 if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc))
0089 // RISCV ELF psABI has the frame pointer at x8/fp/s0.
0090 // -- RISCV psABI Table 18.2
0091 if (ucv->uc_mcontext.__gregs[8] == frame_pointer)
0092 return new_frame_pointer;
0093
0094 // Check frame size. In strict mode, we assume frames to be under 100,000
0095 // bytes. In non-strict mode, we relax the limit to 1MB.
0096 const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
0097 const uintptr_t frame_size =
0098 ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
0099 if (frame_size == kUnknownFrameSize) {
0100 if (STRICT_UNWINDING)
0101 return nullptr;
0102
0103 // In non-strict mode permit non-contiguous stacks (e.g. alternate signal
0104 // frame handling).
0105 if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first ||
0106 reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second)
0107 return nullptr;
0108 }
0109
0110 if (frame_size > max_size)
0111 return nullptr;
0112
0113 return new_frame_pointer;
0114 }
0115
0116 template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
0117 ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
0118 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
0119 static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
0120 const void *ucp, int *min_dropped_frames) {
0121 // The `frame_pointer` that is computed here points to the top of the frame.
0122 // The two words preceding the address are the return address and the previous
0123 // frame pointer.
0124 #if defined(__GNUC__)
0125 void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0));
0126 #else
0127 #error reading stack pointer not yet supported on this platform
0128 #endif
0129
0130 std::pair<size_t, size_t> stack = {
0131 // assume that the first page is not the stack.
0132 static_cast<size_t>(sysconf(_SC_PAGESIZE)),
0133 std::numeric_limits<size_t>::max() - sizeof(void *)
0134 };
0135
0136 int n = 0;
0137 void *return_address = nullptr;
0138 while (frame_pointer && n < max_depth) {
0139 return_address = frame_pointer[-1];
0140
0141 // The absl::GetStackFrames routine is called when we are in some
0142 // informational context (the failure signal handler for example). Use the
0143 // non-strict unwinding rules to produce a stack trace that is as complete
0144 // as possible (even if it contains a few bogus entries in some rare cases).
0145 void **next_frame_pointer =
0146 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp,
0147 stack);
0148
0149 if (skip_count > 0) {
0150 skip_count--;
0151 } else {
0152 result[n] = return_address;
0153 if (IS_STACK_FRAMES) {
0154 sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
0155 }
0156 n++;
0157 }
0158
0159 frame_pointer = next_frame_pointer;
0160 }
0161
0162 if (min_dropped_frames != nullptr) {
0163 // Implementation detail: we clamp the max of frames we are willing to
0164 // count, so as not to spend too much time in the loop below.
0165 const int kMaxUnwind = 200;
0166 int num_dropped_frames = 0;
0167 for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) {
0168 if (skip_count > 0) {
0169 skip_count--;
0170 } else {
0171 num_dropped_frames++;
0172 }
0173 frame_pointer =
0174 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp,
0175 stack);
0176 }
0177 *min_dropped_frames = num_dropped_frames;
0178 }
0179
0180 return n;
0181 }
0182
0183 namespace absl {
0184 ABSL_NAMESPACE_BEGIN
0185 namespace debugging_internal {
0186 bool StackTraceWorksForTest() { return true; }
0187 } // namespace debugging_internal
0188 ABSL_NAMESPACE_END
0189 } // namespace absl
0190
0191 #endif