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