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
25namespace __sanitizer {
26
27void 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
34bool MarkupStackTracePrinter::RenderNeedsSymbolization(const char *format) {
35 return false;
36}
37
38// We don't support the stack_trace_format flag at all.
39void 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
49bool 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
57bool MarkupSymbolizerTool::SymbolizeData(uptr addr, DataInfo *info) {
58 info->Clear();
59 info->start = addr;
60 return true;
61}
62
63const 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
76static 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
84static 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
94static 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
105static 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
131void 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