1 | /* Copyright (c) 2011, 2012, Oracle and/or its affiliates. |
2 | Copyright (c) 2011, 2014, SkySQL Ab. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | #include "mariadb.h" |
18 | #include <signal.h> |
19 | |
20 | //#include "sys_vars.h" |
21 | #include <keycache.h> |
22 | #include "mysqld.h" |
23 | #include "sql_class.h" |
24 | #include "my_stacktrace.h" |
25 | |
26 | #ifdef __WIN__ |
27 | #include <crtdbg.h> |
28 | #define SIGNAL_FMT "exception 0x%x" |
29 | #else |
30 | #define SIGNAL_FMT "signal %d" |
31 | #endif |
32 | |
33 | /* |
34 | We are handling signals/exceptions in this file. |
35 | Any global variables we read should be 'volatile sig_atomic_t' |
36 | to guarantee that we read some consistent value. |
37 | */ |
38 | static volatile sig_atomic_t segfaulted= 0; |
39 | extern ulong max_used_connections; |
40 | extern volatile sig_atomic_t calling_initgroups; |
41 | #ifdef HAVE_NPTL |
42 | extern volatile sig_atomic_t ld_assume_kernel_is_set; |
43 | #endif |
44 | |
45 | extern const char *optimizer_switch_names[]; |
46 | |
47 | /** |
48 | * Handler for fatal signals on POSIX, exception handler on Windows. |
49 | * |
50 | * Fatal events (seg.fault, bus error etc.) will trigger |
51 | * this signal handler. The handler will try to dump relevant |
52 | * debugging information to stderr and dump a core image. |
53 | * |
54 | * POSIX : Signal handlers should, if possible, only use a set of 'safe' system |
55 | * calls and library functions. A list of safe calls in POSIX systems |
56 | * are available at: |
57 | * http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html |
58 | * |
59 | * @param sig Signal number /Exception code |
60 | */ |
61 | extern "C" sig_handler handle_fatal_signal(int sig) |
62 | { |
63 | time_t curr_time; |
64 | struct tm tm; |
65 | #ifdef HAVE_STACKTRACE |
66 | THD *thd; |
67 | /* |
68 | This flag remembers if the query pointer was found invalid. |
69 | We will try and print the query at the end of the signal handler, in case |
70 | we're wrong. |
71 | */ |
72 | bool print_invalid_query_pointer= false; |
73 | #endif |
74 | |
75 | if (segfaulted) |
76 | { |
77 | my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n" , sig); |
78 | goto end; |
79 | } |
80 | |
81 | segfaulted = 1; |
82 | |
83 | curr_time= my_time(0); |
84 | localtime_r(&curr_time, &tm); |
85 | |
86 | my_safe_printf_stderr("%02d%02d%02d %2d:%02d:%02d " , |
87 | tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, |
88 | tm.tm_hour, tm.tm_min, tm.tm_sec); |
89 | if (opt_expect_abort |
90 | #ifdef _WIN32 |
91 | && sig == (int)EXCEPTION_BREAKPOINT /* __debugbreak in my_sigabrt_hander() */ |
92 | #else |
93 | && sig == SIGABRT |
94 | #endif |
95 | ) |
96 | { |
97 | fprintf(stderr,"[Note] mysqld did an expected abort\n" ); |
98 | goto end; |
99 | } |
100 | |
101 | my_safe_printf_stderr("[ERROR] mysqld got " SIGNAL_FMT " ;\n" ,sig); |
102 | |
103 | my_safe_printf_stderr("%s" , |
104 | "This could be because you hit a bug. It is also possible that this binary\n" |
105 | "or one of the libraries it was linked against is corrupt, improperly built,\n" |
106 | "or misconfigured. This error can also be caused by malfunctioning hardware.\n\n" ); |
107 | |
108 | my_safe_printf_stderr("%s" , |
109 | "To report this bug, see https://mariadb.com/kb/en/reporting-bugs\n\n" ); |
110 | |
111 | my_safe_printf_stderr("%s" , |
112 | "We will try our best to scrape up some info that will hopefully help\n" |
113 | "diagnose the problem, but since we have already crashed, \n" |
114 | "something is definitely wrong and this may fail.\n\n" ); |
115 | |
116 | set_server_version(server_version, sizeof(server_version)); |
117 | my_safe_printf_stderr("Server version: %s\n" , server_version); |
118 | |
119 | if (dflt_key_cache) |
120 | my_safe_printf_stderr("key_buffer_size=%lu\n" , |
121 | (ulong) dflt_key_cache->key_cache_mem_size); |
122 | |
123 | my_safe_printf_stderr("read_buffer_size=%ld\n" , |
124 | (long) global_system_variables.read_buff_size); |
125 | |
126 | my_safe_printf_stderr("max_used_connections=%lu\n" , |
127 | (ulong) max_used_connections); |
128 | |
129 | if (thread_scheduler) |
130 | my_safe_printf_stderr("max_threads=%u\n" , |
131 | (uint) thread_scheduler->max_threads + |
132 | (uint) extra_max_connections); |
133 | |
134 | my_safe_printf_stderr("thread_count=%u\n" , (uint) thread_count); |
135 | |
136 | if (dflt_key_cache && thread_scheduler) |
137 | { |
138 | my_safe_printf_stderr("It is possible that mysqld could use up to \n" |
139 | "key_buffer_size + " |
140 | "(read_buffer_size + sort_buffer_size)*max_threads = " |
141 | "%lu K bytes of memory\n" , |
142 | (ulong) |
143 | (dflt_key_cache->key_cache_mem_size + |
144 | (global_system_variables.read_buff_size + |
145 | global_system_variables.sortbuff_size) * |
146 | (thread_scheduler->max_threads + extra_max_connections) + |
147 | (max_connections + extra_max_connections) * |
148 | sizeof(THD)) / 1024); |
149 | my_safe_printf_stderr("%s" , |
150 | "Hope that's ok; if not, decrease some variables in " |
151 | "the equation.\n\n" ); |
152 | } |
153 | |
154 | #ifdef HAVE_STACKTRACE |
155 | thd= current_thd; |
156 | |
157 | if (opt_stack_trace) |
158 | { |
159 | my_safe_printf_stderr("Thread pointer: %p\n" , thd); |
160 | my_safe_printf_stderr("%s" , |
161 | "Attempting backtrace. You can use the following " |
162 | "information to find out\n" |
163 | "where mysqld died. If you see no messages after this, something went\n" |
164 | "terribly wrong...\n" ); |
165 | my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, |
166 | (ulong)my_thread_stack_size, 0); |
167 | } |
168 | if (thd) |
169 | { |
170 | const char *kreason= "UNKNOWN" ; |
171 | switch (thd->killed) { |
172 | case NOT_KILLED: |
173 | case KILL_HARD_BIT: |
174 | kreason= "NOT_KILLED" ; |
175 | break; |
176 | case KILL_BAD_DATA: |
177 | case KILL_BAD_DATA_HARD: |
178 | kreason= "KILL_BAD_DATA" ; |
179 | break; |
180 | case KILL_CONNECTION: |
181 | case KILL_CONNECTION_HARD: |
182 | kreason= "KILL_CONNECTION" ; |
183 | break; |
184 | case KILL_QUERY: |
185 | case KILL_QUERY_HARD: |
186 | kreason= "KILL_QUERY" ; |
187 | break; |
188 | case KILL_TIMEOUT: |
189 | case KILL_TIMEOUT_HARD: |
190 | kreason= "KILL_TIMEOUT" ; |
191 | break; |
192 | case KILL_SYSTEM_THREAD: |
193 | case KILL_SYSTEM_THREAD_HARD: |
194 | kreason= "KILL_SYSTEM_THREAD" ; |
195 | break; |
196 | case KILL_SERVER: |
197 | case KILL_SERVER_HARD: |
198 | kreason= "KILL_SERVER" ; |
199 | break; |
200 | case ABORT_QUERY: |
201 | case ABORT_QUERY_HARD: |
202 | kreason= "ABORT_QUERY" ; |
203 | break; |
204 | case KILL_SLAVE_SAME_ID: |
205 | kreason= "KILL_SLAVE_SAME_ID" ; |
206 | break; |
207 | case KILL_WAIT_TIMEOUT: |
208 | case KILL_WAIT_TIMEOUT_HARD: |
209 | kreason= "KILL_WAIT_TIMEOUT" ; |
210 | break; |
211 | } |
212 | my_safe_printf_stderr("%s" , "\n" |
213 | "Trying to get some variables.\n" |
214 | "Some pointers may be invalid and cause the dump to abort.\n" ); |
215 | |
216 | my_safe_printf_stderr("Query (%p): " , thd->query()); |
217 | if (my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length()))) |
218 | { |
219 | // Query was found invalid. We will try to print it at the end. |
220 | print_invalid_query_pointer= true; |
221 | } |
222 | |
223 | my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n" , |
224 | (ulong) thd->thread_id); |
225 | my_safe_printf_stderr("Status: %s\n\n" , kreason); |
226 | my_safe_printf_stderr("%s" , "Optimizer switch: " ); |
227 | ulonglong optsw= thd->variables.optimizer_switch; |
228 | for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1) |
229 | { |
230 | if (i) |
231 | my_safe_printf_stderr("%s" , "," ); |
232 | my_safe_printf_stderr("%s=%s" , |
233 | optimizer_switch_names[i], optsw & 1 ? "on" : "off" ); |
234 | } |
235 | my_safe_printf_stderr("%s" , "\n\n" ); |
236 | } |
237 | my_safe_printf_stderr("%s" , |
238 | "The manual page at " |
239 | "http://dev.mysql.com/doc/mysql/en/crashing.html contains\n" |
240 | "information that should help you find out what is causing the crash.\n" ); |
241 | |
242 | #endif /* HAVE_STACKTRACE */ |
243 | |
244 | #ifdef HAVE_INITGROUPS |
245 | if (calling_initgroups) |
246 | { |
247 | my_safe_printf_stderr("%s" , "\n" |
248 | "This crash occurred while the server was calling initgroups(). This is\n" |
249 | "often due to the use of a mysqld that is statically linked against \n" |
250 | "glibc and configured to use LDAP in /etc/nsswitch.conf.\n" |
251 | "You will need to either upgrade to a version of glibc that does not\n" |
252 | "have this problem (2.3.4 or later when used with nscd),\n" |
253 | "disable LDAP in your nsswitch.conf, or use a " |
254 | "mysqld that is not statically linked.\n" ); |
255 | } |
256 | #endif |
257 | |
258 | #ifdef HAVE_NPTL |
259 | if (thd_lib_detected == THD_LIB_LT && !ld_assume_kernel_is_set) |
260 | { |
261 | my_safe_printf_stderr("%s" , |
262 | "You are running a statically-linked LinuxThreads binary on an NPTL\n" |
263 | "system. This can result in crashes on some distributions due to " |
264 | "LT/NPTL conflicts.\n" |
265 | "You should either build a dynamically-linked binary, " |
266 | "or force LinuxThreads\n" |
267 | "to be used with the LD_ASSUME_KERNEL environment variable.\n" |
268 | "Please consult the documentation for your distribution " |
269 | "on how to do that.\n" ); |
270 | } |
271 | #endif |
272 | |
273 | if (locked_in_memory) |
274 | { |
275 | my_safe_printf_stderr("%s" , "\n" |
276 | "The \"--memlock\" argument, which was enabled, " |
277 | "uses system calls that are\n" |
278 | "unreliable and unstable on some operating systems and " |
279 | "operating-system versions (notably, some versions of Linux).\n" |
280 | "This crash could be due to use of those buggy OS calls.\n" |
281 | "You should consider whether you really need the " |
282 | "\"--memlock\" parameter and/or consult the OS distributer about " |
283 | "\"mlockall\" bugs.\n" ); |
284 | } |
285 | |
286 | #ifdef HAVE_STACKTRACE |
287 | if (print_invalid_query_pointer) |
288 | { |
289 | my_safe_printf_stderr( |
290 | "\nWe think the query pointer is invalid, but we will try " |
291 | "to print it anyway. \n" |
292 | "Query: " ); |
293 | my_write_stderr(thd->query(), MY_MIN(65536U, thd->query_length())); |
294 | my_safe_printf_stderr("\n\n" ); |
295 | } |
296 | #endif |
297 | |
298 | #ifdef HAVE_WRITE_CORE |
299 | if (test_flags & TEST_CORE_ON_SIGNAL) |
300 | { |
301 | char buff[80]; |
302 | my_getwd(buff, sizeof(buff), 0); |
303 | my_safe_printf_stderr("Writing a core file at %s\n" , buff); |
304 | fflush(stderr); |
305 | my_write_core(sig); |
306 | } |
307 | #endif |
308 | |
309 | end: |
310 | #ifndef __WIN__ |
311 | /* |
312 | Quit, without running destructors (etc.) |
313 | Use a signal, because the parent (systemd) can check that with WIFSIGNALED |
314 | On Windows, do not terminate, but pass control to exception filter. |
315 | */ |
316 | signal(sig, SIG_DFL); |
317 | kill(getpid(), sig); |
318 | #else |
319 | return; |
320 | #endif |
321 | } |
322 | |