diff options
Diffstat (limited to 'sql/signal_handler.cc')
-rw-r--r-- | sql/signal_handler.cc | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc new file mode 100644 index 00000000..534a9a1c --- /dev/null +++ b/sql/signal_handler.cc @@ -0,0 +1,373 @@ +/* Copyright (c) 2011, 2012, Oracle and/or its affiliates. + Copyright (c) 2011, 2021, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "mariadb.h" +#include "my_dbug.h" +#include <signal.h> + +//#include "sys_vars.h" +#include <keycache.h> +#include "mysqld.h" +#include "sql_class.h" +#include "my_stacktrace.h" +#include <source_revision.h> + +#ifdef _WIN32 +#include <crtdbg.h> +#include <direct.h> +#define SIGNAL_FMT "exception 0x%x" +#else +#define SIGNAL_FMT "signal %d" +#endif + + +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <sys/sysctl.h> +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/* + We are handling signals/exceptions in this file. + Any global variables we read should be 'volatile sig_atomic_t' + to guarantee that we read some consistent value. + */ +static volatile sig_atomic_t segfaulted= 0; +extern ulong max_used_connections; +extern volatile sig_atomic_t calling_initgroups; + +extern const char *optimizer_switch_names[]; + +static inline void output_core_info() +{ + /* proc is optional on some BSDs so it can't hurt to look */ +#if defined(HAVE_READLINK) && !defined(__APPLE__) && !defined(__FreeBSD__) + char buff[PATH_MAX]; + ssize_t len; + int fd; + if ((len= readlink("/proc/self/cwd", buff, sizeof(buff)-1)) >= 0) + { + buff[len]= 0; + my_safe_printf_stderr("Writing a core file...\nWorking directory at %.*s\n", + (int) len, buff); + } +#ifdef __FreeBSD__ + if ((fd= open("/proc/curproc/rlimit", O_RDONLY)) >= 0) +#else + if ((fd= open("/proc/self/limits", O_RDONLY)) >= 0) +#endif + { + my_safe_printf_stderr("Resource Limits:\n"); + while ((len= read(fd, (uchar*)buff, sizeof(buff))) > 0) + { + my_write_stderr(buff, len); + } + close(fd); + } +#ifdef __linux__ + if ((fd= open("/proc/sys/kernel/core_pattern", O_RDONLY)) >= 0) + { + len= read(fd, (uchar*)buff, sizeof(buff)); + my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff); + close(fd); + } + if ((fd= open("/proc/version", O_RDONLY)) >= 0) + { + len= read(fd, (uchar*)buff, sizeof(buff)); + my_safe_printf_stderr("Kernel version: %.*s\n", (int) len, buff); + close(fd); + } +#endif +#elif defined(__APPLE__) || defined(__FreeBSD__) + char buff[PATH_MAX]; + size_t len = sizeof(buff); + if (sysctlbyname("kern.corefile", buff, &len, NULL, 0) == 0) + { + my_safe_printf_stderr("Core pattern: %.*s\n", (int) len, buff); + } + if (sysctlbyname("kern.version", buff, &len, NULL, 0) == 0) + { + my_safe_printf_stderr("Kernel version: %.*s\n", (int) len, buff); + } +#elif defined(HAVE_GETCWD) + char buff[80]; + + if (getcwd(buff, sizeof(buff))) + { + my_safe_printf_stderr("Writing a core file at %.*s\n", (int) sizeof(buff), buff); + fflush(stderr); + } +#endif +} + +/** + * Handler for fatal signals on POSIX, exception handler on Windows. + * + * Fatal events (seg.fault, bus error etc.) will trigger + * this signal handler. The handler will try to dump relevant + * debugging information to stderr and dump a core image. + * + * POSIX : Signal handlers should, if possible, only use a set of 'safe' system + * calls and library functions. A list of safe calls in POSIX systems + * are available at: + * http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html + * + * @param sig Signal number /Exception code +*/ +extern "C" sig_handler handle_fatal_signal(int sig) +{ + time_t curr_time; + struct tm tm; +#ifdef HAVE_STACKTRACE + THD *thd; + /* + This flag remembers if the query pointer was found invalid. + We will try and print the query at the end of the signal handler, in case + we're wrong. + */ + bool print_invalid_query_pointer= false; +#endif + + if (segfaulted) + { + my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig); + goto end; + } + segfaulted = 1; + DBUG_PRINT("error", ("handling fatal signal")); + + curr_time= my_time(0); + localtime_r(&curr_time, &tm); + + my_safe_printf_stderr("%02d%02d%02d %2d:%02d:%02d ", + tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + if (opt_expect_abort +#ifdef _WIN32 + && sig == (int)EXCEPTION_BREAKPOINT /* __debugbreak in my_sigabrt_hander() */ +#else + && sig == SIGABRT +#endif + ) + { + fprintf(stderr,"[Note] mysqld did an expected abort\n"); + goto end; + } + + my_safe_printf_stderr("[ERROR] mysqld got " SIGNAL_FMT " ;\n",sig); + + my_safe_printf_stderr("%s", + "Sorry, we probably made a mistake, and this is a bug.\n\n" + "Your assistance in bug reporting will enable us to fix this for the next release.\n" + "To report this bug, see https://mariadb.com/kb/en/reporting-bugs\n\n"); + + my_safe_printf_stderr("%s", + "We will try our best to scrape up some info that will hopefully help\n" + "diagnose the problem, but since we have already crashed, \n" + "something is definitely wrong and this may fail.\n\n"); + + set_server_version(server_version, sizeof(server_version)); + my_safe_printf_stderr("Server version: %s source revision: %s\n", + server_version, SOURCE_REVISION); + + if (dflt_key_cache) + my_safe_printf_stderr("key_buffer_size=%zu\n", + dflt_key_cache->key_cache_mem_size); + + my_safe_printf_stderr("read_buffer_size=%lu\n", + global_system_variables.read_buff_size); + + my_safe_printf_stderr("max_used_connections=%lu\n", + max_used_connections); + + if (thread_scheduler) + my_safe_printf_stderr("max_threads=%lu\n", + thread_scheduler->max_threads + + extra_max_connections); + + my_safe_printf_stderr("thread_count=%u\n", THD_count::value()); + + if (dflt_key_cache && thread_scheduler) + { + size_t used_mem= + (dflt_key_cache->key_cache_mem_size + + (global_system_variables.read_buff_size + + (size_t) global_system_variables.sortbuff_size) * + (thread_scheduler->max_threads + extra_max_connections) + + (max_connections + extra_max_connections) * sizeof(THD)) / 1024; + + my_safe_printf_stderr("It is possible that mysqld could use up to \n" + "key_buffer_size + " + "(read_buffer_size + sort_buffer_size)*max_threads = " + "%zu K bytes of memory\n", used_mem); + + my_safe_printf_stderr("%s", + "Hope that's ok; if not, decrease some variables in " + "the equation.\n\n"); + } + +#ifdef HAVE_STACKTRACE + thd= current_thd; + + if (opt_stack_trace) + { + my_safe_printf_stderr("Thread pointer: %p\n", thd); + my_safe_printf_stderr("%s", + "Attempting backtrace. You can use the following " + "information to find out\n" + "where mysqld died. If you see no messages after this, something went\n" + "terribly wrong...\n"); + my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, + (ulong)my_thread_stack_size, 0); + } + if (thd) + { + const char *kreason= "UNKNOWN"; + switch (thd->killed) { + case NOT_KILLED: + case KILL_HARD_BIT: + kreason= "NOT_KILLED"; + break; + case KILL_BAD_DATA: + case KILL_BAD_DATA_HARD: + kreason= "KILL_BAD_DATA"; + break; + case KILL_CONNECTION: + case KILL_CONNECTION_HARD: + kreason= "KILL_CONNECTION"; + break; + case KILL_QUERY: + case KILL_QUERY_HARD: + kreason= "KILL_QUERY"; + break; + case KILL_TIMEOUT: + case KILL_TIMEOUT_HARD: + kreason= "KILL_TIMEOUT"; + break; + case KILL_SYSTEM_THREAD: + case KILL_SYSTEM_THREAD_HARD: + kreason= "KILL_SYSTEM_THREAD"; + break; + case KILL_SERVER: + case KILL_SERVER_HARD: + kreason= "KILL_SERVER"; + break; + case ABORT_QUERY: + case ABORT_QUERY_HARD: + kreason= "ABORT_QUERY"; + break; + case KILL_SLAVE_SAME_ID: + kreason= "KILL_SLAVE_SAME_ID"; + break; + case KILL_WAIT_TIMEOUT: + case KILL_WAIT_TIMEOUT_HARD: + kreason= "KILL_WAIT_TIMEOUT"; + break; + } + my_safe_printf_stderr("%s", "\n" + "Trying to get some variables.\n" + "Some pointers may be invalid and cause the dump to abort.\n"); + + my_safe_printf_stderr("Query (%p): ", thd->query()); + if (my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length()))) + { + // Query was found invalid. We will try to print it at the end. + print_invalid_query_pointer= true; + } + + my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n", + (ulong) thd->thread_id); + my_safe_printf_stderr("Status: %s\n\n", kreason); + my_safe_printf_stderr("%s", "Optimizer switch: "); + ulonglong optsw= thd->variables.optimizer_switch; + for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1) + { + if (i) + my_safe_printf_stderr("%s", ","); + my_safe_printf_stderr("%s=%s", + optimizer_switch_names[i], optsw & 1 ? "on" : "off"); + } + my_safe_printf_stderr("%s", "\n\n"); + } + my_safe_printf_stderr("%s", + "The manual page at " + "https://mariadb.com/kb/en/how-to-produce-a-full-stack-trace-for-mariadbd/ contains\n" + "information that should help you find out what is causing the crash.\n"); + +#endif /* HAVE_STACKTRACE */ + +#ifdef HAVE_INITGROUPS + if (calling_initgroups) + { + my_safe_printf_stderr("%s", "\n" + "This crash occurred while the server was calling initgroups(). This is\n" + "often due to the use of a mysqld that is statically linked against \n" + "glibc and configured to use LDAP in /etc/nsswitch.conf.\n" + "You will need to either upgrade to a version of glibc that does not\n" + "have this problem (2.3.4 or later when used with nscd),\n" + "disable LDAP in your nsswitch.conf, or use a " + "mysqld that is not statically linked.\n"); + } +#endif + + if (locked_in_memory) + { + my_safe_printf_stderr("%s", "\n" + "The \"--memlock\" argument, which was enabled, " + "uses system calls that are\n" + "unreliable and unstable on some operating systems and " + "operating-system versions (notably, some versions of Linux).\n" + "This crash could be due to use of those buggy OS calls.\n" + "You should consider whether you really need the " + "\"--memlock\" parameter and/or consult the OS distributer about " + "\"mlockall\" bugs.\n"); + } + +#ifdef HAVE_STACKTRACE + if (print_invalid_query_pointer) + { + my_safe_printf_stderr( + "\nWe think the query pointer is invalid, but we will try " + "to print it anyway. \n" + "Query: "); + my_write_stderr(thd->query(), MY_MIN(65536U, thd->query_length())); + my_safe_printf_stderr("\n\n"); + } +#endif + + output_core_info(); +#ifdef HAVE_WRITE_CORE + if (test_flags & TEST_CORE_ON_SIGNAL) + { + my_write_core(sig); + } +#endif + +end: +#ifndef _WIN32 + /* + Quit, without running destructors (etc.) + Use a signal, because the parent (systemd) can check that with WIFSIGNALED + On Windows, do not terminate, but pass control to exception filter. + */ + signal(sig, SIG_DFL); + kill(getpid(), sig); +#else + return; +#endif +} |