1 /* Copyright (c) 2011, 2012, Oracle and/or its affiliates.
2 Copyright (c) 2011, 2021, MariaDB Corporation.
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-1335 USA */
16
17 #include "mariadb.h"
18 #include "my_dbug.h"
19 #include <signal.h>
20
21 //#include "sys_vars.h"
22 #include <keycache.h>
23 #include "mysqld.h"
24 #include "sql_class.h"
25 #include "my_stacktrace.h"
26
27 #ifdef __WIN__
28 #include <crtdbg.h>
29 #define SIGNAL_FMT "exception 0x%x"
30 #else
31 #define SIGNAL_FMT "signal %d"
32 #endif
33
34
35 #if defined(__APPLE__) || defined(__FreeBSD__)
36 #include <sys/sysctl.h>
37 #endif
38
39 #ifndef PATH_MAX
40 #define PATH_MAX 4096
41 #endif
42
43 /*
44 We are handling signals/exceptions in this file.
45 Any global variables we read should be 'volatile sig_atomic_t'
46 to guarantee that we read some consistent value.
47 */
48 static volatile sig_atomic_t segfaulted= 0;
49 extern ulong max_used_connections;
50 extern volatile sig_atomic_t calling_initgroups;
51
52 extern const char *optimizer_switch_names[];
53
output_core_info()54 static inline void output_core_info()
55 {
56 /* proc is optional on some BSDs so it can't hurt to look */
57 #if defined(HAVE_READLINK) && !defined(__APPLE__) && !defined(__FreeBSD__)
58 char buff[PATH_MAX];
59 ssize_t len;
60 int fd;
61 if ((len= readlink("/proc/self/cwd", buff, sizeof(buff)-1)) >= 0)
62 {
63 buff[len]= 0;
64 my_safe_printf_stderr("Writing a core file...\nWorking directory at %.*s\n",
65 (int) len, buff);
66 }
67 #ifdef __FreeBSD__
68 if ((fd= my_open("/proc/curproc/rlimit", O_RDONLY, MYF(0))) >= 0)
69 #else
70 if ((fd= my_open("/proc/self/limits", O_RDONLY, MYF(0))) >= 0)
71 #endif
72 {
73 my_safe_printf_stderr("Resource Limits:\n");
74 while ((len= my_read(fd, (uchar*)buff, sizeof(buff), MYF(0))) > 0)
75 {
76 my_write_stderr(buff, len);
77 }
78 my_close(fd, MYF(0));
79 }
80 #ifdef __linux__
81 if ((fd= my_open("/proc/sys/kernel/core_pattern", O_RDONLY, MYF(0))) >= 0)
82 {
83 len= my_read(fd, (uchar*)buff, sizeof(buff), MYF(0));
84 my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff);
85 my_close(fd, MYF(0));
86 }
87 #endif
88 #elif defined(__APPLE__) || defined(__FreeBSD__)
89 char buff[PATH_MAX];
90 size_t len = sizeof(buff);
91 if (sysctlbyname("kern.corefile", buff, &len, NULL, 0) == 0)
92 {
93 my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff);
94 }
95 #else
96 char buff[80];
97 my_getwd(buff, sizeof(buff), 0);
98 my_safe_printf_stderr("Writing a core file at %s\n", buff);
99 fflush(stderr);
100 #endif
101 }
102
103 /**
104 * Handler for fatal signals on POSIX, exception handler on Windows.
105 *
106 * Fatal events (seg.fault, bus error etc.) will trigger
107 * this signal handler. The handler will try to dump relevant
108 * debugging information to stderr and dump a core image.
109 *
110 * POSIX : Signal handlers should, if possible, only use a set of 'safe' system
111 * calls and library functions. A list of safe calls in POSIX systems
112 * are available at:
113 * http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
114 *
115 * @param sig Signal number /Exception code
116 */
handle_fatal_signal(int sig)117 extern "C" sig_handler handle_fatal_signal(int sig)
118 {
119 time_t curr_time;
120 struct tm tm;
121 #ifdef HAVE_STACKTRACE
122 THD *thd;
123 /*
124 This flag remembers if the query pointer was found invalid.
125 We will try and print the query at the end of the signal handler, in case
126 we're wrong.
127 */
128 bool print_invalid_query_pointer= false;
129 #endif
130
131 if (segfaulted)
132 {
133 my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig);
134 goto end;
135 }
136 segfaulted = 1;
137 DBUG_PRINT("error", ("handling fatal signal"));
138
139 curr_time= my_time(0);
140 localtime_r(&curr_time, &tm);
141
142 my_safe_printf_stderr("%02d%02d%02d %2d:%02d:%02d ",
143 tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday,
144 tm.tm_hour, tm.tm_min, tm.tm_sec);
145 if (opt_expect_abort
146 #ifdef _WIN32
147 && sig == (int)EXCEPTION_BREAKPOINT /* __debugbreak in my_sigabrt_hander() */
148 #else
149 && sig == SIGABRT
150 #endif
151 )
152 {
153 fprintf(stderr,"[Note] mysqld did an expected abort\n");
154 goto end;
155 }
156
157 my_safe_printf_stderr("[ERROR] mysqld got " SIGNAL_FMT " ;\n",sig);
158
159 my_safe_printf_stderr("%s",
160 "This could be because you hit a bug. It is also possible that this binary\n"
161 "or one of the libraries it was linked against is corrupt, improperly built,\n"
162 "or misconfigured. This error can also be caused by malfunctioning hardware.\n\n");
163
164 my_safe_printf_stderr("%s",
165 "To report this bug, see https://mariadb.com/kb/en/reporting-bugs\n\n");
166
167 my_safe_printf_stderr("%s",
168 "We will try our best to scrape up some info that will hopefully help\n"
169 "diagnose the problem, but since we have already crashed, \n"
170 "something is definitely wrong and this may fail.\n\n");
171
172 set_server_version(server_version, sizeof(server_version));
173 my_safe_printf_stderr("Server version: %s\n", server_version);
174
175 if (dflt_key_cache)
176 my_safe_printf_stderr("key_buffer_size=%zu\n",
177 dflt_key_cache->key_cache_mem_size);
178
179 my_safe_printf_stderr("read_buffer_size=%lu\n",
180 global_system_variables.read_buff_size);
181
182 my_safe_printf_stderr("max_used_connections=%lu\n",
183 max_used_connections);
184
185 if (thread_scheduler)
186 my_safe_printf_stderr("max_threads=%lu\n",
187 thread_scheduler->max_threads +
188 extra_max_connections);
189
190 my_safe_printf_stderr("thread_count=%u\n", THD_count::value());
191
192 if (dflt_key_cache && thread_scheduler)
193 {
194 size_t used_mem=
195 (dflt_key_cache->key_cache_mem_size +
196 (global_system_variables.read_buff_size +
197 (size_t) global_system_variables.sortbuff_size) *
198 (thread_scheduler->max_threads + extra_max_connections) +
199 (max_connections + extra_max_connections) * sizeof(THD)) / 1024;
200
201 my_safe_printf_stderr("It is possible that mysqld could use up to \n"
202 "key_buffer_size + "
203 "(read_buffer_size + sort_buffer_size)*max_threads = "
204 "%zu K bytes of memory\n", used_mem);
205
206 my_safe_printf_stderr("%s",
207 "Hope that's ok; if not, decrease some variables in "
208 "the equation.\n\n");
209 }
210
211 #ifdef HAVE_STACKTRACE
212 thd= current_thd;
213
214 if (opt_stack_trace)
215 {
216 my_safe_printf_stderr("Thread pointer: %p\n", thd);
217 my_safe_printf_stderr("%s",
218 "Attempting backtrace. You can use the following "
219 "information to find out\n"
220 "where mysqld died. If you see no messages after this, something went\n"
221 "terribly wrong...\n");
222 my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL,
223 (ulong)my_thread_stack_size, 0);
224 }
225 if (thd)
226 {
227 const char *kreason= "UNKNOWN";
228 switch (thd->killed) {
229 case NOT_KILLED:
230 case KILL_HARD_BIT:
231 kreason= "NOT_KILLED";
232 break;
233 case KILL_BAD_DATA:
234 case KILL_BAD_DATA_HARD:
235 kreason= "KILL_BAD_DATA";
236 break;
237 case KILL_CONNECTION:
238 case KILL_CONNECTION_HARD:
239 kreason= "KILL_CONNECTION";
240 break;
241 case KILL_QUERY:
242 case KILL_QUERY_HARD:
243 kreason= "KILL_QUERY";
244 break;
245 case KILL_TIMEOUT:
246 case KILL_TIMEOUT_HARD:
247 kreason= "KILL_TIMEOUT";
248 break;
249 case KILL_SYSTEM_THREAD:
250 case KILL_SYSTEM_THREAD_HARD:
251 kreason= "KILL_SYSTEM_THREAD";
252 break;
253 case KILL_SERVER:
254 case KILL_SERVER_HARD:
255 kreason= "KILL_SERVER";
256 break;
257 case ABORT_QUERY:
258 case ABORT_QUERY_HARD:
259 kreason= "ABORT_QUERY";
260 break;
261 case KILL_SLAVE_SAME_ID:
262 kreason= "KILL_SLAVE_SAME_ID";
263 break;
264 case KILL_WAIT_TIMEOUT:
265 case KILL_WAIT_TIMEOUT_HARD:
266 kreason= "KILL_WAIT_TIMEOUT";
267 break;
268 }
269 my_safe_printf_stderr("%s", "\n"
270 "Trying to get some variables.\n"
271 "Some pointers may be invalid and cause the dump to abort.\n");
272
273 my_safe_printf_stderr("Query (%p): ", thd->query());
274 if (my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length())))
275 {
276 // Query was found invalid. We will try to print it at the end.
277 print_invalid_query_pointer= true;
278 }
279
280 my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n",
281 (ulong) thd->thread_id);
282 my_safe_printf_stderr("Status: %s\n\n", kreason);
283 my_safe_printf_stderr("%s", "Optimizer switch: ");
284 ulonglong optsw= thd->variables.optimizer_switch;
285 for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1)
286 {
287 if (i)
288 my_safe_printf_stderr("%s", ",");
289 my_safe_printf_stderr("%s=%s",
290 optimizer_switch_names[i], optsw & 1 ? "on" : "off");
291 }
292 my_safe_printf_stderr("%s", "\n\n");
293 }
294 my_safe_printf_stderr("%s",
295 "The manual page at "
296 "https://mariadb.com/kb/en/how-to-produce-a-full-stack-trace-for-mysqld/ contains\n"
297 "information that should help you find out what is causing the crash.\n");
298
299 #endif /* HAVE_STACKTRACE */
300
301 #ifdef HAVE_INITGROUPS
302 if (calling_initgroups)
303 {
304 my_safe_printf_stderr("%s", "\n"
305 "This crash occurred while the server was calling initgroups(). This is\n"
306 "often due to the use of a mysqld that is statically linked against \n"
307 "glibc and configured to use LDAP in /etc/nsswitch.conf.\n"
308 "You will need to either upgrade to a version of glibc that does not\n"
309 "have this problem (2.3.4 or later when used with nscd),\n"
310 "disable LDAP in your nsswitch.conf, or use a "
311 "mysqld that is not statically linked.\n");
312 }
313 #endif
314
315 if (locked_in_memory)
316 {
317 my_safe_printf_stderr("%s", "\n"
318 "The \"--memlock\" argument, which was enabled, "
319 "uses system calls that are\n"
320 "unreliable and unstable on some operating systems and "
321 "operating-system versions (notably, some versions of Linux).\n"
322 "This crash could be due to use of those buggy OS calls.\n"
323 "You should consider whether you really need the "
324 "\"--memlock\" parameter and/or consult the OS distributer about "
325 "\"mlockall\" bugs.\n");
326 }
327
328 #ifdef HAVE_STACKTRACE
329 if (print_invalid_query_pointer)
330 {
331 my_safe_printf_stderr(
332 "\nWe think the query pointer is invalid, but we will try "
333 "to print it anyway. \n"
334 "Query: ");
335 my_write_stderr(thd->query(), MY_MIN(65536U, thd->query_length()));
336 my_safe_printf_stderr("\n\n");
337 }
338 #endif
339
340 output_core_info();
341 #ifdef HAVE_WRITE_CORE
342 if (test_flags & TEST_CORE_ON_SIGNAL)
343 {
344 my_write_core(sig);
345 }
346 #endif
347
348 end:
349 #ifndef __WIN__
350 /*
351 Quit, without running destructors (etc.)
352 Use a signal, because the parent (systemd) can check that with WIFSIGNALED
353 On Windows, do not terminate, but pass control to exception filter.
354 */
355 signal(sig, SIG_DFL);
356 kill(getpid(), sig);
357 #else
358 return;
359 #endif
360 }
361