1 //===-- sanitizer_common.cpp ----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is shared between sanitizers' run-time libraries.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_stacktrace_printer.h"
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"
21 namespace __sanitizer
{
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
;
30 stacktrace_printer
= StackTracePrinter::NewStackTracePrinter();
32 CHECK(stacktrace_printer
);
33 return stacktrace_printer
;
36 const char *StackTracePrinter::StripFunctionName(const char *function
) {
37 if (!common_flags()->demangle
)
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
;
47 if (SANITIZER_APPLE
) {
48 if (const char *s
= try_strip("wrap_"))
50 } else if (SANITIZER_WINDOWS
) {
51 if (const char *s
= try_strip("__asan_wrap_"))
54 if (const char *s
= try_strip("___interceptor_"))
56 if (const char *s
= try_strip("__interceptor_"))
62 // sanitizer_symbolizer_markup.cpp implements these differently.
63 #if !SANITIZER_SYMBOLIZER_MARKUP
65 StackTracePrinter
*StackTracePrinter::NewStackTracePrinter() {
66 if (common_flags()->enable_symbolizer_markup
)
67 return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
69 return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
72 static const char *DemangleFunctionName(const char *function
) {
73 if (!common_flags()->demangle
)
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.
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";
151 static void MaybeBuildIdToBuffer(const AddressInfo
&info
, bool PrefixSpace
,
152 InternalScopedString
*buffer
) {
153 if (info
.uuid_size
) {
156 buffer
->Append("(BuildId: ");
157 for (uptr i
= 0; i
< info
.uuid_size
; ++i
) {
158 buffer
->AppendF("%02x", info
.uuid
[i
]);
164 static const char kDefaultFormat
[] = " #%n %p %F %L";
166 void FormattedStackTracePrinter::RenderFrame(InternalScopedString
*buffer
,
167 const char *format
, int frame_no
,
169 const AddressInfo
*info
,
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
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
++) {
182 buffer
->AppendF("%c", *p
);
190 // Frame number and all fields of AddressInfo structure.
192 buffer
->AppendF("%u", frame_no
);
195 buffer
->AppendF("%p", (void *)address
);
198 buffer
->AppendF("%s", StripPathPrefix(info
->module
, strip_path_prefix
));
201 buffer
->AppendF("0x%zx", info
->module_offset
);
204 MaybeBuildIdToBuffer(*info
, /*PrefixSpace=*/false, buffer
);
207 buffer
->AppendF("%s",
208 DemangleFunctionName(StripFunctionName(info
->function
)));
211 buffer
->AppendF("0x%zx", info
->function_offset
!= AddressInfo::kUnknown
212 ? info
->function_offset
216 buffer
->AppendF("%s", StripPathPrefix(info
->file
, strip_path_prefix
));
219 buffer
->AppendF("%d", info
->line
);
222 buffer
->AppendF("%d", info
->column
);
224 // Smarter special cases.
226 // Function name and offset, if file is unknown.
227 if (info
->function
) {
229 "in %s", DemangleFunctionName(StripFunctionName(info
->function
)));
230 if (!info
->file
&& info
->function_offset
!= AddressInfo::kUnknown
)
231 buffer
->AppendF("+0x%zx", info
->function_offset
);
235 // File/line information.
236 RenderSourceLocation(buffer
, info
->file
, info
->line
, info
->column
,
237 vs_style
, strip_path_prefix
);
240 // Source location, or module location.
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
);
249 MaybeBuildIdToBuffer(*info
, /*PrefixSpace=*/true, buffer
);
252 buffer
->Append("(<unknown module>)");
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
, "");
264 MaybeBuildIdToBuffer(*info
, /*PrefixSpace=*/true, buffer
);
267 buffer
->AppendF("(%p)", (void *)address
);
271 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p
,
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
++) {
301 void FormattedStackTracePrinter::RenderData(InternalScopedString
*buffer
,
304 const char *strip_path_prefix
) {
305 for (const char *p
= format
; *p
!= '\0'; p
++) {
307 buffer
->AppendF("%c", *p
);
316 buffer
->AppendF("%s", StripPathPrefix(DI
->file
, strip_path_prefix
));
319 buffer
->AppendF("%zu", DI
->line
);
322 buffer
->AppendF("%s", DI
->name
);
325 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p
,
332 #endif // !SANITIZER_SYMBOLIZER_MARKUP
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
);
341 buffer
->AppendF(",%d", column
);
346 buffer
->AppendF("%s", StripPathPrefix(file
, strip_path_prefix
));
348 buffer
->AppendF(":%d", line
);
350 buffer
->AppendF(":%d", column
);
354 void StackTracePrinter::RenderModuleLocation(InternalScopedString
*buffer
,
355 const char *module
, uptr offset
,
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
));
362 buffer
->AppendF("+0x%zx)", offset
);
365 } // namespace __sanitizer