xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
1 //===-- sanitizer_common.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 sanitizers' run-time libraries.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "sanitizer_stacktrace_printer.h"
14 
15 #include "sanitizer_common.h"
16 #include "sanitizer_file.h"
17 #include "sanitizer_flags.h"
18 #include "sanitizer_fuchsia.h"
19 #include "sanitizer_symbolizer_markup.h"
20 
21 namespace __sanitizer {
22 
23 StackTracePrinter *StackTracePrinter::GetOrInit() {
24   static StackTracePrinter *stacktrace_printer;
25   static StaticSpinMutex init_mu;
26   SpinMutexLock l(&init_mu);
27   if (stacktrace_printer)
28     return stacktrace_printer;
29 
30   stacktrace_printer = StackTracePrinter::NewStackTracePrinter();
31 
32   CHECK(stacktrace_printer);
33   return stacktrace_printer;
34 }
35 
36 const char *StackTracePrinter::StripFunctionName(const char *function) {
37   if (!common_flags()->demangle)
38     return function;
39   if (!function)
40     return nullptr;
41   auto try_strip = [function](const char *prefix) -> const char * {
42     const uptr prefix_len = internal_strlen(prefix);
43     if (!internal_strncmp(function, prefix, prefix_len))
44       return function + prefix_len;
45     return nullptr;
46   };
47   if (SANITIZER_APPLE) {
48     if (const char *s = try_strip("wrap_"))
49       return s;
50   } else if (SANITIZER_WINDOWS) {
51     if (const char *s = try_strip("__asan_wrap_"))
52       return s;
53   } else {
54     if (const char *s = try_strip("___interceptor_"))
55       return s;
56     if (const char *s = try_strip("__interceptor_"))
57       return s;
58   }
59   return function;
60 }
61 
62 // sanitizer_symbolizer_markup.cpp implements these differently.
63 #if !SANITIZER_SYMBOLIZER_MARKUP
64 
65 StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
66   if (common_flags()->enable_symbolizer_markup)
67     return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
68 
69   return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
70 }
71 
72 static const char *DemangleFunctionName(const char *function) {
73   if (!common_flags()->demangle)
74     return function;
75   if (!function)
76     return nullptr;
77 
78   // NetBSD uses indirection for old threading functions for historical reasons
79   // The mangled names are internal implementation detail and should not be
80   // exposed even in backtraces.
81 #if SANITIZER_NETBSD
82   if (!internal_strcmp(function, "__libc_mutex_init"))
83     return "pthread_mutex_init";
84   if (!internal_strcmp(function, "__libc_mutex_lock"))
85     return "pthread_mutex_lock";
86   if (!internal_strcmp(function, "__libc_mutex_trylock"))
87     return "pthread_mutex_trylock";
88   if (!internal_strcmp(function, "__libc_mutex_unlock"))
89     return "pthread_mutex_unlock";
90   if (!internal_strcmp(function, "__libc_mutex_destroy"))
91     return "pthread_mutex_destroy";
92   if (!internal_strcmp(function, "__libc_mutexattr_init"))
93     return "pthread_mutexattr_init";
94   if (!internal_strcmp(function, "__libc_mutexattr_settype"))
95     return "pthread_mutexattr_settype";
96   if (!internal_strcmp(function, "__libc_mutexattr_destroy"))
97     return "pthread_mutexattr_destroy";
98   if (!internal_strcmp(function, "__libc_cond_init"))
99     return "pthread_cond_init";
100   if (!internal_strcmp(function, "__libc_cond_signal"))
101     return "pthread_cond_signal";
102   if (!internal_strcmp(function, "__libc_cond_broadcast"))
103     return "pthread_cond_broadcast";
104   if (!internal_strcmp(function, "__libc_cond_wait"))
105     return "pthread_cond_wait";
106   if (!internal_strcmp(function, "__libc_cond_timedwait"))
107     return "pthread_cond_timedwait";
108   if (!internal_strcmp(function, "__libc_cond_destroy"))
109     return "pthread_cond_destroy";
110   if (!internal_strcmp(function, "__libc_rwlock_init"))
111     return "pthread_rwlock_init";
112   if (!internal_strcmp(function, "__libc_rwlock_rdlock"))
113     return "pthread_rwlock_rdlock";
114   if (!internal_strcmp(function, "__libc_rwlock_wrlock"))
115     return "pthread_rwlock_wrlock";
116   if (!internal_strcmp(function, "__libc_rwlock_tryrdlock"))
117     return "pthread_rwlock_tryrdlock";
118   if (!internal_strcmp(function, "__libc_rwlock_trywrlock"))
119     return "pthread_rwlock_trywrlock";
120   if (!internal_strcmp(function, "__libc_rwlock_unlock"))
121     return "pthread_rwlock_unlock";
122   if (!internal_strcmp(function, "__libc_rwlock_destroy"))
123     return "pthread_rwlock_destroy";
124   if (!internal_strcmp(function, "__libc_thr_keycreate"))
125     return "pthread_key_create";
126   if (!internal_strcmp(function, "__libc_thr_setspecific"))
127     return "pthread_setspecific";
128   if (!internal_strcmp(function, "__libc_thr_getspecific"))
129     return "pthread_getspecific";
130   if (!internal_strcmp(function, "__libc_thr_keydelete"))
131     return "pthread_key_delete";
132   if (!internal_strcmp(function, "__libc_thr_once"))
133     return "pthread_once";
134   if (!internal_strcmp(function, "__libc_thr_self"))
135     return "pthread_self";
136   if (!internal_strcmp(function, "__libc_thr_exit"))
137     return "pthread_exit";
138   if (!internal_strcmp(function, "__libc_thr_setcancelstate"))
139     return "pthread_setcancelstate";
140   if (!internal_strcmp(function, "__libc_thr_equal"))
141     return "pthread_equal";
142   if (!internal_strcmp(function, "__libc_thr_curcpu"))
143     return "pthread_curcpu_np";
144   if (!internal_strcmp(function, "__libc_thr_sigsetmask"))
145     return "pthread_sigmask";
146 #endif
147 
148   return function;
149 }
150 
151 static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
152                                  InternalScopedString *buffer) {
153   if (info.uuid_size) {
154     if (PrefixSpace)
155       buffer->AppendF(" ");
156     buffer->AppendF("(BuildId: ");
157     for (uptr i = 0; i < info.uuid_size; ++i) {
158       buffer->AppendF("%02x", info.uuid[i]);
159     }
160     buffer->AppendF(")");
161   }
162 }
163 
164 static const char kDefaultFormat[] = "    #%n %p %F %L";
165 
166 void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
167                                              const char *format, int frame_no,
168                                              uptr address,
169                                              const AddressInfo *info,
170                                              bool vs_style,
171                                              const char *strip_path_prefix) {
172   // info will be null in the case where symbolization is not needed for the
173   // given format. This ensures that the code below will get a hard failure
174   // rather than print incorrect information in case RenderNeedsSymbolization
175   // ever ends up out of sync with this function. If non-null, the addresses
176   // should match.
177   CHECK(!info || address == info->address);
178   if (0 == internal_strcmp(format, "DEFAULT"))
179     format = kDefaultFormat;
180   for (const char *p = format; *p != '\0'; p++) {
181     if (*p != '%') {
182       buffer->AppendF("%c", *p);
183       continue;
184     }
185     p++;
186     switch (*p) {
187     case '%':
188       buffer->Append("%");
189       break;
190     // Frame number and all fields of AddressInfo structure.
191     case 'n':
192       buffer->AppendF("%u", frame_no);
193       break;
194     case 'p':
195       buffer->AppendF("0x%zx", address);
196       break;
197     case 'm':
198       buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix));
199       break;
200     case 'o':
201       buffer->AppendF("0x%zx", info->module_offset);
202       break;
203     case 'b':
204       MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer);
205       break;
206     case 'f':
207       buffer->AppendF("%s",
208                       DemangleFunctionName(StripFunctionName(info->function)));
209       break;
210     case 'q':
211       buffer->AppendF("0x%zx", info->function_offset != AddressInfo::kUnknown
212                                    ? info->function_offset
213                                    : 0x0);
214       break;
215     case 's':
216       buffer->AppendF("%s", StripPathPrefix(info->file, strip_path_prefix));
217       break;
218     case 'l':
219       buffer->AppendF("%d", info->line);
220       break;
221     case 'c':
222       buffer->AppendF("%d", info->column);
223       break;
224     // Smarter special cases.
225     case 'F':
226       // Function name and offset, if file is unknown.
227       if (info->function) {
228         buffer->AppendF(
229             "in %s", DemangleFunctionName(StripFunctionName(info->function)));
230         if (!info->file && info->function_offset != AddressInfo::kUnknown)
231           buffer->AppendF("+0x%zx", info->function_offset);
232       }
233       break;
234     case 'S':
235       // File/line information.
236       RenderSourceLocation(buffer, info->file, info->line, info->column,
237                            vs_style, strip_path_prefix);
238       break;
239     case 'L':
240       // Source location, or module location.
241       if (info->file) {
242         RenderSourceLocation(buffer, info->file, info->line, info->column,
243                              vs_style, strip_path_prefix);
244       } else if (info->module) {
245         RenderModuleLocation(buffer, info->module, info->module_offset,
246                              info->module_arch, strip_path_prefix);
247 
248 #if !SANITIZER_APPLE
249         MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
250 #endif
251       } else {
252         buffer->AppendF("(<unknown module>)");
253       }
254       break;
255     case 'M':
256       // Module basename and offset, or PC.
257       if (address & kExternalPCBit) {
258         // There PCs are not meaningful.
259       } else if (info->module) {
260         // Always strip the module name for %M.
261         RenderModuleLocation(buffer, StripModuleName(info->module),
262                              info->module_offset, info->module_arch, "");
263 #if !SANITIZER_APPLE
264         MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
265 #endif
266       } else {
267         buffer->AppendF("(%p)", (void *)address);
268       }
269       break;
270     default:
271       Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
272              (void *)p);
273       Die();
274     }
275   }
276 }
277 
278 bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) {
279   if (0 == internal_strcmp(format, "DEFAULT"))
280     format = kDefaultFormat;
281   for (const char *p = format; *p != '\0'; p++) {
282     if (*p != '%')
283       continue;
284     p++;
285     switch (*p) {
286       case '%':
287         break;
288       case 'n':
289         // frame_no
290         break;
291       case 'p':
292         // address
293         break;
294       default:
295         return true;
296     }
297   }
298   return false;
299 }
300 
301 void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer,
302                                             const char *format,
303                                             const DataInfo *DI,
304                                             const char *strip_path_prefix) {
305   for (const char *p = format; *p != '\0'; p++) {
306     if (*p != '%') {
307       buffer->AppendF("%c", *p);
308       continue;
309     }
310     p++;
311     switch (*p) {
312       case '%':
313         buffer->Append("%");
314         break;
315       case 's':
316         buffer->AppendF("%s", StripPathPrefix(DI->file, strip_path_prefix));
317         break;
318       case 'l':
319         buffer->AppendF("%zu", DI->line);
320         break;
321       case 'g':
322         buffer->AppendF("%s", DI->name);
323         break;
324       default:
325         Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
326                (void *)p);
327         Die();
328     }
329   }
330 }
331 
332 #endif  // !SANITIZER_SYMBOLIZER_MARKUP
333 
334 void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer,
335                                              const char *file, int line,
336                                              int column, bool vs_style,
337                                              const char *strip_path_prefix) {
338   if (vs_style && line > 0) {
339     buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
340     if (column > 0)
341       buffer->AppendF(",%d", column);
342     buffer->AppendF(")");
343     return;
344   }
345 
346   buffer->AppendF("%s", StripPathPrefix(file, strip_path_prefix));
347   if (line > 0) {
348     buffer->AppendF(":%d", line);
349     if (column > 0)
350       buffer->AppendF(":%d", column);
351   }
352 }
353 
354 void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer,
355                                              const char *module, uptr offset,
356                                              ModuleArch arch,
357                                              const char *strip_path_prefix) {
358   buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix));
359   if (arch != kModuleArchUnknown) {
360     buffer->AppendF(":%s", ModuleArchToString(arch));
361   }
362   buffer->AppendF("+0x%zx)", offset);
363 }
364 
365 } // namespace __sanitizer
366