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
21namespace __sanitizer {
22
23StackTracePrinter *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
36const 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
65StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
66  if (common_flags()->enable_symbolizer_markup)
67    return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
68
69  return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
70}
71
72static 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
151static 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
164static const char kDefaultFormat[] = "    #%n %p %F %L";
165
166void 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
278bool 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
301void 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
334void 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
354void 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