1245614Sandrew//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===//
2245614Sandrew//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6245614Sandrew//
7245614Sandrew//===----------------------------------------------------------------------===//
8245614Sandrew//
9245614Sandrew// This file is shared between AddressSanitizer and ThreadSanitizer
10245614Sandrew// run-time libraries.
11245614Sandrew//===----------------------------------------------------------------------===//
12245614Sandrew#ifndef SANITIZER_STACKTRACE_H
13245614Sandrew#define SANITIZER_STACKTRACE_H
14245614Sandrew
15245614Sandrew#include "sanitizer_internal_defs.h"
16245614Sandrew
17245614Sandrewnamespace __sanitizer {
18245614Sandrew
19353358Sdimstruct BufferedStackTrace;
20353358Sdim
21280031Sdimstatic const u32 kStackTraceMax = 256;
22245614Sandrew
23353358Sdim#if SANITIZER_LINUX && defined(__mips__)
24274201Sdim# define SANITIZER_CAN_FAST_UNWIND 0
25274201Sdim#elif SANITIZER_WINDOWS
26274201Sdim# define SANITIZER_CAN_FAST_UNWIND 0
27341825Sdim#elif SANITIZER_OPENBSD
28341825Sdim# define SANITIZER_CAN_FAST_UNWIND 0
29251034Sed#else
30274201Sdim# define SANITIZER_CAN_FAST_UNWIND 1
31251034Sed#endif
32251034Sed
33276789Sdim// Fast unwind is the only option on Mac for now; we will need to
34276789Sdim// revisit this macro when slow unwind works on Mac, see
35296417Sdim// https://github.com/google/sanitizers/issues/137
36341825Sdim#if SANITIZER_MAC || SANITIZER_OPENBSD || SANITIZER_RTEMS
37276789Sdim# define SANITIZER_CAN_SLOW_UNWIND 0
38276789Sdim#else
39276789Sdim# define SANITIZER_CAN_SLOW_UNWIND 1
40276789Sdim#endif
41276789Sdim
42245614Sandrewstruct StackTrace {
43276789Sdim  const uptr *trace;
44280031Sdim  u32 size;
45280031Sdim  u32 tag;
46274201Sdim
47280031Sdim  static const int TAG_UNKNOWN = 0;
48280031Sdim  static const int TAG_ALLOC = 1;
49280031Sdim  static const int TAG_DEALLOC = 2;
50280031Sdim  static const int TAG_CUSTOM = 100; // Tool specific tags start here.
51276789Sdim
52280031Sdim  StackTrace() : trace(nullptr), size(0), tag(0) {}
53280031Sdim  StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {}
54280031Sdim  StackTrace(const uptr *trace, u32 size, u32 tag)
55280031Sdim      : trace(trace), size(size), tag(tag) {}
56280031Sdim
57274201Sdim  // Prints a symbolized stacktrace, followed by an empty line.
58276789Sdim  void Print() const;
59245614Sandrew
60274201Sdim  static bool WillUseFastUnwind(bool request_fast_unwind) {
61274201Sdim    if (!SANITIZER_CAN_FAST_UNWIND)
62274201Sdim      return false;
63353358Sdim    if (!SANITIZER_CAN_SLOW_UNWIND)
64274201Sdim      return true;
65274201Sdim    return request_fast_unwind;
66274201Sdim  }
67245614Sandrew
68245614Sandrew  static uptr GetCurrentPc();
69280031Sdim  static inline uptr GetPreviousInstructionPc(uptr pc);
70276789Sdim  static uptr GetNextInstructionPc(uptr pc);
71276789Sdim  typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
72276789Sdim                                    int out_size);
73276789Sdim};
74245614Sandrew
75280031Sdim// Performance-critical, must be in the header.
76280031SdimALWAYS_INLINE
77280031Sdimuptr StackTrace::GetPreviousInstructionPc(uptr pc) {
78280031Sdim#if defined(__arm__)
79341825Sdim  // T32 (Thumb) branch instructions might be 16 or 32 bit long,
80341825Sdim  // so we return (pc-2) in that case in order to be safe.
81341825Sdim  // For A32 mode we return (pc-4) because all instructions are 32 bit long.
82341825Sdim  return (pc - 3) & (~1);
83341825Sdim#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
84280031Sdim  // PCs are always 4 byte aligned.
85280031Sdim  return pc - 4;
86280031Sdim#elif defined(__sparc__) || defined(__mips__)
87280031Sdim  return pc - 8;
88280031Sdim#else
89280031Sdim  return pc - 1;
90280031Sdim#endif
91280031Sdim}
92280031Sdim
93276789Sdim// StackTrace that owns the buffer used to store the addresses.
94276789Sdimstruct BufferedStackTrace : public StackTrace {
95276789Sdim  uptr trace_buffer[kStackTraceMax];
96276789Sdim  uptr top_frame_bp;  // Optional bp of a top frame.
97276789Sdim
98276789Sdim  BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
99276789Sdim
100276789Sdim  void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
101353358Sdim
102353358Sdim  // Get the stack trace with the given pc and bp.
103353358Sdim  // The pc will be in the position 0 of the resulting stack trace.
104353358Sdim  // The bp may refer to the current frame or to the caller's frame.
105353358Sdim  void Unwind(uptr pc, uptr bp, void *context, bool request_fast,
106353358Sdim              u32 max_depth = kStackTraceMax) {
107353358Sdim    top_frame_bp = (max_depth > 0) ? bp : 0;
108353358Sdim    // Small max_depth optimization
109353358Sdim    if (max_depth <= 1) {
110353358Sdim      if (max_depth == 1)
111353358Sdim        trace_buffer[0] = pc;
112353358Sdim      size = max_depth;
113353358Sdim      return;
114353358Sdim    }
115353358Sdim    UnwindImpl(pc, bp, context, request_fast, max_depth);
116353358Sdim  }
117353358Sdim
118280031Sdim  void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
119276789Sdim              uptr stack_bottom, bool request_fast_unwind);
120276789Sdim
121327952Sdim  void Reset() {
122327952Sdim    *static_cast<StackTrace *>(this) = StackTrace(trace_buffer, 0);
123327952Sdim    top_frame_bp = 0;
124327952Sdim  }
125327952Sdim
126274201Sdim private:
127353358Sdim  // Every runtime defines its own implementation of this method
128353358Sdim  void UnwindImpl(uptr pc, uptr bp, void *context, bool request_fast,
129353358Sdim                  u32 max_depth);
130353358Sdim
131353358Sdim  // UnwindFast/Slow have platform-specific implementations
132353358Sdim  void UnwindFast(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
133353358Sdim                  u32 max_depth);
134353358Sdim  void UnwindSlow(uptr pc, u32 max_depth);
135353358Sdim  void UnwindSlow(uptr pc, void *context, u32 max_depth);
136353358Sdim
137274201Sdim  void PopStackFrames(uptr count);
138274201Sdim  uptr LocatePcInTrace(uptr pc);
139276789Sdim
140327952Sdim  BufferedStackTrace(const BufferedStackTrace &) = delete;
141327952Sdim  void operator=(const BufferedStackTrace &) = delete;
142353358Sdim
143353358Sdim  friend class FastUnwindTest;
144245614Sandrew};
145245614Sandrew
146309124Sdim// Check if given pointer points into allocated stack area.
147309124Sdimstatic inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
148309124Sdim  return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
149309124Sdim}
150309124Sdim
151245614Sandrew}  // namespace __sanitizer
152245614Sandrew
153245614Sandrew// Use this macro if you want to print stack trace with the caller
154245614Sandrew// of the current function in the top frame.
155353358Sdim#define GET_CALLER_PC_BP \
156353358Sdim  uptr bp = GET_CURRENT_FRAME();              \
157353358Sdim  uptr pc = GET_CALLER_PC();
158353358Sdim
159245614Sandrew#define GET_CALLER_PC_BP_SP \
160353358Sdim  GET_CALLER_PC_BP;                           \
161245614Sandrew  uptr local_stack;                           \
162245614Sandrew  uptr sp = (uptr)&local_stack
163245614Sandrew
164353358Sdim// Use this macro if you want to print stack trace with the current
165353358Sdim// function in the top frame.
166353358Sdim#define GET_CURRENT_PC_BP \
167276789Sdim  uptr bp = GET_CURRENT_FRAME();              \
168353358Sdim  uptr pc = StackTrace::GetCurrentPc()
169276789Sdim
170245614Sandrew#define GET_CURRENT_PC_BP_SP \
171353358Sdim  GET_CURRENT_PC_BP;                          \
172245614Sandrew  uptr local_stack;                           \
173245614Sandrew  uptr sp = (uptr)&local_stack
174245614Sandrew
175245614Sandrew
176245614Sandrew#endif  // SANITIZER_STACKTRACE_H
177