366 lines · cpp
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-exception6//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(prefix);43 if (!internal_strncmp(function, prefix, 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_MARKUP64 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 reasons79 // The mangled names are internal implementation detail and should not be80 // exposed even in backtraces.81#if SANITIZER_NETBSD82 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#endif147 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(" ");156 buffer->Append("(BuildId: ");157 for (uptr i = 0; i < info.uuid_size; ++i) {158 buffer->AppendF("%02x", info.uuid[i]);159 }160 buffer->Append(")");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 the173 // given format. This ensures that the code below will get a hard failure174 // rather than print incorrect information in case RenderNeedsSymbolization175 // ever ends up out of sync with this function. If non-null, the addresses176 // 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;184 }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::kUnknown212 ? info->function_offset213 : 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);232 }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);247 248#if !SANITIZER_APPLE249 MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);250#endif251 } else {252 buffer->Append("(<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, StripModuleName(info->module),262 info->module_offset, info->module_arch, "");263#if !SANITIZER_APPLE264 MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);265#endif266 } else {267 buffer->AppendF("(%p)", (void *)address);268 }269 break;270 default:271 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,272 (const void *)p);273 Die();274 }275 }276}277 278bool 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_no290 break;291 case 'p':292 // address293 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("%c", *p);308 continue;309 }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();328 }329 }330}331 332#endif // !SANITIZER_SYMBOLIZER_MARKUP333 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("%s(%d", StripPathPrefix(file, strip_path_prefix), line);340 if (column > 0)341 buffer->AppendF(",%d", column);342 buffer->Append(")");343 return;344 }345 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);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("(%s", StripPathPrefix(module, strip_path_prefix));359 if (arch != kModuleArchUnknown) {360 buffer->AppendF(":%s", ModuleArchToString(arch));361 }362 buffer->AppendF("+0x%zx)", offset);363}364 365} // namespace __sanitizer366