Import //base/debugging:stacktrace from abseil.
This code is all Apache 2 licensed, and all headers were maintained when concatinated, so we should be completely fine from a legal standpoint. I've scriptified the steps that I took so that if we need to update this code in the future, it hopefully shouldn't be too much of a hassle.
This commit is contained in:
parent
f997cb9038
commit
1a91aab1d7
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// Routines to extract the current stack trace. These functions are
|
||||
// thread-safe and async-signal-safe.
|
||||
// Note that stack trace functionality is platform dependent and requires
|
||||
// additional support from the compiler/build system in many cases. (That is,
|
||||
// this generally only works on platforms/builds that have been specifically
|
||||
// configured to support it.)
|
||||
|
||||
#ifndef ABSL_DEBUGGING_STACKTRACE_H_
|
||||
#define ABSL_DEBUGGING_STACKTRACE_H_
|
||||
|
||||
namespace absl {
|
||||
|
||||
// Skips the most recent "skip_count" stack frames (also skips the
|
||||
// frame generated for the "absl::GetStackFrames" routine itself), and then
|
||||
// records the pc values for up to the next "max_depth" frames in
|
||||
// "result", and the corresponding stack frame sizes in "sizes".
|
||||
// Returns the number of values recorded in "result"/"sizes".
|
||||
//
|
||||
// Example:
|
||||
// main() { foo(); }
|
||||
// foo() { bar(); }
|
||||
// bar() {
|
||||
// void* result[10];
|
||||
// int sizes[10];
|
||||
// int depth = absl::GetStackFrames(result, sizes, 10, 1);
|
||||
// }
|
||||
//
|
||||
// The absl::GetStackFrames call will skip the frame for "bar". It will
|
||||
// return 2 and will produce pc values that map to the following
|
||||
// procedures:
|
||||
// result[0] foo
|
||||
// result[1] main
|
||||
// (Actually, there may be a few more entries after "main" to account for
|
||||
// startup procedures.)
|
||||
// And corresponding stack frame sizes will also be recorded:
|
||||
// sizes[0] 16
|
||||
// sizes[1] 16
|
||||
// (Stack frame sizes of 16 above are just for illustration purposes.)
|
||||
// Stack frame sizes of 0 or less indicate that those frame sizes couldn't
|
||||
// be identified.
|
||||
//
|
||||
// This routine may return fewer stack frame entries than are
|
||||
// available. Also note that "result" and "sizes" must both be non-null.
|
||||
extern int GetStackFrames(void** result, int* sizes, int max_depth,
|
||||
int skip_count);
|
||||
|
||||
// Same as above, but to be used from a signal handler. The "uc" parameter
|
||||
// should be the pointer to ucontext_t which was passed as the 3rd parameter
|
||||
// to sa_sigaction signal handler. It may help the unwinder to get a
|
||||
// better stack trace under certain conditions. The "uc" may safely be null.
|
||||
//
|
||||
// If min_dropped_frames is not null, stores in *min_dropped_frames a
|
||||
// lower bound on the number of dropped stack frames. The stored value is
|
||||
// guaranteed to be >= 0. The number of real stack frames is guaranteed to
|
||||
// be >= skip_count + max_depth + *min_dropped_frames.
|
||||
extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
|
||||
int skip_count, const void* uc,
|
||||
int* min_dropped_frames);
|
||||
|
||||
// This is similar to the absl::GetStackFrames routine, except that it returns
|
||||
// the stack trace only, and not the stack frame sizes as well.
|
||||
// Example:
|
||||
// main() { foo(); }
|
||||
// foo() { bar(); }
|
||||
// bar() {
|
||||
// void* result[10];
|
||||
// int depth = absl::GetStackTrace(result, 10, 1);
|
||||
// }
|
||||
//
|
||||
// This produces:
|
||||
// result[0] foo
|
||||
// result[1] main
|
||||
// .... ...
|
||||
//
|
||||
// "result" must not be null.
|
||||
extern int GetStackTrace(void** result, int max_depth, int skip_count);
|
||||
|
||||
// Same as above, but to be used from a signal handler. The "uc" parameter
|
||||
// should be the pointer to ucontext_t which was passed as the 3rd parameter
|
||||
// to sa_sigaction signal handler. It may help the unwinder to get a
|
||||
// better stack trace under certain conditions. The "uc" may safely be null.
|
||||
//
|
||||
// If min_dropped_frames is not null, stores in *min_dropped_frames a
|
||||
// lower bound on the number of dropped stack frames. The stored value is
|
||||
// guaranteed to be >= 0. The number of real stack frames is guaranteed to
|
||||
// be >= skip_count + max_depth + *min_dropped_frames.
|
||||
extern int GetStackTraceWithContext(void** result, int max_depth,
|
||||
int skip_count, const void* uc,
|
||||
int* min_dropped_frames);
|
||||
|
||||
// Call this to provide a custom function for unwinding stack frames
|
||||
// that will be used every time someone invokes one of the static
|
||||
// GetStack{Frames,Trace}{,WithContext}() functions above.
|
||||
//
|
||||
// The arguments passed to the unwinder function will match the
|
||||
// arguments passed to absl::GetStackFramesWithContext() except that sizes
|
||||
// will be non-null iff the caller is interested in frame sizes.
|
||||
//
|
||||
// If unwinder is null, we revert to the default stack-tracing behavior.
|
||||
//
|
||||
// ****************************************************************
|
||||
// WARNINGS
|
||||
//
|
||||
// absl::SetStackUnwinder is not suitable for general purpose use. It is
|
||||
// provided for custom runtimes.
|
||||
// Some things to watch out for when calling absl::SetStackUnwinder:
|
||||
//
|
||||
// (a) The unwinder may be called from within signal handlers and
|
||||
// therefore must be async-signal-safe.
|
||||
//
|
||||
// (b) Even after a custom stack unwinder has been unregistered, other
|
||||
// threads may still be in the process of using that unwinder.
|
||||
// Therefore do not clean up any state that may be needed by an old
|
||||
// unwinder.
|
||||
// ****************************************************************
|
||||
extern void SetStackUnwinder(int (*unwinder)(void** pcs, int* sizes,
|
||||
int max_depth, int skip_count,
|
||||
const void* uc,
|
||||
int* min_dropped_frames));
|
||||
|
||||
// Function that exposes built-in stack-unwinding behavior, ignoring
|
||||
// any calls to absl::SetStackUnwinder().
|
||||
//
|
||||
// pcs must NOT be null.
|
||||
//
|
||||
// sizes may be null.
|
||||
// uc may be null.
|
||||
// min_dropped_frames may be null.
|
||||
//
|
||||
// The semantics are the same as the corresponding GetStack*() function in the
|
||||
// case where absl::SetStackUnwinder() was never called. Equivalents are:
|
||||
//
|
||||
// null sizes | non-nullptr sizes
|
||||
// |==========================================================|
|
||||
// null uc | GetStackTrace() | GetStackFrames() |
|
||||
// non-null uc | GetStackTraceWithContext() | GetStackFramesWithContext() |
|
||||
// |==========================================================|
|
||||
extern int DefaultStackUnwinder(void** pcs, int* sizes, int max_depth,
|
||||
int skip_count, const void* uc,
|
||||
int* min_dropped_frames);
|
||||
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_DEBUGGING_STACKTRACE_H_
|
|
@ -0,0 +1,178 @@
|
|||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
|
||||
|
||||
// Generate stack tracer for aarch64
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/mman.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
static const uintptr_t kUnknownFrameSize = 0;
|
||||
|
||||
#if defined(__linux__)
|
||||
// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
|
||||
static const unsigned char* GetKernelRtSigreturnAddress() {
|
||||
constexpr uintptr_t kImpossibleAddress = 1;
|
||||
static std::atomic<uintptr_t> memoized{kImpossibleAddress};
|
||||
uintptr_t address = memoized.load(std::memory_order_relaxed);
|
||||
if (address != kImpossibleAddress) {
|
||||
return reinterpret_cast<const unsigned char*>(address);
|
||||
}
|
||||
|
||||
address = reinterpret_cast<uintptr_t>(nullptr);
|
||||
|
||||
#ifdef ABSL_HAVE_VDSO_SUPPORT
|
||||
absl::debug_internal::VDSOSupport vdso;
|
||||
if (vdso.IsPresent()) {
|
||||
absl::debug_internal::VDSOSupport::SymbolInfo symbol_info;
|
||||
if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", STT_FUNC,
|
||||
&symbol_info) ||
|
||||
symbol_info.address == nullptr) {
|
||||
// Unexpected: VDSO is present, yet the expected symbol is missing
|
||||
// or null.
|
||||
assert(false && "VDSO is present, but doesn't have expected symbol");
|
||||
} else {
|
||||
if (reinterpret_cast<uintptr_t>(symbol_info.address) !=
|
||||
kImpossibleAddress) {
|
||||
address = reinterpret_cast<uintptr_t>(symbol_info.address);
|
||||
} else {
|
||||
assert(false && "VDSO returned invalid address");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
memoized.store(address, std::memory_order_relaxed);
|
||||
return reinterpret_cast<const unsigned char*>(address);
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
// Compute the size of a stack frame in [low..high). We assume that
|
||||
// low < high. Return size of kUnknownFrameSize.
|
||||
template<typename T>
|
||||
static inline uintptr_t ComputeStackFrameSize(const T* low,
|
||||
const T* high) {
|
||||
const char* low_char_ptr = reinterpret_cast<const char *>(low);
|
||||
const char* high_char_ptr = reinterpret_cast<const char *>(high);
|
||||
return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
|
||||
}
|
||||
|
||||
// Given a pointer to a stack frame, locate and return the calling
|
||||
// stackframe, or return null if no stackframe can be found. Perform sanity
|
||||
// checks (the strictness of which is controlled by the boolean parameter
|
||||
// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
|
||||
template<bool STRICT_UNWINDING, bool WITH_CONTEXT>
|
||||
static void **NextStackFrame(void **old_frame_pointer, const void *uc) {
|
||||
void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
|
||||
bool check_frame_size = true;
|
||||
|
||||
#if defined(__linux__)
|
||||
if (WITH_CONTEXT && uc != nullptr) {
|
||||
// Check to see if next frame's return address is __kernel_rt_sigreturn.
|
||||
if (old_frame_pointer[1] == GetKernelRtSigreturnAddress()) {
|
||||
const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
|
||||
// old_frame_pointer[0] is not suitable for unwinding, look at
|
||||
// ucontext to discover frame pointer before signal.
|
||||
void **const pre_signal_frame_pointer =
|
||||
reinterpret_cast<void **>(ucv->uc_mcontext.regs[29]);
|
||||
|
||||
// Check that alleged frame pointer is actually readable. This is to
|
||||
// prevent "double fault" in case we hit the first fault due to e.g.
|
||||
// stack corruption.
|
||||
if (!absl::debug_internal::AddressIsReadable(
|
||||
pre_signal_frame_pointer))
|
||||
return nullptr;
|
||||
|
||||
// Alleged frame pointer is readable, use it for further unwinding.
|
||||
new_frame_pointer = pre_signal_frame_pointer;
|
||||
|
||||
// Skip frame size check if we return from a signal. We may be using a
|
||||
// an alternate stack for signals.
|
||||
check_frame_size = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// aarch64 ABI requires stack pointer to be 16-byte-aligned.
|
||||
if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0)
|
||||
return nullptr;
|
||||
|
||||
// Check frame size. In strict mode, we assume frames to be under
|
||||
// 100,000 bytes. In non-strict mode, we relax the limit to 1MB.
|
||||
if (check_frame_size) {
|
||||
const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
|
||||
const uintptr_t frame_size =
|
||||
ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
|
||||
if (frame_size == kUnknownFrameSize || frame_size > max_size)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new_frame_pointer;
|
||||
}
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
||||
const void *ucp, int *min_dropped_frames) {
|
||||
#ifdef __GNUC__
|
||||
void **frame_pointer = reinterpret_cast<void**>(__builtin_frame_address(0));
|
||||
#else
|
||||
# error reading stack point not yet supported on this platform.
|
||||
#endif
|
||||
|
||||
skip_count++; // Skip the frame for this function.
|
||||
int n = 0;
|
||||
|
||||
// The frame pointer points to low address of a frame. The first 64-bit
|
||||
// word of a frame points to the next frame up the call chain, which normally
|
||||
// is just after the high address of the current frame. The second word of
|
||||
// a frame contains return adress of to the caller. To find a pc value
|
||||
// associated with the current frame, we need to go down a level in the call
|
||||
// chain. So we remember return the address of the last frame seen. This
|
||||
// does not work for the first stack frame, which belongs to UnwindImp() but
|
||||
// we skip the frame for UnwindImp() anyway.
|
||||
void* prev_return_address = nullptr;
|
||||
|
||||
while (frame_pointer && n < max_depth) {
|
||||
// The absl::GetStackFrames routine is called when we are in some
|
||||
// informational context (the failure signal handler for example).
|
||||
// Use the non-strict unwinding rules to produce a stack trace
|
||||
// that is as complete as possible (even if it contains a few bogus
|
||||
// entries in some rare cases).
|
||||
void **next_frame_pointer =
|
||||
NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
|
||||
|
||||
if (skip_count > 0) {
|
||||
skip_count--;
|
||||
} else {
|
||||
result[n] = prev_return_address;
|
||||
if (IS_STACK_FRAMES) {
|
||||
sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
prev_return_address = frame_pointer[1];
|
||||
frame_pointer = next_frame_pointer;
|
||||
}
|
||||
if (min_dropped_frames != nullptr) {
|
||||
// Implementation detail: we clamp the max of frames we are willing to
|
||||
// count, so as not to spend too much time in the loop below.
|
||||
const int kMaxUnwind = 200;
|
||||
int j = 0;
|
||||
for (; frame_pointer != nullptr && j < kMaxUnwind; j++) {
|
||||
frame_pointer =
|
||||
NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
|
||||
}
|
||||
*min_dropped_frames = j;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2011 and onwards Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Doug Kwan
|
||||
// This is inspired by Craig Silverstein's PowerPC stacktrace code.
|
||||
//
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
// WARNING:
|
||||
// This only works if all your code is in either ARM or THUMB mode. With
|
||||
// interworking, the frame pointer of the caller can either be in r11 (ARM
|
||||
// mode) or r7 (THUMB mode). A callee only saves the frame pointer of its
|
||||
// mode in a fixed location on its stack frame. If the caller is a different
|
||||
// mode, there is no easy way to find the frame pointer. It can either be
|
||||
// still in the designated register or saved on stack along with other callee
|
||||
// saved registers.
|
||||
|
||||
// Given a pointer to a stack frame, locate and return the calling
|
||||
// stackframe, or return nullptr if no stackframe can be found. Perform sanity
|
||||
// checks (the strictness of which is controlled by the boolean parameter
|
||||
// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
|
||||
template<bool STRICT_UNWINDING>
|
||||
static void **NextStackFrame(void **old_sp) {
|
||||
void **new_sp = (void**) old_sp[-1];
|
||||
|
||||
// Check that the transition from frame pointer old_sp to frame
|
||||
// pointer new_sp isn't clearly bogus
|
||||
if (STRICT_UNWINDING) {
|
||||
// With the stack growing downwards, older stack frame must be
|
||||
// at a greater address that the current one.
|
||||
if (new_sp <= old_sp) return nullptr;
|
||||
// Assume stack frames larger than 100,000 bytes are bogus.
|
||||
if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr;
|
||||
} else {
|
||||
// In the non-strict mode, allow discontiguous stack frames.
|
||||
// (alternate-signal-stacks for example).
|
||||
if (new_sp == old_sp) return nullptr;
|
||||
// And allow frames upto about 1MB.
|
||||
if ((new_sp > old_sp)
|
||||
&& ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr;
|
||||
}
|
||||
if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return nullptr;
|
||||
return new_sp;
|
||||
}
|
||||
|
||||
// This ensures that absl::GetStackTrace sets up the Link Register properly.
|
||||
#ifdef __GNUC__
|
||||
void StacktraceArmDummyFunction() __attribute__((noinline));
|
||||
void StacktraceArmDummyFunction() { __asm__ volatile(""); }
|
||||
#else
|
||||
# error StacktraceArmDummyFunction() needs to be ported to this platform.
|
||||
#endif
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
||||
const void * /* ucp */, int *min_dropped_frames) {
|
||||
#ifdef __GNUC__
|
||||
void **sp = reinterpret_cast<void**>(__builtin_frame_address(0));
|
||||
#else
|
||||
# error reading stack point not yet supported on this platform.
|
||||
#endif
|
||||
|
||||
// On ARM, the return address is stored in the link register (r14).
|
||||
// This is not saved on the stack frame of a leaf function. To
|
||||
// simplify code that reads return addresses, we call a dummy
|
||||
// function so that the return address of this function is also
|
||||
// stored in the stack frame. This works at least for gcc.
|
||||
StacktraceArmDummyFunction();
|
||||
|
||||
int n = 0;
|
||||
while (sp && n < max_depth) {
|
||||
// The absl::GetStackFrames routine is called when we are in some
|
||||
// informational context (the failure signal handler for example).
|
||||
// Use the non-strict unwinding rules to produce a stack trace
|
||||
// that is as complete as possible (even if it contains a few bogus
|
||||
// entries in some rare cases).
|
||||
void **next_sp = NextStackFrame<!IS_STACK_FRAMES>(sp);
|
||||
|
||||
if (skip_count > 0) {
|
||||
skip_count--;
|
||||
} else {
|
||||
result[n] = *sp;
|
||||
|
||||
if (IS_STACK_FRAMES) {
|
||||
if (next_sp > sp) {
|
||||
sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
|
||||
} else {
|
||||
// A frame-size of 0 is used to indicate unknown frame size.
|
||||
sizes[n] = 0;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
}
|
||||
sp = next_sp;
|
||||
}
|
||||
if (min_dropped_frames != nullptr) {
|
||||
// Implementation detail: we clamp the max of frames we are willing to
|
||||
// count, so as not to spend too much time in the loop below.
|
||||
const int kMaxUnwind = 200;
|
||||
int j = 0;
|
||||
for (; sp != nullptr && j < kMaxUnwind; j++) {
|
||||
sp = NextStackFrame<!IS_STACK_FRAMES>(sp);
|
||||
}
|
||||
*min_dropped_frames = j;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2000 - 2007 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Author: Sanjay Ghemawat
|
||||
//
|
||||
// Portable implementation - just use glibc
|
||||
//
|
||||
// Note: The glibc implementation may cause a call to malloc.
|
||||
// This can cause a deadlock in HeapProfiler.
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
||||
const void *ucp, int *min_dropped_frames) {
|
||||
static const int kStackLength = 64;
|
||||
void * stack[kStackLength];
|
||||
int size;
|
||||
|
||||
size = backtrace(stack, kStackLength);
|
||||
skip_count++; // we want to skip the current frame as well
|
||||
int result_count = size - skip_count;
|
||||
if (result_count < 0)
|
||||
result_count = 0;
|
||||
if (result_count > max_depth)
|
||||
result_count = max_depth;
|
||||
for (int i = 0; i < result_count; i++)
|
||||
result[i] = stack[i + skip_count];
|
||||
|
||||
if (IS_STACK_FRAMES) {
|
||||
// No implementation for finding out the stack frame sizes yet.
|
||||
memset(sizes, 0, sizeof(*sizes) * result_count);
|
||||
}
|
||||
if (min_dropped_frames != nullptr) {
|
||||
if (size - skip_count - max_depth > 0) {
|
||||
*min_dropped_frames = size - skip_count - max_depth;
|
||||
} else {
|
||||
*min_dropped_frames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result_count;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Produce stack trace. I'm guessing (hoping!) the code is much like
|
||||
// for x86. For apple machines, at least, it seems to be; see
|
||||
// http://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html
|
||||
// http://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK
|
||||
// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <asm/ptrace.h> // for PT_NIP.
|
||||
#include <ucontext.h> // for ucontext_t
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
// Given a stack pointer, return the saved link register value.
|
||||
// Note that this is the link register for a callee.
|
||||
static inline void *StacktracePowerPCGetLR(void **sp) {
|
||||
// PowerPC has 3 main ABIs, which say where in the stack the
|
||||
// Link Register is. For DARWIN and AIX (used by apple and
|
||||
// linux ppc64), it's in sp[2]. For SYSV (used by linux ppc),
|
||||
// it's in sp[1].
|
||||
#if defined(_CALL_AIX) || defined(_CALL_DARWIN)
|
||||
return *(sp+2);
|
||||
#elif defined(_CALL_SYSV)
|
||||
return *(sp+1);
|
||||
#elif defined(__APPLE__) || (defined(__linux__) && defined(__PPC64__))
|
||||
// This check is in case the compiler doesn't define _CALL_AIX/etc.
|
||||
return *(sp+2);
|
||||
#elif defined(__linux)
|
||||
// This check is in case the compiler doesn't define _CALL_SYSV.
|
||||
return *(sp+1);
|
||||
#else
|
||||
#error Need to specify the PPC ABI for your archiecture.
|
||||
#endif
|
||||
}
|
||||
|
||||
// Given a pointer to a stack frame, locate and return the calling
|
||||
// stackframe, or return null if no stackframe can be found. Perform sanity
|
||||
// checks (the strictness of which is controlled by the boolean parameter
|
||||
// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
|
||||
template<bool STRICT_UNWINDING, bool IS_WITH_CONTEXT>
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
|
||||
static void **NextStackFrame(void **old_sp, const void *uc) {
|
||||
void **new_sp = (void **) *old_sp;
|
||||
enum { kStackAlignment = 16 };
|
||||
|
||||
// Check that the transition from frame pointer old_sp to frame
|
||||
// pointer new_sp isn't clearly bogus
|
||||
if (STRICT_UNWINDING) {
|
||||
// With the stack growing downwards, older stack frame must be
|
||||
// at a greater address that the current one.
|
||||
if (new_sp <= old_sp) return nullptr;
|
||||
// Assume stack frames larger than 100,000 bytes are bogus.
|
||||
if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr;
|
||||
} else {
|
||||
// In the non-strict mode, allow discontiguous stack frames.
|
||||
// (alternate-signal-stacks for example).
|
||||
if (new_sp == old_sp) return nullptr;
|
||||
// And allow frames upto about 1MB.
|
||||
if ((new_sp > old_sp)
|
||||
&& ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return nullptr;
|
||||
}
|
||||
if ((uintptr_t)new_sp % kStackAlignment != 0) return nullptr;
|
||||
|
||||
#if defined(__linux__)
|
||||
enum StackTraceKernelSymbolStatus {
|
||||
kNotInitialized = 0, kAddressValid, kAddressInvalid };
|
||||
|
||||
if (IS_WITH_CONTEXT && uc != nullptr) {
|
||||
static StackTraceKernelSymbolStatus kernel_symbol_status =
|
||||
kNotInitialized; // Sentinel: not computed yet.
|
||||
// Initialize with sentinel value: __kernel_rt_sigtramp_rt64 can not
|
||||
// possibly be there.
|
||||
static const unsigned char *kernel_sigtramp_rt64_address = nullptr;
|
||||
if (kernel_symbol_status == kNotInitialized) {
|
||||
absl::debug_internal::VDSOSupport vdso;
|
||||
if (vdso.IsPresent()) {
|
||||
absl::debug_internal::VDSOSupport::SymbolInfo
|
||||
sigtramp_rt64_symbol_info;
|
||||
if (!vdso.LookupSymbol(
|
||||
"__kernel_sigtramp_rt64", "LINUX_2.6.15",
|
||||
absl::debug_internal::VDSOSupport::kVDSOSymbolType,
|
||||
&sigtramp_rt64_symbol_info) ||
|
||||
sigtramp_rt64_symbol_info.address == nullptr) {
|
||||
// Unexpected: VDSO is present, yet the expected symbol is missing
|
||||
// or null.
|
||||
assert(false && "VDSO is present, but doesn't have expected symbol");
|
||||
kernel_symbol_status = kAddressInvalid;
|
||||
} else {
|
||||
kernel_sigtramp_rt64_address =
|
||||
reinterpret_cast<const unsigned char *>(
|
||||
sigtramp_rt64_symbol_info.address);
|
||||
kernel_symbol_status = kAddressValid;
|
||||
}
|
||||
} else {
|
||||
kernel_symbol_status = kAddressInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_sp != nullptr &&
|
||||
kernel_symbol_status == kAddressValid &&
|
||||
StacktracePowerPCGetLR(new_sp) == kernel_sigtramp_rt64_address) {
|
||||
const ucontext_t* signal_context =
|
||||
reinterpret_cast<const ucontext_t*>(uc);
|
||||
void **const sp_before_signal =
|
||||
reinterpret_cast<void**>(signal_context->uc_mcontext.gp_regs[PT_R1]);
|
||||
// Check that alleged sp before signal is nonnull and is reasonably
|
||||
// aligned.
|
||||
if (sp_before_signal != nullptr &&
|
||||
((uintptr_t)sp_before_signal % kStackAlignment) == 0) {
|
||||
// Check that alleged stack pointer is actually readable. This is to
|
||||
// prevent a "double fault" in case we hit the first fault due to e.g.
|
||||
// a stack corruption.
|
||||
if (absl::debug_internal::AddressIsReadable(sp_before_signal)) {
|
||||
// Alleged stack pointer is readable, use it for further unwinding.
|
||||
new_sp = sp_before_signal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return new_sp;
|
||||
}
|
||||
|
||||
// This ensures that absl::GetStackTrace sets up the Link Register properly.
|
||||
void StacktracePowerPCDummyFunction() __attribute__((noinline));
|
||||
void StacktracePowerPCDummyFunction() { __asm__ volatile(""); }
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
|
||||
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
||||
const void *ucp, int *min_dropped_frames) {
|
||||
void **sp;
|
||||
// Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther)
|
||||
// and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a
|
||||
// different asm syntax. I don't know quite the best way to discriminate
|
||||
// systems using the old as from the new one; I've gone with __APPLE__.
|
||||
#ifdef __APPLE__
|
||||
__asm__ volatile ("mr %0,r1" : "=r" (sp));
|
||||
#else
|
||||
__asm__ volatile ("mr %0,1" : "=r" (sp));
|
||||
#endif
|
||||
|
||||
// On PowerPC, the "Link Register" or "Link Record" (LR), is a stack
|
||||
// entry that holds the return address of the subroutine call (what
|
||||
// instruction we run after our function finishes). This is the
|
||||
// same as the stack-pointer of our parent routine, which is what we
|
||||
// want here. While the compiler will always(?) set up LR for
|
||||
// subroutine calls, it may not for leaf functions (such as this one).
|
||||
// This routine forces the compiler (at least gcc) to push it anyway.
|
||||
StacktracePowerPCDummyFunction();
|
||||
|
||||
// The LR save area is used by the callee, so the top entry is bogus.
|
||||
skip_count++;
|
||||
|
||||
int n = 0;
|
||||
|
||||
// Unlike ABIs of X86 and ARM, PowerPC ABIs say that return address (in
|
||||
// the link register) of a function call is stored in the caller's stack
|
||||
// frame instead of the callee's. When we look for the return address
|
||||
// associated with a stack frame, we need to make sure that there is a
|
||||
// caller frame before it. So we call NextStackFrame before entering the
|
||||
// loop below and check next_sp instead of sp for loop termination.
|
||||
// The outermost frame is set up by runtimes and it does not have a
|
||||
// caller frame, so it is skipped.
|
||||
|
||||
// The absl::GetStackFrames routine is called when we are in some
|
||||
// informational context (the failure signal handler for example).
|
||||
// Use the non-strict unwinding rules to produce a stack trace
|
||||
// that is as complete as possible (even if it contains a few
|
||||
// bogus entries in some rare cases).
|
||||
void **next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp);
|
||||
|
||||
while (next_sp && n < max_depth) {
|
||||
if (skip_count > 0) {
|
||||
skip_count--;
|
||||
} else {
|
||||
result[n] = StacktracePowerPCGetLR(sp);
|
||||
if (IS_STACK_FRAMES) {
|
||||
if (next_sp > sp) {
|
||||
sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
|
||||
} else {
|
||||
// A frame-size of 0 is used to indicate unknown frame size.
|
||||
sizes[n] = 0;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
sp = next_sp;
|
||||
next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp);
|
||||
}
|
||||
|
||||
if (min_dropped_frames != nullptr) {
|
||||
// Implementation detail: we clamp the max of frames we are willing to
|
||||
// count, so as not to spend too much time in the loop below.
|
||||
const int kMaxUnwind = 1000;
|
||||
int j = 0;
|
||||
for (; next_sp != nullptr && j < kMaxUnwind; j++) {
|
||||
next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(next_sp, ucp);
|
||||
}
|
||||
*min_dropped_frames = j;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
static int UnwindImpl(void** /* result */, int* /* sizes */,
|
||||
int /* max_depth */, int /* skip_count */,
|
||||
const void* /* ucp */, int *min_dropped_frames) {
|
||||
if (min_dropped_frames != nullptr) {
|
||||
*min_dropped_frames = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Produces a stack trace for Windows. Normally, one could use
|
||||
// stacktrace_x86-inl.h or stacktrace_x86_64-inl.h -- and indeed, that
|
||||
// should work for binaries compiled using MSVC in "debug" mode.
|
||||
// However, in "release" mode, Windows uses frame-pointer
|
||||
// optimization, which makes getting a stack trace very difficult.
|
||||
//
|
||||
// There are several approaches one can take. One is to use Windows
|
||||
// intrinsics like StackWalk64. These can work, but have restrictions
|
||||
// on how successful they can be. Another attempt is to write a
|
||||
// version of stacktrace_x86-inl.h that has heuristic support for
|
||||
// dealing with FPO, similar to what WinDbg does (see
|
||||
// http://www.nynaeve.net/?p=97). There are (non-working) examples of
|
||||
// these approaches, complete with TODOs, in stacktrace_win32-inl.h#1
|
||||
//
|
||||
// The solution we've ended up doing is to call the undocumented
|
||||
// windows function RtlCaptureStackBackTrace, which probably doesn't
|
||||
// work with FPO but at least is fast, and doesn't require a symbol
|
||||
// server.
|
||||
//
|
||||
// This code is inspired by a patch from David Vitek:
|
||||
// http://code.google.com/p/google-perftools/issues/detail?id=83
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
|
||||
|
||||
#include <windows.h> // for GetProcAddress and GetModuleHandle
|
||||
#include <cassert>
|
||||
|
||||
typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
|
||||
IN ULONG frames_to_skip,
|
||||
IN ULONG frames_to_capture,
|
||||
OUT PVOID *backtrace,
|
||||
OUT PULONG backtrace_hash);
|
||||
|
||||
// Load the function we need at static init time, where we don't have
|
||||
// to worry about someone else holding the loader's lock.
|
||||
static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
|
||||
(RtlCaptureStackBackTrace_Function*)
|
||||
GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
|
||||
const void *ucp, int *min_dropped_frames) {
|
||||
int n = 0;
|
||||
if (!RtlCaptureStackBackTrace_fn) {
|
||||
// can't find a stacktrace with no function to call
|
||||
} else {
|
||||
n = (int)RtlCaptureStackBackTrace_fn(skip_count + 2, max_depth, result, 0);
|
||||
}
|
||||
if (IS_STACK_FRAMES) {
|
||||
// No implementation for finding out the stack frame sizes yet.
|
||||
memset(sizes, 0, sizeof(*sizes) * n);
|
||||
}
|
||||
if (min_dropped_frames != nullptr) {
|
||||
// Not implemented.
|
||||
*min_dropped_frames = 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
|
|
@ -0,0 +1,321 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Produce stack trace
|
||||
|
||||
#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
|
||||
#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
|
||||
|
||||
#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
|
||||
#include <ucontext.h> // for ucontext_t
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
#if defined(__linux__) && defined(__i386__)
|
||||
// Count "push %reg" instructions in VDSO __kernel_vsyscall(),
|
||||
// preceeding "syscall" or "sysenter".
|
||||
// If __kernel_vsyscall uses frame pointer, answer 0.
|
||||
//
|
||||
// kMaxBytes tells how many instruction bytes of __kernel_vsyscall
|
||||
// to analyze before giving up. Up to kMaxBytes+1 bytes of
|
||||
// instructions could be accessed.
|
||||
//
|
||||
// Here are known __kernel_vsyscall instruction sequences:
|
||||
//
|
||||
// SYSENTER (linux-2.6.26/arch/x86/vdso/vdso32/sysenter.S).
|
||||
// Used on Intel.
|
||||
// 0xffffe400 <__kernel_vsyscall+0>: push %ecx
|
||||
// 0xffffe401 <__kernel_vsyscall+1>: push %edx
|
||||
// 0xffffe402 <__kernel_vsyscall+2>: push %ebp
|
||||
// 0xffffe403 <__kernel_vsyscall+3>: mov %esp,%ebp
|
||||
// 0xffffe405 <__kernel_vsyscall+5>: sysenter
|
||||
//
|
||||
// SYSCALL (see linux-2.6.26/arch/x86/vdso/vdso32/syscall.S).
|
||||
// Used on AMD.
|
||||
// 0xffffe400 <__kernel_vsyscall+0>: push %ebp
|
||||
// 0xffffe401 <__kernel_vsyscall+1>: mov %ecx,%ebp
|
||||
// 0xffffe403 <__kernel_vsyscall+3>: syscall
|
||||
//
|
||||
|
||||
// The sequence below isn't actually expected in Google fleet,
|
||||
// here only for completeness. Remove this comment from OSS release.
|
||||
|
||||
// i386 (see linux-2.6.26/arch/x86/vdso/vdso32/int80.S)
|
||||
// 0xffffe400 <__kernel_vsyscall+0>: int $0x80
|
||||
// 0xffffe401 <__kernel_vsyscall+1>: ret
|
||||
//
|
||||
static const int kMaxBytes = 10;
|
||||
|
||||
// We use assert()s instead of DCHECK()s -- this is too low level
|
||||
// for DCHECK().
|
||||
|
||||
static int CountPushInstructions(const unsigned char *const addr) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < kMaxBytes; ++i) {
|
||||
if (addr[i] == 0x89) {
|
||||
// "mov reg,reg"
|
||||
if (addr[i + 1] == 0xE5) {
|
||||
// Found "mov %esp,%ebp".
|
||||
return 0;
|
||||
}
|
||||
++i; // Skip register encoding byte.
|
||||
} else if (addr[i] == 0x0F &&
|
||||
(addr[i + 1] == 0x34 || addr[i + 1] == 0x05)) {
|
||||
// Found "sysenter" or "syscall".
|
||||
return result;
|
||||
} else if ((addr[i] & 0xF0) == 0x50) {
|
||||
// Found "push %reg".
|
||||
++result;
|
||||
} else if (addr[i] == 0xCD && addr[i + 1] == 0x80) {
|
||||
// Found "int $0x80"
|
||||
assert(result == 0);
|
||||
return 0;
|
||||
} else {
|
||||
// Unexpected instruction.
|
||||
assert(false && "unexpected instruction in __kernel_vsyscall");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Unexpected: didn't find SYSENTER or SYSCALL in
|
||||
// [__kernel_vsyscall, __kernel_vsyscall + kMaxBytes) interval.
|
||||
assert(false && "did not find SYSENTER or SYSCALL in __kernel_vsyscall");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Assume stack frames larger than 100,000 bytes are bogus.
|
||||
static const int kMaxFrameBytes = 100000;
|
||||
|
||||
// Returns the stack frame pointer from signal context, 0 if unknown.
|
||||
// vuc is a ucontext_t *. We use void* to avoid the use
|
||||
// of ucontext_t on non-POSIX systems.
|
||||
static uintptr_t GetFP(const void *vuc) {
|
||||
#if defined(__linux__)
|
||||
if (vuc != nullptr) {
|
||||
auto *uc = reinterpret_cast<const ucontext_t *>(vuc);
|
||||
#if defined(__i386__)
|
||||
const auto bp = uc->uc_mcontext.gregs[REG_EBP];
|
||||
const auto sp = uc->uc_mcontext.gregs[REG_ESP];
|
||||
#elif defined(__x86_64__)
|
||||
const auto bp = uc->uc_mcontext.gregs[REG_RBP];
|
||||
const auto sp = uc->uc_mcontext.gregs[REG_RSP];
|
||||
#else
|
||||
const uintptr_t bp = 0;
|
||||
const uintptr_t sp = 0;
|
||||
#endif
|
||||
// Sanity-check that the base pointer is valid. It should be as long as
|
||||
// SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in
|
||||
// the process is compiled with --copt=-fomit-frame-pointer or
|
||||
// --copt=-momit-leaf-frame-pointer.
|
||||
//
|
||||
// TODO(bcmills): -momit-leaf-frame-pointer is currently the default
|
||||
// behavior when building with clang. Talk to the C++ toolchain team about
|
||||
// fixing that.
|
||||
if (bp >= sp && bp - sp <= kMaxFrameBytes) return bp;
|
||||
|
||||
// If bp isn't a plausible frame pointer, return the stack pointer instead.
|
||||
// If we're lucky, it points to the start of a stack frame; otherwise, we'll
|
||||
// get one frame of garbage in the stack trace and fail the sanity check on
|
||||
// the next iteration.
|
||||
return sp;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Given a pointer to a stack frame, locate and return the calling
|
||||
// stackframe, or return null if no stackframe can be found. Perform sanity
|
||||
// checks (the strictness of which is controlled by the boolean parameter
|
||||
// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
|
||||
template <bool STRICT_UNWINDING, bool WITH_CONTEXT>
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
|
||||
static void **NextStackFrame(void **old_fp, const void *uc) {
|
||||
void **new_fp = (void **)*old_fp;
|
||||
|
||||
#if defined(__linux__) && defined(__i386__)
|
||||
if (WITH_CONTEXT && uc != nullptr) {
|
||||
// How many "push %reg" instructions are there at __kernel_vsyscall?
|
||||
// This is constant for a given kernel and processor, so compute
|
||||
// it only once.
|
||||
static int num_push_instructions = -1; // Sentinel: not computed yet.
|
||||
// Initialize with sentinel value: __kernel_rt_sigreturn can not possibly
|
||||
// be there.
|
||||
static const unsigned char *kernel_rt_sigreturn_address = nullptr;
|
||||
static const unsigned char *kernel_vsyscall_address = nullptr;
|
||||
if (num_push_instructions == -1) {
|
||||
absl::debug_internal::VDSOSupport vdso;
|
||||
if (vdso.IsPresent()) {
|
||||
absl::debug_internal::VDSOSupport::SymbolInfo
|
||||
rt_sigreturn_symbol_info;
|
||||
absl::debug_internal::VDSOSupport::SymbolInfo vsyscall_symbol_info;
|
||||
if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.5", STT_FUNC,
|
||||
&rt_sigreturn_symbol_info) ||
|
||||
!vdso.LookupSymbol("__kernel_vsyscall", "LINUX_2.5", STT_FUNC,
|
||||
&vsyscall_symbol_info) ||
|
||||
rt_sigreturn_symbol_info.address == nullptr ||
|
||||
vsyscall_symbol_info.address == nullptr) {
|
||||
// Unexpected: 32-bit VDSO is present, yet one of the expected
|
||||
// symbols is missing or null.
|
||||
assert(false && "VDSO is present, but doesn't have expected symbols");
|
||||
num_push_instructions = 0;
|
||||
} else {
|
||||
kernel_rt_sigreturn_address =
|
||||
reinterpret_cast<const unsigned char *>(
|
||||
rt_sigreturn_symbol_info.address);
|
||||
kernel_vsyscall_address =
|
||||
reinterpret_cast<const unsigned char *>(
|
||||
vsyscall_symbol_info.address);
|
||||
num_push_instructions =
|
||||
CountPushInstructions(kernel_vsyscall_address);
|
||||
}
|
||||
} else {
|
||||
num_push_instructions = 0;
|
||||
}
|
||||
}
|
||||
if (num_push_instructions != 0 && kernel_rt_sigreturn_address != nullptr &&
|
||||
old_fp[1] == kernel_rt_sigreturn_address) {
|
||||
const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
|
||||
// This kernel does not use frame pointer in its VDSO code,
|
||||
// and so %ebp is not suitable for unwinding.
|
||||
void **const reg_ebp =
|
||||
reinterpret_cast<void **>(ucv->uc_mcontext.gregs[REG_EBP]);
|
||||
const unsigned char *const reg_eip =
|
||||
reinterpret_cast<unsigned char *>(ucv->uc_mcontext.gregs[REG_EIP]);
|
||||
if (new_fp == reg_ebp && kernel_vsyscall_address <= reg_eip &&
|
||||
reg_eip - kernel_vsyscall_address < kMaxBytes) {
|
||||
// We "stepped up" to __kernel_vsyscall, but %ebp is not usable.
|
||||
// Restore from 'ucv' instead.
|
||||
void **const reg_esp =
|
||||
reinterpret_cast<void **>(ucv->uc_mcontext.gregs[REG_ESP]);
|
||||
// Check that alleged %esp is not null and is reasonably aligned.
|
||||
if (reg_esp &&
|
||||
((uintptr_t)reg_esp & (sizeof(reg_esp) - 1)) == 0) {
|
||||
// Check that alleged %esp is actually readable. This is to prevent
|
||||
// "double fault" in case we hit the first fault due to e.g. stack
|
||||
// corruption.
|
||||
void *const reg_esp2 = reg_esp[num_push_instructions - 1];
|
||||
if (absl::debug_internal::AddressIsReadable(reg_esp2)) {
|
||||
// Alleged %esp is readable, use it for further unwinding.
|
||||
new_fp = reinterpret_cast<void **>(reg_esp2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const uintptr_t old_fp_u = reinterpret_cast<uintptr_t>(old_fp);
|
||||
const uintptr_t new_fp_u = reinterpret_cast<uintptr_t>(new_fp);
|
||||
|
||||
// Check that the transition from frame pointer old_fp to frame
|
||||
// pointer new_fp isn't clearly bogus. Skip the checks if new_fp
|
||||
// matches the signal context, so that we don't skip out early when
|
||||
// using an alternate signal stack.
|
||||
//
|
||||
// TODO(bcmills): The GetFP call should be completely unnecessary when
|
||||
// SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's
|
||||
// stack by this point), but it is empirically still needed (e.g. when the
|
||||
// stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some
|
||||
// frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what
|
||||
// it's supposed to.
|
||||
if (STRICT_UNWINDING &&
|
||||
(!WITH_CONTEXT || uc == nullptr || new_fp_u != GetFP(uc))) {
|
||||
// With the stack growing downwards, older stack frame must be
|
||||
// at a greater address that the current one.
|
||||
if (new_fp_u <= old_fp_u) return nullptr;
|
||||
if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr;
|
||||
} else {
|
||||
if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below
|
||||
// In the non-strict mode, allow discontiguous stack frames.
|
||||
// (alternate-signal-stacks for example).
|
||||
if (new_fp == old_fp) return nullptr;
|
||||
}
|
||||
|
||||
if (new_fp_u & (sizeof(void *) - 1)) return nullptr;
|
||||
#ifdef __i386__
|
||||
// On 32-bit machines, the stack pointer can be very close to
|
||||
// 0xffffffff, so we explicitly check for a pointer into the
|
||||
// last two pages in the address space
|
||||
if (new_fp_u >= 0xffffe000) return nullptr;
|
||||
#endif
|
||||
#if !defined(_WIN32)
|
||||
if (!STRICT_UNWINDING) {
|
||||
// Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test
|
||||
// on AMD-based machines with VDSO-enabled kernels.
|
||||
// Make an extra sanity check to insure new_fp is readable.
|
||||
// Note: NextStackFrame<false>() is only called while the program
|
||||
// is already on its last leg, so it's ok to be slow here.
|
||||
|
||||
if (!absl::debug_internal::AddressIsReadable(new_fp)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return new_fp;
|
||||
}
|
||||
|
||||
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
|
||||
ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
|
||||
ABSL_ATTRIBUTE_NOINLINE
|
||||
static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
|
||||
const void *ucp, int *min_dropped_frames) {
|
||||
int n = 0;
|
||||
void **fp = reinterpret_cast<void **>(__builtin_frame_address(0));
|
||||
|
||||
while (fp && n < max_depth) {
|
||||
if (*(fp + 1) == reinterpret_cast<void *>(0)) {
|
||||
// In 64-bit code, we often see a frame that
|
||||
// points to itself and has a return address of 0.
|
||||
break;
|
||||
}
|
||||
void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
|
||||
if (skip_count > 0) {
|
||||
skip_count--;
|
||||
} else {
|
||||
result[n] = *(fp + 1);
|
||||
if (IS_STACK_FRAMES) {
|
||||
if (next_fp > fp) {
|
||||
sizes[n] = (uintptr_t)next_fp - (uintptr_t)fp;
|
||||
} else {
|
||||
// A frame-size of 0 is used to indicate unknown frame size.
|
||||
sizes[n] = 0;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
}
|
||||
fp = next_fp;
|
||||
}
|
||||
if (min_dropped_frames != nullptr) {
|
||||
// Implementation detail: we clamp the max of frames we are willing to
|
||||
// count, so as not to spend too much time in the loop below.
|
||||
const int kMaxUnwind = 1000;
|
||||
int j = 0;
|
||||
for (; fp != nullptr && j < kMaxUnwind; j++) {
|
||||
fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp);
|
||||
}
|
||||
*min_dropped_frames = j;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
|
Loading…
Reference in New Issue