]> git.ipfire.org Git - thirdparty/gcc.git/blob - libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / libsanitizer / sanitizer_common / sanitizer_stacktrace_libcdep.cpp
1 //===-- sanitizer_stacktrace_libcdep.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 AddressSanitizer and ThreadSanitizer
10 // run-time libraries.
11 //===----------------------------------------------------------------------===//
12
13 #include "sanitizer_common.h"
14 #include "sanitizer_placement_new.h"
15 #include "sanitizer_stacktrace.h"
16 #include "sanitizer_stacktrace_printer.h"
17 #include "sanitizer_symbolizer.h"
18
19 namespace __sanitizer {
20
21 namespace {
22
23 class StackTraceTextPrinter {
24 public:
25 StackTraceTextPrinter(const char *stack_trace_fmt, char frame_delimiter,
26 InternalScopedString *output,
27 InternalScopedString *dedup_token)
28 : stack_trace_fmt_(stack_trace_fmt),
29 frame_delimiter_(frame_delimiter),
30 output_(output),
31 dedup_token_(dedup_token),
32 symbolize_(RenderNeedsSymbolization(stack_trace_fmt)) {}
33
34 bool ProcessAddressFrames(uptr pc) {
35 SymbolizedStack *frames = symbolize_
36 ? Symbolizer::GetOrInit()->SymbolizePC(pc)
37 : SymbolizedStack::New(pc);
38 if (!frames)
39 return false;
40
41 for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
42 uptr prev_len = output_->length();
43 RenderFrame(output_, stack_trace_fmt_, frame_num_++, cur->info.address,
44 symbolize_ ? &cur->info : nullptr,
45 common_flags()->symbolize_vs_style,
46 common_flags()->strip_path_prefix);
47
48 if (prev_len != output_->length())
49 output_->append("%c", frame_delimiter_);
50
51 ExtendDedupToken(cur);
52 }
53 frames->ClearAll();
54 return true;
55 }
56
57 private:
58 // Extend the dedup token by appending a new frame.
59 void ExtendDedupToken(SymbolizedStack *stack) {
60 if (!dedup_token_)
61 return;
62
63 if (dedup_frames_-- > 0) {
64 if (dedup_token_->length())
65 dedup_token_->append("--");
66 if (stack->info.function != nullptr)
67 dedup_token_->append(stack->info.function);
68 }
69 }
70
71 const char *stack_trace_fmt_;
72 const char frame_delimiter_;
73 int dedup_frames_ = common_flags()->dedup_token_length;
74 uptr frame_num_ = 0;
75 InternalScopedString *output_;
76 InternalScopedString *dedup_token_;
77 const bool symbolize_ = false;
78 };
79
80 static void CopyStringToBuffer(const InternalScopedString &str, char *out_buf,
81 uptr out_buf_size) {
82 if (!out_buf_size)
83 return;
84
85 CHECK_GT(out_buf_size, 0);
86 uptr copy_size = Min(str.length(), out_buf_size - 1);
87 internal_memcpy(out_buf, str.data(), copy_size);
88 out_buf[copy_size] = '\0';
89 }
90
91 } // namespace
92
93 void StackTrace::PrintTo(InternalScopedString *output) const {
94 CHECK(output);
95
96 InternalScopedString dedup_token;
97 StackTraceTextPrinter printer(common_flags()->stack_trace_format, '\n',
98 output, &dedup_token);
99
100 if (trace == nullptr || size == 0) {
101 output->append(" <empty stack>\n\n");
102 return;
103 }
104
105 for (uptr i = 0; i < size && trace[i]; i++) {
106 // PCs in stack traces are actually the return addresses, that is,
107 // addresses of the next instructions after the call.
108 uptr pc = GetPreviousInstructionPc(trace[i]);
109 CHECK(printer.ProcessAddressFrames(pc));
110 }
111
112 // Always add a trailing empty line after stack trace.
113 output->append("\n");
114
115 // Append deduplication token, if non-empty.
116 if (dedup_token.length())
117 output->append("DEDUP_TOKEN: %s\n", dedup_token.data());
118 }
119
120 uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const {
121 CHECK(out_buf);
122
123 InternalScopedString output;
124 PrintTo(&output);
125 CopyStringToBuffer(output, out_buf, out_buf_size);
126
127 return output.length();
128 }
129
130 void StackTrace::Print() const {
131 InternalScopedString output;
132 PrintTo(&output);
133 Printf("%s", output.data());
134 }
135
136 void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
137 uptr stack_top, uptr stack_bottom,
138 bool request_fast_unwind) {
139 // Ensures all call sites get what they requested.
140 CHECK_EQ(request_fast_unwind, WillUseFastUnwind(request_fast_unwind));
141 top_frame_bp = (max_depth > 0) ? bp : 0;
142 // Avoid doing any work for small max_depth.
143 if (max_depth == 0) {
144 size = 0;
145 return;
146 }
147 if (max_depth == 1) {
148 size = 1;
149 trace_buffer[0] = pc;
150 return;
151 }
152 if (!WillUseFastUnwind(request_fast_unwind)) {
153 #if SANITIZER_CAN_SLOW_UNWIND
154 if (context)
155 UnwindSlow(pc, context, max_depth);
156 else
157 UnwindSlow(pc, max_depth);
158 // If there are too few frames, the program may be built with
159 // -fno-asynchronous-unwind-tables. Fall back to fast unwinder below.
160 if (size > 2 || size >= max_depth)
161 return;
162 #else
163 UNREACHABLE("slow unwind requested but not available");
164 #endif
165 }
166 UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
167 }
168
169 static int GetModuleAndOffsetForPc(uptr pc, char *module_name,
170 uptr module_name_len, uptr *pc_offset) {
171 const char *found_module_name = nullptr;
172 bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
173 pc, &found_module_name, pc_offset);
174
175 if (!ok) return false;
176
177 if (module_name && module_name_len) {
178 internal_strncpy(module_name, found_module_name, module_name_len);
179 module_name[module_name_len - 1] = '\x00';
180 }
181 return true;
182 }
183
184 } // namespace __sanitizer
185 using namespace __sanitizer;
186
187 extern "C" {
188 SANITIZER_INTERFACE_ATTRIBUTE
189 void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
190 uptr out_buf_size) {
191 if (!out_buf_size)
192 return;
193
194 pc = StackTrace::GetPreviousInstructionPc(pc);
195
196 InternalScopedString output;
197 StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
198 if (!printer.ProcessAddressFrames(pc)) {
199 output.clear();
200 output.append("<can't symbolize>");
201 }
202 CopyStringToBuffer(output, out_buf, out_buf_size);
203 }
204
205 SANITIZER_INTERFACE_ATTRIBUTE
206 void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
207 char *out_buf, uptr out_buf_size) {
208 if (!out_buf_size) return;
209 out_buf[0] = 0;
210 DataInfo DI;
211 if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
212 InternalScopedString data_desc;
213 RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix);
214 internal_strncpy(out_buf, data_desc.data(), out_buf_size);
215 out_buf[out_buf_size - 1] = 0;
216 }
217
218 SANITIZER_INTERFACE_ATTRIBUTE
219 int __sanitizer_get_module_and_offset_for_pc(uptr pc, char *module_name,
220 uptr module_name_len,
221 uptr *pc_offset) {
222 return __sanitizer::GetModuleAndOffsetForPc(pc, module_name, module_name_len,
223 pc_offset);
224 }
225 } // extern "C"