1// 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 {
60namespace {
61
62typedef int (*Unwinder)(void**, int*, int, int, const void*, int*);
63std::atomic<Unwinder> custom;
64
65template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
66ABSL_ATTRIBUTE_ALWAYS_INLINE inline int Unwind(void** result, int* sizes,
67 int max_depth, int skip_count,
68 const void* uc,
69 int* min_dropped_frames) {
70 Unwinder f = &UnwindImpl<IS_STACK_FRAMES, IS_WITH_CONTEXT>;
71 Unwinder g = custom.load(std::memory_order_acquire);
72 if (g != nullptr) f = g;
73
74 // Add 1 to skip count for the unwinder function itself
75 int size = (*f)(result, sizes, max_depth, skip_count + 1, uc,
76 min_dropped_frames);
77 // To disable tail call to (*f)(...)
78 ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
79 return size;
80}
81
82} // anonymous namespace
83
84ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackFrames(
85 void** result, int* sizes, int max_depth, int skip_count) {
86 return Unwind<true, false>(result, sizes, max_depth, skip_count, nullptr,
87 nullptr);
88}
89
90ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
91GetStackFramesWithContext(void** result, int* sizes, int max_depth,
92 int skip_count, const void* uc,
93 int* min_dropped_frames) {
94 return Unwind<true, true>(result, sizes, max_depth, skip_count, uc,
95 min_dropped_frames);
96}
97
98ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int GetStackTrace(
99 void** result, int max_depth, int skip_count) {
100 return Unwind<false, false>(result, nullptr, max_depth, skip_count, nullptr,
101 nullptr);
102}
103
104ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL int
105GetStackTraceWithContext(void** result, int max_depth, int skip_count,
106 const void* uc, int* min_dropped_frames) {
107 return Unwind<false, true>(result, nullptr, max_depth, skip_count, uc,
108 min_dropped_frames);
109}
110
111void SetStackUnwinder(Unwinder w) {
112 custom.store(w, std::memory_order_release);
113}
114
115int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip,
116 const void* uc, int* min_dropped_frames) {
117 skip++; // For this function
118 Unwinder f = nullptr;
119 if (sizes == nullptr) {
120 if (uc == nullptr) {
121 f = &UnwindImpl<false, false>;
122 } else {
123 f = &UnwindImpl<false, true>;
124 }
125 } else {
126 if (uc == nullptr) {
127 f = &UnwindImpl<true, false>;
128 } else {
129 f = &UnwindImpl<true, true>;
130 }
131 }
132 volatile int x = 0;
133 int n = (*f)(pcs, sizes, depth, skip, uc, min_dropped_frames);
134 x = 1; (void) x; // To disable tail call to (*f)(...)
135 return n;
136}
137
138} // namespace absl
139