diff options
author | misterg <misterg@google.com> | 2017-09-19T20·54-0400 |
---|---|---|
committer | misterg <misterg@google.com> | 2017-09-19T20·54-0400 |
commit | c2e754829628d1e9b7a16b3389cfdace76950fdf (patch) | |
tree | 5a7f056f44e27c30e10025113b644f0b3b5801fc /absl/debugging/stacktrace.cc |
Initial Commit
Diffstat (limited to 'absl/debugging/stacktrace.cc')
-rw-r--r-- | absl/debugging/stacktrace.cc | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc new file mode 100644 index 000000000000..6c1690426381 --- /dev/null +++ b/absl/debugging/stacktrace.cc @@ -0,0 +1,133 @@ +// 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. +// +// There are three different ways we can try to get the stack trace: +// +// 1) Our hand-coded stack-unwinder. This depends on a certain stack +// layout, which is used by gcc (and those systems using a +// gcc-compatible ABI) on x86 systems, at least since gcc 2.95. +// It uses the frame pointer to do its work. +// +// 2) The libunwind library. This is still in development, and as a +// separate library adds a new dependency, but doesn't need a frame +// pointer. It also doesn't call malloc. +// +// 3) The gdb unwinder -- also the one used by the c++ exception code. +// It's obviously well-tested, but has a fatal flaw: it can call +// malloc() from the unwinder. This is a problem because we're +// trying to use the unwinder to instrument malloc(). +// +// Note: if you add a new implementation here, make sure it works +// correctly when absl::GetStackTrace() is called with max_depth == 0. +// Some code may do that. + +#include "absl/debugging/stacktrace.h" + +#include <atomic> + +#include "absl/base/port.h" +#include "absl/debugging/internal/stacktrace_config.h" + +#if defined(ABSL_STACKTRACE_INL_HEADER) +#include ABSL_STACKTRACE_INL_HEADER +#else +# error Cannot calculate stack trace: will need to write for your environment +# include "absl/debugging/internal/stacktrace_x86-inl.inc" +# include "absl/debugging/internal/stacktrace_win32-inl.inc" +# include "absl/debugging/internal/stacktrace_unimplemented-inl.inc" +# include "absl/debugging/internal/stacktrace_libunwind-inl.inc" +# include "absl/debugging/internal/stacktrace_generic-inl.inc" +# include "absl/debugging/internal/stacktrace_powerpc-inl.inc" +# include "absl/debugging/internal/stacktrace_arm-inl.inc" +# include "absl/debugging/internal/stacktrace_aarch64-inl.inc" +#endif + +namespace absl { +namespace { + +typedef int (*Unwinder)(void**, int*, int, int, const void*, int*); +std::atomic<Unwinder> custom; + +template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> +ABSL_ATTRIBUTE_ALWAYS_INLINE inline int Unwind(void** result, int* sizes, + int max_depth, int skip_count, + const void* uc, + int* min_dropped_frames) { + Unwinder f = &UnwindImpl<IS_STACK_FRAMES, IS_WITH_CONTEXT>; + Unwinder g = custom.load(std::memory_order_acquire); + if (g != nullptr) f = g; + + // Add 1 to skip count for the unwinder function itself + int size = (*f)(result, sizes, max_depth, skip_count + 1, uc, + min_dropped_frames); + // To disable tail call to (*f)(...) + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); + return size; +} + +} // anonymous namespace + +int GetStackFrames(void** result, int* sizes, int max_depth, int skip_count) { + return Unwind<true, false>(result, sizes, max_depth, skip_count, nullptr, + nullptr); +} + +int GetStackFramesWithContext(void** result, int* sizes, int max_depth, + int skip_count, const void* uc, + int* min_dropped_frames) { + return Unwind<true, true>(result, sizes, max_depth, skip_count, uc, + min_dropped_frames); +} + +int GetStackTrace(void** result, int max_depth, int skip_count) { + return Unwind<false, false>(result, nullptr, max_depth, skip_count, nullptr, + nullptr); +} + +int GetStackTraceWithContext(void** result, int max_depth, int skip_count, + const void* uc, int* min_dropped_frames) { + return Unwind<false, true>(result, nullptr, max_depth, skip_count, uc, + min_dropped_frames); +} + +void SetStackUnwinder(Unwinder w) { + custom.store(w, std::memory_order_release); +} + +int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip, + const void* uc, int* min_dropped_frames) { + skip++; // For this function + Unwinder f = nullptr; + if (sizes == nullptr) { + if (uc == nullptr) { + f = &UnwindImpl<false, false>; + } else { + f = &UnwindImpl<false, true>; + } + } else { + if (uc == nullptr) { + f = &UnwindImpl<true, false>; + } else { + f = &UnwindImpl<true, true>; + } + } + volatile int x = 0; + int n = (*f)(pcs, sizes, depth, skip, uc, min_dropped_frames); + x = 1; (void) x; // To disable tail call to (*f)(...) + return n; +} + +} // namespace absl |