diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/Makefile.am | 15 | ||||
-rw-r--r-- | sql/mysqld.cc | 292 | ||||
-rw-r--r-- | sql/stacktrace.c | 215 | ||||
-rw-r--r-- | sql/stacktrace.h | 51 |
4 files changed, 321 insertions, 252 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 596a1d3acf7..774ef06f17c 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -54,7 +54,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ ha_gemini.h opt_range.h opt_ft.h \ sql_select.h structs.h table.h sql_udf.h hash_filo.h\ lex.h lex_symbol.h sql_acl.h sql_crypt.h md5.h \ - log_event.h mini_client.h sql_repl.h slave.h + log_event.h mini_client.h sql_repl.h slave.h \ + stacktrace.h mysqld_SOURCES = sql_lex.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -67,17 +68,19 @@ mysqld_SOURCES = sql_lex.cc \ sql_base.cc table.cc sql_select.cc sql_insert.cc \ sql_update.cc sql_delete.cc \ procedure.cc item_uniq.cc sql_test.cc \ - log.cc init.cc derror.cc sql_acl.cc unireg.cc \ + log.cc log_event.cc init.cc derror.cc sql_acl.cc \ + unireg.cc \ time.cc opt_range.cc opt_sum.cc opt_ft.cc \ records.cc filesort.cc handler.cc \ - ha_isam.cc ha_isammrg.cc ha_heap.cc \ - ha_myisam.cc ha_myisammrg.cc ha_berkeley.cc \ - ha_innobase.cc ha_gemini.cc \ + ha_heap.cc ha_myisam.cc ha_myisammrg.cc \ + ha_berkeley.cc ha_innobase.cc ha_gemini.cc \ + ha_isam.cc ha_isammrg.cc \ sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ slave.cc sql_repl.cc \ - md5.c log_event.cc mini_client.cc mini_client_errors.c + mini_client.cc mini_client_errors.c \ + md5.c stacktrace.c gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) mysqlbinlog_SOURCES = mysqlbinlog.cc mini_client.cc net_serv.cc \ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 503a45a8da0..c9e19b65a6c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -20,6 +20,7 @@ #include <my_dir.h> #include "sql_acl.h" #include "slave.h" +#include "stacktrace.h" #ifdef HAVE_BERKELEY_DB #include "ha_berkeley.h" #endif @@ -1125,236 +1126,33 @@ static void start_signal_handler(void) #else /* if ! __WIN__ && ! __EMX__ */ #ifdef HAVE_LINUXTHREADS -static sig_handler write_core(int sig); - -#if defined (__i386__) || (defined(__alpha__) && defined(__GNUC__)) -#define LINUX_STACK_TRACE -#endif - -#ifdef LINUX_STACK_TRACE -#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end) - -extern char* __bss_start; -static char* heap_start, *heap_end; - -inline __volatile__ void print_str(const char* name, - const char* val, int max_len) -{ - fprintf(stderr, "%s at %p ", name, val); - if(!PTR_SANE(val)) - { - fprintf(stderr, " is invalid pointer\n"); - return; - } - - fprintf(stderr, "= "); - for(; max_len && PTR_SANE(val) && *val; --max_len) - fputc(*val++, stderr); - fputc('\n', stderr); -} -#endif - - -#ifdef LINUX_STACK_TRACE -#define SIGRETURN_FRAME_COUNT 1 - -#if defined(__alpha__) && defined(__GNUC__) -/* - The only way to backtrace without a symbol table on alpha - is to find stq fp,N(sp), and the first byte - of the instruction opcode will give us the value of N. From this - we can find where the old value of fp is stored -*/ - -#define MAX_INSTR_IN_FUNC 10000 - -inline uchar** find_prev_fp(uint32* pc, uchar** fp) -{ - int i; - for(i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) - { - uchar* p = (uchar*)pc; - if(p[2] == 222 && p[3] == 35) - { - return (uchar**)((uchar*)fp - *(short int*)p); - } - } - return 0; -} - -inline uint32* find_prev_pc(uint32* pc, uchar** fp) -{ - int i; - for(i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) - { - char* p = (char*)pc; - if(p[1] == 0 && p[2] == 94 && p[3] == -73) - { - uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp))); - return prev_pc; - } - } - return 0; -} - -#endif - -inline __volatile__ void trace_stack() -{ - uchar **stack_bottom; - uchar** fp; - LINT_INIT(fp); - LINT_INIT(stack_bottom); - - fprintf(stderr, -"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"); - THD* thd = current_thd; - uint frame_count = 0; -#ifdef __i386__ - __asm __volatile__ ("movl %%ebp,%0" - :"=r"(fp) - :"r"(fp)); - if (!fp) - { - fprintf(stderr, "frame pointer (ebp) is NULL, did you compile with\n\ --fomit-frame-pointer? Aborting backtrace!\n"); - return; - } -#endif -#if defined(__alpha__) && defined(__GNUC__) - __asm __volatile__ ("mov $15,%0" - :"=r"(fp) - :"r"(fp)); - if (!fp) - { - fprintf(stderr, "frame pointer (fp) is NULL, did you compile with\n\ --fomit-frame-pointer? Aborting backtrace!\n"); - return; - } -#endif /* __alpha__ */ - - if (!thd) - { - fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp); - /* Assume that the stack starts at the previous even 65K */ - ulong tmp= min(0x10000,thread_stack); - stack_bottom= (uchar**) (((ulong) &stack_bottom + tmp) & - ~(ulong) 0xFFFF); - } - else - stack_bottom = (uchar**) thd->thread_stack; - if (fp > stack_bottom || fp < stack_bottom - thread_stack) - { - fprintf(stderr, "Bogus stack limit or frame pointer,\ - fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n", - fp, stack_bottom, thread_stack); - return; - } - - fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n"); -#if defined(__alpha__) && defined(__GNUC__) - fprintf(stderr, "Warning: Alpha stacks are difficult -\ - will be taking some wild guesses, stack trace may be incorrect or \ - terminate abruptly\n"); - // On Alpha, we need to get pc - uint32* pc; - __asm __volatile__ ("bsr %0, do_next; do_next: " - :"=r"(pc) - :"r"(pc)); -#endif /* __alpha__ */ - - while (fp < stack_bottom) - { -#ifdef __i386__ - uchar** new_fp = (uchar**)*fp; - fprintf(stderr, "%p\n", frame_count == SIGRETURN_FRAME_COUNT ? - *(fp+17) : *(fp+1)); -#endif -#if defined(__alpha__) && defined(__GNUC__) - uchar** new_fp = find_prev_fp(pc, fp); - if(frame_count == SIGRETURN_FRAME_COUNT - 1) - { - new_fp += 90; - } - - if(fp && pc) - { - pc = find_prev_pc(pc, fp); - if(pc) - fprintf(stderr, "%p\n", pc); - else - { - fprintf(stderr, "Not smart enough to deal with the rest\ - of this stack\n"); - goto print_glob_vars; - } - } - else - { - fprintf(stderr, "Not smart enough to deal with the rest of\ - this stack\n"); - goto print_glob_vars; - } -#endif - if (new_fp <= fp ) - { - fprintf(stderr, "New value of fp=%p failed sanity check,\ - terminating stack trace!\n", new_fp); - goto print_glob_vars; - } - fp = new_fp; - ++frame_count; - } - - fprintf(stderr, "Stack trace seems successful - bottom reached\n"); - - print_glob_vars: - fprintf(stderr, "Please read http://www.mysql.com/doc/U/s/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\ -stack trace is much more helpful in diagnosing the problem, so please do \n\ -resolve it\n"); - fprintf(stderr, "Trying to get some variables.\n\ -Some pointers may be invalid and cause the dump to abort...\n"); - heap_end = (char*)sbrk(0); - print_str("thd->query", thd->query, 1024); - fprintf(stderr, "thd->thread_id = %ld\n", thd->thread_id); - fprintf(stderr, "Successfully dumped variables, if you ran with --log,\n\ -take a look at the details of what thread %ld did to cause the crash.\n\ -In some cases of really bad corruption, this value may be invalid\n", - thd->thread_id); - fprintf(stderr, "Please use the information above to create a repeatable\n\ -test case for the crash, and send it to bugs@lists.mysql.com\n"); -} -#endif -#endif - -#ifdef HAVE_LINUXTHREADS #define UNSAFE_DEFAULT_LINUX_THREADS 200 #endif static sig_handler handle_segfault(int sig) { + THD *thd=current_thd; // strictly speaking, one needs a mutex here // but since we have got SIGSEGV already, things are a mess // so not having the mutex is not as bad as possibly using a buggy // mutex - so we keep things simple if (segfaulted) - { - fprintf(stderr, "Fatal signal %d while backtracing\n", sig); - exit(1); - } + { + fprintf(stderr, "Fatal signal %d while backtracing\n", sig); + exit(1); + } segfaulted = 1; fprintf(stderr,"\ mysqld got signal %d;\n\ -This could be because you hit a bug. It is also possible that \n\ -this binary or one of the libraries it was linked agaist is \n\ -corrupt, improperly built, or misconfigured. This error can also be\n\ -caused by malfunctioning hardware.", sig); - fprintf(stderr, "We will try our best to scrape up some info\n\ -that will hopefully help diagnose the problem, but since we have already\n\ -crashed, something is definitely wrong and this may fail\n"); +This could be because you hit a bug. It is also possible that this binary\n\ +or one of the libraries it was linked agaist is corrupt, improperly built,\n\ +or misconfigured. This error can also be caused by malfunctioning hardware.\n", + sig); + fprintf(stderr, "\ +We will try our best to scrape up some info that will hopefully help diagnose\n\ +the problem, but since we have already crashed, something is definitely wrong\n\ +and this may fail\n\n"); fprintf(stderr, "key_buffer_size=%ld\n", keybuff_size); fprintf(stderr, "record_buffer=%ld\n", my_default_record_cache_size); fprintf(stderr, "sort_buffer=%ld\n", sortbuff_size); @@ -1365,42 +1163,47 @@ crashed, something is definitely wrong and this may fail\n"); key_buffer_size + (record_buffer + sort_buffer)*max_connections = %ld K\n\ bytes of memory\n", (keybuff_size + (my_default_record_cache_size + sortbuff_size) * max_connections)/ 1024); - fprintf(stderr, "Hope that's ok, if not, decrease some variables in the\n\ -equation\n"); + fprintf(stderr, "Hope that's ok, if not, decrease some variables in the equation\n\n"); #if defined(HAVE_LINUXTHREADS) + if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) + { + fprintf(stderr, "\ +You seem to be running 32-bit Linux and have %d concurrent connections.\n\ +If you have not changed STACK_SIZE in LinuxThreads and build the binary \n\ +yourself, LinuxThreads is quite likely to steal a part of global heap for\n\ +the thread stack. Please read http://www.mysql.com/doc/L/i/Linux.html\n\n", + thread_count); + } +#endif /* HAVE_LINUXTHREADS */ - if(sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) - { - fprintf(stderr, "You seem to be running 32-bit Linux and\n\ - have %d concurrent connections. If you have not\n\ -changed STACK_SIZE in LinuxThreads and build the binary yourself,\n\ -LinuxThreads is quite likely to steal a part of global heap for a \n\ -thread stack. Please read http://www.mysql.com/doc/L/i/Linux.html\n", - thread_count); - } -#ifdef LINUX_STACK_TRACE +#ifdef HAVE_STACKTRACE if(!(test_flags & TEST_NO_STACKTRACE)) - trace_stack(); + print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0, + thread_stack); + if (thd) + { + fprintf(stderr, "Trying to get some variables.\n\ +Some pointers may be invalid and cause the dump to abort...\n"); + safe_print_str("thd->query", thd->query, 1024); + fprintf(stderr, "thd->thread_id=%ld\n", thd->thread_id); + fprintf(stderr, "\n +Successfully dumped variables, if you ran with --log, take a look at the\n\ +details of what thread %ld did to cause the crash. In some cases of really\n\ +bad corruption, the above values may be invalid\n\n", + thd->thread_id); + } + fprintf(stderr, "\ +Please use the information above to create a repeatable test case for the\n\ +crash, and send it to bugs@lists.mysql.com\n"); fflush(stderr); -#endif /* LINUX_STACK_TRACE */ +#endif /* HAVE_STACKTRACE */ + if (test_flags & TEST_CORE_ON_SIGNAL) write_core(sig); -#endif /* HAVE_LINUXTHREADS */ exit(1); } -/* Produce a core for the thread */ - -#ifdef HAVE_LINUXTHREADS -static sig_handler write_core(int sig) -{ - signal(sig, SIG_DFL); - if (fork() != 0) exit(1); // Abort main program - // Core will be written at exit -} -#endif - static void init_signals(void) { @@ -1413,12 +1216,9 @@ static void init_signals(void) sigemptyset(&sa.sa_mask); sigprocmask(SIG_SETMASK,&sa.sa_mask,NULL); -#ifdef LINUX_STACK_TRACE - heap_start = (char*)&__bss_start; -#endif - if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL)) { + init_stacktrace(); sa.sa_handler=handle_segfault; sigaction(SIGSEGV, &sa, NULL); #ifdef SIGBUS diff --git a/sql/stacktrace.c b/sql/stacktrace.c new file mode 100644 index 00000000000..ba5bc9d2e85 --- /dev/null +++ b/sql/stacktrace.c @@ -0,0 +1,215 @@ +/* Copyright (C) 2000 MySQL AB + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <global.h> +#include "stacktrace.h" +#include <signal.h> + +#ifdef HAVE_STACKTRACE +#include <unistd.h> + +#define PTR_SANE(p) ((p) && (char*)(p) >= heap_start && (char*)(p) <= heap_end) + +char *heap_start; + +void safe_print_str(const char* name, const char* val, int max_len) +{ + char *heap_end= (char*) sbrk(0); + fprintf(stderr, "%s at %p ", name, val); + + if (!PTR_SANE(val)) + { + fprintf(stderr, " is invalid pointer\n"); + return; + } + + fprintf(stderr, "= "); + for(; max_len && PTR_SANE(val) && *val; --max_len) + fputc(*val++, stderr); + fputc('\n', stderr); +} + +#ifdef HAVE_LINUXTHREADS +#define SIGRETURN_FRAME_COUNT 1 + +#if defined(__alpha__) && defined(__GNUC__) +/* + The only way to backtrace without a symbol table on alpha + is to find stq fp,N(sp), and the first byte + of the instruction opcode will give us the value of N. From this + we can find where the old value of fp is stored +*/ + +#define MAX_INSTR_IN_FUNC 10000 + +inline uchar** find_prev_fp(uint32* pc, uchar** fp) +{ + int i; + for(i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) + { + uchar* p = (uchar*)pc; + if (p[2] == 222 && p[3] == 35) + { + return (uchar**)((uchar*)fp - *(short int*)p); + } + } + return 0; +} + +inline uint32* find_prev_pc(uint32* pc, uchar** fp) +{ + int i; + for(i = 0; i < MAX_INSTR_IN_FUNC; ++i,--pc) + { + char* p = (char*)pc; + if (p[1] == 0 && p[2] == 94 && p[3] == -73) + { + uint32* prev_pc = (uint32*)*((fp+p[0]/sizeof(fp))); + return prev_pc; + } + } + return 0; +} +#endif /* defined(__alpha__) && defined(__GNUC__) */ + + +void print_stacktrace(gptr stack_bottom, ulong thread_stack) +{ + uchar** fp; + uint frame_count = 0; +#if defined(__alpha__) && defined(__GNUC__) + uint32* pc; +#endif + LINT_INIT(fp); + + fprintf(stderr,"\ +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"); +#ifdef __i386__ + __asm __volatile__ ("movl %%ebp,%0" + :"=r"(fp) + :"r"(fp)); + if (!fp) + { + fprintf(stderr, "frame pointer (ebp) is NULL, did you compile with\n\ +-fomit-frame-pointer? Aborting backtrace!\n"); + return; + } +#endif +#if defined(__alpha__) && defined(__GNUC__) + __asm __volatile__ ("mov $15,%0" + :"=r"(fp) + :"r"(fp)); + if (!fp) + { + fprintf(stderr, "frame pointer (fp) is NULL, did you compile with\n\ +-fomit-frame-pointer? Aborting backtrace!\n"); + return; + } +#endif /* __alpha__ */ + + if (!stack_bottom) + { + ulong tmp= min(0x10000,thread_stack); + /* Assume that the stack starts at the previous even 65K */ + stack_bottom= (gptr) (((ulong) &fp + tmp) & + ~(ulong) 0xFFFF); + fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp); + } + if (fp > (uchar**) stack_bottom || + fp < (uchar**) stack_bottom - thread_stack) + { + fprintf(stderr, "Bogus stack limit or frame pointer,\ + fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n", + fp, stack_bottom, thread_stack); + return; + } + + fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n"); +#if defined(__alpha__) && defined(__GNUC__) + fprintf(stderr, "Warning: Alpha stacks are difficult -\ + will be taking some wild guesses, stack trace may be incorrect or \ + terminate abruptly\n"); + // On Alpha, we need to get pc + __asm __volatile__ ("bsr %0, do_next; do_next: " + :"=r"(pc) + :"r"(pc)); +#endif /* __alpha__ */ + + while (fp < (uchar**) stack_bottom) + { +#ifdef __i386__ + uchar** new_fp = (uchar**)*fp; + fprintf(stderr, "%p\n", frame_count == SIGRETURN_FRAME_COUNT ? + *(fp+17) : *(fp+1)); +#endif /* __386__ */ + +#if defined(__alpha__) && defined(__GNUC__) + uchar** new_fp = find_prev_fp(pc, fp); + if (frame_count == SIGRETURN_FRAME_COUNT - 1) + { + new_fp += 90; + } + + if (fp && pc) + { + pc = find_prev_pc(pc, fp); + if (pc) + fprintf(stderr, "%p\n", pc); + else + { + fprintf(stderr, "Not smart enough to deal with the rest\ + of this stack\n"); + goto end; + } + } + else + { + fprintf(stderr, "Not smart enough to deal with the rest of this stack\n"); + goto end; + } +#endif /* defined(__alpha__) && defined(__GNUC__) */ + if (new_fp <= fp ) + { + fprintf(stderr, "New value of fp=%p failed sanity check,\ + terminating stack trace!\n", new_fp); + goto end; + } + fp = new_fp; + ++frame_count; + } + + fprintf(stderr, "Stack trace seems successful - bottom reached\n"); + +end: + fprintf(stderr, "Please read http://www.mysql.com/doc/U/s/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\ +stack trace is much more helpful in diagnosing the problem, so please do \n\ +resolve it\n"); +} +#endif /* HAVE_LINUXTHREADS */ +#endif /* HAVE_STACKTRACE */ + +/* Produce a core for the thread */ + +#ifdef HAVE_WRITE_CORE +void write_core(int sig) +{ + signal(sig, SIG_DFL); + if (fork() != 0) exit(1); // Abort main program + // Core will be written at exit +} +#endif /* HAVE_WRITE_CORE */ diff --git a/sql/stacktrace.h b/sql/stacktrace.h new file mode 100644 index 00000000000..b6c0ec43a0f --- /dev/null +++ b/sql/stacktrace.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2000 MySQL AB + + 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; either version 2 of the License, or + (at your option) any later version. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_LINUXTHREADS +#if defined(HAVE_STACKTRACE) || (defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) +#undef HAVE_STACKTRACE +#define HAVE_STACKTRACE + +extern char* __bss_start; +extern char* heap_start; + +#define init_stacktrace() { heap_start = (char*) &__bss_start; } +void print_stacktrace(gptr stack_bottom, ulong thread_stack); +void safe_print_str(const char* name, const char* val, int max_len); +#endif /* (defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */ + +#define HAVE_WRITE_CORE +void write_core(int sig); +#endif /* HAVE_LINUXTHREADS */ + +/* Define empty prototypes for functions that are not implemented */ +#ifndef HAVE_STACKTRACE +#define init_stacktrace() {} +#define print_stacktrace(A,B) {} +#define safe_print_str(A,B,C) {} +#endif /* HAVE_STACKTRACE */ + +#ifndef HAVE_WRITE_CORE +#define write_core(A) {} +#endif + +#ifdef __cplusplus +} +#endif |