blob: 1f7c7d82b2d2b60f00f5a92562b062a7ee50d310 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// Copyright 2017 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// Produce stack trace.
16//
17// There are three different ways we can try to get the stack trace:
18//
19// 1) Our hand-coded stack-unwinder. This depends on a certain stack
20// layout, which is used by gcc (and those systems using a
21// gcc-compatible ABI) on x86 systems, at least since gcc 2.95.
22// It uses the frame pointer to do its work.
23//
24// 2) The libunwind library. This is still in development, and as a
25// separate library adds a new dependency, but doesn't need a frame
26// pointer. It also doesn't call malloc.
27//
28// 3) The gdb unwinder -- also the one used by the c++ exception code.
29// It's obviously well-tested, but has a fatal flaw: it can call
30// malloc() from the unwinder. This is a problem because we're
31// trying to use the unwinder to instrument malloc().
32//
33// Note: if you add a new implementation here, make sure it works
34// correctly when absl::GetStackTrace() is called with max_depth == 0.
35// Some code may do that.
36
37#include "absl/debugging/stacktrace.h"
38
39#include <atomic>
40
41#include "absl/base/attributes.h"
42#include "absl/base/port.h"
43#include "absl/debugging/internal/stacktrace_config.h"
44
45#if defined(ABSL_STACKTRACE_INL_HEADER)
46#include ABSL_STACKTRACE_INL_HEADER
47#else
48# error Cannot calculate stack trace: will need to write for your environment
49
50# include "absl/debugging/internal/stacktrace_aarch64-inl.inc"
51# include "absl/debugging/internal/stacktrace_arm-inl.inc"
52# include "absl/debugging/internal/stacktrace_generic-inl.inc"
53# include "absl/debugging/internal/stacktrace_powerpc-inl.inc"
54# include "absl/debugging/internal/stacktrace_unimplemented-inl.inc"
55# include "absl/debugging/internal/stacktrace_win32-inl.inc"
56# include "absl/debugging/internal/stacktrace_x86-inl.inc"
57#endif
58
59namespace absl {
Austin Schuhb4691e92020-12-31 12:37:18 -080060ABSL_NAMESPACE_BEGIN
Austin Schuh36244a12019-09-21 17:52:38 -070061namespace {
62
63typedef int (*Unwinder)(void**, int*, int, int, const void*, int*);
64std::atomic<Unwinder> custom;
65
66template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
67ABSL_ATTRIBUTE_ALWAYS_INLINE inline int Unwind(void** result, int* sizes,
68 int max_depth, int skip_count,
69 const void* uc,
70 int* min_dropped_frames) {
71 Unwinder f = &UnwindImpl<IS_STACK_FRAMES, IS_WITH_CONTEXT>;
72 Unwinder g = custom.load(std::memory_order_acquire);
73 if (g != nullptr) f = g;
74
75 // Add 1 to skip count for the unwinder function itself
76 int size = (*f)(result, sizes, max_depth, skip_count + 1, uc,
77 min_dropped_frames);
78 // To disable tail call to (*f)(...)
79 ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
80 return size;
81}
82
83} // anonymous namespace
84
85ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackFrames(
86 void** result, int* sizes, int max_depth, int skip_count) {
87 return Unwind<true, false>(result, sizes, max_depth, skip_count, nullptr,
88 nullptr);
89}
90
91ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
92GetStackFramesWithContext(void** result, int* sizes, int max_depth,
93 int skip_count, const void* uc,
94 int* min_dropped_frames) {
95 return Unwind<true, true>(result, sizes, max_depth, skip_count, uc,
96 min_dropped_frames);
97}
98
99ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackTrace(
100 void** result, int max_depth, int skip_count) {
101 return Unwind<false, false>(result, nullptr, max_depth, skip_count, nullptr,
102 nullptr);
103}
104
105ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
106GetStackTraceWithContext(void** result, int max_depth, int skip_count,
107 const void* uc, int* min_dropped_frames) {
108 return Unwind<false, true>(result, nullptr, max_depth, skip_count, uc,
109 min_dropped_frames);
110}
111
112void SetStackUnwinder(Unwinder w) {
113 custom.store(w, std::memory_order_release);
114}
115
116int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip,
117 const void* uc, int* min_dropped_frames) {
118 skip++; // For this function
119 Unwinder f = nullptr;
120 if (sizes == nullptr) {
121 if (uc == nullptr) {
122 f = &UnwindImpl<false, false>;
123 } else {
124 f = &UnwindImpl<false, true>;
125 }
126 } else {
127 if (uc == nullptr) {
128 f = &UnwindImpl<true, false>;
129 } else {
130 f = &UnwindImpl<true, true>;
131 }
132 }
133 volatile int x = 0;
134 int n = (*f)(pcs, sizes, depth, skip, uc, min_dropped_frames);
135 x = 1; (void) x; // To disable tail call to (*f)(...)
136 return n;
137}
138
Austin Schuhb4691e92020-12-31 12:37:18 -0800139ABSL_NAMESPACE_END
Austin Schuh36244a12019-09-21 17:52:38 -0700140} // namespace absl