blob: 2a2c648223e652a62b85ec1bbd77fa9bdc786a44 [file] [log] [blame]
Austin Schuh745610d2015-09-06 18:19:50 -07001// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2// Copyright (c) 2005, Google Inc.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// ---
32// Author: Sanjay Ghemawat
33//
34// Produce stack trace.
35//
36// There are three different ways we can try to get the stack trace:
37//
38// 1) Our hand-coded stack-unwinder. This depends on a certain stack
39// layout, which is used by gcc (and those systems using a
40// gcc-compatible ABI) on x86 systems, at least since gcc 2.95.
41// It uses the frame pointer to do its work.
42//
43// 2) The libunwind library. This is still in development, and as a
44// separate library adds a new dependency, abut doesn't need a frame
45// pointer. It also doesn't call malloc.
46//
47// 3) The gdb unwinder -- also the one used by the c++ exception code.
48// It's obviously well-tested, but has a fatal flaw: it can call
49// malloc() from the unwinder. This is a problem because we're
50// trying to use the unwinder to instrument malloc().
51//
52// Note: if you add a new implementation here, make sure it works
53// correctly when GetStackTrace() is called with max_depth == 0.
54// Some code may do that.
55
56#include <config.h>
57#include <stdlib.h> // for getenv
58#include <string.h> // for strcmp
59#include <stdio.h> // for fprintf
60#include "gperftools/stacktrace.h"
61#include "base/commandlineflags.h"
62#include "base/googleinit.h"
Brian Silverman20350ac2021-11-17 18:19:55 -080063#include "getenv_safe.h"
Austin Schuh745610d2015-09-06 18:19:50 -070064
65
66// we're using plain struct and not class to avoid any possible issues
67// during initialization. Struct of pointers is easy to init at
68// link-time.
69struct GetStackImplementation {
70 int (*GetStackFramesPtr)(void** result, int* sizes, int max_depth,
71 int skip_count);
72
73 int (*GetStackFramesWithContextPtr)(void** result, int* sizes, int max_depth,
74 int skip_count, const void *uc);
75
76 int (*GetStackTracePtr)(void** result, int max_depth,
77 int skip_count);
78
79 int (*GetStackTraceWithContextPtr)(void** result, int max_depth,
80 int skip_count, const void *uc);
81
82 const char *name;
83};
84
85#if HAVE_DECL_BACKTRACE
86#define STACKTRACE_INL_HEADER "stacktrace_generic-inl.h"
87#define GST_SUFFIX generic
88#include "stacktrace_impl_setup-inl.h"
89#undef GST_SUFFIX
90#undef STACKTRACE_INL_HEADER
91#define HAVE_GST_generic
92#endif
93
Brian Silverman20350ac2021-11-17 18:19:55 -080094#ifdef HAVE_UNWIND_BACKTRACE
95#define STACKTRACE_INL_HEADER "stacktrace_libgcc-inl.h"
96#define GST_SUFFIX libgcc
97#include "stacktrace_impl_setup-inl.h"
98#undef GST_SUFFIX
99#undef STACKTRACE_INL_HEADER
100#define HAVE_GST_libgcc
101#endif
102
Austin Schuh745610d2015-09-06 18:19:50 -0700103// libunwind uses __thread so we check for both libunwind.h and
104// __thread support
105#if defined(HAVE_LIBUNWIND_H) && defined(HAVE_TLS)
106#define STACKTRACE_INL_HEADER "stacktrace_libunwind-inl.h"
107#define GST_SUFFIX libunwind
108#include "stacktrace_impl_setup-inl.h"
109#undef GST_SUFFIX
110#undef STACKTRACE_INL_HEADER
111#define HAVE_GST_libunwind
112#endif // HAVE_LIBUNWIND_H
113
114#if defined(__i386__) || defined(__x86_64__)
115#define STACKTRACE_INL_HEADER "stacktrace_x86-inl.h"
116#define GST_SUFFIX x86
117#include "stacktrace_impl_setup-inl.h"
118#undef GST_SUFFIX
119#undef STACKTRACE_INL_HEADER
120#define HAVE_GST_x86
121#endif // i386 || x86_64
122
Brian Silverman20350ac2021-11-17 18:19:55 -0800123// Sadly, different OSes have very different mcontexts even for
124// identical hardware arch. So keep it linux-only for now.
125#if defined(__GNUC__) && __linux__ && (defined(__x86_64__) || defined(__aarch64__) || defined(__riscv))
126#define STACKTRACE_INL_HEADER "stacktrace_generic_fp-inl.h"
127#define GST_SUFFIX generic_fp
128#include "stacktrace_impl_setup-inl.h"
129#undef GST_SUFFIX
130#undef STACKTRACE_INL_HEADER
131#define HAVE_GST_generic_fp
132
133#undef TCMALLOC_UNSAFE_GENERIC_FP_STACKTRACE
134#define TCMALLOC_UNSAFE_GENERIC_FP_STACKTRACE 1
135
136#define STACKTRACE_INL_HEADER "stacktrace_generic_fp-inl.h"
137#define GST_SUFFIX generic_fp_unsafe
138#include "stacktrace_impl_setup-inl.h"
139#undef GST_SUFFIX
140#undef STACKTRACE_INL_HEADER
141#define HAVE_GST_generic_fp_unsafe
142#endif
143
Austin Schuh745610d2015-09-06 18:19:50 -0700144#if defined(__ppc__) || defined(__PPC__)
145#if defined(__linux__)
146#define STACKTRACE_INL_HEADER "stacktrace_powerpc-linux-inl.h"
147#else
148#define STACKTRACE_INL_HEADER "stacktrace_powerpc-darwin-inl.h"
149#endif
150#define GST_SUFFIX ppc
151#include "stacktrace_impl_setup-inl.h"
152#undef GST_SUFFIX
153#undef STACKTRACE_INL_HEADER
154#define HAVE_GST_ppc
155#endif
156
157#if defined(__arm__)
158#define STACKTRACE_INL_HEADER "stacktrace_arm-inl.h"
159#define GST_SUFFIX arm
160#include "stacktrace_impl_setup-inl.h"
161#undef GST_SUFFIX
162#undef STACKTRACE_INL_HEADER
163#define HAVE_GST_arm
164#endif
165
166#ifdef TCMALLOC_ENABLE_INSTRUMENT_STACKTRACE
167#define STACKTRACE_INL_HEADER "stacktrace_instrument-inl.h"
168#define GST_SUFFIX instrument
169#include "stacktrace_impl_setup-inl.h"
170#undef GST_SUFFIX
171#undef STACKTRACE_INL_HEADER
172#define HAVE_GST_instrument
173#endif
174
175// The Windows case -- probably cygwin and mingw will use one of the
176// x86-includes above, but if not, we can fall back to windows intrinsics.
177#if defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__MINGW32__)
178#define STACKTRACE_INL_HEADER "stacktrace_win32-inl.h"
179#define GST_SUFFIX win32
180#include "stacktrace_impl_setup-inl.h"
181#undef GST_SUFFIX
182#undef STACKTRACE_INL_HEADER
183#define HAVE_GST_win32
184#endif
185
186static GetStackImplementation *all_impls[] = {
Brian Silverman20350ac2021-11-17 18:19:55 -0800187#ifdef HAVE_GST_libgcc
188 &impl__libgcc,
189#endif
Austin Schuh745610d2015-09-06 18:19:50 -0700190#ifdef HAVE_GST_generic
191 &impl__generic,
192#endif
Brian Silverman20350ac2021-11-17 18:19:55 -0800193#ifdef HAVE_GST_generic_fp
194 &impl__generic_fp,
195#endif
196#ifdef HAVE_GST_generic_fp
197 &impl__generic_fp_unsafe,
198#endif
Austin Schuh745610d2015-09-06 18:19:50 -0700199#ifdef HAVE_GST_libunwind
200 &impl__libunwind,
201#endif
202#ifdef HAVE_GST_x86
203 &impl__x86,
204#endif
205#ifdef HAVE_GST_arm
206 &impl__arm,
207#endif
208#ifdef HAVE_GST_ppc
209 &impl__ppc,
210#endif
211#ifdef HAVE_GST_instrument
212 &impl__instrument,
213#endif
214#ifdef HAVE_GST_win32
215 &impl__win32,
216#endif
217 NULL
218};
219
220// ppc and i386 implementations prefer arch-specific asm implementations.
221// arm's asm implementation is broken
Brian Silverman20350ac2021-11-17 18:19:55 -0800222#if defined(__i386__) || defined(__ppc__) || defined(__PPC__)
Austin Schuh745610d2015-09-06 18:19:50 -0700223#if !defined(NO_FRAME_POINTER)
224#define TCMALLOC_DONT_PREFER_LIBUNWIND
225#endif
226#endif
227
Brian Silverman20350ac2021-11-17 18:19:55 -0800228static bool get_stack_impl_inited;
229
Austin Schuh745610d2015-09-06 18:19:50 -0700230#if defined(HAVE_GST_instrument)
231static GetStackImplementation *get_stack_impl = &impl__instrument;
232#elif defined(HAVE_GST_win32)
233static GetStackImplementation *get_stack_impl = &impl__win32;
Brian Silverman20350ac2021-11-17 18:19:55 -0800234#elif defined(HAVE_GST_generic_fp) && !defined(NO_FRAME_POINTER) \
235 && !defined(__riscv) \
236 && (!defined(HAVE_GST_libunwind) || defined(TCMALLOC_DONT_PREFER_LIBUNWIND))
237static GetStackImplementation *get_stack_impl = &impl__generic_fp;
Austin Schuh745610d2015-09-06 18:19:50 -0700238#elif defined(HAVE_GST_x86) && defined(TCMALLOC_DONT_PREFER_LIBUNWIND)
239static GetStackImplementation *get_stack_impl = &impl__x86;
240#elif defined(HAVE_GST_ppc) && defined(TCMALLOC_DONT_PREFER_LIBUNWIND)
241static GetStackImplementation *get_stack_impl = &impl__ppc;
242#elif defined(HAVE_GST_libunwind)
243static GetStackImplementation *get_stack_impl = &impl__libunwind;
Brian Silverman20350ac2021-11-17 18:19:55 -0800244#elif defined(HAVE_GST_libgcc)
245static GetStackImplementation *get_stack_impl = &impl__libgcc;
Austin Schuh745610d2015-09-06 18:19:50 -0700246#elif defined(HAVE_GST_generic)
247static GetStackImplementation *get_stack_impl = &impl__generic;
Brian Silverman20350ac2021-11-17 18:19:55 -0800248#elif defined(HAVE_GST_arm)
249static GetStackImplementation *get_stack_impl = &impl__arm;
Austin Schuh745610d2015-09-06 18:19:50 -0700250#elif 0
251// This is for the benefit of code analysis tools that may have
252// trouble with the computed #include above.
253# include "stacktrace_x86-inl.h"
254# include "stacktrace_libunwind-inl.h"
255# include "stacktrace_generic-inl.h"
256# include "stacktrace_powerpc-inl.h"
257# include "stacktrace_win32-inl.h"
258# include "stacktrace_arm-inl.h"
259# include "stacktrace_instrument-inl.h"
260#else
261#error Cannot calculate stack trace: will need to write for your environment
262#endif
263
264static int ATTRIBUTE_NOINLINE frame_forcer(int rv) {
265 return rv;
266}
267
Brian Silverman20350ac2021-11-17 18:19:55 -0800268static void init_default_stack_impl_inner(void);
269
270namespace tcmalloc {
271 bool EnterStacktraceScope(void);
272 void LeaveStacktraceScope(void);
273}
274
275namespace {
276 using tcmalloc::EnterStacktraceScope;
277 using tcmalloc::LeaveStacktraceScope;
278
279 class StacktraceScope {
280 bool stacktrace_allowed;
281 public:
282 StacktraceScope() {
283 stacktrace_allowed = true;
284 stacktrace_allowed = EnterStacktraceScope();
285 }
286 bool IsStacktraceAllowed() {
287 return stacktrace_allowed;
288 }
289 ~StacktraceScope() {
290 if (stacktrace_allowed) {
291 LeaveStacktraceScope();
292 }
293 }
294 };
295}
296
Austin Schuh745610d2015-09-06 18:19:50 -0700297PERFTOOLS_DLL_DECL int GetStackFrames(void** result, int* sizes, int max_depth,
298 int skip_count) {
Brian Silverman20350ac2021-11-17 18:19:55 -0800299 StacktraceScope scope;
300 if (!scope.IsStacktraceAllowed()) {
301 return 0;
302 }
303 init_default_stack_impl_inner();
Austin Schuh745610d2015-09-06 18:19:50 -0700304 return frame_forcer(get_stack_impl->GetStackFramesPtr(result, sizes, max_depth, skip_count));
305}
306
307PERFTOOLS_DLL_DECL int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
308 int skip_count, const void *uc) {
Brian Silverman20350ac2021-11-17 18:19:55 -0800309 StacktraceScope scope;
310 if (!scope.IsStacktraceAllowed()) {
311 return 0;
312 }
313 init_default_stack_impl_inner();
Austin Schuh745610d2015-09-06 18:19:50 -0700314 return frame_forcer(get_stack_impl->GetStackFramesWithContextPtr(
315 result, sizes, max_depth,
316 skip_count, uc));
317}
318
319PERFTOOLS_DLL_DECL int GetStackTrace(void** result, int max_depth,
320 int skip_count) {
Brian Silverman20350ac2021-11-17 18:19:55 -0800321 StacktraceScope scope;
322 if (!scope.IsStacktraceAllowed()) {
323 return 0;
324 }
325 init_default_stack_impl_inner();
Austin Schuh745610d2015-09-06 18:19:50 -0700326 return frame_forcer(get_stack_impl->GetStackTracePtr(result, max_depth, skip_count));
327}
328
329PERFTOOLS_DLL_DECL int GetStackTraceWithContext(void** result, int max_depth,
330 int skip_count, const void *uc) {
Brian Silverman20350ac2021-11-17 18:19:55 -0800331 StacktraceScope scope;
332 if (!scope.IsStacktraceAllowed()) {
333 return 0;
334 }
335 init_default_stack_impl_inner();
Austin Schuh745610d2015-09-06 18:19:50 -0700336 return frame_forcer(get_stack_impl->GetStackTraceWithContextPtr(
337 result, max_depth, skip_count, uc));
338}
339
Brian Silverman20350ac2021-11-17 18:19:55 -0800340// As of this writing, aarch64 has completely borked libunwind, so
341// lets test this case and fall back to frame pointers (which is
342// nearly but not quite perfect).
343ATTRIBUTE_NOINLINE
344static void maybe_convert_libunwind_to_generic_fp() {
345#if defined(HAVE_GST_libunwind) && defined(HAVE_GST_generic_fp)
346 if (get_stack_impl != &impl__libunwind) {
347 return;
348 }
349
350 // Okay we're on libunwind and we have generic_fp, check if
351 // libunwind returns bogus results.
352 void* stack[4];
353 int rv = get_stack_impl->GetStackTracePtr(stack, 4, 0);
354 if (rv > 2) {
355 // Seems fine
356 return;
357 }
358 // bogus. So replacing with generic_fp
359 get_stack_impl = &impl__generic_fp;
360#endif
361}
362
Austin Schuh745610d2015-09-06 18:19:50 -0700363static void init_default_stack_impl_inner(void) {
Brian Silverman20350ac2021-11-17 18:19:55 -0800364 if (get_stack_impl_inited) {
365 return;
366 }
367 get_stack_impl_inited = true;
368 const char *val = TCMallocGetenvSafe("TCMALLOC_STACKTRACE_METHOD");
Austin Schuh745610d2015-09-06 18:19:50 -0700369 if (!val || !*val) {
Brian Silverman20350ac2021-11-17 18:19:55 -0800370 maybe_convert_libunwind_to_generic_fp();
Austin Schuh745610d2015-09-06 18:19:50 -0700371 return;
372 }
373 for (GetStackImplementation **p = all_impls; *p; p++) {
374 GetStackImplementation *c = *p;
375 if (strcmp(c->name, val) == 0) {
376 get_stack_impl = c;
377 return;
378 }
379 }
380 fprintf(stderr, "Unknown or unsupported stacktrace method requested: %s. Ignoring it\n", val);
381}
382
Brian Silverman20350ac2021-11-17 18:19:55 -0800383ATTRIBUTE_NOINLINE
Austin Schuh745610d2015-09-06 18:19:50 -0700384static void init_default_stack_impl(void) {
385 init_default_stack_impl_inner();
386 if (EnvToBool("TCMALLOC_STACKTRACE_METHOD_VERBOSE", false)) {
387 fprintf(stderr, "Chosen stacktrace method is %s\nSupported methods:\n", get_stack_impl->name);
388 for (GetStackImplementation **p = all_impls; *p; p++) {
389 GetStackImplementation *c = *p;
390 fprintf(stderr, "* %s\n", c->name);
391 }
392 fputs("\n", stderr);
393 }
394}
395
396REGISTER_MODULE_INITIALIZER(stacktrace_init_default_stack_impl, init_default_stack_impl());