Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -22,6 +22,7 @@ sanitizer_procmaps_mac.cc sanitizer_stackdepot.cc sanitizer_stacktrace.cc + sanitizer_stacktrace_printer.cc sanitizer_suppressions.cc sanitizer_symbolizer.cc sanitizer_symbolizer_libbacktrace.cc @@ -82,6 +83,7 @@ sanitizer_stackdepot.h sanitizer_stackdepotbase.h sanitizer_stacktrace.h + sanitizer_stacktrace_printer.h sanitizer_stoptheworld.h sanitizer_suppressions.h sanitizer_symbolizer.h Index: lib/sanitizer_common/sanitizer_common.h =================================================================== --- lib/sanitizer_common/sanitizer_common.h +++ lib/sanitizer_common/sanitizer_common.h @@ -174,10 +174,6 @@ const char *strip_file_prefix); // Strip the directories from the module name. const char *StripModuleName(const char *module); -void PrintSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column); -void PrintModuleAndOffset(InternalScopedString *buffer, - const char *module, uptr offset); // OS void DisableCoreDumperIfNecessary(); Index: lib/sanitizer_common/sanitizer_common.cc =================================================================== --- lib/sanitizer_common/sanitizer_common.cc +++ lib/sanitizer_common/sanitizer_common.cc @@ -163,25 +163,6 @@ return module; } -void PrintSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column) { - CHECK(file); - buffer->append("%s", - StripPathPrefix(file, common_flags()->strip_path_prefix)); - if (line > 0) { - buffer->append(":%d", line); - if (column > 0) - buffer->append(":%d", column); - } -} - -void PrintModuleAndOffset(InternalScopedString *buffer, const char *module, - uptr offset) { - buffer->append("(%s+0x%zx)", - StripPathPrefix(module, common_flags()->strip_path_prefix), - offset); -} - void ReportErrorSummary(const char *error_message) { if (!common_flags()->print_summary) return; Index: lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -13,15 +13,11 @@ #include "sanitizer_common.h" #include "sanitizer_stacktrace.h" +#include "sanitizer_stacktrace_printer.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num, - uptr pc) { - buffer->append(" #%zu 0x%zx", frame_num, pc); -} - void StackTrace::Print() const { if (trace == nullptr || size == 0) { Printf(" \n\n"); @@ -37,29 +33,14 @@ uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC( pc, addr_frames.data(), addr_frames.size()); if (addr_frames_num == 0) { - frame_desc.clear(); - PrintStackFramePrefix(&frame_desc, frame_num++, pc); - frame_desc.append(" ()"); - Printf("%s\n", frame_desc.data()); - continue; + addr_frames[0].address = pc; + addr_frames_num = 1; } for (uptr j = 0; j < addr_frames_num; j++) { AddressInfo &info = addr_frames[j]; frame_desc.clear(); - PrintStackFramePrefix(&frame_desc, frame_num++, pc); - if (info.function) { - frame_desc.append(" in %s", info.function); - // Print offset in function if we don't know the source file. - if (!info.file && info.function_offset != AddressInfo::kUnknown) - frame_desc.append("+0x%zx", info.function_offset); - } - if (info.file) { - frame_desc.append(" "); - PrintSourceLocation(&frame_desc, info.file, info.line, info.column); - } else if (info.module) { - frame_desc.append(" "); - PrintModuleAndOffset(&frame_desc, info.module, info.module_offset); - } + RenderFrame(&frame_desc, "DEFAULT", frame_num++, info, + common_flags()->strip_path_prefix); Printf("%s\n", frame_desc.data()); info.Clear(); } Index: lib/sanitizer_common/sanitizer_stacktrace_printer.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_stacktrace_printer.h @@ -0,0 +1,62 @@ +//===-- sanitizer_stacktrace_printer.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STACKTRACE_PRINTER_H +#define SANITIZER_STACKTRACE_PRINTER_H + +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +// Render the contents of "info" structure, which represents the contents of +// stack frame "frame_no" and appends it to the "buffer". "format" is a +// string with placeholders, which is copied to the output with +// placeholders substituted with the contents of "info". For example, +// format string +// " frame %n: function %F at %S" +// will be turned into +// " frame 10: function foo::bar() at my/file.cc:10" +// You may additionally pass "strip_path_prefix" to strip prefixes of paths to +// source files and modules, and "strip_func_prefix" to strip prefixes of +// function names. +// Here's the full list of available placeholders: +// %% - represents a '%' character; +// %n - frame number (copy of frame_no); +// %p - PC in hex format; +// %m - path to module (binary or shared object); +// %o - offset in the module in hex format; +// %f - function name; +// %q - offset in the function in hex format (*if available*); +// %s - path to source file; +// %l - line in the source file; +// %c - column in the source file; +// %F - if function is known to be , prints "in ", possibly +// followed by the offset in this function, but only if source file +// is unknown; +// %S - prints file/line/column information; +// %L - prints location information: file/line/column, if it is known, or +// module+offset if it is known, or () string. +// %M - prints module basename and offset, if it is known, or PC. +void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, + const AddressInfo &info, const char *strip_path_prefix = "", + const char *strip_func_prefix = ""); + +void RenderSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column, const char *strip_path_prefix); + +void RenderModuleLocation(InternalScopedString *buffer, const char *module, + uptr offset, const char *strip_path_prefix); + +} // namespace __sanitizer + +#endif // SANITIZER_STACKTRACE_PRINTER_H Index: lib/sanitizer_common/sanitizer_stacktrace_printer.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -0,0 +1,132 @@ +//===-- sanitizer_common.cc -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// +#include "sanitizer_stacktrace_printer.h" + +namespace __sanitizer { + +static const char *StripFunctionName(const char *function, const char *prefix) { + if (function == 0) return 0; + if (prefix == 0) return function; + uptr prefix_len = internal_strlen(prefix); + if (0 == internal_strncmp(function, prefix, prefix_len)) + return function + prefix_len; + return function; +} + +const char kDefaultFormat[] = " #%n %p %F %L"; + +void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, + const AddressInfo &info, const char *strip_path_prefix, + const char *strip_func_prefix) { + if (0 == internal_strcmp(format, "DEFAULT")) + format = kDefaultFormat; + for (const char *p = format; *p != '\0'; p++) { + if (*p != '%') { + buffer->append("%c", *p); + continue; + } + p++; + switch (*p) { + case '%': + buffer->append("%%"); + break; + // Frame number and all fields of AddressInfo structure. + case 'n': + buffer->append("%zu", frame_no); + break; + case 'p': + buffer->append("0x%zx", info.address); + break; + case 'm': + buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix)); + break; + case 'o': + buffer->append("0x%zx", info.module_offset); + break; + case 'f': + buffer->append("%s", StripFunctionName(info.function, strip_func_prefix)); + break; + case 'q': + buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown + ? info.function_offset + : 0x0); + break; + case 's': + buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix)); + break; + case 'l': + buffer->append("%d", info.line); + break; + case 'c': + buffer->append("%d", info.column); + break; + // Smarter special cases. + case 'F': + // Function name and offset, if file is unknown. + if (info.function) { + buffer->append("in %s", + StripFunctionName(info.function, strip_func_prefix)); + if (!info.file && info.function_offset != AddressInfo::kUnknown) + buffer->append("+0x%zx", info.function_offset); + } + break; + case 'S': + // File/line information. + RenderSourceLocation(buffer, info.file, info.line, info.column, + strip_path_prefix); + break; + case 'L': + // Source location, or module location. + if (info.file) { + RenderSourceLocation(buffer, info.file, info.line, info.column, + strip_path_prefix); + } else if (info.module) { + RenderModuleLocation(buffer, info.module, info.module_offset, + strip_path_prefix); + } else { + buffer->append("()"); + } + break; + case 'M': + // Module basename and offset, or PC. + if (info.module) + buffer->append("(%s+%p)", StripModuleName(info.module), + (void *)info.module_offset); + else + buffer->append("(%p)", (void *)info.address); + break; + default: + Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", + *p, *p); + Die(); + } + } +} + +void RenderSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column, const char *strip_path_prefix) { + buffer->append("%s", StripPathPrefix(file, strip_path_prefix)); + if (line > 0) { + buffer->append(":%d", line); + if (column > 0) + buffer->append(":%d", column); + } +} + +void RenderModuleLocation(InternalScopedString *buffer, const char *module, + uptr offset, const char *strip_path_prefix) { + buffer->append("(%s+0x%zx)", StripPathPrefix(module, strip_path_prefix), + offset); +} + +} // namespace __sanitizer Index: lib/sanitizer_common/tests/CMakeLists.txt =================================================================== --- lib/sanitizer_common/tests/CMakeLists.txt +++ lib/sanitizer_common/tests/CMakeLists.txt @@ -21,6 +21,7 @@ sanitizer_printf_test.cc sanitizer_procmaps_test.cc sanitizer_stackdepot_test.cc + sanitizer_stacktrace_printer_test.cc sanitizer_stacktrace_test.cc sanitizer_stoptheworld_test.cc sanitizer_suppressions_test.cc Index: lib/sanitizer_common/tests/sanitizer_common_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_common_test.cc +++ lib/sanitizer_common/tests/sanitizer_common_test.cc @@ -226,40 +226,4 @@ EXPECT_STREQ("012345678", str.data()); } -TEST(SanitizerCommon, PrintSourceLocation) { - InternalScopedString str(128); - PrintSourceLocation(&str, "/dir/file.cc", 10, 5); - EXPECT_STREQ("/dir/file.cc:10:5", str.data()); - - str.clear(); - PrintSourceLocation(&str, "/dir/file.cc", 11, 0); - EXPECT_STREQ("/dir/file.cc:11", str.data()); - - str.clear(); - PrintSourceLocation(&str, "/dir/file.cc", 0, 0); - EXPECT_STREQ("/dir/file.cc", str.data()); - - // Check that we strip file prefix if necessary. - const char *old_strip_path_prefix = common_flags()->strip_path_prefix; - common_flags()->strip_path_prefix = "/dir/"; - str.clear(); - PrintSourceLocation(&str, "/dir/file.cc", 10, 5); - EXPECT_STREQ("file.cc:10:5", str.data()); - common_flags()->strip_path_prefix = old_strip_path_prefix; -} - -TEST(SanitizerCommon, PrintModuleAndOffset) { - InternalScopedString str(128); - PrintModuleAndOffset(&str, "/dir/exe", 0x123); - EXPECT_STREQ("(/dir/exe+0x123)", str.data()); - - // Check that we strip file prefix if necessary. - const char *old_strip_path_prefix = common_flags()->strip_path_prefix; - common_flags()->strip_path_prefix = "/dir/"; - str.clear(); - PrintModuleAndOffset(&str, "/dir/exe", 0x123); - EXPECT_STREQ("(exe+0x123)", str.data()); - common_flags()->strip_path_prefix = old_strip_path_prefix; -} - } // namespace __sanitizer Index: lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc @@ -0,0 +1,122 @@ +//===-- sanitizer_common_printer_test.cc ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_stacktrace_printer.h" + +#include "gtest/gtest.h" + +namespace __sanitizer { + +TEST(SanitizerStacktracePrinter, RenderSourceLocation) { + InternalScopedString str(128); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, ""); + EXPECT_STREQ("/dir/file.cc:10:5", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 11, 0, ""); + EXPECT_STREQ("/dir/file.cc:11", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 0, 0, ""); + EXPECT_STREQ("/dir/file.cc", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "/dir/"); + EXPECT_STREQ("file.cc:10:5", str.data()); +} + +TEST(SanitizerStacktracePrinter, RenderModuleLocation) { + InternalScopedString str(128); + RenderModuleLocation(&str, "/dir/exe", 0x123, ""); + EXPECT_STREQ("(/dir/exe+0x123)", str.data()); + + // Check that we strip file prefix if necessary. + str.clear(); + RenderModuleLocation(&str, "/dir/exe", 0x123, "/dir/"); + EXPECT_STREQ("(exe+0x123)", str.data()); +} + +TEST(SanitizerStacktracePrinter, RenderFrame) { + int frame_no = 42; + AddressInfo info; + info.address = 0x400000; + info.module = internal_strdup("/path/to/my/module"); + info.module_offset = 0x200; + info.function = internal_strdup("function_foo"); + info.function_offset = 0x100; + info.file = internal_strdup("/path/to/my/source"); + info.line = 10; + info.column = 5; + InternalScopedString str(256); + + // Dump all the AddressInfo fields. + RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o " + "Function:%f FunctionOffset:%q Source:%s Line:%l " + "Column:%c", + frame_no, info, "/path/to/", "function_"); + EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 " + "Function:foo FunctionOffset:0x100 Source:my/source Line:10 " + "Column:5", + str.data()); + info.Clear(); + str.clear(); + + // Test special format specifiers. + info.address = 0x400000; + RenderFrame(&str, "%M", frame_no, info); + EXPECT_NE(nullptr, internal_strstr(str.data(), "400000")); + str.clear(); + + RenderFrame(&str, "%L", frame_no, info); + EXPECT_STREQ("()", str.data()); + str.clear(); + + info.module = internal_strdup("/path/to/module"); + info.module_offset = 0x200; + RenderFrame(&str, "%M", frame_no, info); + EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x")); + EXPECT_NE(nullptr, internal_strstr(str.data(), "200")); + str.clear(); + + RenderFrame(&str, "%L", frame_no, info); + EXPECT_STREQ("(/path/to/module+0x200)", str.data()); + str.clear(); + + info.function = internal_strdup("my_function"); + RenderFrame(&str, "%F", frame_no, info); + EXPECT_STREQ("in my_function", str.data()); + str.clear(); + + info.function_offset = 0x100; + RenderFrame(&str, "%F %S", frame_no, info); + EXPECT_STREQ("in my_function+0x100 ", str.data()); + str.clear(); + + info.file = internal_strdup("my_file"); + RenderFrame(&str, "%F %S", frame_no, info); + EXPECT_STREQ("in my_function my_file", str.data()); + str.clear(); + + info.line = 10; + RenderFrame(&str, "%F %S", frame_no, info); + EXPECT_STREQ("in my_function my_file:10", str.data()); + str.clear(); + + info.column = 5; + RenderFrame(&str, "%S %L", frame_no, info); + EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data()); + str.clear(); + + info.Clear(); +} + +} // namespace __sanitizer Index: lib/tsan/rtl/tsan_report.cc =================================================================== --- lib/tsan/rtl/tsan_report.cc +++ lib/tsan/rtl/tsan_report.cc @@ -15,6 +15,7 @@ #include "tsan_rtl.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stacktrace_printer.h" namespace __tsan { @@ -112,34 +113,16 @@ return ""; } -static const char *StripFunctionName(const char *function, const char *prefix) { - if (function == 0) return 0; - if (prefix == 0) return function; - uptr prefix_len = internal_strlen(prefix); - if (0 == internal_strncmp(function, prefix, prefix_len)) - return function + prefix_len; - return function; -} - void PrintStack(const ReportStack *ent) { if (ent == 0) { Printf(" [failed to restore the stack]\n\n"); return; } for (int i = 0; ent; ent = ent->next, i++) { - const AddressInfo &info = ent->info; - Printf(" #%d %s %s:%d", i, - StripFunctionName(info.function, "__interceptor_"), - StripPathPrefix(info.file, common_flags()->strip_path_prefix), - info.line); - if (info.column) - Printf(":%d", info.column); - if (info.module && info.module_offset) { - Printf(" (%s+%p)\n", StripModuleName(info.module), - (void *)info.module_offset); - } else { - Printf(" (%p)\n", (void *)info.address); - } + InternalScopedString res(2 * GetPageSizeCached()); + RenderFrame(&res, " #%n %f %S %M", i, ent->info, + common_flags()->strip_path_prefix, "__interceptor_"); + Printf("%s\n", res.data()); } Printf("\n"); } Index: lib/ubsan/ubsan_diag.cc =================================================================== --- lib/ubsan/ubsan_diag.cc +++ lib/ubsan/ubsan_diag.cc @@ -16,6 +16,7 @@ #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stacktrace_printer.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include @@ -129,14 +130,16 @@ if (SLoc.isInvalid()) LocBuffer.append(""); else - PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), - SLoc.getColumn()); + RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), + SLoc.getColumn(), common_flags()->strip_path_prefix); break; } - case Location::LK_Module: - PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(), - Loc.getModuleLocation().getOffset()); + case Location::LK_Module: { + ModuleLocation MLoc = Loc.getModuleLocation(); + RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(), + common_flags()->strip_path_prefix); break; + } case Location::LK_Memory: LocBuffer.append("%p", Loc.getMemoryLocation()); break;