/src/abseil-cpp/absl/debugging/internal/examine_stack.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Copyright 2018 The Abseil Authors. |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | // you may not use this file except in compliance with the License. |
6 | | // You may obtain a copy of the License at |
7 | | // |
8 | | // https://www.apache.org/licenses/LICENSE-2.0 |
9 | | // |
10 | | // Unless required by applicable law or agreed to in writing, software |
11 | | // distributed under the License is distributed on an "AS IS" BASIS, |
12 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | // See the License for the specific language governing permissions and |
14 | | // limitations under the License. |
15 | | // |
16 | | |
17 | | #include "absl/debugging/internal/examine_stack.h" |
18 | | |
19 | | #ifndef _WIN32 |
20 | | #include <unistd.h> |
21 | | #endif |
22 | | |
23 | | #include "absl/base/config.h" |
24 | | |
25 | | #ifdef ABSL_HAVE_MMAP |
26 | | #include <sys/mman.h> |
27 | | #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) |
28 | | #define MAP_ANONYMOUS MAP_ANON |
29 | | #endif |
30 | | #endif |
31 | | |
32 | | #if defined(__linux__) || defined(__APPLE__) |
33 | | #include <sys/ucontext.h> |
34 | | #endif |
35 | | |
36 | | #include <csignal> |
37 | | #include <cstdio> |
38 | | |
39 | | #include "absl/base/attributes.h" |
40 | | #include "absl/base/internal/raw_logging.h" |
41 | | #include "absl/base/macros.h" |
42 | | #include "absl/debugging/stacktrace.h" |
43 | | #include "absl/debugging/symbolize.h" |
44 | | |
45 | | namespace absl { |
46 | | ABSL_NAMESPACE_BEGIN |
47 | | namespace debugging_internal { |
48 | | |
49 | | namespace { |
50 | | constexpr int kDefaultDumpStackFramesLimit = 64; |
51 | | // The %p field width for printf() functions is two characters per byte, |
52 | | // and two extra for the leading "0x". |
53 | | constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*); |
54 | | |
55 | | ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr; |
56 | | |
57 | | // Async-signal safe mmap allocator. |
58 | 0 | void* Allocate(size_t num_bytes) { |
59 | 0 | #ifdef ABSL_HAVE_MMAP |
60 | 0 | void* p = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE, |
61 | 0 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
62 | 0 | return p == MAP_FAILED ? nullptr : p; |
63 | | #else |
64 | | (void)num_bytes; |
65 | | return nullptr; |
66 | | #endif // ABSL_HAVE_MMAP |
67 | 0 | } |
68 | | |
69 | 0 | void Deallocate(void* p, size_t size) { |
70 | 0 | #ifdef ABSL_HAVE_MMAP |
71 | 0 | ::munmap(p, size); |
72 | | #else |
73 | | (void)p; |
74 | | (void)size; |
75 | | #endif // ABSL_HAVE_MMAP |
76 | 0 | } |
77 | | |
78 | | // Print a program counter only. |
79 | | void DumpPC(OutputWriter* writer, void* writer_arg, void* const pc, |
80 | 0 | const char* const prefix) { |
81 | 0 | char buf[100]; |
82 | 0 | snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc); |
83 | 0 | writer(buf, writer_arg); |
84 | 0 | } |
85 | | |
86 | | // Print a program counter and the corresponding stack frame size. |
87 | | void DumpPCAndFrameSize(OutputWriter* writer, void* writer_arg, void* const pc, |
88 | 0 | int framesize, const char* const prefix) { |
89 | 0 | char buf[100]; |
90 | 0 | if (framesize <= 0) { |
91 | 0 | snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix, |
92 | 0 | kPrintfPointerFieldWidth, pc); |
93 | 0 | } else { |
94 | 0 | snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix, |
95 | 0 | kPrintfPointerFieldWidth, pc, framesize); |
96 | 0 | } |
97 | 0 | writer(buf, writer_arg); |
98 | 0 | } |
99 | | |
100 | | // Print a program counter and the corresponding symbol. |
101 | | void DumpPCAndSymbol(OutputWriter* writer, void* writer_arg, void* const pc, |
102 | 0 | const char* const prefix) { |
103 | 0 | char tmp[1024]; |
104 | 0 | const char* symbol = "(unknown)"; |
105 | | // Symbolizes the previous address of pc because pc may be in the |
106 | | // next function. The overrun happens when the function ends with |
107 | | // a call to a function annotated noreturn (e.g. CHECK). |
108 | | // If symbolization of pc-1 fails, also try pc on the off-chance |
109 | | // that we crashed on the first instruction of a function (that |
110 | | // actually happens very often for e.g. __restore_rt). |
111 | 0 | const uintptr_t prev_pc = reinterpret_cast<uintptr_t>(pc) - 1; |
112 | 0 | if (absl::Symbolize(reinterpret_cast<const char*>(prev_pc), tmp, |
113 | 0 | sizeof(tmp)) || |
114 | 0 | absl::Symbolize(pc, tmp, sizeof(tmp))) { |
115 | 0 | symbol = tmp; |
116 | 0 | } |
117 | 0 | char buf[1024]; |
118 | 0 | snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth, |
119 | 0 | pc, symbol); |
120 | 0 | writer(buf, writer_arg); |
121 | 0 | } |
122 | | |
123 | | // Print a program counter, its stack frame size, and its symbol name. |
124 | | // Note that there is a separate symbolize_pc argument. Return addresses may be |
125 | | // at the end of the function, and this allows the caller to back up from pc if |
126 | | // appropriate. |
127 | | void DumpPCAndFrameSizeAndSymbol(OutputWriter* writer, void* writer_arg, |
128 | | void* const pc, void* const symbolize_pc, |
129 | 0 | int framesize, const char* const prefix) { |
130 | 0 | char tmp[1024]; |
131 | 0 | const char* symbol = "(unknown)"; |
132 | 0 | if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) { |
133 | 0 | symbol = tmp; |
134 | 0 | } |
135 | 0 | char buf[1024]; |
136 | 0 | if (framesize <= 0) { |
137 | 0 | snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix, |
138 | 0 | kPrintfPointerFieldWidth, pc, symbol); |
139 | 0 | } else { |
140 | 0 | snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix, |
141 | 0 | kPrintfPointerFieldWidth, pc, framesize, symbol); |
142 | 0 | } |
143 | 0 | writer(buf, writer_arg); |
144 | 0 | } |
145 | | |
146 | | } // namespace |
147 | | |
148 | 0 | void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) { |
149 | 0 | debug_stack_trace_hook = hook; |
150 | 0 | } |
151 | | |
152 | 0 | SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; } |
153 | | |
154 | | // Returns the program counter from signal context, nullptr if |
155 | | // unknown. vuc is a ucontext_t*. We use void* to avoid the use of |
156 | | // ucontext_t on non-POSIX systems. |
157 | 0 | void* GetProgramCounter(void* const vuc) { |
158 | 0 | #ifdef __linux__ |
159 | 0 | if (vuc != nullptr) { |
160 | 0 | ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc); |
161 | | #if defined(__aarch64__) |
162 | | return reinterpret_cast<void*>(context->uc_mcontext.pc); |
163 | | #elif defined(__alpha__) |
164 | | return reinterpret_cast<void*>(context->uc_mcontext.sc_pc); |
165 | | #elif defined(__arm__) |
166 | | return reinterpret_cast<void*>(context->uc_mcontext.arm_pc); |
167 | | #elif defined(__hppa__) |
168 | | return reinterpret_cast<void*>(context->uc_mcontext.sc_iaoq[0]); |
169 | | #elif defined(__i386__) |
170 | | if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs)) |
171 | | return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]); |
172 | | #elif defined(__ia64__) |
173 | | return reinterpret_cast<void*>(context->uc_mcontext.sc_ip); |
174 | | #elif defined(__m68k__) |
175 | | return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]); |
176 | | #elif defined(__mips__) |
177 | | return reinterpret_cast<void*>(context->uc_mcontext.pc); |
178 | | #elif defined(__powerpc64__) |
179 | | return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]); |
180 | | #elif defined(__powerpc__) |
181 | | return reinterpret_cast<void*>(context->uc_mcontext.uc_regs->gregs[32]); |
182 | | #elif defined(__riscv) |
183 | | return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]); |
184 | | #elif defined(__s390__) && !defined(__s390x__) |
185 | | return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff); |
186 | | #elif defined(__s390__) && defined(__s390x__) |
187 | | return reinterpret_cast<void*>(context->uc_mcontext.psw.addr); |
188 | | #elif defined(__sh__) |
189 | | return reinterpret_cast<void*>(context->uc_mcontext.pc); |
190 | | #elif defined(__sparc__) && !defined(__arch64__) |
191 | | return reinterpret_cast<void*>(context->uc_mcontext.gregs[19]); |
192 | | #elif defined(__sparc__) && defined(__arch64__) |
193 | | return reinterpret_cast<void*>(context->uc_mcontext.mc_gregs[19]); |
194 | | #elif defined(__x86_64__) |
195 | 0 | if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs)) |
196 | 0 | return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]); |
197 | | #elif defined(__e2k__) |
198 | | return reinterpret_cast<void*>(context->uc_mcontext.cr0_hi); |
199 | | #elif defined(__loongarch__) |
200 | | return reinterpret_cast<void*>(context->uc_mcontext.__pc); |
201 | | #else |
202 | | #error "Undefined Architecture." |
203 | | #endif |
204 | 0 | } |
205 | | #elif defined(__APPLE__) |
206 | | if (vuc != nullptr) { |
207 | | ucontext_t* signal_ucontext = reinterpret_cast<ucontext_t*>(vuc); |
208 | | #if defined(__aarch64__) |
209 | | return reinterpret_cast<void*>( |
210 | | __darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss)); |
211 | | #elif defined(__arm__) |
212 | | #if __DARWIN_UNIX03 |
213 | | return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__pc); |
214 | | #else |
215 | | return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.pc); |
216 | | #endif |
217 | | #elif defined(__i386__) |
218 | | #if __DARWIN_UNIX03 |
219 | | return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__eip); |
220 | | #else |
221 | | return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.eip); |
222 | | #endif |
223 | | #elif defined(__x86_64__) |
224 | | #if __DARWIN_UNIX03 |
225 | | return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__rip); |
226 | | #else |
227 | | return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.rip); |
228 | | #endif |
229 | | #endif |
230 | | } |
231 | | #elif defined(__akaros__) |
232 | | auto* ctx = reinterpret_cast<struct user_context*>(vuc); |
233 | | return reinterpret_cast<void*>(get_user_ctx_pc(ctx)); |
234 | | #endif |
235 | 0 | static_cast<void>(vuc); |
236 | 0 | return nullptr; |
237 | 0 | } |
238 | | |
239 | | void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[], |
240 | | int frame_sizes[], int depth, |
241 | | int min_dropped_frames, |
242 | | bool symbolize_stacktrace, |
243 | 0 | OutputWriter* writer, void* writer_arg) { |
244 | 0 | if (pc != nullptr) { |
245 | | // We don't know the stack frame size for PC, use 0. |
246 | 0 | if (symbolize_stacktrace) { |
247 | 0 | DumpPCAndFrameSizeAndSymbol(writer, writer_arg, pc, pc, 0, "PC: "); |
248 | 0 | } else { |
249 | 0 | DumpPCAndFrameSize(writer, writer_arg, pc, 0, "PC: "); |
250 | 0 | } |
251 | 0 | } |
252 | 0 | for (int i = 0; i < depth; i++) { |
253 | 0 | if (symbolize_stacktrace) { |
254 | | // Pass the previous address of pc as the symbol address because pc is a |
255 | | // return address, and an overrun may occur when the function ends with a |
256 | | // call to a function annotated noreturn (e.g. CHECK). Note that we don't |
257 | | // do this for pc above, as the adjustment is only correct for return |
258 | | // addresses. |
259 | 0 | DumpPCAndFrameSizeAndSymbol(writer, writer_arg, stack[i], |
260 | 0 | reinterpret_cast<char*>(stack[i]) - 1, |
261 | 0 | frame_sizes[i], " "); |
262 | 0 | } else { |
263 | 0 | DumpPCAndFrameSize(writer, writer_arg, stack[i], frame_sizes[i], " "); |
264 | 0 | } |
265 | 0 | } |
266 | 0 | if (min_dropped_frames > 0) { |
267 | 0 | char buf[100]; |
268 | 0 | snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n", |
269 | 0 | min_dropped_frames); |
270 | 0 | writer(buf, writer_arg); |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | // Dump current stack trace as directed by writer. |
275 | | // Make sure this function is not inlined to avoid skipping too many top frames. |
276 | | ABSL_ATTRIBUTE_NOINLINE |
277 | | void DumpStackTrace(int min_dropped_frames, int max_num_frames, |
278 | | bool symbolize_stacktrace, OutputWriter* writer, |
279 | 0 | void* writer_arg) { |
280 | | // Print stack trace |
281 | 0 | void* stack_buf[kDefaultDumpStackFramesLimit]; |
282 | 0 | void** stack = stack_buf; |
283 | 0 | int num_stack = kDefaultDumpStackFramesLimit; |
284 | 0 | size_t allocated_bytes = 0; |
285 | |
|
286 | 0 | if (num_stack >= max_num_frames) { |
287 | | // User requested fewer frames than we already have space for. |
288 | 0 | num_stack = max_num_frames; |
289 | 0 | } else { |
290 | 0 | const size_t needed_bytes = |
291 | 0 | static_cast<size_t>(max_num_frames) * sizeof(stack[0]); |
292 | 0 | void* p = Allocate(needed_bytes); |
293 | 0 | if (p != nullptr) { // We got the space. |
294 | 0 | num_stack = max_num_frames; |
295 | 0 | stack = reinterpret_cast<void**>(p); |
296 | 0 | allocated_bytes = needed_bytes; |
297 | 0 | } |
298 | 0 | } |
299 | |
|
300 | 0 | int depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); |
301 | 0 | for (int i = 0; i < depth; i++) { |
302 | 0 | if (symbolize_stacktrace) { |
303 | 0 | DumpPCAndSymbol(writer, writer_arg, stack[static_cast<size_t>(i)], |
304 | 0 | " "); |
305 | 0 | } else { |
306 | 0 | DumpPC(writer, writer_arg, stack[static_cast<size_t>(i)], " "); |
307 | 0 | } |
308 | 0 | } |
309 | |
|
310 | 0 | auto hook = GetDebugStackTraceHook(); |
311 | 0 | if (hook != nullptr) { |
312 | 0 | (*hook)(stack, depth, writer, writer_arg); |
313 | 0 | } |
314 | |
|
315 | 0 | if (allocated_bytes != 0) Deallocate(stack, allocated_bytes); |
316 | 0 | } |
317 | | |
318 | | } // namespace debugging_internal |
319 | | ABSL_NAMESPACE_END |
320 | | } // namespace absl |