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