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