1 //===-- sanitizer_stacktrace_libcdep.cpp ----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is shared between AddressSanitizer and ThreadSanitizer
10 // run-time libraries.
11 //===----------------------------------------------------------------------===//
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"
19 namespace __sanitizer
{
23 class StackTraceTextPrinter
{
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
),
31 dedup_token_(dedup_token
),
32 symbolize_(StackTracePrinter::GetOrInit()->RenderNeedsSymbolization(
35 bool ProcessAddressFrames(uptr pc
) {
36 SymbolizedStack
*frames
= symbolize_
37 ? Symbolizer::GetOrInit()->SymbolizePC(pc
)
38 : SymbolizedStack::New(pc
);
42 for (SymbolizedStack
*cur
= frames
; cur
; cur
= cur
->next
) {
43 uptr prev_len
= output_
->length();
44 StackTracePrinter::GetOrInit()->RenderFrame(
45 output_
, stack_trace_fmt_
, frame_num_
++, cur
->info
.address
,
46 symbolize_
? &cur
->info
: nullptr, common_flags()->symbolize_vs_style
,
47 common_flags()->strip_path_prefix
);
49 if (prev_len
!= output_
->length())
50 output_
->AppendF("%c", frame_delimiter_
);
52 ExtendDedupToken(cur
);
59 // Extend the dedup token by appending a new frame.
60 void ExtendDedupToken(SymbolizedStack
*stack
) {
64 if (dedup_frames_
-- > 0) {
65 if (dedup_token_
->length())
66 dedup_token_
->AppendF("--");
67 if (stack
->info
.function
)
68 dedup_token_
->Append(stack
->info
.function
);
72 const char *stack_trace_fmt_
;
73 const char frame_delimiter_
;
74 int dedup_frames_
= common_flags()->dedup_token_length
;
76 InternalScopedString
*output_
;
77 InternalScopedString
*dedup_token_
;
78 const bool symbolize_
= false;
81 static void CopyStringToBuffer(const InternalScopedString
&str
, char *out_buf
,
86 CHECK_GT(out_buf_size
, 0);
87 uptr copy_size
= Min(str
.length(), out_buf_size
- 1);
88 internal_memcpy(out_buf
, str
.data(), copy_size
);
89 out_buf
[copy_size
] = '\0';
94 void StackTrace::PrintTo(InternalScopedString
*output
) const {
97 InternalScopedString dedup_token
;
98 StackTraceTextPrinter
printer(common_flags()->stack_trace_format
, '\n',
99 output
, &dedup_token
);
101 if (trace
== nullptr || size
== 0) {
102 output
->AppendF(" <empty stack>\n\n");
106 for (uptr i
= 0; i
< size
&& trace
[i
]; i
++) {
107 // PCs in stack traces are actually the return addresses, that is,
108 // addresses of the next instructions after the call.
109 uptr pc
= GetPreviousInstructionPc(trace
[i
]);
110 CHECK(printer
.ProcessAddressFrames(pc
));
113 // Always add a trailing empty line after stack trace.
114 output
->AppendF("\n");
116 // Append deduplication token, if non-empty.
117 if (dedup_token
.length())
118 output
->AppendF("DEDUP_TOKEN: %s\n", dedup_token
.data());
121 uptr
StackTrace::PrintTo(char *out_buf
, uptr out_buf_size
) const {
124 InternalScopedString output
;
126 CopyStringToBuffer(output
, out_buf
, out_buf_size
);
128 return output
.length();
131 void StackTrace::Print() const {
132 InternalScopedString output
;
134 Printf("%s", output
.data());
137 void BufferedStackTrace::Unwind(u32 max_depth
, uptr pc
, uptr bp
, void *context
,
138 uptr stack_top
, uptr stack_bottom
,
139 bool request_fast_unwind
) {
140 // Ensures all call sites get what they requested.
141 CHECK_EQ(request_fast_unwind
, WillUseFastUnwind(request_fast_unwind
));
142 top_frame_bp
= (max_depth
> 0) ? bp
: 0;
143 // Avoid doing any work for small max_depth.
144 if (max_depth
== 0) {
148 if (max_depth
== 1) {
150 trace_buffer
[0] = pc
;
153 if (!WillUseFastUnwind(request_fast_unwind
)) {
154 #if SANITIZER_CAN_SLOW_UNWIND
156 UnwindSlow(pc
, context
, max_depth
);
158 UnwindSlow(pc
, max_depth
);
159 // If there are too few frames, the program may be built with
160 // -fno-asynchronous-unwind-tables. Fall back to fast unwinder below.
161 if (size
> 2 || size
>= max_depth
)
164 UNREACHABLE("slow unwind requested but not available");
167 UnwindFast(pc
, bp
, stack_top
, stack_bottom
, max_depth
);
170 int GetModuleAndOffsetForPc(uptr pc
, char *module_name
, uptr module_name_len
,
172 const char *found_module_name
= nullptr;
173 bool ok
= Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
174 pc
, &found_module_name
, pc_offset
);
176 if (!ok
) return false;
178 if (module_name
&& module_name_len
) {
179 internal_strncpy(module_name
, found_module_name
, module_name_len
);
180 module_name
[module_name_len
- 1] = '\x00';
185 } // namespace __sanitizer
186 using namespace __sanitizer
;
189 SANITIZER_INTERFACE_ATTRIBUTE
190 void __sanitizer_symbolize_pc(uptr pc
, const char *fmt
, char *out_buf
,
195 pc
= StackTrace::GetPreviousInstructionPc(pc
);
197 InternalScopedString output
;
198 StackTraceTextPrinter
printer(fmt
, '\0', &output
, nullptr);
199 if (!printer
.ProcessAddressFrames(pc
)) {
201 output
.AppendF("<can't symbolize>");
203 CopyStringToBuffer(output
, out_buf
, out_buf_size
);
206 SANITIZER_INTERFACE_ATTRIBUTE
207 void __sanitizer_symbolize_global(uptr data_addr
, const char *fmt
,
208 char *out_buf
, uptr out_buf_size
) {
209 if (!out_buf_size
) return;
212 if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr
, &DI
)) return;
213 InternalScopedString data_desc
;
214 StackTracePrinter::GetOrInit()->RenderData(&data_desc
, fmt
, &DI
,
215 common_flags()->strip_path_prefix
);
216 internal_strncpy(out_buf
, data_desc
.data(), out_buf_size
);
217 out_buf
[out_buf_size
- 1] = 0;
220 SANITIZER_INTERFACE_ATTRIBUTE
221 int __sanitizer_get_module_and_offset_for_pc(void *pc
, char *module_name
,
222 uptr module_name_len
,
224 return __sanitizer::GetModuleAndOffsetForPc(
225 reinterpret_cast<uptr
>(pc
), module_name
, module_name_len
,
226 reinterpret_cast<uptr
*>(pc_offset
));