| 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 | |