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
2006-12-23 20:17:15 +01:00
the Free Software Foundation ; version 2 of the License .
2001-05-14 00:12:40 +02:00
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 */
2008-02-19 12:37:39 +01:00
/* Workaround for Bug#32082: VOID redefinition on Win results in compile errors*/
# define DONT_DEFINE_VOID 1
2001-09-14 01:54:33 +02:00
# include <my_global.h>
2001-05-14 00:12:40 +02:00
# include "stacktrace.h"
2008-02-19 12:37:39 +01:00
# ifndef __WIN__
2001-05-14 00:12:40 +02:00
# 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 ) ;
2008-02-19 12:37:39 +01:00
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 ) ;
2007-07-07 07:46:17 +02:00
# ifdef HAVE_gcov
/*
For GCOV build , crashing will prevent the writing of code coverage
information from this process , causing gcov output to be incomplete .
So we force the writing of coverage information here before terminating .
*/
extern void __gcov_flush ( void ) ;
__gcov_flush ( ) ;
# endif
2001-08-22 11:22:46 +02:00
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
2008-02-19 12:37:39 +01:00
# else /* __WIN__*/
# include <dbghelp.h>
/*
Stack tracing on Windows is implemented using Debug Helper library ( dbghelp . dll )
We do not redistribute dbghelp and the one comes with older OS ( up to Windows 2000 )
is missing some important functions like functions StackWalk64 or MinidumpWriteDump .
Hence , we have to load functions at runtime using LoadLibrary / GetProcAddress .
*/
typedef DWORD ( WINAPI * SymSetOptions_FctType ) ( DWORD dwOptions ) ;
typedef BOOL ( WINAPI * SymGetModuleInfo64_FctType )
( HANDLE , DWORD64 , PIMAGEHLP_MODULE64 ) ;
typedef BOOL ( WINAPI * SymGetSymFromAddr64_FctType )
( HANDLE , DWORD64 , PDWORD64 , PIMAGEHLP_SYMBOL64 ) ;
typedef BOOL ( WINAPI * SymGetLineFromAddr64_FctType )
( HANDLE , DWORD64 , PDWORD , PIMAGEHLP_LINE64 ) ;
typedef BOOL ( WINAPI * SymInitialize_FctType )
( HANDLE , PSTR , BOOL ) ;
typedef BOOL ( WINAPI * StackWalk64_FctType )
( DWORD , HANDLE , HANDLE , LPSTACKFRAME64 , PVOID , PREAD_PROCESS_MEMORY_ROUTINE64 ,
PFUNCTION_TABLE_ACCESS_ROUTINE64 , PGET_MODULE_BASE_ROUTINE64 ,
PTRANSLATE_ADDRESS_ROUTINE64 ) ;
typedef BOOL ( WINAPI * MiniDumpWriteDump_FctType ) (
IN HANDLE hProcess ,
IN DWORD ProcessId ,
IN HANDLE hFile ,
IN MINIDUMP_TYPE DumpType ,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam , OPTIONAL
IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam , OPTIONAL
IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
) ;
static SymSetOptions_FctType pSymSetOptions ;
static SymGetModuleInfo64_FctType pSymGetModuleInfo64 ;
static SymGetSymFromAddr64_FctType pSymGetSymFromAddr64 ;
static SymInitialize_FctType pSymInitialize ;
static StackWalk64_FctType pStackWalk64 ;
static SymGetLineFromAddr64_FctType pSymGetLineFromAddr64 ;
static MiniDumpWriteDump_FctType pMiniDumpWriteDump ;
static EXCEPTION_POINTERS * exception_ptrs ;
# define MODULE64_SIZE_WINXP 576
# define STACKWALK_MAX_FRAMES 64
/*
Dynamically load dbghelp functions
*/
BOOL init_dbghelp_functions ( )
{
static BOOL first_time = TRUE ;
static BOOL rc ;
HMODULE hDbghlp ;
if ( first_time )
{
first_time = FALSE ;
hDbghlp = LoadLibrary ( " dbghelp " ) ;
if ( ! hDbghlp )
{
rc = FALSE ;
return rc ;
}
pSymSetOptions = ( SymSetOptions_FctType )
GetProcAddress ( hDbghlp , " SymSetOptions " ) ;
pSymInitialize = ( SymInitialize_FctType )
GetProcAddress ( hDbghlp , " SymInitialize " ) ;
pSymGetModuleInfo64 = ( SymGetModuleInfo64_FctType )
GetProcAddress ( hDbghlp , " SymGetModuleInfo64 " ) ;
pSymGetLineFromAddr64 = ( SymGetLineFromAddr64_FctType )
GetProcAddress ( hDbghlp , " SymGetLineFromAddr64 " ) ;
pSymGetSymFromAddr64 = ( SymGetSymFromAddr64_FctType )
GetProcAddress ( hDbghlp , " SymGetSymFromAddr64 " ) ;
pStackWalk64 = ( StackWalk64_FctType )
GetProcAddress ( hDbghlp , " StackWalk64 " ) ;
pMiniDumpWriteDump = ( MiniDumpWriteDump_FctType )
GetProcAddress ( hDbghlp , " MiniDumpWriteDump " ) ;
rc = ( BOOL ) ( pSymSetOptions & & pSymInitialize & & pSymGetModuleInfo64
& & pSymGetLineFromAddr64 & & pSymGetSymFromAddr64 & & pStackWalk64 ) ;
}
return rc ;
}
void set_exception_pointers ( EXCEPTION_POINTERS * ep )
{
exception_ptrs = ep ;
}
/* Platform SDK in VS2003 does not have definition for SYMOPT_NO_PROMPTS*/
# ifndef SYMOPT_NO_PROMPTS
# define SYMOPT_NO_PROMPTS 0
# endif
void print_stacktrace ( gptr unused1 , ulong unused2 )
{
HANDLE hProcess = GetCurrentProcess ( ) ;
HANDLE hThread = GetCurrentThread ( ) ;
static IMAGEHLP_MODULE64 module = { sizeof ( module ) } ;
static IMAGEHLP_SYMBOL64_PACKAGE package ;
DWORD64 addr ;
DWORD machine ;
int i ;
CONTEXT context ;
STACKFRAME64 frame = { 0 } ;
if ( ! exception_ptrs | | ! init_dbghelp_functions ( ) )
return ;
/* Copy context, as stackwalking on original will unwind the stack */
context = * ( exception_ptrs - > ContextRecord ) ;
/*Initialize symbols.*/
pSymSetOptions ( SYMOPT_LOAD_LINES | SYMOPT_NO_PROMPTS | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG ) ;
pSymInitialize ( hProcess , NULL , TRUE ) ;
/*Prepare stackframe for the first StackWalk64 call*/
frame . AddrFrame . Mode = frame . AddrPC . Mode = frame . AddrStack . Mode = AddrModeFlat ;
# if (defined _M_IX86)
machine = IMAGE_FILE_MACHINE_I386 ;
frame . AddrFrame . Offset = context . Ebp ;
frame . AddrPC . Offset = context . Eip ;
frame . AddrStack . Offset = context . Esp ;
# elif (defined _M_X64)
machine = IMAGE_FILE_MACHINE_AMD64 ;
frame . AddrFrame . Offset = context . Rbp ;
frame . AddrPC . Offset = context . Rip ;
frame . AddrStack . Offset = context . Rsp ;
# else
/*There is currently no need to support IA64*/
# pragma error ("unsupported architecture")
# endif
package . sym . SizeOfStruct = sizeof ( package . sym ) ;
package . sym . MaxNameLength = sizeof ( package . name ) ;
/*Walk the stack, output useful information*/
for ( i = 0 ; i < STACKWALK_MAX_FRAMES ; i + + )
{
DWORD64 function_offset = 0 ;
DWORD line_offset = 0 ;
IMAGEHLP_LINE64 line = { sizeof ( line ) } ;
BOOL have_module = FALSE ;
BOOL have_symbol = FALSE ;
BOOL have_source = FALSE ;
if ( ! pStackWalk64 ( machine , hProcess , hThread , & frame , & context , 0 , 0 , 0 , 0 ) )
break ;
addr = frame . AddrPC . Offset ;
have_module = pSymGetModuleInfo64 ( hProcess , addr , & module ) ;
# ifdef _M_IX86
if ( ! have_module )
{
/*
ModuleInfo structure has been " compatibly " extended in releases after XP ,
and its size was increased . To make XP dbghelp . dll function
happy , pretend passing the old structure .
*/
module . SizeOfStruct = MODULE64_SIZE_WINXP ;
have_module = pSymGetModuleInfo64 ( hProcess , addr , & module ) ;
}
# endif
have_symbol = pSymGetSymFromAddr64 ( hProcess , addr , & function_offset ,
& ( package . sym ) ) ;
have_source = pSymGetLineFromAddr64 ( hProcess , addr , & line_offset , & line ) ;
fprintf ( stderr , " %p " , addr ) ;
if ( have_module )
{
char * base_image_name = strrchr ( module . ImageName , ' \\ ' ) ;
if ( base_image_name )
base_image_name + + ;
else
base_image_name = module . ImageName ;
fprintf ( stderr , " %s! " , base_image_name ) ;
}
if ( have_symbol )
fprintf ( stderr , " %s() " , package . sym . Name ) ;
else if ( have_module )
fprintf ( stderr , " ??? " ) ;
if ( have_source )
{
char * base_file_name = strrchr ( line . FileName , ' \\ ' ) ;
if ( base_file_name )
base_file_name + + ;
else
base_file_name = line . FileName ;
fprintf ( stderr , " [%s:%u] " , base_file_name , line . LineNumber ) ;
}
fprintf ( stderr , " \n " ) ;
}
fflush ( stderr ) ;
}
/*
Write dump . The dump is created in current directory ,
file name is constructed from executable name plus
" .dmp " extension
*/
void write_core ( int unused )
{
char path [ MAX_PATH ] ;
char dump_fname [ MAX_PATH ] = " core.dmp " ;
MINIDUMP_EXCEPTION_INFORMATION info ;
HANDLE hFile ;
if ( ! exception_ptrs | | ! init_dbghelp_functions ( ) | | ! pMiniDumpWriteDump )
return ;
info . ExceptionPointers = exception_ptrs ;
info . ClientPointers = FALSE ;
info . ThreadId = GetCurrentThreadId ( ) ;
if ( GetModuleFileName ( NULL , path , sizeof ( path ) ) )
{
_splitpath ( path , NULL , NULL , dump_fname , NULL ) ;
strncat ( dump_fname , " .dmp " , sizeof ( dump_fname ) ) ;
}
hFile = CreateFile ( dump_fname , GENERIC_WRITE , 0 , 0 , CREATE_ALWAYS ,
FILE_ATTRIBUTE_NORMAL , 0 ) ;
if ( hFile )
{
/* Create minidump */
if ( pMiniDumpWriteDump ( GetCurrentProcess ( ) , GetCurrentProcessId ( ) ,
hFile , MiniDumpNormal , & info , 0 , 0 ) )
{
fprintf ( stderr , " Minidump written to %s \n " ,
_fullpath ( path , dump_fname , sizeof ( path ) ) ? path : dump_fname ) ;
}
else
{
fprintf ( stderr , " MiniDumpWriteDump() failed, last error %u \n " ,
GetLastError ( ) ) ;
}
CloseHandle ( hFile ) ;
}
else
{
fprintf ( stderr , " CreateFile(%s) failed, last error %u \n " , dump_fname ,
GetLastError ( ) ) ;
}
fflush ( stderr ) ;
}
void safe_print_str ( const char * name , const char * val , int len )
{
fprintf ( stderr , " %s at %p " , name , val ) ;
__try
{
fprintf ( stderr , " =%.*s \n " , len , val ) ;
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
fprintf ( stderr , " is an invalid string pointer \n " ) ;
}
}
# endif /*__WIN__*/