]> git.proxmox.com Git - ceph.git/blob - ceph/src/crimson/common/fatal_signal.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / crimson / common / fatal_signal.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "fatal_signal.h"
5
6 #include <csignal>
7 #include <iostream>
8 #include <string_view>
9
10 #define BOOST_STACKTRACE_USE_ADDR2LINE
11 #include <boost/stacktrace.hpp>
12 #include <seastar/core/reactor.hh>
13
14 #include "common/safe_io.h"
15 #include "include/scope_guard.h"
16
17 FatalSignal::FatalSignal()
18 {
19 install_oneshot_signals_handler<SIGSEGV,
20 SIGABRT,
21 SIGBUS,
22 SIGILL,
23 SIGFPE,
24 SIGXCPU,
25 SIGXFSZ,
26 SIGSYS>();
27 }
28
29 template <int... SigNums>
30 void FatalSignal::install_oneshot_signals_handler()
31 {
32 (install_oneshot_signal_handler<SigNums>() , ...);
33 }
34
35 static void reraise_fatal(const int signum)
36 {
37 // use default handler to dump core
38 ::signal(signum, SIG_DFL);
39
40 // normally, we won't get here. if we do, something is very weird.
41 if (::raise(signum)) {
42 std::cerr << "reraise_fatal: failed to re-raise signal " << signum
43 << std::endl;
44 } else {
45 std::cerr << "reraise_fatal: default handler for signal " << signum
46 << " didn't terminate the process?" << std::endl;
47 }
48 std::cerr << std::flush;
49 ::_exit(1);
50 }
51
52 [[gnu::noinline]] void FatalSignal::signal_entry(
53 const int signum,
54 siginfo_t* const info,
55 void*)
56 {
57 if (static std::atomic_bool handled{false}; handled.exchange(true)) {
58 return;
59 }
60 assert(info);
61 FatalSignal::signaled(signum, *info);
62 reraise_fatal(signum);
63 }
64
65 template <int SigNum>
66 void FatalSignal::install_oneshot_signal_handler()
67 {
68 struct sigaction sa;
69 // it's a bad idea to use a lambda here. On GCC there are `operator()`
70 // and `_FUN()`. Controlling their inlineability is hard (impossible?).
71 sa.sa_sigaction = signal_entry;
72 sigemptyset(&sa.sa_mask);
73 sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
74 if constexpr (SigNum == SIGSEGV) {
75 sa.sa_flags |= SA_ONSTACK;
76 }
77 [[maybe_unused]] auto r = ::sigaction(SigNum, &sa, nullptr);
78 assert(r == 0);
79 }
80
81
82 [[gnu::noinline]] static void print_backtrace(std::string_view cause) {
83 std::cerr << cause;
84 if (seastar::engine_is_ready()) {
85 std::cerr << " on shard " << seastar::this_shard_id();
86 }
87 // nobody wants to see things like `FatalSignal::signaled()` or
88 // `print_backtrace()` in our backtraces. `+ 1` is for the extra
89 // frame created by kernel (signal trampoline, it will take care
90 // about e.g. sigreturn(2) calling; see the man page).
91 constexpr std::size_t FRAMES_TO_SKIP = 3 + 1;
92 std::cerr << ".\nBacktrace:\n";
93 std::cerr << boost::stacktrace::stacktrace(
94 FRAMES_TO_SKIP,
95 static_cast<std::size_t>(-1)/* max depth same as the default one */);
96 std::cerr << std::flush;
97 // TODO: dump crash related meta data to $crash_dir
98 // see handle_fatal_signal()
99 }
100
101 static void print_segv_info(const siginfo_t& siginfo)
102 {
103 std::cerr \
104 << "Dump of siginfo:" << std::endl
105 << " si_signo: " << siginfo.si_signo << std::endl
106 << " si_errno: " << siginfo.si_errno << std::endl
107 << " si_code: " << siginfo.si_code << std::endl
108 << " si_pid: " << siginfo.si_pid << std::endl
109 << " si_uid: " << siginfo.si_uid << std::endl
110 << " si_status: " << siginfo.si_status << std::endl
111 << " si_utime: " << siginfo.si_utime << std::endl
112 << " si_stime: " << siginfo.si_stime << std::endl
113 << " si_int: " << siginfo.si_int << std::endl
114 << " si_ptr: " << siginfo.si_ptr << std::endl
115 << " si_overrun: " << siginfo.si_overrun << std::endl
116 << " si_timerid: " << siginfo.si_timerid << std::endl
117 << " si_addr: " << siginfo.si_addr << std::endl
118 << " si_band: " << siginfo.si_band << std::endl
119 << " si_fd: " << siginfo.si_fd << std::endl
120 << " si_addr_lsb: " << siginfo.si_addr_lsb << std::endl
121 << " si_lower: " << siginfo.si_lower << std::endl
122 << " si_upper: " << siginfo.si_upper << std::endl
123 << " si_pkey: " << siginfo.si_pkey << std::endl
124 << " si_call_addr: " << siginfo.si_call_addr << std::endl
125 << " si_syscall: " << siginfo.si_syscall << std::endl
126 << " si_arch: " << siginfo.si_arch << std::endl;
127 std::cerr << std::flush;
128 }
129
130 static void print_proc_maps()
131 {
132 const int fd = ::open("/proc/self/maps", O_RDONLY);
133 if (fd < 0) {
134 std::cerr << "can't open /proc/self/maps. procfs not mounted?" << std::endl;
135 return;
136 }
137 const auto fd_guard = make_scope_guard([fd] {
138 ::close(fd);
139 });
140 std::cerr << "Content of /proc/self/maps:" << std::endl;
141 while (true) {
142 char chunk[4096] = {0, };
143 const ssize_t r = safe_read(fd, chunk, sizeof(chunk) - 1);
144 if (r < 0) {
145 std::cerr << "error while reading /proc/self/maps: " << r << std::endl;
146 return;
147 } else {
148 std::cerr << chunk << std::flush;
149 if (r < static_cast<ssize_t>(sizeof(chunk) - 1)) {
150 return; // eof
151 }
152 }
153 }
154 }
155
156 [[gnu::noinline]] void FatalSignal::signaled(const int signum,
157 const siginfo_t& siginfo)
158 {
159 switch (signum) {
160 case SIGSEGV:
161 print_backtrace("Segmentation fault");
162 print_segv_info(siginfo);
163 break;
164 case SIGABRT:
165 print_backtrace("Aborting");
166 break;
167 default:
168 print_backtrace(fmt::format("Signal {}", signum));
169 break;
170 }
171 print_proc_maps();
172 }