| 1 | //===-- sanitizer_symbolizer_markup.cpp -----------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is shared between various sanitizers' runtime libraries. |
| 10 | // |
| 11 | // This generic support for offline symbolizing is based on the |
| 12 | // Fuchsia port. We don't do any actual symbolization per se. |
| 13 | // Instead, we emit text containing raw addresses and raw linkage |
| 14 | // symbol names, embedded in Fuchsia's symbolization markup format. |
| 15 | // See the spec at: |
| 16 | // https://llvm.org/docs/SymbolizerMarkupFormat.html |
| 17 | //===----------------------------------------------------------------------===// |
| 18 | |
| 19 | #include "sanitizer_symbolizer_markup.h" |
| 20 | |
| 21 | #include "sanitizer_common.h" |
| 22 | #include "sanitizer_symbolizer.h" |
| 23 | #include "sanitizer_symbolizer_markup_constants.h" |
| 24 | |
| 25 | namespace __sanitizer { |
| 26 | |
| 27 | void MarkupStackTracePrinter::RenderData(InternalScopedString *buffer, |
| 28 | const char *format, const DataInfo *DI, |
| 29 | const char *strip_path_prefix) { |
| 30 | RenderContext(buffer); |
| 31 | buffer->AppendF(format: kFormatData, reinterpret_cast<void *>(DI->start)); |
| 32 | } |
| 33 | |
| 34 | bool MarkupStackTracePrinter::RenderNeedsSymbolization(const char *format) { |
| 35 | return false; |
| 36 | } |
| 37 | |
| 38 | // We don't support the stack_trace_format flag at all. |
| 39 | void MarkupStackTracePrinter::RenderFrame(InternalScopedString *buffer, |
| 40 | const char *format, int frame_no, |
| 41 | uptr address, const AddressInfo *info, |
| 42 | bool vs_style, |
| 43 | const char *strip_path_prefix) { |
| 44 | CHECK(!RenderNeedsSymbolization(format)); |
| 45 | RenderContext(buffer); |
| 46 | buffer->AppendF(format: kFormatFrame, frame_no, reinterpret_cast<void *>(address)); |
| 47 | } |
| 48 | |
| 49 | bool MarkupSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *stack) { |
| 50 | char buffer[kFormatFunctionMax]; |
| 51 | internal_snprintf(buffer, length: sizeof(buffer), format: kFormatFunction, |
| 52 | reinterpret_cast<void *>(addr)); |
| 53 | stack->info.function = internal_strdup(s: buffer); |
| 54 | return true; |
| 55 | } |
| 56 | |
| 57 | bool MarkupSymbolizerTool::SymbolizeData(uptr addr, DataInfo *info) { |
| 58 | info->Clear(); |
| 59 | info->start = addr; |
| 60 | return true; |
| 61 | } |
| 62 | |
| 63 | const char *MarkupSymbolizerTool::Demangle(const char *name) { |
| 64 | static char buffer[kFormatDemangleMax]; |
| 65 | internal_snprintf(buffer, length: sizeof(buffer), format: kFormatDemangle, name); |
| 66 | return buffer; |
| 67 | } |
| 68 | |
| 69 | // Fuchsia's implementation of symbolizer markup doesn't need to emit contextual |
| 70 | // elements at this point. |
| 71 | // Fuchsia's logging infrastructure emits enough information about |
| 72 | // process memory layout that a post-processing filter can do the |
| 73 | // symbolization and pretty-print the markup. |
| 74 | #if !SANITIZER_FUCHSIA |
| 75 | |
| 76 | static bool ModulesEq(const LoadedModule &module, |
| 77 | const RenderedModule &renderedModule) { |
| 78 | return module.base_address() == renderedModule.base_address && |
| 79 | internal_memcmp(s1: module.uuid(), s2: renderedModule.uuid, |
| 80 | n: module.uuid_size()) == 0 && |
| 81 | internal_strcmp(s1: module.full_name(), s2: renderedModule.full_name) == 0; |
| 82 | } |
| 83 | |
| 84 | static bool ModuleHasBeenRendered( |
| 85 | const LoadedModule &module, |
| 86 | const InternalMmapVectorNoCtor<RenderedModule> &renderedModules) { |
| 87 | for (const auto &renderedModule : renderedModules) |
| 88 | if (ModulesEq(module, renderedModule)) |
| 89 | return true; |
| 90 | |
| 91 | return false; |
| 92 | } |
| 93 | |
| 94 | static void RenderModule(InternalScopedString *buffer, |
| 95 | const LoadedModule &module, uptr moduleId) { |
| 96 | InternalScopedString buildIdBuffer; |
| 97 | for (uptr i = 0; i < module.uuid_size(); i++) |
| 98 | buildIdBuffer.AppendF(format: "%02x" , module.uuid()[i]); |
| 99 | |
| 100 | buffer->AppendF(format: kFormatModule, moduleId, module.full_name(), |
| 101 | buildIdBuffer.data()); |
| 102 | buffer->Append(str: "\n" ); |
| 103 | } |
| 104 | |
| 105 | static void RenderMmaps(InternalScopedString *buffer, |
| 106 | const LoadedModule &module, uptr moduleId) { |
| 107 | InternalScopedString accessBuffer; |
| 108 | |
| 109 | // All module mmaps are readable at least |
| 110 | for (const auto &range : module.ranges()) { |
| 111 | accessBuffer.Append(str: "r" ); |
| 112 | if (range.writable) |
| 113 | accessBuffer.Append(str: "w" ); |
| 114 | if (range.executable) |
| 115 | accessBuffer.Append(str: "x" ); |
| 116 | |
| 117 | //{{{mmap:%starting_addr:%size_in_hex:load:%moduleId:r%(w|x):%relative_addr}}} |
| 118 | |
| 119 | // module.base_address == dlpi_addr |
| 120 | // range.beg == dlpi_addr + p_vaddr |
| 121 | // relative address == p_vaddr == range.beg - module.base_address |
| 122 | buffer->AppendF(format: kFormatMmap, reinterpret_cast<void *>(range.beg), |
| 123 | range.end - range.beg, static_cast<int>(moduleId), |
| 124 | accessBuffer.data(), range.beg - module.base_address()); |
| 125 | |
| 126 | buffer->Append(str: "\n" ); |
| 127 | accessBuffer.clear(); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | void MarkupStackTracePrinter::RenderContext(InternalScopedString *buffer) { |
| 132 | if (renderedModules_.size() == 0) |
| 133 | buffer->Append(str: "{{{reset}}}\n" ); |
| 134 | |
| 135 | const auto &modules = Symbolizer::GetOrInit()->GetRefreshedListOfModules(); |
| 136 | |
| 137 | for (const auto &module : modules) { |
| 138 | if (ModuleHasBeenRendered(module, renderedModules: renderedModules_)) |
| 139 | continue; |
| 140 | |
| 141 | // symbolizer markup id, used to refer to this modules from other contextual |
| 142 | // elements |
| 143 | uptr moduleId = renderedModules_.size(); |
| 144 | |
| 145 | RenderModule(buffer, module, moduleId); |
| 146 | RenderMmaps(buffer, module, moduleId); |
| 147 | |
| 148 | renderedModules_.push_back(element: { |
| 149 | .full_name: internal_strdup(s: module.full_name()), |
| 150 | .base_address: module.base_address(), |
| 151 | .uuid: {}, |
| 152 | }); |
| 153 | |
| 154 | // kModuleUUIDSize is the size of curModule.uuid |
| 155 | CHECK_GE(kModuleUUIDSize, module.uuid_size()); |
| 156 | internal_memcpy(dest: renderedModules_.back().uuid, src: module.uuid(), |
| 157 | n: module.uuid_size()); |
| 158 | } |
| 159 | } |
| 160 | #endif // !SANITIZER_FUCHSIA |
| 161 | |
| 162 | } // namespace __sanitizer |
| 163 | |