2001-05-14 00:12:40 +02:00
/* 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 */
2001-09-14 01:54:33 +02:00
# include <my_global.h>
2001-05-14 00:12:40 +02:00
# include "stacktrace.h"
# include <signal.h>
2001-09-02 15:03:37 +02:00
# include <my_pthread.h>
2001-05-14 00:12:40 +02:00
# ifdef HAVE_STACKTRACE
# include <unistd.h>
2006-08-25 15:59:47 +02:00
# include <strings.h>
2001-05-14 00:12:40 +02:00
# 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 , " = " ) ;
2002-12-28 00:01:05 +01:00
for ( ; max_len & & PTR_SANE ( val ) & & * val ; - - max_len )
2001-05-14 00:12:40 +02:00
fputc ( * val + + , stderr ) ;
fputc ( ' \n ' , stderr ) ;
}
2005-04-20 20:38:57 +02:00
# ifdef TARGET_OS_LINUX
2006-08-25 15:59:47 +02:00
# ifdef __i386__
# define SIGRETURN_FRAME_OFFSET 17
# endif
# ifdef __x86_64__
# define SIGRETURN_FRAME_OFFSET 23
# endif
static my_bool is_nptl ;
/* Check if we are using NPTL or LinuxThreads on Linux */
void check_thread_lib ( void )
{
char buf [ 5 ] ;
2006-08-26 08:21:26 +02:00
# ifdef _CS_GNU_LIBPTHREAD_VERSION
2006-08-25 15:59:47 +02:00
confstr ( _CS_GNU_LIBPTHREAD_VERSION , buf , sizeof ( buf ) ) ;
is_nptl = ! strncasecmp ( buf , " NPTL " , sizeof ( buf ) ) ;
2006-08-26 08:21:26 +02:00
# else
is_nptl = 0 ;
# endif
2006-08-25 15:59:47 +02:00
}
2001-05-14 00:12:40 +02:00
# 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 ;
2002-12-28 00:01:05 +01:00
for ( i = 0 ; i < MAX_INSTR_IN_FUNC ; + + i , - - pc )
2001-05-14 00:12:40 +02:00
{
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 ;
2002-12-28 00:01:05 +01:00
for ( i = 0 ; i < MAX_INSTR_IN_FUNC ; + + i , - - pc )
2001-05-14 00:12:40 +02:00
{
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 ;
2006-08-25 15:59:47 +02:00
uint frame_count = 0 , sigreturn_frame_count ;
2001-05-14 00:12:40 +02:00
# 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 " );
2006-08-25 15:59:47 +02:00
# ifdef __i386__
2001-05-14 00:12:40 +02:00
__asm __volatile__ ( " movl %%ebp,%0 "
: " =r " ( fp )
: " r " ( fp ) ) ;
2006-08-25 15:59:47 +02:00
# endif
# ifdef __x86_64__
__asm __volatile__ ( " movq %%rbp,%0 "
: " =r " ( fp )
: " r " ( fp ) ) ;
2001-05-14 00:12:40 +02:00
# endif
# if defined(__alpha__) && defined(__GNUC__)
2001-05-14 22:11:26 +02:00
__asm __volatile__ ( " mov $30,%0 "
2001-05-14 00:12:40 +02:00
: " =r " ( fp )
: " r " ( fp ) ) ;
2006-08-25 15:59:47 +02:00
# endif
2001-05-14 00:12:40 +02:00
if ( ! fp )
{
2006-08-25 15:59:47 +02:00
fprintf ( stderr , " frame pointer is NULL, did you compile with \n \
2001-05-14 00:12:40 +02:00
- fomit - frame - pointer ? Aborting backtrace ! \ n " );
return ;
}
2001-12-06 13:10:51 +01:00
2002-01-20 03:16:52 +01:00
if ( ! stack_bottom | | ( gptr ) stack_bottom > ( gptr ) & fp )
2001-05-14 00:12:40 +02:00
{
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 " );
2002-07-17 14:17:20 +02:00
/* On Alpha, we need to get pc */
2001-05-14 00:12:40 +02:00
__asm __volatile__ ( " bsr %0, do_next; do_next: "
: " =r " ( pc )
: " r " ( pc ) ) ;
# endif /* __alpha__ */
2001-12-06 13:10:51 +01:00
2006-08-25 15:59:47 +02:00
/* We are 1 frame above signal frame with NPTL and 2 frames above with LT */
sigreturn_frame_count = is_nptl ? 1 : 2 ;
2001-05-14 00:12:40 +02:00
while ( fp < ( uchar * * ) stack_bottom )
{
2006-08-25 15:59:47 +02:00
# if defined(__i386__) || defined(__x86_64__)
2001-05-14 00:12:40 +02:00
uchar * * new_fp = ( uchar * * ) * fp ;
2006-08-25 15:59:47 +02:00
fprintf ( stderr , " %p \n " , frame_count = = sigreturn_frame_count ?
* ( fp + SIGRETURN_FRAME_OFFSET ) : * ( fp + 1 ) ) ;
# endif /* defined(__386__) || defined(__x86_64__) */
2001-05-14 00:12:40 +02:00
# if defined(__alpha__) && defined(__GNUC__)
uchar * * new_fp = find_prev_fp ( pc , fp ) ;
2006-10-20 12:31:47 +02:00
if ( frame_count = = sigreturn_frame_count - 1 )
2001-05-14 00:12:40 +02:00
{
new_fp + = 90 ;
}
2001-12-06 13:10:51 +01:00
2001-05-14 00:12:40 +02:00
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 " ) ;
2001-12-06 13:10:51 +01:00
2001-05-14 00:12:40 +02:00
end :
2006-09-28 19:58:53 +02:00
fprintf ( stderr , " Please read http://dev.mysql.com/doc/mysql/en/using-stack-trace.html and follow instructions on how to resolve the stack trace. Resolved \n \
2001-05-14 00:12:40 +02:00
stack trace is much more helpful in diagnosing the problem , so please do \ n \
resolve it \ n " );
}
2005-04-20 20:38:57 +02:00
# endif /* TARGET_OS_LINUX */
2001-05-14 00:12:40 +02:00
# endif /* HAVE_STACKTRACE */
/* Produce a core for the thread */
2002-11-04 23:04:36 +01:00
# ifdef NOT_USED /* HAVE_LINUXTHREADS */
2001-05-14 00:12:40 +02:00
void write_core ( int sig )
{
signal ( sig , SIG_DFL ) ;
2002-07-17 14:17:20 +02:00
if ( fork ( ) ! = 0 ) exit ( 1 ) ; /* Abort main program */
/* Core will be written at exit */
2001-05-14 00:12:40 +02:00
}
2001-08-22 11:22:46 +02:00
# else
void write_core ( int sig )
{
signal ( sig , SIG_DFL ) ;
pthread_kill ( pthread_self ( ) , sig ) ;
2001-11-28 02:47:15 +01:00
# if defined(P_MYID) && !defined(SCO)
2001-11-21 14:08:01 +01:00
/* On Solaris, the above kill is not enough */
sigsend ( P_PID , P_MYID , sig ) ;
2001-11-22 12:07:55 +01:00
# endif
2001-08-22 11:22:46 +02:00
}
# endif