xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp (revision 47ef2a131091508e049ab10cad7f91a3c1342cd9)
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 
19 namespace __sanitizer {
20 
21 namespace {
22 
23 class 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 
81 static 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 
94 void 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 
121 uptr 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 
131 void StackTrace::Print() const {
132   InternalScopedString output;
133   PrintTo(&output);
134   Printf("%s", output.data());
135 }
136 
137 void 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 
170 int 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
186 using namespace __sanitizer;
187 
188 extern "C" {
189 SANITIZER_INTERFACE_ATTRIBUTE
190 void __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 
206 SANITIZER_INTERFACE_ATTRIBUTE
207 void __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 
220 SANITIZER_INTERFACE_ATTRIBUTE
221 int __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"
229