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(s: prefix);
43 if (!internal_strncmp(s1: function, s2: prefix, n: 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->Append(str: " ");
156 buffer->Append(str: "(BuildId: ");
157 for (uptr i = 0; i < info.uuid_size; ++i) {
158 buffer->AppendF(format: "%02x", info.uuid[i]);
159 }
160 buffer->Append(str: ")");
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(s1: format, s2: "DEFAULT"))
179 format = kDefaultFormat;
180 for (const char *p = format; *p != '\0'; p++) {
181 if (*p != '%') {
182 buffer->AppendF(format: "%c", *p);
183 continue;
184 }
185 p++;
186 switch (*p) {
187 case '%':
188 buffer->Append(str: "%");
189 break;
190 // Frame number and all fields of AddressInfo structure.
191 case 'n':
192 buffer->AppendF(format: "%u", frame_no);
193 break;
194 case 'p':
195 buffer->AppendF(format: "0x%zx", address);
196 break;
197 case 'm':
198 buffer->AppendF(format: "%s", StripPathPrefix(filepath: info->module, strip_file_prefix: strip_path_prefix));
199 break;
200 case 'o':
201 buffer->AppendF(format: "0x%zx", info->module_offset);
202 break;
203 case 'b':
204 MaybeBuildIdToBuffer(info: *info, /*PrefixSpace=*/false, buffer);
205 break;
206 case 'f':
207 buffer->AppendF(format: "%s",
208 DemangleFunctionName(function: StripFunctionName(function: info->function)));
209 break;
210 case 'q':
211 buffer->AppendF(format: "0x%zx", info->function_offset != AddressInfo::kUnknown
212 ? info->function_offset
213 : 0x0);
214 break;
215 case 's':
216 buffer->AppendF(format: "%s", StripPathPrefix(filepath: info->file, strip_file_prefix: strip_path_prefix));
217 break;
218 case 'l':
219 buffer->AppendF(format: "%d", info->line);
220 break;
221 case 'c':
222 buffer->AppendF(format: "%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 format: "in %s", DemangleFunctionName(function: StripFunctionName(function: info->function)));
230 if (!info->file && info->function_offset != AddressInfo::kUnknown)
231 buffer->AppendF(format: "+0x%zx", info->function_offset);
232 }
233 break;
234 case 'S':
235 // File/line information.
236 RenderSourceLocation(buffer, file: info->file, line: info->line, column: 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, file: info->file, line: info->line, column: info->column,
243 vs_style, strip_path_prefix);
244 } else if (info->module) {
245 RenderModuleLocation(buffer, module: info->module, offset: info->module_offset,
246 arch: info->module_arch, strip_path_prefix);
247
248#if !SANITIZER_APPLE
249 MaybeBuildIdToBuffer(info: *info, /*PrefixSpace=*/true, buffer);
250#endif
251 } else {
252 buffer->Append(str: "(<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, module: StripModuleName(module: info->module),
262 offset: info->module_offset, arch: info->module_arch, strip_path_prefix: "");
263#if !SANITIZER_APPLE
264 MaybeBuildIdToBuffer(info: *info, /*PrefixSpace=*/true, buffer);
265#endif
266 } else {
267 buffer->AppendF(format: "(%p)", (void *)address);
268 }
269 break;
270 default:
271 Report(format: "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(s1: format, s2: "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(format: "%c", *p);
308 continue;
309 }
310 p++;
311 switch (*p) {
312 case '%':
313 buffer->Append(str: "%");
314 break;
315 case 's':
316 buffer->AppendF(format: "%s", StripPathPrefix(filepath: DI->file, strip_file_prefix: strip_path_prefix));
317 break;
318 case 'l':
319 buffer->AppendF(format: "%zu", DI->line);
320 break;
321 case 'g':
322 buffer->AppendF(format: "%s", DI->name);
323 break;
324 default:
325 Report(format: "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(format: "%s(%d", StripPathPrefix(filepath: file, strip_file_prefix: strip_path_prefix), line);
340 if (column > 0)
341 buffer->AppendF(format: ",%d", column);
342 buffer->Append(str: ")");
343 return;
344 }
345
346 buffer->AppendF(format: "%s", StripPathPrefix(filepath: file, strip_file_prefix: strip_path_prefix));
347 if (line > 0) {
348 buffer->AppendF(format: ":%d", line);
349 if (column > 0)
350 buffer->AppendF(format: ":%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(format: "(%s", StripPathPrefix(filepath: module, strip_file_prefix: strip_path_prefix));
359 if (arch != kModuleArchUnknown) {
360 buffer->AppendF(format: ":%s", ModuleArchToString(arch));
361 }
362 buffer->AppendF(format: "+0x%zx)", offset);
363}
364
365} // namespace __sanitizer
366

source code of compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp