xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
168d75effSDimitry Andric //===-- sanitizer_common.cpp ----------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is shared between sanitizers' run-time libraries.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric 
1368d75effSDimitry Andric #include "sanitizer_stacktrace_printer.h"
1406c3fb27SDimitry Andric 
155f757f3fSDimitry Andric #include "sanitizer_common.h"
1668d75effSDimitry Andric #include "sanitizer_file.h"
1706c3fb27SDimitry Andric #include "sanitizer_flags.h"
1868d75effSDimitry Andric #include "sanitizer_fuchsia.h"
195f757f3fSDimitry Andric #include "sanitizer_symbolizer_markup.h"
2068d75effSDimitry Andric 
2168d75effSDimitry Andric namespace __sanitizer {
2268d75effSDimitry Andric 
GetOrInit()235f757f3fSDimitry Andric StackTracePrinter *StackTracePrinter::GetOrInit() {
245f757f3fSDimitry Andric   static StackTracePrinter *stacktrace_printer;
255f757f3fSDimitry Andric   static StaticSpinMutex init_mu;
265f757f3fSDimitry Andric   SpinMutexLock l(&init_mu);
275f757f3fSDimitry Andric   if (stacktrace_printer)
285f757f3fSDimitry Andric     return stacktrace_printer;
295f757f3fSDimitry Andric 
305f757f3fSDimitry Andric   stacktrace_printer = StackTracePrinter::NewStackTracePrinter();
315f757f3fSDimitry Andric 
325f757f3fSDimitry Andric   CHECK(stacktrace_printer);
335f757f3fSDimitry Andric   return stacktrace_printer;
345f757f3fSDimitry Andric }
355f757f3fSDimitry Andric 
StripFunctionName(const char * function)365f757f3fSDimitry Andric const char *StackTracePrinter::StripFunctionName(const char *function) {
3706c3fb27SDimitry Andric   if (!common_flags()->demangle)
3806c3fb27SDimitry Andric     return function;
3906c3fb27SDimitry Andric   if (!function)
4006c3fb27SDimitry Andric     return nullptr;
4106c3fb27SDimitry Andric   auto try_strip = [function](const char *prefix) -> const char * {
4206c3fb27SDimitry Andric     const uptr prefix_len = internal_strlen(prefix);
4306c3fb27SDimitry Andric     if (!internal_strncmp(function, prefix, prefix_len))
4468d75effSDimitry Andric       return function + prefix_len;
4506c3fb27SDimitry Andric     return nullptr;
4606c3fb27SDimitry Andric   };
4706c3fb27SDimitry Andric   if (SANITIZER_APPLE) {
4806c3fb27SDimitry Andric     if (const char *s = try_strip("wrap_"))
4906c3fb27SDimitry Andric       return s;
5006c3fb27SDimitry Andric   } else if (SANITIZER_WINDOWS) {
5106c3fb27SDimitry Andric     if (const char *s = try_strip("__asan_wrap_"))
5206c3fb27SDimitry Andric       return s;
5306c3fb27SDimitry Andric   } else {
5406c3fb27SDimitry Andric     if (const char *s = try_strip("___interceptor_"))
5506c3fb27SDimitry Andric       return s;
5606c3fb27SDimitry Andric     if (const char *s = try_strip("__interceptor_"))
5706c3fb27SDimitry Andric       return s;
5806c3fb27SDimitry Andric   }
5968d75effSDimitry Andric   return function;
6068d75effSDimitry Andric }
6168d75effSDimitry Andric 
6206c3fb27SDimitry Andric // sanitizer_symbolizer_markup.cpp implements these differently.
6306c3fb27SDimitry Andric #if !SANITIZER_SYMBOLIZER_MARKUP
6406c3fb27SDimitry Andric 
NewStackTracePrinter()655f757f3fSDimitry Andric StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
665f757f3fSDimitry Andric   if (common_flags()->enable_symbolizer_markup)
675f757f3fSDimitry Andric     return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
685f757f3fSDimitry Andric 
695f757f3fSDimitry Andric   return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
705f757f3fSDimitry Andric }
715f757f3fSDimitry Andric 
DemangleFunctionName(const char * function)7268d75effSDimitry Andric static const char *DemangleFunctionName(const char *function) {
7306c3fb27SDimitry Andric   if (!common_flags()->demangle)
7406c3fb27SDimitry Andric     return function;
7506c3fb27SDimitry Andric   if (!function)
7606c3fb27SDimitry Andric     return nullptr;
7768d75effSDimitry Andric 
7868d75effSDimitry Andric   // NetBSD uses indirection for old threading functions for historical reasons
7968d75effSDimitry Andric   // The mangled names are internal implementation detail and should not be
8068d75effSDimitry Andric   // exposed even in backtraces.
8168d75effSDimitry Andric #if SANITIZER_NETBSD
8268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutex_init"))
8368d75effSDimitry Andric     return "pthread_mutex_init";
8468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutex_lock"))
8568d75effSDimitry Andric     return "pthread_mutex_lock";
8668d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutex_trylock"))
8768d75effSDimitry Andric     return "pthread_mutex_trylock";
8868d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutex_unlock"))
8968d75effSDimitry Andric     return "pthread_mutex_unlock";
9068d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutex_destroy"))
9168d75effSDimitry Andric     return "pthread_mutex_destroy";
9268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutexattr_init"))
9368d75effSDimitry Andric     return "pthread_mutexattr_init";
9468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutexattr_settype"))
9568d75effSDimitry Andric     return "pthread_mutexattr_settype";
9668d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_mutexattr_destroy"))
9768d75effSDimitry Andric     return "pthread_mutexattr_destroy";
9868d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_cond_init"))
9968d75effSDimitry Andric     return "pthread_cond_init";
10068d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_cond_signal"))
10168d75effSDimitry Andric     return "pthread_cond_signal";
10268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_cond_broadcast"))
10368d75effSDimitry Andric     return "pthread_cond_broadcast";
10468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_cond_wait"))
10568d75effSDimitry Andric     return "pthread_cond_wait";
10668d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_cond_timedwait"))
10768d75effSDimitry Andric     return "pthread_cond_timedwait";
10868d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_cond_destroy"))
10968d75effSDimitry Andric     return "pthread_cond_destroy";
11068d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_init"))
11168d75effSDimitry Andric     return "pthread_rwlock_init";
11268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_rdlock"))
11368d75effSDimitry Andric     return "pthread_rwlock_rdlock";
11468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_wrlock"))
11568d75effSDimitry Andric     return "pthread_rwlock_wrlock";
11668d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_tryrdlock"))
11768d75effSDimitry Andric     return "pthread_rwlock_tryrdlock";
11868d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_trywrlock"))
11968d75effSDimitry Andric     return "pthread_rwlock_trywrlock";
12068d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_unlock"))
12168d75effSDimitry Andric     return "pthread_rwlock_unlock";
12268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_rwlock_destroy"))
12368d75effSDimitry Andric     return "pthread_rwlock_destroy";
12468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_keycreate"))
12568d75effSDimitry Andric     return "pthread_key_create";
12668d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_setspecific"))
12768d75effSDimitry Andric     return "pthread_setspecific";
12868d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_getspecific"))
12968d75effSDimitry Andric     return "pthread_getspecific";
13068d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_keydelete"))
13168d75effSDimitry Andric     return "pthread_key_delete";
13268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_once"))
13368d75effSDimitry Andric     return "pthread_once";
13468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_self"))
13568d75effSDimitry Andric     return "pthread_self";
13668d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_exit"))
13768d75effSDimitry Andric     return "pthread_exit";
13868d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_setcancelstate"))
13968d75effSDimitry Andric     return "pthread_setcancelstate";
14068d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_equal"))
14168d75effSDimitry Andric     return "pthread_equal";
14268d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_curcpu"))
14368d75effSDimitry Andric     return "pthread_curcpu_np";
14468d75effSDimitry Andric   if (!internal_strcmp(function, "__libc_thr_sigsetmask"))
14568d75effSDimitry Andric     return "pthread_sigmask";
14668d75effSDimitry Andric #endif
14768d75effSDimitry Andric 
14868d75effSDimitry Andric   return function;
14968d75effSDimitry Andric }
15068d75effSDimitry Andric 
MaybeBuildIdToBuffer(const AddressInfo & info,bool PrefixSpace,InternalScopedString * buffer)1510eae32dcSDimitry Andric static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
1520eae32dcSDimitry Andric                                  InternalScopedString *buffer) {
1530eae32dcSDimitry Andric   if (info.uuid_size) {
1540eae32dcSDimitry Andric     if (PrefixSpace)
155*0fca6ea1SDimitry Andric       buffer->Append(" ");
156*0fca6ea1SDimitry Andric     buffer->Append("(BuildId: ");
1570eae32dcSDimitry Andric     for (uptr i = 0; i < info.uuid_size; ++i) {
1585f757f3fSDimitry Andric       buffer->AppendF("%02x", info.uuid[i]);
1590eae32dcSDimitry Andric     }
160*0fca6ea1SDimitry Andric     buffer->Append(")");
1610eae32dcSDimitry Andric   }
1620eae32dcSDimitry Andric }
1630eae32dcSDimitry Andric 
16468d75effSDimitry Andric static const char kDefaultFormat[] = "    #%n %p %F %L";
16568d75effSDimitry Andric 
RenderFrame(InternalScopedString * buffer,const char * format,int frame_no,uptr address,const AddressInfo * info,bool vs_style,const char * strip_path_prefix)1665f757f3fSDimitry Andric void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
1675f757f3fSDimitry Andric                                              const char *format, int frame_no,
1685f757f3fSDimitry Andric                                              uptr address,
1695f757f3fSDimitry Andric                                              const AddressInfo *info,
1705f757f3fSDimitry Andric                                              bool vs_style,
17106c3fb27SDimitry Andric                                              const char *strip_path_prefix) {
172e8d8bef9SDimitry Andric   // info will be null in the case where symbolization is not needed for the
173e8d8bef9SDimitry Andric   // given format. This ensures that the code below will get a hard failure
174e8d8bef9SDimitry Andric   // rather than print incorrect information in case RenderNeedsSymbolization
175e8d8bef9SDimitry Andric   // ever ends up out of sync with this function. If non-null, the addresses
176e8d8bef9SDimitry Andric   // should match.
177e8d8bef9SDimitry Andric   CHECK(!info || address == info->address);
17868d75effSDimitry Andric   if (0 == internal_strcmp(format, "DEFAULT"))
17968d75effSDimitry Andric     format = kDefaultFormat;
18068d75effSDimitry Andric   for (const char *p = format; *p != '\0'; p++) {
18168d75effSDimitry Andric     if (*p != '%') {
1825f757f3fSDimitry Andric       buffer->AppendF("%c", *p);
18368d75effSDimitry Andric       continue;
18468d75effSDimitry Andric     }
18568d75effSDimitry Andric     p++;
18668d75effSDimitry Andric     switch (*p) {
18768d75effSDimitry Andric     case '%':
1885f757f3fSDimitry Andric       buffer->Append("%");
18968d75effSDimitry Andric       break;
19068d75effSDimitry Andric     // Frame number and all fields of AddressInfo structure.
19168d75effSDimitry Andric     case 'n':
1925f757f3fSDimitry Andric       buffer->AppendF("%u", frame_no);
19368d75effSDimitry Andric       break;
19468d75effSDimitry Andric     case 'p':
195*0fca6ea1SDimitry Andric       buffer->AppendF("%p", (void *)address);
19668d75effSDimitry Andric       break;
19768d75effSDimitry Andric     case 'm':
1985f757f3fSDimitry Andric       buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix));
19968d75effSDimitry Andric       break;
20068d75effSDimitry Andric     case 'o':
2015f757f3fSDimitry Andric       buffer->AppendF("0x%zx", info->module_offset);
20268d75effSDimitry Andric       break;
2030eae32dcSDimitry Andric     case 'b':
2040eae32dcSDimitry Andric       MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer);
2050eae32dcSDimitry Andric       break;
20668d75effSDimitry Andric     case 'f':
2075f757f3fSDimitry Andric       buffer->AppendF("%s",
20806c3fb27SDimitry Andric                       DemangleFunctionName(StripFunctionName(info->function)));
20968d75effSDimitry Andric       break;
21068d75effSDimitry Andric     case 'q':
2115f757f3fSDimitry Andric       buffer->AppendF("0x%zx", info->function_offset != AddressInfo::kUnknown
212e8d8bef9SDimitry Andric                                    ? info->function_offset
21368d75effSDimitry Andric                                    : 0x0);
21468d75effSDimitry Andric       break;
21568d75effSDimitry Andric     case 's':
2165f757f3fSDimitry Andric       buffer->AppendF("%s", StripPathPrefix(info->file, strip_path_prefix));
21768d75effSDimitry Andric       break;
21868d75effSDimitry Andric     case 'l':
2195f757f3fSDimitry Andric       buffer->AppendF("%d", info->line);
22068d75effSDimitry Andric       break;
22168d75effSDimitry Andric     case 'c':
2225f757f3fSDimitry Andric       buffer->AppendF("%d", info->column);
22368d75effSDimitry Andric       break;
22468d75effSDimitry Andric     // Smarter special cases.
22568d75effSDimitry Andric     case 'F':
22668d75effSDimitry Andric       // Function name and offset, if file is unknown.
227e8d8bef9SDimitry Andric       if (info->function) {
2285f757f3fSDimitry Andric         buffer->AppendF(
2295f757f3fSDimitry Andric             "in %s", DemangleFunctionName(StripFunctionName(info->function)));
230e8d8bef9SDimitry Andric         if (!info->file && info->function_offset != AddressInfo::kUnknown)
2315f757f3fSDimitry Andric           buffer->AppendF("+0x%zx", info->function_offset);
23268d75effSDimitry Andric       }
23368d75effSDimitry Andric       break;
23468d75effSDimitry Andric     case 'S':
23568d75effSDimitry Andric       // File/line information.
236e8d8bef9SDimitry Andric       RenderSourceLocation(buffer, info->file, info->line, info->column,
237e8d8bef9SDimitry Andric                            vs_style, strip_path_prefix);
23868d75effSDimitry Andric       break;
23968d75effSDimitry Andric     case 'L':
24068d75effSDimitry Andric       // Source location, or module location.
241e8d8bef9SDimitry Andric       if (info->file) {
242e8d8bef9SDimitry Andric         RenderSourceLocation(buffer, info->file, info->line, info->column,
24368d75effSDimitry Andric                              vs_style, strip_path_prefix);
244e8d8bef9SDimitry Andric       } else if (info->module) {
245e8d8bef9SDimitry Andric         RenderModuleLocation(buffer, info->module, info->module_offset,
246e8d8bef9SDimitry Andric                              info->module_arch, strip_path_prefix);
2470eae32dcSDimitry Andric 
24806c3fb27SDimitry Andric #if !SANITIZER_APPLE
2490eae32dcSDimitry Andric         MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
25006c3fb27SDimitry Andric #endif
25168d75effSDimitry Andric       } else {
252*0fca6ea1SDimitry Andric         buffer->Append("(<unknown module>)");
25368d75effSDimitry Andric       }
25468d75effSDimitry Andric       break;
25568d75effSDimitry Andric     case 'M':
25668d75effSDimitry Andric       // Module basename and offset, or PC.
257e8d8bef9SDimitry Andric       if (address & kExternalPCBit) {
258e8d8bef9SDimitry Andric         // There PCs are not meaningful.
259e8d8bef9SDimitry Andric       } else if (info->module) {
26068d75effSDimitry Andric         // Always strip the module name for %M.
261e8d8bef9SDimitry Andric         RenderModuleLocation(buffer, StripModuleName(info->module),
262e8d8bef9SDimitry Andric                              info->module_offset, info->module_arch, "");
26306c3fb27SDimitry Andric #if !SANITIZER_APPLE
2640eae32dcSDimitry Andric         MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
26506c3fb27SDimitry Andric #endif
266e8d8bef9SDimitry Andric       } else {
2675f757f3fSDimitry Andric         buffer->AppendF("(%p)", (void *)address);
268e8d8bef9SDimitry Andric       }
26968d75effSDimitry Andric       break;
27068d75effSDimitry Andric     default:
271349cc55cSDimitry Andric       Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
272*0fca6ea1SDimitry Andric              (const void *)p);
27368d75effSDimitry Andric       Die();
27468d75effSDimitry Andric     }
27568d75effSDimitry Andric   }
27668d75effSDimitry Andric }
27768d75effSDimitry Andric 
RenderNeedsSymbolization(const char * format)2785f757f3fSDimitry Andric bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) {
279e8d8bef9SDimitry Andric   if (0 == internal_strcmp(format, "DEFAULT"))
280e8d8bef9SDimitry Andric     format = kDefaultFormat;
281e8d8bef9SDimitry Andric   for (const char *p = format; *p != '\0'; p++) {
282e8d8bef9SDimitry Andric     if (*p != '%')
283e8d8bef9SDimitry Andric       continue;
284e8d8bef9SDimitry Andric     p++;
285e8d8bef9SDimitry Andric     switch (*p) {
286e8d8bef9SDimitry Andric       case '%':
287e8d8bef9SDimitry Andric         break;
288e8d8bef9SDimitry Andric       case 'n':
289e8d8bef9SDimitry Andric         // frame_no
290e8d8bef9SDimitry Andric         break;
291e8d8bef9SDimitry Andric       case 'p':
292e8d8bef9SDimitry Andric         // address
293e8d8bef9SDimitry Andric         break;
294e8d8bef9SDimitry Andric       default:
295e8d8bef9SDimitry Andric         return true;
296e8d8bef9SDimitry Andric     }
297e8d8bef9SDimitry Andric   }
298e8d8bef9SDimitry Andric   return false;
299e8d8bef9SDimitry Andric }
300e8d8bef9SDimitry Andric 
RenderData(InternalScopedString * buffer,const char * format,const DataInfo * DI,const char * strip_path_prefix)3015f757f3fSDimitry Andric void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer,
3025f757f3fSDimitry Andric                                             const char *format,
3035f757f3fSDimitry Andric                                             const DataInfo *DI,
3045f757f3fSDimitry Andric                                             const char *strip_path_prefix) {
30568d75effSDimitry Andric   for (const char *p = format; *p != '\0'; p++) {
30668d75effSDimitry Andric     if (*p != '%') {
3075f757f3fSDimitry Andric       buffer->AppendF("%c", *p);
30868d75effSDimitry Andric       continue;
30968d75effSDimitry Andric     }
31068d75effSDimitry Andric     p++;
31168d75effSDimitry Andric     switch (*p) {
31268d75effSDimitry Andric       case '%':
3135f757f3fSDimitry Andric         buffer->Append("%");
31468d75effSDimitry Andric         break;
31568d75effSDimitry Andric       case 's':
3165f757f3fSDimitry Andric         buffer->AppendF("%s", StripPathPrefix(DI->file, strip_path_prefix));
31768d75effSDimitry Andric         break;
31868d75effSDimitry Andric       case 'l':
3195f757f3fSDimitry Andric         buffer->AppendF("%zu", DI->line);
32068d75effSDimitry Andric         break;
32168d75effSDimitry Andric       case 'g':
3225f757f3fSDimitry Andric         buffer->AppendF("%s", DI->name);
32368d75effSDimitry Andric         break;
32468d75effSDimitry Andric       default:
325349cc55cSDimitry Andric         Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
326*0fca6ea1SDimitry Andric                (const void *)p);
32768d75effSDimitry Andric         Die();
32868d75effSDimitry Andric     }
32968d75effSDimitry Andric   }
33068d75effSDimitry Andric }
33168d75effSDimitry Andric 
33268d75effSDimitry Andric #endif  // !SANITIZER_SYMBOLIZER_MARKUP
33368d75effSDimitry Andric 
RenderSourceLocation(InternalScopedString * buffer,const char * file,int line,int column,bool vs_style,const char * strip_path_prefix)3345f757f3fSDimitry Andric void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer,
3355f757f3fSDimitry Andric                                              const char *file, int line,
3365f757f3fSDimitry Andric                                              int column, bool vs_style,
33768d75effSDimitry Andric                                              const char *strip_path_prefix) {
33868d75effSDimitry Andric   if (vs_style && line > 0) {
3395f757f3fSDimitry Andric     buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
34068d75effSDimitry Andric     if (column > 0)
3415f757f3fSDimitry Andric       buffer->AppendF(",%d", column);
342*0fca6ea1SDimitry Andric     buffer->Append(")");
34368d75effSDimitry Andric     return;
34468d75effSDimitry Andric   }
34568d75effSDimitry Andric 
3465f757f3fSDimitry Andric   buffer->AppendF("%s", StripPathPrefix(file, strip_path_prefix));
34768d75effSDimitry Andric   if (line > 0) {
3485f757f3fSDimitry Andric     buffer->AppendF(":%d", line);
34968d75effSDimitry Andric     if (column > 0)
3505f757f3fSDimitry Andric       buffer->AppendF(":%d", column);
35168d75effSDimitry Andric   }
35268d75effSDimitry Andric }
35368d75effSDimitry Andric 
RenderModuleLocation(InternalScopedString * buffer,const char * module,uptr offset,ModuleArch arch,const char * strip_path_prefix)3545f757f3fSDimitry Andric void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer,
3555f757f3fSDimitry Andric                                              const char *module, uptr offset,
3565f757f3fSDimitry Andric                                              ModuleArch arch,
35768d75effSDimitry Andric                                              const char *strip_path_prefix) {
3585f757f3fSDimitry Andric   buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix));
35968d75effSDimitry Andric   if (arch != kModuleArchUnknown) {
3605f757f3fSDimitry Andric     buffer->AppendF(":%s", ModuleArchToString(arch));
36168d75effSDimitry Andric   }
3625f757f3fSDimitry Andric   buffer->AppendF("+0x%zx)", offset);
36368d75effSDimitry Andric }
36468d75effSDimitry Andric 
36568d75effSDimitry Andric } // namespace __sanitizer
366