diff options
Diffstat (limited to 'contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp')
-rw-r--r-- | contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp new file mode 100644 index 000000000000..5631d132da74 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp @@ -0,0 +1,365 @@ +//===-- sanitizer_common.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is shared between sanitizers' run-time libraries. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_stacktrace_printer.h" + +#include "sanitizer_common.h" +#include "sanitizer_file.h" +#include "sanitizer_flags.h" +#include "sanitizer_fuchsia.h" +#include "sanitizer_symbolizer_markup.h" + +namespace __sanitizer { + +StackTracePrinter *StackTracePrinter::GetOrInit() { + static StackTracePrinter *stacktrace_printer; + static StaticSpinMutex init_mu; + SpinMutexLock l(&init_mu); + if (stacktrace_printer) + return stacktrace_printer; + + stacktrace_printer = StackTracePrinter::NewStackTracePrinter(); + + CHECK(stacktrace_printer); + return stacktrace_printer; +} + +const char *StackTracePrinter::StripFunctionName(const char *function) { + if (!common_flags()->demangle) + return function; + if (!function) + return nullptr; + auto try_strip = [function](const char *prefix) -> const char * { + const uptr prefix_len = internal_strlen(prefix); + if (!internal_strncmp(function, prefix, prefix_len)) + return function + prefix_len; + return nullptr; + }; + if (SANITIZER_APPLE) { + if (const char *s = try_strip("wrap_")) + return s; + } else if (SANITIZER_WINDOWS) { + if (const char *s = try_strip("__asan_wrap_")) + return s; + } else { + if (const char *s = try_strip("___interceptor_")) + return s; + if (const char *s = try_strip("__interceptor_")) + return s; + } + return function; +} + +// sanitizer_symbolizer_markup.cpp implements these differently. +#if !SANITIZER_SYMBOLIZER_MARKUP + +StackTracePrinter *StackTracePrinter::NewStackTracePrinter() { + if (common_flags()->enable_symbolizer_markup) + return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter(); + + return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter(); +} + +static const char *DemangleFunctionName(const char *function) { + if (!common_flags()->demangle) + return function; + if (!function) + return nullptr; + + // NetBSD uses indirection for old threading functions for historical reasons + // The mangled names are internal implementation detail and should not be + // exposed even in backtraces. +#if SANITIZER_NETBSD + if (!internal_strcmp(function, "__libc_mutex_init")) + return "pthread_mutex_init"; + if (!internal_strcmp(function, "__libc_mutex_lock")) + return "pthread_mutex_lock"; + if (!internal_strcmp(function, "__libc_mutex_trylock")) + return "pthread_mutex_trylock"; + if (!internal_strcmp(function, "__libc_mutex_unlock")) + return "pthread_mutex_unlock"; + if (!internal_strcmp(function, "__libc_mutex_destroy")) + return "pthread_mutex_destroy"; + if (!internal_strcmp(function, "__libc_mutexattr_init")) + return "pthread_mutexattr_init"; + if (!internal_strcmp(function, "__libc_mutexattr_settype")) + return "pthread_mutexattr_settype"; + if (!internal_strcmp(function, "__libc_mutexattr_destroy")) + return "pthread_mutexattr_destroy"; + if (!internal_strcmp(function, "__libc_cond_init")) + return "pthread_cond_init"; + if (!internal_strcmp(function, "__libc_cond_signal")) + return "pthread_cond_signal"; + if (!internal_strcmp(function, "__libc_cond_broadcast")) + return "pthread_cond_broadcast"; + if (!internal_strcmp(function, "__libc_cond_wait")) + return "pthread_cond_wait"; + if (!internal_strcmp(function, "__libc_cond_timedwait")) + return "pthread_cond_timedwait"; + if (!internal_strcmp(function, "__libc_cond_destroy")) + return "pthread_cond_destroy"; + if (!internal_strcmp(function, "__libc_rwlock_init")) + return "pthread_rwlock_init"; + if (!internal_strcmp(function, "__libc_rwlock_rdlock")) + return "pthread_rwlock_rdlock"; + if (!internal_strcmp(function, "__libc_rwlock_wrlock")) + return "pthread_rwlock_wrlock"; + if (!internal_strcmp(function, "__libc_rwlock_tryrdlock")) + return "pthread_rwlock_tryrdlock"; + if (!internal_strcmp(function, "__libc_rwlock_trywrlock")) + return "pthread_rwlock_trywrlock"; + if (!internal_strcmp(function, "__libc_rwlock_unlock")) + return "pthread_rwlock_unlock"; + if (!internal_strcmp(function, "__libc_rwlock_destroy")) + return "pthread_rwlock_destroy"; + if (!internal_strcmp(function, "__libc_thr_keycreate")) + return "pthread_key_create"; + if (!internal_strcmp(function, "__libc_thr_setspecific")) + return "pthread_setspecific"; + if (!internal_strcmp(function, "__libc_thr_getspecific")) + return "pthread_getspecific"; + if (!internal_strcmp(function, "__libc_thr_keydelete")) + return "pthread_key_delete"; + if (!internal_strcmp(function, "__libc_thr_once")) + return "pthread_once"; + if (!internal_strcmp(function, "__libc_thr_self")) + return "pthread_self"; + if (!internal_strcmp(function, "__libc_thr_exit")) + return "pthread_exit"; + if (!internal_strcmp(function, "__libc_thr_setcancelstate")) + return "pthread_setcancelstate"; + if (!internal_strcmp(function, "__libc_thr_equal")) + return "pthread_equal"; + if (!internal_strcmp(function, "__libc_thr_curcpu")) + return "pthread_curcpu_np"; + if (!internal_strcmp(function, "__libc_thr_sigsetmask")) + return "pthread_sigmask"; +#endif + + return function; +} + +static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace, + InternalScopedString *buffer) { + if (info.uuid_size) { + if (PrefixSpace) + buffer->Append(" "); + buffer->Append("(BuildId: "); + for (uptr i = 0; i < info.uuid_size; ++i) { + buffer->AppendF("%02x", info.uuid[i]); + } + buffer->Append(")"); + } +} + +static const char kDefaultFormat[] = " #%n %p %F %L"; + +void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer, + const char *format, int frame_no, + uptr address, + const AddressInfo *info, + bool vs_style, + const char *strip_path_prefix) { + // info will be null in the case where symbolization is not needed for the + // given format. This ensures that the code below will get a hard failure + // rather than print incorrect information in case RenderNeedsSymbolization + // ever ends up out of sync with this function. If non-null, the addresses + // should match. + CHECK(!info || address == info->address); + if (0 == internal_strcmp(format, "DEFAULT")) + format = kDefaultFormat; + for (const char *p = format; *p != '\0'; p++) { + if (*p != '%') { + buffer->AppendF("%c", *p); + continue; + } + p++; + switch (*p) { + case '%': + buffer->Append("%"); + break; + // Frame number and all fields of AddressInfo structure. + case 'n': + buffer->AppendF("%u", frame_no); + break; + case 'p': + buffer->AppendF("%p", (void *)address); + break; + case 'm': + buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix)); + break; + case 'o': + buffer->AppendF("0x%zx", info->module_offset); + break; + case 'b': + MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer); + break; + case 'f': + buffer->AppendF("%s", + DemangleFunctionName(StripFunctionName(info->function))); + break; + case 'q': + buffer->AppendF("0x%zx", info->function_offset != AddressInfo::kUnknown + ? info->function_offset + : 0x0); + break; + case 's': + buffer->AppendF("%s", StripPathPrefix(info->file, strip_path_prefix)); + break; + case 'l': + buffer->AppendF("%d", info->line); + break; + case 'c': + buffer->AppendF("%d", info->column); + break; + // Smarter special cases. + case 'F': + // Function name and offset, if file is unknown. + if (info->function) { + buffer->AppendF( + "in %s", DemangleFunctionName(StripFunctionName(info->function))); + if (!info->file && info->function_offset != AddressInfo::kUnknown) + buffer->AppendF("+0x%zx", info->function_offset); + } + break; + case 'S': + // File/line information. + RenderSourceLocation(buffer, info->file, info->line, info->column, + vs_style, strip_path_prefix); + break; + case 'L': + // Source location, or module location. + if (info->file) { + RenderSourceLocation(buffer, info->file, info->line, info->column, + vs_style, strip_path_prefix); + } else if (info->module) { + RenderModuleLocation(buffer, info->module, info->module_offset, + info->module_arch, strip_path_prefix); + +#if !SANITIZER_APPLE + MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); +#endif + } else { + buffer->Append("(<unknown module>)"); + } + break; + case 'M': + // Module basename and offset, or PC. + if (address & kExternalPCBit) { + // There PCs are not meaningful. + } else if (info->module) { + // Always strip the module name for %M. + RenderModuleLocation(buffer, StripModuleName(info->module), + info->module_offset, info->module_arch, ""); +#if !SANITIZER_APPLE + MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); +#endif + } else { + buffer->AppendF("(%p)", (void *)address); + } + break; + default: + Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p, + (const void *)p); + Die(); + } + } +} + +bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) { + if (0 == internal_strcmp(format, "DEFAULT")) + format = kDefaultFormat; + for (const char *p = format; *p != '\0'; p++) { + if (*p != '%') + continue; + p++; + switch (*p) { + case '%': + break; + case 'n': + // frame_no + break; + case 'p': + // address + break; + default: + return true; + } + } + return false; +} + +void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer, + const char *format, + const DataInfo *DI, + const char *strip_path_prefix) { + for (const char *p = format; *p != '\0'; p++) { + if (*p != '%') { + buffer->AppendF("%c", *p); + continue; + } + p++; + switch (*p) { + case '%': + buffer->Append("%"); + break; + case 's': + buffer->AppendF("%s", StripPathPrefix(DI->file, strip_path_prefix)); + break; + case 'l': + buffer->AppendF("%zu", DI->line); + break; + case 'g': + buffer->AppendF("%s", DI->name); + break; + default: + Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p, + (const void *)p); + Die(); + } + } +} + +#endif // !SANITIZER_SYMBOLIZER_MARKUP + +void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer, + const char *file, int line, + int column, bool vs_style, + const char *strip_path_prefix) { + if (vs_style && line > 0) { + buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line); + if (column > 0) + buffer->AppendF(",%d", column); + buffer->Append(")"); + return; + } + + buffer->AppendF("%s", StripPathPrefix(file, strip_path_prefix)); + if (line > 0) { + buffer->AppendF(":%d", line); + if (column > 0) + buffer->AppendF(":%d", column); + } +} + +void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer, + const char *module, uptr offset, + ModuleArch arch, + const char *strip_path_prefix) { + buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix)); + if (arch != kModuleArchUnknown) { + buffer->AppendF(":%s", ModuleArchToString(arch)); + } + buffer->AppendF("+0x%zx)", offset); +} + +} // namespace __sanitizer |