xref: /openbsd-src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- sanitizer_common.cpp ----------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is shared between sanitizers' run-time libraries.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick 
133cab2bb3Spatrick #include "sanitizer_stacktrace_printer.h"
143cab2bb3Spatrick #include "sanitizer_file.h"
153cab2bb3Spatrick #include "sanitizer_fuchsia.h"
163cab2bb3Spatrick 
173cab2bb3Spatrick namespace __sanitizer {
183cab2bb3Spatrick 
193cab2bb3Spatrick // sanitizer_symbolizer_markup.cpp implements these differently.
203cab2bb3Spatrick #if !SANITIZER_SYMBOLIZER_MARKUP
213cab2bb3Spatrick 
StripFunctionName(const char * function,const char * prefix)223cab2bb3Spatrick static const char *StripFunctionName(const char *function, const char *prefix) {
233cab2bb3Spatrick   if (!function) return nullptr;
243cab2bb3Spatrick   if (!prefix) return function;
253cab2bb3Spatrick   uptr prefix_len = internal_strlen(prefix);
263cab2bb3Spatrick   if (0 == internal_strncmp(function, prefix, prefix_len))
273cab2bb3Spatrick     return function + prefix_len;
283cab2bb3Spatrick   return function;
293cab2bb3Spatrick }
303cab2bb3Spatrick 
DemangleFunctionName(const char * function)313cab2bb3Spatrick static const char *DemangleFunctionName(const char *function) {
323cab2bb3Spatrick   if (!function) return nullptr;
333cab2bb3Spatrick 
343cab2bb3Spatrick   // NetBSD uses indirection for old threading functions for historical reasons
353cab2bb3Spatrick   // The mangled names are internal implementation detail and should not be
363cab2bb3Spatrick   // exposed even in backtraces.
373cab2bb3Spatrick #if SANITIZER_NETBSD
383cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutex_init"))
393cab2bb3Spatrick     return "pthread_mutex_init";
403cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutex_lock"))
413cab2bb3Spatrick     return "pthread_mutex_lock";
423cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutex_trylock"))
433cab2bb3Spatrick     return "pthread_mutex_trylock";
443cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutex_unlock"))
453cab2bb3Spatrick     return "pthread_mutex_unlock";
463cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutex_destroy"))
473cab2bb3Spatrick     return "pthread_mutex_destroy";
483cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutexattr_init"))
493cab2bb3Spatrick     return "pthread_mutexattr_init";
503cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutexattr_settype"))
513cab2bb3Spatrick     return "pthread_mutexattr_settype";
523cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_mutexattr_destroy"))
533cab2bb3Spatrick     return "pthread_mutexattr_destroy";
543cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_cond_init"))
553cab2bb3Spatrick     return "pthread_cond_init";
563cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_cond_signal"))
573cab2bb3Spatrick     return "pthread_cond_signal";
583cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_cond_broadcast"))
593cab2bb3Spatrick     return "pthread_cond_broadcast";
603cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_cond_wait"))
613cab2bb3Spatrick     return "pthread_cond_wait";
623cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_cond_timedwait"))
633cab2bb3Spatrick     return "pthread_cond_timedwait";
643cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_cond_destroy"))
653cab2bb3Spatrick     return "pthread_cond_destroy";
663cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_init"))
673cab2bb3Spatrick     return "pthread_rwlock_init";
683cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_rdlock"))
693cab2bb3Spatrick     return "pthread_rwlock_rdlock";
703cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_wrlock"))
713cab2bb3Spatrick     return "pthread_rwlock_wrlock";
723cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_tryrdlock"))
733cab2bb3Spatrick     return "pthread_rwlock_tryrdlock";
743cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_trywrlock"))
753cab2bb3Spatrick     return "pthread_rwlock_trywrlock";
763cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_unlock"))
773cab2bb3Spatrick     return "pthread_rwlock_unlock";
783cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_rwlock_destroy"))
793cab2bb3Spatrick     return "pthread_rwlock_destroy";
803cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_keycreate"))
813cab2bb3Spatrick     return "pthread_key_create";
823cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_setspecific"))
833cab2bb3Spatrick     return "pthread_setspecific";
843cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_getspecific"))
853cab2bb3Spatrick     return "pthread_getspecific";
863cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_keydelete"))
873cab2bb3Spatrick     return "pthread_key_delete";
883cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_once"))
893cab2bb3Spatrick     return "pthread_once";
903cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_self"))
913cab2bb3Spatrick     return "pthread_self";
923cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_exit"))
933cab2bb3Spatrick     return "pthread_exit";
943cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_setcancelstate"))
953cab2bb3Spatrick     return "pthread_setcancelstate";
963cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_equal"))
973cab2bb3Spatrick     return "pthread_equal";
983cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_curcpu"))
993cab2bb3Spatrick     return "pthread_curcpu_np";
1003cab2bb3Spatrick   if (!internal_strcmp(function, "__libc_thr_sigsetmask"))
1013cab2bb3Spatrick     return "pthread_sigmask";
1023cab2bb3Spatrick #endif
1033cab2bb3Spatrick 
1043cab2bb3Spatrick   return function;
1053cab2bb3Spatrick }
1063cab2bb3Spatrick 
MaybeBuildIdToBuffer(const AddressInfo & info,bool PrefixSpace,InternalScopedString * buffer)107*810390e3Srobert static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
108*810390e3Srobert                                  InternalScopedString *buffer) {
109*810390e3Srobert   if (info.uuid_size) {
110*810390e3Srobert     if (PrefixSpace)
111*810390e3Srobert       buffer->append(" ");
112*810390e3Srobert     buffer->append("(BuildId: ");
113*810390e3Srobert     for (uptr i = 0; i < info.uuid_size; ++i) {
114*810390e3Srobert       buffer->append("%02x", info.uuid[i]);
115*810390e3Srobert     }
116*810390e3Srobert     buffer->append(")");
117*810390e3Srobert   }
118*810390e3Srobert }
119*810390e3Srobert 
1203cab2bb3Spatrick static const char kDefaultFormat[] = "    #%n %p %F %L";
1213cab2bb3Spatrick 
RenderFrame(InternalScopedString * buffer,const char * format,int frame_no,uptr address,const AddressInfo * info,bool vs_style,const char * strip_path_prefix,const char * strip_func_prefix)1223cab2bb3Spatrick void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
123d89ec533Spatrick                  uptr address, const AddressInfo *info, bool vs_style,
1243cab2bb3Spatrick                  const char *strip_path_prefix, const char *strip_func_prefix) {
125d89ec533Spatrick   // info will be null in the case where symbolization is not needed for the
126d89ec533Spatrick   // given format. This ensures that the code below will get a hard failure
127d89ec533Spatrick   // rather than print incorrect information in case RenderNeedsSymbolization
128d89ec533Spatrick   // ever ends up out of sync with this function. If non-null, the addresses
129d89ec533Spatrick   // should match.
130d89ec533Spatrick   CHECK(!info || address == info->address);
1313cab2bb3Spatrick   if (0 == internal_strcmp(format, "DEFAULT"))
1323cab2bb3Spatrick     format = kDefaultFormat;
1333cab2bb3Spatrick   for (const char *p = format; *p != '\0'; p++) {
1343cab2bb3Spatrick     if (*p != '%') {
1353cab2bb3Spatrick       buffer->append("%c", *p);
1363cab2bb3Spatrick       continue;
1373cab2bb3Spatrick     }
1383cab2bb3Spatrick     p++;
1393cab2bb3Spatrick     switch (*p) {
1403cab2bb3Spatrick     case '%':
1413cab2bb3Spatrick       buffer->append("%%");
1423cab2bb3Spatrick       break;
1433cab2bb3Spatrick     // Frame number and all fields of AddressInfo structure.
1443cab2bb3Spatrick     case 'n':
145*810390e3Srobert       buffer->append("%u", frame_no);
1463cab2bb3Spatrick       break;
1473cab2bb3Spatrick     case 'p':
148d89ec533Spatrick       buffer->append("0x%zx", address);
1493cab2bb3Spatrick       break;
1503cab2bb3Spatrick     case 'm':
151d89ec533Spatrick       buffer->append("%s", StripPathPrefix(info->module, strip_path_prefix));
1523cab2bb3Spatrick       break;
1533cab2bb3Spatrick     case 'o':
154d89ec533Spatrick       buffer->append("0x%zx", info->module_offset);
1553cab2bb3Spatrick       break;
156*810390e3Srobert     case 'b':
157*810390e3Srobert       MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer);
158*810390e3Srobert       break;
1593cab2bb3Spatrick     case 'f':
160d89ec533Spatrick       buffer->append("%s", DemangleFunctionName(StripFunctionName(
161d89ec533Spatrick                                info->function, strip_func_prefix)));
1623cab2bb3Spatrick       break;
1633cab2bb3Spatrick     case 'q':
164d89ec533Spatrick       buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
165d89ec533Spatrick                                   ? info->function_offset
1663cab2bb3Spatrick                                   : 0x0);
1673cab2bb3Spatrick       break;
1683cab2bb3Spatrick     case 's':
169d89ec533Spatrick       buffer->append("%s", StripPathPrefix(info->file, strip_path_prefix));
1703cab2bb3Spatrick       break;
1713cab2bb3Spatrick     case 'l':
172d89ec533Spatrick       buffer->append("%d", info->line);
1733cab2bb3Spatrick       break;
1743cab2bb3Spatrick     case 'c':
175d89ec533Spatrick       buffer->append("%d", info->column);
1763cab2bb3Spatrick       break;
1773cab2bb3Spatrick     // Smarter special cases.
1783cab2bb3Spatrick     case 'F':
1793cab2bb3Spatrick       // Function name and offset, if file is unknown.
180d89ec533Spatrick       if (info->function) {
181d89ec533Spatrick         buffer->append("in %s", DemangleFunctionName(StripFunctionName(
182d89ec533Spatrick                                     info->function, strip_func_prefix)));
183d89ec533Spatrick         if (!info->file && info->function_offset != AddressInfo::kUnknown)
184d89ec533Spatrick           buffer->append("+0x%zx", info->function_offset);
1853cab2bb3Spatrick       }
1863cab2bb3Spatrick       break;
1873cab2bb3Spatrick     case 'S':
1883cab2bb3Spatrick       // File/line information.
189d89ec533Spatrick       RenderSourceLocation(buffer, info->file, info->line, info->column,
190d89ec533Spatrick                            vs_style, strip_path_prefix);
1913cab2bb3Spatrick       break;
1923cab2bb3Spatrick     case 'L':
1933cab2bb3Spatrick       // Source location, or module location.
194d89ec533Spatrick       if (info->file) {
195d89ec533Spatrick         RenderSourceLocation(buffer, info->file, info->line, info->column,
1963cab2bb3Spatrick                              vs_style, strip_path_prefix);
197d89ec533Spatrick       } else if (info->module) {
198d89ec533Spatrick         RenderModuleLocation(buffer, info->module, info->module_offset,
199d89ec533Spatrick                              info->module_arch, strip_path_prefix);
200*810390e3Srobert 
201*810390e3Srobert         MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
2023cab2bb3Spatrick       } else {
2033cab2bb3Spatrick         buffer->append("(<unknown module>)");
2043cab2bb3Spatrick       }
2053cab2bb3Spatrick       break;
2063cab2bb3Spatrick     case 'M':
2073cab2bb3Spatrick       // Module basename and offset, or PC.
208d89ec533Spatrick       if (address & kExternalPCBit) {
209d89ec533Spatrick         // There PCs are not meaningful.
210d89ec533Spatrick       } else if (info->module) {
2113cab2bb3Spatrick         // Always strip the module name for %M.
212d89ec533Spatrick         RenderModuleLocation(buffer, StripModuleName(info->module),
213d89ec533Spatrick                              info->module_offset, info->module_arch, "");
214*810390e3Srobert         MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
215d89ec533Spatrick       } else {
216d89ec533Spatrick         buffer->append("(%p)", (void *)address);
217d89ec533Spatrick       }
2183cab2bb3Spatrick       break;
2193cab2bb3Spatrick     default:
220*810390e3Srobert       Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
221*810390e3Srobert              (void *)p);
2223cab2bb3Spatrick       Die();
2233cab2bb3Spatrick     }
2243cab2bb3Spatrick   }
2253cab2bb3Spatrick }
2263cab2bb3Spatrick 
RenderNeedsSymbolization(const char * format)227d89ec533Spatrick bool RenderNeedsSymbolization(const char *format) {
228d89ec533Spatrick   if (0 == internal_strcmp(format, "DEFAULT"))
229d89ec533Spatrick     format = kDefaultFormat;
230d89ec533Spatrick   for (const char *p = format; *p != '\0'; p++) {
231d89ec533Spatrick     if (*p != '%')
232d89ec533Spatrick       continue;
233d89ec533Spatrick     p++;
234d89ec533Spatrick     switch (*p) {
235d89ec533Spatrick       case '%':
236d89ec533Spatrick         break;
237d89ec533Spatrick       case 'n':
238d89ec533Spatrick         // frame_no
239d89ec533Spatrick         break;
240d89ec533Spatrick       case 'p':
241d89ec533Spatrick         // address
242d89ec533Spatrick         break;
243d89ec533Spatrick       default:
244d89ec533Spatrick         return true;
245d89ec533Spatrick     }
246d89ec533Spatrick   }
247d89ec533Spatrick   return false;
248d89ec533Spatrick }
249d89ec533Spatrick 
RenderData(InternalScopedString * buffer,const char * format,const DataInfo * DI,const char * strip_path_prefix)2503cab2bb3Spatrick void RenderData(InternalScopedString *buffer, const char *format,
2513cab2bb3Spatrick                 const DataInfo *DI, const char *strip_path_prefix) {
2523cab2bb3Spatrick   for (const char *p = format; *p != '\0'; p++) {
2533cab2bb3Spatrick     if (*p != '%') {
2543cab2bb3Spatrick       buffer->append("%c", *p);
2553cab2bb3Spatrick       continue;
2563cab2bb3Spatrick     }
2573cab2bb3Spatrick     p++;
2583cab2bb3Spatrick     switch (*p) {
2593cab2bb3Spatrick       case '%':
2603cab2bb3Spatrick         buffer->append("%%");
2613cab2bb3Spatrick         break;
2623cab2bb3Spatrick       case 's':
2633cab2bb3Spatrick         buffer->append("%s", StripPathPrefix(DI->file, strip_path_prefix));
2643cab2bb3Spatrick         break;
2653cab2bb3Spatrick       case 'l':
266*810390e3Srobert         buffer->append("%zu", DI->line);
2673cab2bb3Spatrick         break;
2683cab2bb3Spatrick       case 'g':
2693cab2bb3Spatrick         buffer->append("%s", DI->name);
2703cab2bb3Spatrick         break;
2713cab2bb3Spatrick       default:
272*810390e3Srobert         Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
273*810390e3Srobert                (void *)p);
2743cab2bb3Spatrick         Die();
2753cab2bb3Spatrick     }
2763cab2bb3Spatrick   }
2773cab2bb3Spatrick }
2783cab2bb3Spatrick 
2793cab2bb3Spatrick #endif  // !SANITIZER_SYMBOLIZER_MARKUP
2803cab2bb3Spatrick 
RenderSourceLocation(InternalScopedString * buffer,const char * file,int line,int column,bool vs_style,const char * strip_path_prefix)2813cab2bb3Spatrick void RenderSourceLocation(InternalScopedString *buffer, const char *file,
2823cab2bb3Spatrick                           int line, int column, bool vs_style,
2833cab2bb3Spatrick                           const char *strip_path_prefix) {
2843cab2bb3Spatrick   if (vs_style && line > 0) {
2853cab2bb3Spatrick     buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
2863cab2bb3Spatrick     if (column > 0)
2873cab2bb3Spatrick       buffer->append(",%d", column);
2883cab2bb3Spatrick     buffer->append(")");
2893cab2bb3Spatrick     return;
2903cab2bb3Spatrick   }
2913cab2bb3Spatrick 
2923cab2bb3Spatrick   buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
2933cab2bb3Spatrick   if (line > 0) {
2943cab2bb3Spatrick     buffer->append(":%d", line);
2953cab2bb3Spatrick     if (column > 0)
2963cab2bb3Spatrick       buffer->append(":%d", column);
2973cab2bb3Spatrick   }
2983cab2bb3Spatrick }
2993cab2bb3Spatrick 
RenderModuleLocation(InternalScopedString * buffer,const char * module,uptr offset,ModuleArch arch,const char * strip_path_prefix)3003cab2bb3Spatrick void RenderModuleLocation(InternalScopedString *buffer, const char *module,
3013cab2bb3Spatrick                           uptr offset, ModuleArch arch,
3023cab2bb3Spatrick                           const char *strip_path_prefix) {
3033cab2bb3Spatrick   buffer->append("(%s", StripPathPrefix(module, strip_path_prefix));
3043cab2bb3Spatrick   if (arch != kModuleArchUnknown) {
3053cab2bb3Spatrick     buffer->append(":%s", ModuleArchToString(arch));
3063cab2bb3Spatrick   }
3073cab2bb3Spatrick   buffer->append("+0x%zx)", offset);
3083cab2bb3Spatrick }
3093cab2bb3Spatrick 
3103cab2bb3Spatrick } // namespace __sanitizer
311