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"
14 #include "sanitizer_file.h"
15 #include "sanitizer_fuchsia.h"
17 namespace __sanitizer
{
19 // sanitizer_symbolizer_markup.cpp implements these differently.
20 #if !SANITIZER_SYMBOLIZER_MARKUP
22 static const char *StripFunctionName(const char *function
, const char *prefix
) {
23 if (!function
) return nullptr;
24 if (!prefix
) return function
;
25 uptr prefix_len
= internal_strlen(prefix
);
26 if (0 == internal_strncmp(function
, prefix
, prefix_len
))
27 return function
+ prefix_len
;
31 static const char *DemangleFunctionName(const char *function
) {
32 if (!function
) return nullptr;
34 // NetBSD uses indirection for old threading functions for historical reasons
35 // The mangled names are internal implementation detail and should not be
36 // exposed even in backtraces.
38 if (!internal_strcmp(function
, "__libc_mutex_init"))
39 return "pthread_mutex_init";
40 if (!internal_strcmp(function
, "__libc_mutex_lock"))
41 return "pthread_mutex_lock";
42 if (!internal_strcmp(function
, "__libc_mutex_trylock"))
43 return "pthread_mutex_trylock";
44 if (!internal_strcmp(function
, "__libc_mutex_unlock"))
45 return "pthread_mutex_unlock";
46 if (!internal_strcmp(function
, "__libc_mutex_destroy"))
47 return "pthread_mutex_destroy";
48 if (!internal_strcmp(function
, "__libc_mutexattr_init"))
49 return "pthread_mutexattr_init";
50 if (!internal_strcmp(function
, "__libc_mutexattr_settype"))
51 return "pthread_mutexattr_settype";
52 if (!internal_strcmp(function
, "__libc_mutexattr_destroy"))
53 return "pthread_mutexattr_destroy";
54 if (!internal_strcmp(function
, "__libc_cond_init"))
55 return "pthread_cond_init";
56 if (!internal_strcmp(function
, "__libc_cond_signal"))
57 return "pthread_cond_signal";
58 if (!internal_strcmp(function
, "__libc_cond_broadcast"))
59 return "pthread_cond_broadcast";
60 if (!internal_strcmp(function
, "__libc_cond_wait"))
61 return "pthread_cond_wait";
62 if (!internal_strcmp(function
, "__libc_cond_timedwait"))
63 return "pthread_cond_timedwait";
64 if (!internal_strcmp(function
, "__libc_cond_destroy"))
65 return "pthread_cond_destroy";
66 if (!internal_strcmp(function
, "__libc_rwlock_init"))
67 return "pthread_rwlock_init";
68 if (!internal_strcmp(function
, "__libc_rwlock_rdlock"))
69 return "pthread_rwlock_rdlock";
70 if (!internal_strcmp(function
, "__libc_rwlock_wrlock"))
71 return "pthread_rwlock_wrlock";
72 if (!internal_strcmp(function
, "__libc_rwlock_tryrdlock"))
73 return "pthread_rwlock_tryrdlock";
74 if (!internal_strcmp(function
, "__libc_rwlock_trywrlock"))
75 return "pthread_rwlock_trywrlock";
76 if (!internal_strcmp(function
, "__libc_rwlock_unlock"))
77 return "pthread_rwlock_unlock";
78 if (!internal_strcmp(function
, "__libc_rwlock_destroy"))
79 return "pthread_rwlock_destroy";
80 if (!internal_strcmp(function
, "__libc_thr_keycreate"))
81 return "pthread_key_create";
82 if (!internal_strcmp(function
, "__libc_thr_setspecific"))
83 return "pthread_setspecific";
84 if (!internal_strcmp(function
, "__libc_thr_getspecific"))
85 return "pthread_getspecific";
86 if (!internal_strcmp(function
, "__libc_thr_keydelete"))
87 return "pthread_key_delete";
88 if (!internal_strcmp(function
, "__libc_thr_once"))
89 return "pthread_once";
90 if (!internal_strcmp(function
, "__libc_thr_self"))
91 return "pthread_self";
92 if (!internal_strcmp(function
, "__libc_thr_exit"))
93 return "pthread_exit";
94 if (!internal_strcmp(function
, "__libc_thr_setcancelstate"))
95 return "pthread_setcancelstate";
96 if (!internal_strcmp(function
, "__libc_thr_equal"))
97 return "pthread_equal";
98 if (!internal_strcmp(function
, "__libc_thr_curcpu"))
99 return "pthread_curcpu_np";
100 if (!internal_strcmp(function
, "__libc_thr_sigsetmask"))
101 return "pthread_sigmask";
107 static void MaybeBuildIdToBuffer(const AddressInfo
&info
, bool PrefixSpace
,
108 InternalScopedString
*buffer
) {
109 if (info
.uuid_size
) {
112 buffer
->append("(BuildId: ");
113 for (uptr i
= 0; i
< info
.uuid_size
; ++i
) {
114 buffer
->append("%02x", info
.uuid
[i
]);
120 static const char kDefaultFormat
[] = " #%n %p %F %L";
122 void RenderFrame(InternalScopedString
*buffer
, const char *format
, int frame_no
,
123 uptr address
, const AddressInfo
*info
, bool vs_style
,
124 const char *strip_path_prefix
, const char *strip_func_prefix
) {
125 // info will be null in the case where symbolization is not needed for the
126 // given format. This ensures that the code below will get a hard failure
127 // rather than print incorrect information in case RenderNeedsSymbolization
128 // ever ends up out of sync with this function. If non-null, the addresses
130 CHECK(!info
|| address
== info
->address
);
131 if (0 == internal_strcmp(format
, "DEFAULT"))
132 format
= kDefaultFormat
;
133 for (const char *p
= format
; *p
!= '\0'; p
++) {
135 buffer
->append("%c", *p
);
141 buffer
->append("%%");
143 // Frame number and all fields of AddressInfo structure.
145 buffer
->append("%u", frame_no
);
148 buffer
->append("0x%zx", address
);
151 buffer
->append("%s", StripPathPrefix(info
->module
, strip_path_prefix
));
154 buffer
->append("0x%zx", info
->module_offset
);
157 MaybeBuildIdToBuffer(*info
, /*PrefixSpace=*/false, buffer
);
160 buffer
->append("%s", DemangleFunctionName(StripFunctionName(
161 info
->function
, strip_func_prefix
)));
164 buffer
->append("0x%zx", info
->function_offset
!= AddressInfo::kUnknown
165 ? info
->function_offset
169 buffer
->append("%s", StripPathPrefix(info
->file
, strip_path_prefix
));
172 buffer
->append("%d", info
->line
);
175 buffer
->append("%d", info
->column
);
177 // Smarter special cases.
179 // Function name and offset, if file is unknown.
180 if (info
->function
) {
181 buffer
->append("in %s", DemangleFunctionName(StripFunctionName(
182 info
->function
, strip_func_prefix
)));
183 if (!info
->file
&& info
->function_offset
!= AddressInfo::kUnknown
)
184 buffer
->append("+0x%zx", info
->function_offset
);
188 // File/line information.
189 RenderSourceLocation(buffer
, info
->file
, info
->line
, info
->column
,
190 vs_style
, strip_path_prefix
);
193 // Source location, or module location.
195 RenderSourceLocation(buffer
, info
->file
, info
->line
, info
->column
,
196 vs_style
, strip_path_prefix
);
197 } else if (info
->module
) {
198 RenderModuleLocation(buffer
, info
->module
, info
->module_offset
,
199 info
->module_arch
, strip_path_prefix
);
201 MaybeBuildIdToBuffer(*info
, /*PrefixSpace=*/true, buffer
);
203 buffer
->append("(<unknown module>)");
207 // Module basename and offset, or PC.
208 if (address
& kExternalPCBit
) {
209 // There PCs are not meaningful.
210 } else if (info
->module
) {
211 // Always strip the module name for %M.
212 RenderModuleLocation(buffer
, StripModuleName(info
->module
),
213 info
->module_offset
, info
->module_arch
, "");
214 MaybeBuildIdToBuffer(*info
, /*PrefixSpace=*/true, buffer
);
216 buffer
->append("(%p)", (void *)address
);
220 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p
,
227 bool RenderNeedsSymbolization(const char *format
) {
228 if (0 == internal_strcmp(format
, "DEFAULT"))
229 format
= kDefaultFormat
;
230 for (const char *p
= format
; *p
!= '\0'; p
++) {
250 void RenderData(InternalScopedString
*buffer
, const char *format
,
251 const DataInfo
*DI
, const char *strip_path_prefix
) {
252 for (const char *p
= format
; *p
!= '\0'; p
++) {
254 buffer
->append("%c", *p
);
260 buffer
->append("%%");
263 buffer
->append("%s", StripPathPrefix(DI
->file
, strip_path_prefix
));
266 buffer
->append("%zu", DI
->line
);
269 buffer
->append("%s", DI
->name
);
272 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p
,
279 #endif // !SANITIZER_SYMBOLIZER_MARKUP
281 void RenderSourceLocation(InternalScopedString
*buffer
, const char *file
,
282 int line
, int column
, bool vs_style
,
283 const char *strip_path_prefix
) {
284 if (vs_style
&& line
> 0) {
285 buffer
->append("%s(%d", StripPathPrefix(file
, strip_path_prefix
), line
);
287 buffer
->append(",%d", column
);
292 buffer
->append("%s", StripPathPrefix(file
, strip_path_prefix
));
294 buffer
->append(":%d", line
);
296 buffer
->append(":%d", column
);
300 void RenderModuleLocation(InternalScopedString
*buffer
, const char *module
,
301 uptr offset
, ModuleArch arch
,
302 const char *strip_path_prefix
) {
303 buffer
->append("(%s", StripPathPrefix(module
, strip_path_prefix
));
304 if (arch
!= kModuleArchUnknown
) {
305 buffer
->append(":%s", ModuleArchToString(arch
));
307 buffer
->append("+0x%zx)", offset
);
310 } // namespace __sanitizer