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_(RenderNeedsSymbolization(stack_trace_fmt
)) {}
34 bool ProcessAddressFrames(uptr pc
) {
35 SymbolizedStack
*frames
= symbolize_
36 ? Symbolizer::GetOrInit()->SymbolizePC(pc
)
37 : SymbolizedStack::New(pc
);
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
);
48 if (prev_len
!= output_
->length())
49 output_
->append("%c", frame_delimiter_
);
51 ExtendDedupToken(cur
);
58 // Extend the dedup token by appending a new frame.
59 void ExtendDedupToken(SymbolizedStack
*stack
) {
63 if (dedup_frames_
-- > 0) {
64 if (dedup_token_
->length())
65 dedup_token_
->append("--");
66 if (stack
->info
.function
!= nullptr)
67 dedup_token_
->append("%s", stack
->info
.function
);
71 const char *stack_trace_fmt_
;
72 const char frame_delimiter_
;
73 int dedup_frames_
= common_flags()->dedup_token_length
;
75 InternalScopedString
*output_
;
76 InternalScopedString
*dedup_token_
;
77 const bool symbolize_
= false;
80 static void CopyStringToBuffer(const InternalScopedString
&str
, char *out_buf
,
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';
93 void StackTrace::PrintTo(InternalScopedString
*output
) const {
96 InternalScopedString dedup_token
;
97 StackTraceTextPrinter
printer(common_flags()->stack_trace_format
, '\n',
98 output
, &dedup_token
);
100 if (trace
== nullptr || size
== 0) {
101 output
->append(" <empty stack>\n\n");
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
));
112 // Always add a trailing empty line after stack trace.
113 output
->append("\n");
115 // Append deduplication token, if non-empty.
116 if (dedup_token
.length())
117 output
->append("DEDUP_TOKEN: %s\n", dedup_token
.data());
120 uptr
StackTrace::PrintTo(char *out_buf
, uptr out_buf_size
) const {
123 InternalScopedString output
;
125 CopyStringToBuffer(output
, out_buf
, out_buf_size
);
127 return output
.length();
130 void StackTrace::Print() const {
131 InternalScopedString output
;
133 Printf("%s", output
.data());
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) {
147 if (max_depth
== 1) {
149 trace_buffer
[0] = pc
;
152 if (!WillUseFastUnwind(request_fast_unwind
)) {
153 #if SANITIZER_CAN_SLOW_UNWIND
155 UnwindSlow(pc
, context
, max_depth
);
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
)
163 UNREACHABLE("slow unwind requested but not available");
166 UnwindFast(pc
, bp
, stack_top
, stack_bottom
, max_depth
);
169 int GetModuleAndOffsetForPc(uptr pc
, char *module_name
, uptr module_name_len
,
171 const char *found_module_name
= nullptr;
172 bool ok
= Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
173 pc
, &found_module_name
, pc_offset
);
175 if (!ok
) return false;
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';
184 } // namespace __sanitizer
185 using namespace __sanitizer
;
188 SANITIZER_INTERFACE_ATTRIBUTE
189 void __sanitizer_symbolize_pc(uptr pc
, const char *fmt
, char *out_buf
,
194 pc
= StackTrace::GetPreviousInstructionPc(pc
);
196 InternalScopedString output
;
197 StackTraceTextPrinter
printer(fmt
, '\0', &output
, nullptr);
198 if (!printer
.ProcessAddressFrames(pc
)) {
200 output
.append("<can't symbolize>");
202 CopyStringToBuffer(output
, out_buf
, out_buf_size
);
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;
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;
218 SANITIZER_INTERFACE_ATTRIBUTE
219 int __sanitizer_get_module_and_offset_for_pc(void *pc
, char *module_name
,
220 uptr module_name_len
,
222 return __sanitizer::GetModuleAndOffsetForPc(
223 reinterpret_cast<uptr
>(pc
), module_name
, module_name_len
,
224 reinterpret_cast<uptr
*>(pc_offset
));