[Reland][Runtimes] Merge 'compile_commands.json' files from runtimes build (#116303)
[llvm-project.git] / compiler-rt / lib / sanitizer_common / sanitizer_stacktrace_printer.cpp
blob5631d132da74a5c116e8491c923e4749a6bc729d
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.
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)
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;
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;
59 return function;
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)
74 return function;
75 if (!function)
76 return nullptr;
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
148 return function;
151 static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
152 InternalScopedString *buffer) {
153 if (info.uuid_size) {
154 if (PrefixSpace)
155 buffer->Append(" ");
156 buffer->Append("(BuildId: ");
157 for (uptr i = 0; i < info.uuid_size; ++i) {
158 buffer->AppendF("%02x", info.uuid[i]);
160 buffer->Append(")");
164 static const char kDefaultFormat[] = " #%n %p %F %L";
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;
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("%p", (void *)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);
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);
248 #if !SANITIZER_APPLE
249 MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
250 #endif
251 } else {
252 buffer->Append("(<unknown module>)");
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);
269 break;
270 default:
271 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
272 (const void *)p);
273 Die();
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;
298 return false;
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;
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 (const void *)p);
327 Die();
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);
340 if (column > 0)
341 buffer->AppendF(",%d", column);
342 buffer->Append(")");
343 return;
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);
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));
362 buffer->AppendF("+0x%zx)", offset);
365 } // namespace __sanitizer