master
  1//===-- sanitizer_stacktrace_libcdep.cpp ----------------------------------===//
  2//
  3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4// See https://llvm.org/LICENSE.txt for license information.
  5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6//
  7//===----------------------------------------------------------------------===//
  8//
  9// This file is shared between AddressSanitizer and ThreadSanitizer
 10// run-time libraries.
 11//===----------------------------------------------------------------------===//
 12
 13#include "sanitizer_common.h"
 14#include "sanitizer_placement_new.h"
 15#include "sanitizer_stacktrace.h"
 16#include "sanitizer_stacktrace_printer.h"
 17#include "sanitizer_symbolizer.h"
 18
 19namespace __sanitizer {
 20
 21namespace {
 22
 23class StackTraceTextPrinter {
 24 public:
 25  StackTraceTextPrinter(const char *stack_trace_fmt, char frame_delimiter,
 26                        InternalScopedString *output,
 27                        InternalScopedString *dedup_token)
 28      : stack_trace_fmt_(stack_trace_fmt),
 29        frame_delimiter_(frame_delimiter),
 30        output_(output),
 31        dedup_token_(dedup_token),
 32        symbolize_(StackTracePrinter::GetOrInit()->RenderNeedsSymbolization(
 33            stack_trace_fmt)) {}
 34
 35  bool ProcessAddressFrames(uptr pc) {
 36    SymbolizedStackHolder symbolized_stack(
 37        symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc)
 38                   : SymbolizedStack::New(pc));
 39    const SymbolizedStack *frames = symbolized_stack.get();
 40    if (!frames)
 41      return false;
 42
 43    for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
 44      uptr prev_len = output_->length();
 45      StackTracePrinter::GetOrInit()->RenderFrame(
 46          output_, stack_trace_fmt_, frame_num_++, cur->info.address,
 47          symbolize_ ? &cur->info : nullptr, common_flags()->symbolize_vs_style,
 48          common_flags()->strip_path_prefix);
 49
 50      if (prev_len != output_->length())
 51        output_->AppendF("%c", frame_delimiter_);
 52
 53      ExtendDedupToken(cur);
 54    }
 55    return true;
 56  }
 57
 58 private:
 59  // Extend the dedup token by appending a new frame.
 60  void ExtendDedupToken(const SymbolizedStack *stack) {
 61    if (!dedup_token_)
 62      return;
 63
 64    if (dedup_frames_-- > 0) {
 65      if (dedup_token_->length())
 66        dedup_token_->Append("--");
 67      if (stack->info.function)
 68        dedup_token_->Append(stack->info.function);
 69    }
 70  }
 71
 72  const char *stack_trace_fmt_;
 73  const char frame_delimiter_;
 74  int dedup_frames_ = common_flags()->dedup_token_length;
 75  uptr frame_num_ = 0;
 76  InternalScopedString *output_;
 77  InternalScopedString *dedup_token_;
 78  const bool symbolize_ = false;
 79};
 80
 81static void CopyStringToBuffer(const InternalScopedString &str, char *out_buf,
 82                               uptr out_buf_size) {
 83  if (!out_buf_size)
 84    return;
 85
 86  CHECK_GT(out_buf_size, 0);
 87  uptr copy_size = Min(str.length(), out_buf_size - 1);
 88  internal_memcpy(out_buf, str.data(), copy_size);
 89  out_buf[copy_size] = '\0';
 90}
 91
 92}  // namespace
 93
 94void StackTrace::PrintTo(InternalScopedString *output) const {
 95  CHECK(output);
 96
 97  InternalScopedString dedup_token;
 98  StackTraceTextPrinter printer(common_flags()->stack_trace_format, '\n',
 99                                output, &dedup_token);
100
101  if (trace == nullptr || size == 0) {
102    output->Append("    <empty stack>\n\n");
103    return;
104  }
105
106  for (uptr i = 0; i < size && trace[i]; i++) {
107    // PCs in stack traces are actually the return addresses, that is,
108    // addresses of the next instructions after the call.
109    uptr pc = GetPreviousInstructionPc(trace[i]);
110    CHECK(printer.ProcessAddressFrames(pc));
111  }
112
113  // Always add a trailing empty line after stack trace.
114  output->Append("\n");
115
116  // Append deduplication token, if non-empty.
117  if (dedup_token.length())
118    output->AppendF("DEDUP_TOKEN: %s\n", dedup_token.data());
119}
120
121uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const {
122  CHECK(out_buf);
123
124  InternalScopedString output;
125  PrintTo(&output);
126  CopyStringToBuffer(output, out_buf, out_buf_size);
127
128  return output.length();
129}
130
131void StackTrace::Print() const {
132  InternalScopedString output;
133  PrintTo(&output);
134  Printf("%s", output.data());
135}
136
137void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
138                                uptr stack_top, uptr stack_bottom,
139                                bool request_fast_unwind) {
140  // Ensures all call sites get what they requested.
141  CHECK_EQ(request_fast_unwind, WillUseFastUnwind(request_fast_unwind));
142  top_frame_bp = (max_depth > 0) ? bp : 0;
143  // Avoid doing any work for small max_depth.
144  if (max_depth == 0) {
145    size = 0;
146    return;
147  }
148  if (max_depth == 1) {
149    size = 1;
150    trace_buffer[0] = pc;
151    return;
152  }
153  if (!WillUseFastUnwind(request_fast_unwind)) {
154#if SANITIZER_CAN_SLOW_UNWIND
155    if (context)
156      UnwindSlow(pc, context, max_depth);
157    else
158      UnwindSlow(pc, max_depth);
159    // If there are too few frames, the program may be built with
160    // -fno-asynchronous-unwind-tables. Fall back to fast unwinder below.
161    if (size > 2 || size >= max_depth)
162      return;
163#else
164    UNREACHABLE("slow unwind requested but not available");
165#endif
166  }
167  UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
168}
169
170int GetModuleAndOffsetForPc(uptr pc, char *module_name, uptr module_name_len,
171                            uptr *pc_offset) {
172  const char *found_module_name = nullptr;
173  bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
174      pc, &found_module_name, pc_offset);
175
176  if (!ok) return false;
177
178  if (module_name && module_name_len) {
179    internal_strncpy(module_name, found_module_name, module_name_len);
180    module_name[module_name_len - 1] = '\x00';
181  }
182  return true;
183}
184
185}  // namespace __sanitizer
186using namespace __sanitizer;
187
188extern "C" {
189SANITIZER_INTERFACE_ATTRIBUTE
190void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
191                              uptr out_buf_size) {
192  if (!out_buf_size)
193    return;
194
195  pc = StackTrace::GetPreviousInstructionPc(pc);
196
197  InternalScopedString output;
198  StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
199  if (!printer.ProcessAddressFrames(pc)) {
200    output.clear();
201    output.Append("<can't symbolize>");
202  }
203  CopyStringToBuffer(output, out_buf, out_buf_size);
204}
205
206SANITIZER_INTERFACE_ATTRIBUTE
207void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
208                                  char *out_buf, uptr out_buf_size) {
209  if (!out_buf_size) return;
210  out_buf[0] = 0;
211  DataInfo DI;
212  if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
213  InternalScopedString data_desc;
214  StackTracePrinter::GetOrInit()->RenderData(&data_desc, fmt, &DI,
215                                             common_flags()->strip_path_prefix);
216  internal_strncpy(out_buf, data_desc.data(), out_buf_size);
217  out_buf[out_buf_size - 1] = 0;
218}
219
220SANITIZER_INTERFACE_ATTRIBUTE
221int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_name,
222                                             uptr module_name_len,
223                                             void **pc_offset) {
224  return __sanitizer::GetModuleAndOffsetForPc(
225      reinterpret_cast<uptr>(pc), module_name, module_name_len,
226      reinterpret_cast<uptr *>(pc_offset));
227}
228}  // extern "C"