diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h index 7fb7928dce0d8..82cd9bc227916 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h @@ -154,6 +154,8 @@ class Symbolizer final { void InvalidateModuleList(); + const ListOfModules &GetRefreshedListOfModules(); + private: // GetModuleNameAndOffsetForPC has to return a string to the caller. // Since the corresponding module might get unloaded later, we should create diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp index 81141023386ea..74458028ae8f5 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp @@ -191,6 +191,13 @@ void Symbolizer::RefreshModules() { modules_fresh_ = true; } +const ListOfModules &Symbolizer::GetRefreshedListOfModules() { + if (!modules_fresh_) + RefreshModules(); + + return modules_; +} + static const LoadedModule *SearchForModule(const ListOfModules &modules, uptr address) { for (uptr i = 0; i < modules.size(); i++) { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.cpp index 1627908185f20..b2a1069a9a61c 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.cpp @@ -19,7 +19,6 @@ #include "sanitizer_symbolizer_markup.h" #include "sanitizer_common.h" -#include "sanitizer_stacktrace_printer.h" #include "sanitizer_symbolizer.h" #include "sanitizer_symbolizer_markup_constants.h" @@ -28,6 +27,7 @@ namespace __sanitizer { void MarkupStackTracePrinter::RenderData(InternalScopedString *buffer, const char *format, const DataInfo *DI, const char *strip_path_prefix) { + RenderContext(buffer); buffer->AppendF(kFormatData, DI->start); } @@ -42,6 +42,7 @@ void MarkupStackTracePrinter::RenderFrame(InternalScopedString *buffer, bool vs_style, const char *strip_path_prefix) { CHECK(!RenderNeedsSymbolization(format)); + RenderContext(buffer); buffer->AppendF(kFormatFrame, frame_no, address); } @@ -64,4 +65,96 @@ const char *MarkupSymbolizerTool::Demangle(const char *name) { return buffer; } +// Fuchsia's implementation of symbolizer markup doesn't need to emit contextual +// elements at this point. +// Fuchsia's logging infrastructure emits enough information about +// process memory layout that a post-processing filter can do the +// symbolization and pretty-print the markup. +#if !SANITIZER_FUCHSIA + +static bool ModulesEq(const LoadedModule &module, + const RenderedModule &renderedModule) { + return module.base_address() == renderedModule.base_address && + internal_memcmp(module.uuid(), renderedModule.uuid, + module.uuid_size()) == 0 && + internal_strcmp(module.full_name(), renderedModule.full_name) == 0; +} + +static bool ModuleHasBeenRendered( + const LoadedModule &module, + const InternalMmapVectorNoCtor &renderedModules) { + for (const auto &renderedModule : renderedModules) + if (ModulesEq(module, renderedModule)) + return true; + + return false; +} + +static void RenderModule(InternalScopedString *buffer, + const LoadedModule &module, uptr moduleId) { + InternalScopedString buildIdBuffer; + for (uptr i = 0; i < module.uuid_size(); i++) + buildIdBuffer.AppendF("%02x", module.uuid()[i]); + + buffer->AppendF(kFormatModule, moduleId, module.full_name(), + buildIdBuffer.data()); + buffer->Append("\n"); +} + +static void RenderMmaps(InternalScopedString *buffer, + const LoadedModule &module, uptr moduleId) { + InternalScopedString accessBuffer; + + // All module mmaps are readable at least + for (const auto &range : module.ranges()) { + accessBuffer.Append("r"); + if (range.writable) + accessBuffer.Append("w"); + if (range.executable) + accessBuffer.Append("x"); + + //{{{mmap:%starting_addr:%size_in_hex:load:%moduleId:r%(w|x):%relative_addr}}} + + // module.base_address == dlpi_addr + // range.beg == dlpi_addr + p_vaddr + // relative address == p_vaddr == range.beg - module.base_address + buffer->AppendF(kFormatMmap, range.beg, range.end - range.beg, moduleId, + accessBuffer.data(), range.beg - module.base_address()); + + buffer->Append("\n"); + accessBuffer.clear(); + } +} + +void MarkupStackTracePrinter::RenderContext(InternalScopedString *buffer) { + if (renderedModules_.size() == 0) + buffer->Append("{{{reset}}}\n"); + + const auto &modules = Symbolizer::GetOrInit()->GetRefreshedListOfModules(); + + for (const auto &module : modules) { + if (ModuleHasBeenRendered(module, renderedModules_)) + continue; + + // symbolizer markup id, used to refer to this modules from other contextual + // elements + uptr moduleId = renderedModules_.size(); + + RenderModule(buffer, module, moduleId); + RenderMmaps(buffer, module, moduleId); + + renderedModules_.push_back({ + internal_strdup(module.full_name()), + module.base_address(), + {}, + }); + + // kModuleUUIDSize is the size of curModule.uuid + CHECK_GE(kModuleUUIDSize, module.uuid_size()); + internal_memcpy(renderedModules_.back().uuid, module.uuid(), + module.uuid_size()); + } +} +#endif // !SANITIZER_FUCHSIA + } // namespace __sanitizer diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.h b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.h index 07630d0b3bdf4..bc2ab24d625bb 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup.h @@ -20,6 +20,14 @@ namespace __sanitizer { +// Simplier view of a LoadedModule. It only holds information necessary to +// identify unique modules. +struct RenderedModule { + char *full_name; + uptr base_address; + u8 uuid[kModuleUUIDSize]; // BuildId +}; + class MarkupStackTracePrinter : public StackTracePrinter { public: // We don't support the stack_trace_format flag at all. @@ -35,6 +43,9 @@ class MarkupStackTracePrinter : public StackTracePrinter { const char *strip_path_prefix = "") override; private: + // Keeps track of the modules that have been rendered to avoid re-rendering + // them + InternalMmapVector renderedModules_; void RenderContext(InternalScopedString *buffer); protected: diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_constants.h b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_constants.h index 0f96ca78600a5..83643504e1289 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_constants.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_constants.h @@ -35,6 +35,12 @@ constexpr const char *kFormatData = "{{{data:%p}}}"; // One frame in a backtrace (printed on a line by itself). constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}"; +// Module contextual element. +constexpr const char *kFormatModule = "{{{module:%d:%s:elf:%s}}}"; + +// mmap for a module segment. +constexpr const char *kFormatMmap = "{{{mmap:%p:0x%x:load:%d:%s:0x%x}}}"; + // Dump trigger element. #define FORMAT_DUMPFILE "{{{dumpfile:%s:%s}}}" diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp index f6c49aa59d7b5..08b06c2faf30d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_markup_fuchsia.cpp @@ -72,6 +72,8 @@ StackTracePrinter *StackTracePrinter::NewStackTracePrinter() { return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter(); } +void MarkupStackTracePrinter::RenderContext(InternalScopedString *) {} + Symbolizer *Symbolizer::PlatformInit() { return new (symbolizer_allocator_) Symbolizer({}); } diff --git a/compiler-rt/test/sanitizer_common/TestCases/print-stack-trace-markup.cpp b/compiler-rt/test/sanitizer_common/TestCases/print-stack-trace-markup.cpp new file mode 100644 index 0000000000000..15d89d091a99e --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/print-stack-trace-markup.cpp @@ -0,0 +1,38 @@ +// RUN: %clangxx %s -o %t +// RUN: %env_tool_opts=enable_symbolizer_markup=1 %run %t 2>&1 | FileCheck %s + +// REQUIRES: linux +#include + +void Bar() { __sanitizer_print_stack_trace(); } + +void Foo() { + Bar(); + return; +} + +void Baz() { __sanitizer_print_stack_trace(); } + +int main() { + Foo(); + Baz(); + return 0; +} + +// COM: For element syntax see: https://llvm.org/docs/SymbolizerMarkupFormat.html +// COM: OPEN is {{{ and CLOSE is }}} + +// CHECK: [[OPEN:{{{]]reset[[CLOSE:}}}]] +// CHECK: [[OPEN]]module:[[MOD_ID:[0-9]+]]:{{.+}}:elf:{{[0-9a-fA-F]+}}[[CLOSE]] +// CHECK: [[OPEN]]mmap:{{0x[0-9a-fA-F]+:0x[0-9a-fA-F]+}}:load:[[MOD_ID]]:{{r[wx]{0,2}:0x[0-9a-fA-F]+}}[[CLOSE]] +// CHECK: [[OPEN]]bt:0:0x{{[0-9a-fA-F]+}}[[CLOSE]] +// CHECK-NEXT: [[OPEN]]bt:1:0x{{[0-9a-fA-F]+}}[[CLOSE]] +// CHECK-NEXT: [[OPEN]]bt:2:0x{{[0-9a-fA-F]+}}[[CLOSE]] + +// COM: Emitting a second backtrace should not emit contextual elements in this case. +// CHECK-NOT: [[OPEN:{{{]]reset[[CLOSE:}}}]] +// CHECK-NOT: [[OPEN]]module:[[MOD_ID:[0-9]+]]:{{.+}}:elf:{{[0-9a-fA-F]+}}[[CLOSE]] +// CHECK-NOT: [[OPEN]]mmap:{{0x[0-9a-fA-F]+:0x[0-9a-fA-F]+}}:load:[[MOD_ID]]:{{r[wx]{0,2}:0x[0-9a-fA-F]+}}[[CLOSE]] + +// CHECK: [[OPEN]]bt:0:0x{{[0-9a-fA-F]+}}[[CLOSE]] +// CHECK-NEXT: [[OPEN]]bt:1:0x{{[0-9a-fA-F]+}}[[CLOSE]]