xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- sanitizer_stacktrace_libcdep.cpp ----------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is shared between AddressSanitizer and ThreadSanitizer
103cab2bb3Spatrick // run-time libraries.
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick 
133cab2bb3Spatrick #include "sanitizer_common.h"
143cab2bb3Spatrick #include "sanitizer_placement_new.h"
153cab2bb3Spatrick #include "sanitizer_stacktrace.h"
163cab2bb3Spatrick #include "sanitizer_stacktrace_printer.h"
173cab2bb3Spatrick #include "sanitizer_symbolizer.h"
183cab2bb3Spatrick 
193cab2bb3Spatrick namespace __sanitizer {
203cab2bb3Spatrick 
21d89ec533Spatrick namespace {
22d89ec533Spatrick 
23d89ec533Spatrick class StackTraceTextPrinter {
24d89ec533Spatrick  public:
StackTraceTextPrinter(const char * stack_trace_fmt,char frame_delimiter,InternalScopedString * output,InternalScopedString * dedup_token)25d89ec533Spatrick   StackTraceTextPrinter(const char *stack_trace_fmt, char frame_delimiter,
26d89ec533Spatrick                         InternalScopedString *output,
27d89ec533Spatrick                         InternalScopedString *dedup_token)
28d89ec533Spatrick       : stack_trace_fmt_(stack_trace_fmt),
29d89ec533Spatrick         frame_delimiter_(frame_delimiter),
30d89ec533Spatrick         output_(output),
31d89ec533Spatrick         dedup_token_(dedup_token),
32d89ec533Spatrick         symbolize_(RenderNeedsSymbolization(stack_trace_fmt)) {}
33d89ec533Spatrick 
ProcessAddressFrames(uptr pc)34d89ec533Spatrick   bool ProcessAddressFrames(uptr pc) {
35d89ec533Spatrick     SymbolizedStack *frames = symbolize_
36d89ec533Spatrick                                   ? Symbolizer::GetOrInit()->SymbolizePC(pc)
37d89ec533Spatrick                                   : SymbolizedStack::New(pc);
38d89ec533Spatrick     if (!frames)
39d89ec533Spatrick       return false;
40d89ec533Spatrick 
41d89ec533Spatrick     for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
42d89ec533Spatrick       uptr prev_len = output_->length();
43d89ec533Spatrick       RenderFrame(output_, stack_trace_fmt_, frame_num_++, cur->info.address,
44d89ec533Spatrick                   symbolize_ ? &cur->info : nullptr,
45d89ec533Spatrick                   common_flags()->symbolize_vs_style,
46d89ec533Spatrick                   common_flags()->strip_path_prefix);
47d89ec533Spatrick 
48d89ec533Spatrick       if (prev_len != output_->length())
49d89ec533Spatrick         output_->append("%c", frame_delimiter_);
50d89ec533Spatrick 
51d89ec533Spatrick       ExtendDedupToken(cur);
52d89ec533Spatrick     }
53d89ec533Spatrick     frames->ClearAll();
54d89ec533Spatrick     return true;
55d89ec533Spatrick   }
56d89ec533Spatrick 
57d89ec533Spatrick  private:
58d89ec533Spatrick   // Extend the dedup token by appending a new frame.
ExtendDedupToken(SymbolizedStack * stack)59d89ec533Spatrick   void ExtendDedupToken(SymbolizedStack *stack) {
60d89ec533Spatrick     if (!dedup_token_)
61d89ec533Spatrick       return;
62d89ec533Spatrick 
63d89ec533Spatrick     if (dedup_frames_-- > 0) {
64d89ec533Spatrick       if (dedup_token_->length())
65d89ec533Spatrick         dedup_token_->append("--");
66d89ec533Spatrick       if (stack->info.function != nullptr)
67*810390e3Srobert         dedup_token_->append("%s", stack->info.function);
68d89ec533Spatrick     }
69d89ec533Spatrick   }
70d89ec533Spatrick 
71d89ec533Spatrick   const char *stack_trace_fmt_;
72d89ec533Spatrick   const char frame_delimiter_;
73d89ec533Spatrick   int dedup_frames_ = common_flags()->dedup_token_length;
74d89ec533Spatrick   uptr frame_num_ = 0;
75d89ec533Spatrick   InternalScopedString *output_;
76d89ec533Spatrick   InternalScopedString *dedup_token_;
77d89ec533Spatrick   const bool symbolize_ = false;
78d89ec533Spatrick };
79d89ec533Spatrick 
CopyStringToBuffer(const InternalScopedString & str,char * out_buf,uptr out_buf_size)80d89ec533Spatrick static void CopyStringToBuffer(const InternalScopedString &str, char *out_buf,
81d89ec533Spatrick                                uptr out_buf_size) {
82d89ec533Spatrick   if (!out_buf_size)
83d89ec533Spatrick     return;
84d89ec533Spatrick 
85d89ec533Spatrick   CHECK_GT(out_buf_size, 0);
86d89ec533Spatrick   uptr copy_size = Min(str.length(), out_buf_size - 1);
87d89ec533Spatrick   internal_memcpy(out_buf, str.data(), copy_size);
88d89ec533Spatrick   out_buf[copy_size] = '\0';
89d89ec533Spatrick }
90d89ec533Spatrick 
91d89ec533Spatrick }  // namespace
92d89ec533Spatrick 
PrintTo(InternalScopedString * output) const93d89ec533Spatrick void StackTrace::PrintTo(InternalScopedString *output) const {
94d89ec533Spatrick   CHECK(output);
95d89ec533Spatrick 
96d89ec533Spatrick   InternalScopedString dedup_token;
97d89ec533Spatrick   StackTraceTextPrinter printer(common_flags()->stack_trace_format, '\n',
98d89ec533Spatrick                                 output, &dedup_token);
99d89ec533Spatrick 
1003cab2bb3Spatrick   if (trace == nullptr || size == 0) {
101d89ec533Spatrick     output->append("    <empty stack>\n\n");
1023cab2bb3Spatrick     return;
1033cab2bb3Spatrick   }
104d89ec533Spatrick 
1053cab2bb3Spatrick   for (uptr i = 0; i < size && trace[i]; i++) {
1063cab2bb3Spatrick     // PCs in stack traces are actually the return addresses, that is,
1073cab2bb3Spatrick     // addresses of the next instructions after the call.
1083cab2bb3Spatrick     uptr pc = GetPreviousInstructionPc(trace[i]);
109d89ec533Spatrick     CHECK(printer.ProcessAddressFrames(pc));
110d89ec533Spatrick   }
111d89ec533Spatrick 
112d89ec533Spatrick   // Always add a trailing empty line after stack trace.
113d89ec533Spatrick   output->append("\n");
114d89ec533Spatrick 
115d89ec533Spatrick   // Append deduplication token, if non-empty.
1163cab2bb3Spatrick   if (dedup_token.length())
117d89ec533Spatrick     output->append("DEDUP_TOKEN: %s\n", dedup_token.data());
1183cab2bb3Spatrick }
119d89ec533Spatrick 
PrintTo(char * out_buf,uptr out_buf_size) const120d89ec533Spatrick uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const {
121d89ec533Spatrick   CHECK(out_buf);
122d89ec533Spatrick 
123d89ec533Spatrick   InternalScopedString output;
124d89ec533Spatrick   PrintTo(&output);
125d89ec533Spatrick   CopyStringToBuffer(output, out_buf, out_buf_size);
126d89ec533Spatrick 
127d89ec533Spatrick   return output.length();
1283cab2bb3Spatrick }
129d89ec533Spatrick 
Print() const130d89ec533Spatrick void StackTrace::Print() const {
131d89ec533Spatrick   InternalScopedString output;
132d89ec533Spatrick   PrintTo(&output);
133d89ec533Spatrick   Printf("%s", output.data());
1343cab2bb3Spatrick }
1353cab2bb3Spatrick 
Unwind(u32 max_depth,uptr pc,uptr bp,void * context,uptr stack_top,uptr stack_bottom,bool request_fast_unwind)1363cab2bb3Spatrick void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
1373cab2bb3Spatrick                                 uptr stack_top, uptr stack_bottom,
1383cab2bb3Spatrick                                 bool request_fast_unwind) {
1393cab2bb3Spatrick   // Ensures all call sites get what they requested.
1403cab2bb3Spatrick   CHECK_EQ(request_fast_unwind, WillUseFastUnwind(request_fast_unwind));
1413cab2bb3Spatrick   top_frame_bp = (max_depth > 0) ? bp : 0;
1423cab2bb3Spatrick   // Avoid doing any work for small max_depth.
1433cab2bb3Spatrick   if (max_depth == 0) {
1443cab2bb3Spatrick     size = 0;
1453cab2bb3Spatrick     return;
1463cab2bb3Spatrick   }
1473cab2bb3Spatrick   if (max_depth == 1) {
1483cab2bb3Spatrick     size = 1;
1493cab2bb3Spatrick     trace_buffer[0] = pc;
1503cab2bb3Spatrick     return;
1513cab2bb3Spatrick   }
1523cab2bb3Spatrick   if (!WillUseFastUnwind(request_fast_unwind)) {
1533cab2bb3Spatrick #if SANITIZER_CAN_SLOW_UNWIND
1543cab2bb3Spatrick     if (context)
1553cab2bb3Spatrick       UnwindSlow(pc, context, max_depth);
1563cab2bb3Spatrick     else
1573cab2bb3Spatrick       UnwindSlow(pc, max_depth);
158d89ec533Spatrick     // If there are too few frames, the program may be built with
159d89ec533Spatrick     // -fno-asynchronous-unwind-tables. Fall back to fast unwinder below.
160d89ec533Spatrick     if (size > 2 || size >= max_depth)
161d89ec533Spatrick       return;
1623cab2bb3Spatrick #else
1633cab2bb3Spatrick     UNREACHABLE("slow unwind requested but not available");
1643cab2bb3Spatrick #endif
1653cab2bb3Spatrick   }
166d89ec533Spatrick   UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
1673cab2bb3Spatrick }
1683cab2bb3Spatrick 
GetModuleAndOffsetForPc(uptr pc,char * module_name,uptr module_name_len,uptr * pc_offset)169*810390e3Srobert int GetModuleAndOffsetForPc(uptr pc, char *module_name, uptr module_name_len,
170*810390e3Srobert                             uptr *pc_offset) {
1713cab2bb3Spatrick   const char *found_module_name = nullptr;
1723cab2bb3Spatrick   bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
1733cab2bb3Spatrick       pc, &found_module_name, pc_offset);
1743cab2bb3Spatrick 
1753cab2bb3Spatrick   if (!ok) return false;
1763cab2bb3Spatrick 
1773cab2bb3Spatrick   if (module_name && module_name_len) {
1783cab2bb3Spatrick     internal_strncpy(module_name, found_module_name, module_name_len);
1793cab2bb3Spatrick     module_name[module_name_len - 1] = '\x00';
1803cab2bb3Spatrick   }
1813cab2bb3Spatrick   return true;
1823cab2bb3Spatrick }
1833cab2bb3Spatrick 
1843cab2bb3Spatrick }  // namespace __sanitizer
1853cab2bb3Spatrick using namespace __sanitizer;
1863cab2bb3Spatrick 
1873cab2bb3Spatrick extern "C" {
1883cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_symbolize_pc(uptr pc,const char * fmt,char * out_buf,uptr out_buf_size)1893cab2bb3Spatrick void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
1903cab2bb3Spatrick                               uptr out_buf_size) {
191d89ec533Spatrick   if (!out_buf_size)
1923cab2bb3Spatrick     return;
193d89ec533Spatrick 
194d89ec533Spatrick   pc = StackTrace::GetPreviousInstructionPc(pc);
195d89ec533Spatrick 
196d89ec533Spatrick   InternalScopedString output;
197d89ec533Spatrick   StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
198d89ec533Spatrick   if (!printer.ProcessAddressFrames(pc)) {
199d89ec533Spatrick     output.clear();
200d89ec533Spatrick     output.append("<can't symbolize>");
2013cab2bb3Spatrick   }
202d89ec533Spatrick   CopyStringToBuffer(output, out_buf, out_buf_size);
2033cab2bb3Spatrick }
2043cab2bb3Spatrick 
2053cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_symbolize_global(uptr data_addr,const char * fmt,char * out_buf,uptr out_buf_size)2063cab2bb3Spatrick void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
2073cab2bb3Spatrick                                   char *out_buf, uptr out_buf_size) {
2083cab2bb3Spatrick   if (!out_buf_size) return;
2093cab2bb3Spatrick   out_buf[0] = 0;
2103cab2bb3Spatrick   DataInfo DI;
2113cab2bb3Spatrick   if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
212d89ec533Spatrick   InternalScopedString data_desc;
2133cab2bb3Spatrick   RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix);
2143cab2bb3Spatrick   internal_strncpy(out_buf, data_desc.data(), out_buf_size);
2153cab2bb3Spatrick   out_buf[out_buf_size - 1] = 0;
2163cab2bb3Spatrick }
2173cab2bb3Spatrick 
2183cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_get_module_and_offset_for_pc(void * pc,char * module_name,uptr module_name_len,void ** pc_offset)219*810390e3Srobert int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_name,
2203cab2bb3Spatrick                                              uptr module_name_len,
221*810390e3Srobert                                              void **pc_offset) {
222*810390e3Srobert   return __sanitizer::GetModuleAndOffsetForPc(
223*810390e3Srobert       reinterpret_cast<uptr>(pc), module_name, module_name_len,
224*810390e3Srobert       reinterpret_cast<uptr *>(pc_offset));
2253cab2bb3Spatrick }
2263cab2bb3Spatrick }  // extern "C"
227