v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
stack_trace_posix.cc
Go to the documentation of this file.
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Slightly adapted for inclusion in V8.
6// Copyright 2016 the V8 project authors. All rights reserved.
7
9
10#include <errno.h>
11#include <fcntl.h>
12#include <signal.h>
13#include <stddef.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <sys/param.h>
18#include <sys/stat.h>
19#include <sys/types.h>
20#include <unistd.h>
21
22#include <map>
23#include <memory>
24#include <ostream>
25#include <string>
26#include <vector>
27
28#if V8_LIBC_GLIBC || V8_LIBC_BSD || V8_LIBC_UCLIBC || V8_OS_SOLARIS
29#define HAVE_EXECINFO_H 1
30#endif
31
32#if HAVE_EXECINFO_H
33#include <cxxabi.h>
34#include <execinfo.h>
35#endif
36#if V8_OS_DARWIN
37#include <AvailabilityMacros.h>
38#endif
39
42#include "src/base/logging.h"
43#include "src/base/macros.h"
44
45namespace v8 {
46namespace base {
47namespace debug {
48
49namespace internal {
50
51// POSIX doesn't define any async-signal safe function for converting
52// an integer to ASCII. We'll have to define our own version.
53// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
54// conversion was successful or nullptr otherwise. It never writes more than
55// "sz" bytes. Output will be truncated as needed, and a NUL character is always
56// appended.
57char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding);
58
59} // namespace internal
60
61namespace {
62
63volatile sig_atomic_t in_signal_handler = 0;
64bool dump_stack_in_signal_handler = true;
65
66// The prefix used for mangled symbols, per the Itanium C++ ABI:
67// http://www.codesourcery.com/cxx-abi/abi.html#mangling
68const char kMangledSymbolPrefix[] = "_Z";
69
70// Characters that can be used for symbols, generated by Ruby:
71// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
72const char kSymbolCharacters[] =
73 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
74
75#if HAVE_EXECINFO_H
76// Demangles C++ symbols in the given text. Example:
77//
78// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
79// =>
80// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
81void DemangleSymbols(std::string* text) {
82 // Note: code in this function is NOT async-signal safe (std::string uses
83 // malloc internally).
84
85
86 std::string::size_type search_from = 0;
87 while (search_from < text->size()) {
88 // Look for the start of a mangled symbol, from search_from.
89 std::string::size_type mangled_start =
90 text->find(kMangledSymbolPrefix, search_from);
91 if (mangled_start == std::string::npos) {
92 break; // Mangled symbol not found.
93 }
94
95 // Look for the end of the mangled symbol.
96 std::string::size_type mangled_end =
97 text->find_first_not_of(kSymbolCharacters, mangled_start);
98 if (mangled_end == std::string::npos) {
99 mangled_end = text->size();
100 }
101 std::string mangled_symbol =
102 text->substr(mangled_start, mangled_end - mangled_start);
103
104 // Try to demangle the mangled symbol candidate.
105 int status = 0;
106 std::unique_ptr<char, FreeDeleter> demangled_symbol(
107 abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, nullptr, &status));
108 if (status == 0) { // Demangling is successful.
109 // Remove the mangled symbol.
110 text->erase(mangled_start, mangled_end - mangled_start);
111 // Insert the demangled symbol.
112 text->insert(mangled_start, demangled_symbol.get());
113 // Next time, we'll start right after the demangled symbol we inserted.
114 search_from = mangled_start + strlen(demangled_symbol.get());
115 } else {
116 // Failed to demangle. Retry after the "_Z" we just found.
117 search_from = mangled_start + 2;
118 }
119 }
120}
121#endif // HAVE_EXECINFO_H
122
123class BacktraceOutputHandler {
124 public:
125 virtual void HandleOutput(const char* output) = 0;
126
127 // If this output handler writes directly to a file descriptor, this file
128 // descriptor can be exposed by overwriting this method. That is in turn
129 // useful for ProcessBacktrace which can then use backtrace_symbols_fd.
130 virtual int OutputFileDescriptor() const { return 0; }
131
132 protected:
133 virtual ~BacktraceOutputHandler() = default;
134};
135
136#if HAVE_EXECINFO_H
137void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
138 // This should be more than enough to store a 64-bit number in hex:
139 // 16 hex digits + 1 for null-terminator.
140 char buf[17] = {'\0'};
141 handler->HandleOutput("0x");
142 internal::itoa_r(reinterpret_cast<intptr_t>(pointer), buf, sizeof(buf), 16,
143 12);
144 handler->HandleOutput(buf);
145}
146
147void ProcessBacktrace(void* const* trace, size_t size,
148 BacktraceOutputHandler* handler) {
149 // NOTE: This code MUST be async-signal safe (it's used by in-process
150 // stack dumping signal handler). NO malloc or stdio is allowed here.
151 handler->HandleOutput("\n");
152 handler->HandleOutput("==== C stack trace ===============================\n");
153 handler->HandleOutput("\n");
154
155 bool printed = false;
156
157 // Below part is async-signal unsafe (uses malloc), so execute it only
158 // when we are not executing the signal handler.
159 if (in_signal_handler == 0) {
160 std::unique_ptr<char*, FreeDeleter> trace_symbols(
161 backtrace_symbols(trace, static_cast<int>(size)));
162 if (trace_symbols) {
163 for (size_t i = 0; i < size; ++i) {
164 std::string trace_symbol = trace_symbols.get()[i];
165 DemangleSymbols(&trace_symbol);
166 handler->HandleOutput(" ");
167 handler->HandleOutput(trace_symbol.c_str());
168 handler->HandleOutput("\n");
169 }
170
171 printed = true;
172 }
173 } else if (handler->OutputFileDescriptor() != 0) {
174 // In this case, we can use backtrace_symbols_fd to write directly to the
175 // output file descriptor. This isn't quite as nice as we don't control the
176 // formatting and because mangled function names will be used, but still
177 // better than just raw addresses (which are also included in this output).
178 backtrace_symbols_fd(trace, static_cast<int>(size),
179 handler->OutputFileDescriptor());
180 printed = true;
181 }
182
183 if (!printed) {
184 for (size_t i = 0; i < size; ++i) {
185 handler->HandleOutput(" [");
186 OutputPointer(trace[i], handler);
187 handler->HandleOutput("]\n");
188 }
189 }
190}
191#endif // HAVE_EXECINFO_H
192
193void PrintToStderr(const char* output) {
194 // NOTE: This code MUST be async-signal safe (it's used by in-process
195 // stack dumping signal handler). NO malloc or stdio is allowed here.
196 ssize_t return_val = write(STDERR_FILENO, output, strlen(output));
197 USE(return_val);
198}
199
200void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
201 // NOTE: This code MUST be async-signal safe.
202 // NO malloc or stdio is allowed here.
203
204 // Record the fact that we are in the signal handler now, so that the rest
205 // of StackTrace can behave in an async-signal-safe manner.
206 in_signal_handler = 1;
207
208 PrintToStderr("Received signal ");
209 char buf[1024] = {0};
210 internal::itoa_r(signal, buf, sizeof(buf), 10, 0);
211 PrintToStderr(buf);
212 if (signal == SIGBUS) {
213 if (info->si_code == BUS_ADRALN)
214 PrintToStderr(" BUS_ADRALN ");
215 else if (info->si_code == BUS_ADRERR)
216 PrintToStderr(" BUS_ADRERR ");
217 else if (info->si_code == BUS_OBJERR)
218 PrintToStderr(" BUS_OBJERR ");
219 else
220 PrintToStderr(" <unknown> ");
221 } else if (signal == SIGFPE) {
222 if (info->si_code == FPE_FLTDIV)
223 PrintToStderr(" FPE_FLTDIV ");
224 else if (info->si_code == FPE_FLTINV)
225 PrintToStderr(" FPE_FLTINV ");
226 else if (info->si_code == FPE_FLTOVF)
227 PrintToStderr(" FPE_FLTOVF ");
228 else if (info->si_code == FPE_FLTRES)
229 PrintToStderr(" FPE_FLTRES ");
230 else if (info->si_code == FPE_FLTSUB)
231 PrintToStderr(" FPE_FLTSUB ");
232 else if (info->si_code == FPE_FLTUND)
233 PrintToStderr(" FPE_FLTUND ");
234 else if (info->si_code == FPE_INTDIV)
235 PrintToStderr(" FPE_INTDIV ");
236 else if (info->si_code == FPE_INTOVF)
237 PrintToStderr(" FPE_INTOVF ");
238 else
239 PrintToStderr(" <unknown> ");
240 } else if (signal == SIGILL) {
241 if (info->si_code == ILL_BADSTK)
242 PrintToStderr(" ILL_BADSTK ");
243 else if (info->si_code == ILL_COPROC)
244 PrintToStderr(" ILL_COPROC ");
245 else if (info->si_code == ILL_ILLOPN)
246 PrintToStderr(" ILL_ILLOPN ");
247 else if (info->si_code == ILL_ILLADR)
248 PrintToStderr(" ILL_ILLADR ");
249 else if (info->si_code == ILL_ILLTRP)
250 PrintToStderr(" ILL_ILLTRP ");
251 else if (info->si_code == ILL_PRVOPC)
252 PrintToStderr(" ILL_PRVOPC ");
253 else if (info->si_code == ILL_PRVREG)
254 PrintToStderr(" ILL_PRVREG ");
255 else
256 PrintToStderr(" <unknown> ");
257 } else if (signal == SIGSEGV) {
258 if (info->si_code == SEGV_MAPERR)
259 PrintToStderr(" SEGV_MAPERR ");
260 else if (info->si_code == SEGV_ACCERR)
261 PrintToStderr(" SEGV_ACCERR ");
262 else
263 PrintToStderr(" <unknown> ");
264 }
265 if (signal == SIGBUS || signal == SIGFPE || signal == SIGILL ||
266 signal == SIGSEGV) {
267 internal::itoa_r(reinterpret_cast<intptr_t>(info->si_addr), buf,
268 sizeof(buf), 16, 12);
269 PrintToStderr(buf);
270 }
271 PrintToStderr("\n");
272 if (dump_stack_in_signal_handler) {
273 debug::StackTrace().Print();
274 PrintToStderr("[end of stack trace]\n");
275 }
276
277 if (::signal(signal, SIG_DFL) == SIG_ERR) _exit(1);
278}
279
280class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
281 public:
282 PrintBacktraceOutputHandler() = default;
283 PrintBacktraceOutputHandler(const PrintBacktraceOutputHandler&) = delete;
284 PrintBacktraceOutputHandler& operator=(const PrintBacktraceOutputHandler&) =
285 delete;
286
287 void HandleOutput(const char* output) override {
288 // NOTE: This code MUST be async-signal safe (it's used by in-process
289 // stack dumping signal handler). NO malloc or stdio is allowed here.
290 PrintToStderr(output);
291 }
292
293 int OutputFileDescriptor() const override { return STDERR_FILENO; }
294};
295
296class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
297 public:
298 explicit StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {}
299 StreamBacktraceOutputHandler(const StreamBacktraceOutputHandler&) = delete;
300 StreamBacktraceOutputHandler& operator=(const StreamBacktraceOutputHandler&) =
301 delete;
302
303 void HandleOutput(const char* output) override { (*os_) << output; }
304
305 private:
306 std::ostream* os_;
307};
308
309void WarmUpBacktrace() {
310 // Warm up stack trace infrastructure. It turns out that on the first
311 // call glibc initializes some internal data structures using pthread_once,
312 // and even backtrace() can call malloc(), leading to hangs.
313 //
314 // Example stack trace snippet (with tcmalloc):
315 //
316 // #8 0x0000000000a173b5 in tc_malloc
317 // at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161
318 // #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517
319 // #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262
320 // #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
321 // #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1")
322 // at dl-open.c:639
323 // #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89
324 // #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
325 // #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48
326 // #16 __GI___libc_dlopen_mode at dl-libc.c:165
327 // #17 0x00007ffff61ef8f5 in init
328 // at ../sysdeps/x86_64/../ia64/backtrace.c:53
329 // #18 0x00007ffff6aad400 in pthread_once
330 // at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104
331 // #19 0x00007ffff61efa14 in __GI___backtrace
332 // at ../sysdeps/x86_64/../ia64/backtrace.c:104
333 // #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace
334 // at base/debug/stack_trace_posix.cc:175
335 // #21 0x00000000007a4ae5 in
336 // base::(anonymous namespace)::StackDumpSignalHandler
337 // at base/process_util_posix.cc:172
338 // #22 <signal handler called>
339 StackTrace stack_trace;
340}
341
342} // namespace
343
345 // When running in an application, our code typically expects SIGPIPE
346 // to be ignored. Therefore, when testing that same code, it should run
347 // with SIGPIPE ignored as well.
348 struct sigaction sigpipe_action;
349 memset(&sigpipe_action, 0, sizeof(sigpipe_action));
350 sigpipe_action.sa_handler = SIG_IGN;
351 sigemptyset(&sigpipe_action.sa_mask);
352 bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0);
353
354 // Avoid hangs during backtrace initialization, see above.
355 WarmUpBacktrace();
356
357 struct sigaction action;
358 memset(&action, 0, sizeof(action));
359 // Use SA_ONSTACK so that iff an alternate stack has been registered, the
360 // handler will run on that stack instead of the default stack. This can be
361 // useful for example if the stack pointer gets corrupted or in case of stack
362 // overflows, since that might prevent the handler from running properly.
363 action.sa_flags = SA_RESETHAND | SA_SIGINFO | SA_ONSTACK;
364 action.sa_sigaction = &StackDumpSignalHandler;
365 sigemptyset(&action.sa_mask);
366
367 success &= (sigaction(SIGILL, &action, nullptr) == 0);
368 success &= (sigaction(SIGABRT, &action, nullptr) == 0);
369 success &= (sigaction(SIGFPE, &action, nullptr) == 0);
370 success &= (sigaction(SIGBUS, &action, nullptr) == 0);
371 success &= (sigaction(SIGSEGV, &action, nullptr) == 0);
372 success &= (sigaction(SIGSYS, &action, nullptr) == 0);
373
374 dump_stack_in_signal_handler = true;
375
376 return success;
377}
378
380 dump_stack_in_signal_handler = false;
381}
382
384 // NOTE: This code MUST be async-signal safe (it's used by in-process
385 // stack dumping signal handler). NO malloc or stdio is allowed here.
386
387#if HAVE_EXECINFO_H
388 // Though the backtrace API man page does not list any possible negative
389 // return values, we take no chance.
390 count_ = static_cast<size_t>(backtrace(trace_, arraysize(trace_)));
391#else
392 count_ = 0;
393#endif
394}
395
396void StackTrace::Print() const {
397 // NOTE: This code MUST be async-signal safe (it's used by in-process
398 // stack dumping signal handler). NO malloc or stdio is allowed here.
399
400#if HAVE_EXECINFO_H
401 PrintBacktraceOutputHandler handler;
402 ProcessBacktrace(trace_, count_, &handler);
403#endif
404}
405
406void StackTrace::OutputToStream(std::ostream* os) const {
407#if HAVE_EXECINFO_H
408 StreamBacktraceOutputHandler handler(os);
409 ProcessBacktrace(trace_, count_, &handler);
410#endif
411}
412
413namespace internal {
414
415// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
416char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding) {
417 // Make sure we can write at least one NUL byte.
418 size_t n = 1;
419 if (n > sz) return nullptr;
420
421 if (base < 2 || base > 16) {
422 buf[0] = '\0';
423 return nullptr;
424 }
425
426 char* start = buf;
427
428 uintptr_t j = i;
429
430 // Handle negative numbers (only for base 10).
431 if (i < 0 && base == 10) {
432 // This does "j = -i" while avoiding integer overflow.
433 j = static_cast<uintptr_t>(-(i + 1)) + 1;
434
435 // Make sure we can write the '-' character.
436 if (++n > sz) {
437 buf[0] = '\0';
438 return nullptr;
439 }
440 *start++ = '-';
441 }
442
443 // Loop until we have converted the entire number. Output at least one
444 // character (i.e. '0').
445 char* ptr = start;
446 do {
447 // Make sure there is still enough space left in our output buffer.
448 if (++n > sz) {
449 buf[0] = '\0';
450 return nullptr;
451 }
452
453 // Output the next digit.
454 *ptr++ = "0123456789abcdef"[j % base];
455 j /= base;
456
457 if (padding > 0) padding--;
458 } while (j > 0 || padding > 0);
459
460 // Terminate the output with a NUL character.
461 *ptr = '\0';
462
463 // Conversion to ASCII actually resulted in the digits being in reverse
464 // order. We can't easily generate them in forward order, as we can't tell
465 // the number of characters needed until we are done converting.
466 // So, now, we reverse the string (except for the possible "-" sign).
467 while (--ptr > start) {
468 char ch = *ptr;
469 *ptr = *start;
470 *start++ = ch;
471 }
472 return buf;
473}
474
475} // namespace internal
476
477} // namespace debug
478} // namespace base
479} // namespace v8
void OutputToStream(std::ostream *os) const
void * trace_[kMaxTraces]
Definition stack_trace.h:87
int start
MovableLabel handler
char * itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding)
V8_BASE_EXPORT bool EnableInProcessStackDumping()
V8_BASE_EXPORT void DisableSignalStackDump()
#define USE(...)
Definition macros.h:293
#define arraysize(array)
Definition macros.h:67
std::ostream * os_