Commit 9624e880 authored by unknown's avatar unknown

Bug#31745 - crash handler does not work on Windows

- Replace per-thread signal()'s with  SetUnhandledExceptionFilter(). 
  The only remaining signal() is for SIGABRT (default abort()
  handler in VS2005 is broken, i.e removes user exception filter)
- remove MessageBox()'es  from error handling code
- Windows port for print_stacktrace() and write_core() 
- Cleanup, removed some unused functions


sql/CMakeLists.txt:
  Implement stack tracing on and generating crash dumps on Windows
sql/mysqld.cc:
  Correct signal handling on Windows. 
  - For console events, like CTRL-C use SetConsoleCtrlHandler
  - For exceptions like access violation, use SetUnhandledExceptionFilter
  - For SIGABRT generate exception via __debugbreak() intrinsic
    if built with VS2005 and later , since default SIGABRT handler 
    replaces unhandled exception filter specified by user
  - make provisions to debug exception filter, as it is not trivial 
  (should be compiled with /DDEBUG_UNHANDLED_EXCEPTION_FILTER)
sql/sql_parse.cc:
  Remove message box from windows signal handler.
  The only thread specific handler left is for SIGABRT,
  which is broken on VS2005 and later (user specified unhandled exception 
  filter gets overwritten)
sql/stacktrace.c:
  Stack tracing and generating crash dumps on Windows
sql/stacktrace.h:
  Implement print_stacktrace and write_core on Windows
parent 2989a539
...@@ -48,7 +48,7 @@ ENDIF(DISABLE_GRANT_OPTIONS) ...@@ -48,7 +48,7 @@ ENDIF(DISABLE_GRANT_OPTIONS)
ADD_EXECUTABLE(mysqld${MYSQLD_EXE_SUFFIX} ADD_EXECUTABLE(mysqld${MYSQLD_EXE_SUFFIX}
../sql-common/client.c derror.cc des_key_file.cc ../sql-common/client.c derror.cc des_key_file.cc
discover.cc ../libmysql/errmsg.c field.cc field_conv.cc discover.cc ../libmysql/errmsg.c field.cc stacktrace.c stacktrace.h field_conv.cc
filesort.cc gstream.cc ha_blackhole.cc filesort.cc gstream.cc ha_blackhole.cc
ha_archive.cc ha_heap.cc ha_myisam.cc ha_myisammrg.cc ha_archive.cc ha_heap.cc ha_myisam.cc ha_myisammrg.cc
ha_innodb.cc ha_federated.cc ha_berkeley.cc ha_innodb.cc ha_federated.cc ha_berkeley.cc
......
...@@ -138,6 +138,13 @@ extern "C" { // Because of SCO 3.2V4.2 ...@@ -138,6 +138,13 @@ extern "C" { // Because of SCO 3.2V4.2
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#ifdef __WIN__
#include <crtdbg.h>
#define SIGNAL_FMT "exception 0x%x"
#else
#define SIGNAL_FMT "signal %d"
#endif
#ifdef __NETWARE__ #ifdef __NETWARE__
#define zVOLSTATE_ACTIVE 6 #define zVOLSTATE_ACTIVE 6
#define zVOLSTATE_DEACTIVE 2 #define zVOLSTATE_DEACTIVE 2
...@@ -227,6 +234,7 @@ inline void set_proper_floating_point_mode() ...@@ -227,6 +234,7 @@ inline void set_proper_floating_point_mode()
extern "C" int gethostname(char *name, int namelen); extern "C" int gethostname(char *name, int namelen);
#endif #endif
extern "C" sig_handler handle_segfault(int sig);
/* Constants */ /* Constants */
...@@ -1031,9 +1039,6 @@ static void __cdecl kill_server(int sig_ptr) ...@@ -1031,9 +1039,6 @@ static void __cdecl kill_server(int sig_ptr)
#endif #endif
close_connections(); close_connections();
if (sig != MYSQL_KILL_SIGNAL && if (sig != MYSQL_KILL_SIGNAL &&
#ifdef __WIN__
sig != SIGINT && /* Bug#18235 */
#endif
sig != 0) sig != 0)
unireg_abort(1); /* purecov: inspected */ unireg_abort(1); /* purecov: inspected */
else else
...@@ -1592,8 +1597,7 @@ static void network_init(void) ...@@ -1592,8 +1597,7 @@ static void network_init(void)
FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL ); (LPTSTR) &lpMsgBuf, 0, NULL );
MessageBox(NULL, (LPTSTR) lpMsgBuf, "Error from CreateNamedPipe", sql_perror((char *)lpMsgBuf);
MB_OK|MB_ICONINFORMATION);
LocalFree(lpMsgBuf); LocalFree(lpMsgBuf);
unireg_abort(1); unireg_abort(1);
} }
...@@ -1796,17 +1800,163 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused))) ...@@ -1796,17 +1800,163 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
******************************************************************************/ ******************************************************************************/
#if defined(__WIN__) || defined(OS2) #if defined(__WIN__)
/*
On Windows, we use native SetConsoleCtrlHandler for handle events like Ctrl-C
with graceful shutdown.
Also, we do not use signal(), but SetUnhandledExceptionFilter instead - as it
provides possibility to pass the exception to just-in-time debugger, collect
dumps and potentially also the exception and thread context used to output
callstack.
*/
static BOOL WINAPI console_event_handler( DWORD type )
{
DBUG_ENTER("console_event_handler");
if(type == CTRL_C_EVENT)
{
/*
Do not shutdown before startup is finished and shutdown
thread is initialized. Otherwise there is a race condition
between main thread doing initialization and CTRL-C thread doing
cleanup, which can result into crash.
*/
if(hEventShutdown)
kill_mysql();
else
sql_print_warning("CTRL-C ignored during startup");
DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
}
/*
In Visual Studio 2005 and later, default SIGABRT handler will overwrite
any unhandled exception filter set by the application and will try to
call JIT debugger. This is not what we want, this we calling __debugbreak
to stop in debugger, if process is being debugged or to generate
EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
*/
#if (_MSC_VER >= 1400)
static void my_sigabrt_handler(int sig)
{
__debugbreak();
}
#endif /*_MSC_VER >=1400 */
void win_install_sigabrt_handler(void)
{
#if (_MSC_VER >=1400)
/*abort() should not override our exception filter*/
_set_abort_behavior(0,_CALL_REPORTFAULT);
signal(SIGABRT,my_sigabrt_handler);
#endif /* _MSC_VER >=1400 */
}
#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
#define DEBUGGER_ATTACH_TIMEOUT 120
/*
Wait for debugger to attach and break into debugger. If debugger is not attached,
resume after timeout.
*/
static void wait_for_debugger(int timeout_sec)
{
if(!IsDebuggerPresent())
{
int i;
printf("Waiting for debugger to attach, pid=%u\n",GetCurrentProcessId());
fflush(stdout);
for(i= 0; i < timeout_sec; i++)
{
Sleep(1000);
if(IsDebuggerPresent())
{
/* Break into debugger */
__debugbreak();
return;
}
}
printf("pid=%u, debugger not attached after %d seconds, resuming\n",GetCurrentProcessId(),
timeout_sec);
fflush(stdout);
}
}
#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
{
static BOOL first_time= TRUE;
if(!first_time)
{
/*
This routine can be called twice, typically
when detaching in JIT debugger.
Return EXCEPTION_EXECUTE_HANDLER to terminate process.
*/
return EXCEPTION_EXECUTE_HANDLER;
}
first_time= FALSE;
#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
/*
Unfortunately there is no clean way to debug unhandled exception filters,
as debugger does not stop there(also documented in MSDN)
To overcome, one could put a MessageBox, but this will not work in service.
Better solution is to print error message and sleep some minutes
until debugger is attached
*/
wait_for_debugger(DEBUGGER_ATTACH_TIMEOUT);
#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
__try
{
set_exception_pointers(ex_pointers);
handle_segfault(ex_pointers->ExceptionRecord->ExceptionCode);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DWORD written;
const char msg[] = "Got exception in exception handler!\n";
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),msg, sizeof(msg)-1,
&written,NULL);
}
/*
Return EXCEPTION_CONTINUE_SEARCH to give JIT debugger
(drwtsn32 or vsjitdebugger) possibility to attach,
if JIT debugger is configured.
Windows Error reporting might generate a dump here.
*/
return EXCEPTION_CONTINUE_SEARCH;
}
static void init_signals(void) static void init_signals(void)
{ {
int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT } ; win_install_sigabrt_handler();
for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++) if(opt_console)
signal(signals[i], kill_server) ; SetConsoleCtrlHandler(console_event_handler,TRUE);
#if defined(__WIN__) else
signal(SIGBREAK,SIG_IGN); //ignore SIGBREAK for NT {
#else /* Avoid MessageBox()es*/
signal(SIGBREAK, kill_server); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
#endif _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
/*
Do not use SEM_NOGPFAULTERRORBOX in the following SetErrorMode (),
because it would prevent JIT debugger and Windows error reporting
from working. We need WER or JIT-debugging, since our own unhandled
exception filter is not guaranteed to work in all situation
(like heap corruption or stack overflow)
*/
SetErrorMode(SetErrorMode(0)|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
}
SetUnhandledExceptionFilter(my_unhandler_exception_filter);
} }
static void start_signal_handler(void) static void start_signal_handler(void)
...@@ -2094,8 +2244,8 @@ static void start_signal_handler(void) ...@@ -2094,8 +2244,8 @@ static void start_signal_handler(void)
static void check_data_home(const char *path) static void check_data_home(const char *path)
{} {}
#endif /*__WIN__ || __NETWARE || __EMX__*/
#else /* if ! __WIN__ && ! __EMX__ */
#ifdef HAVE_LINUXTHREADS #ifdef HAVE_LINUXTHREADS
#define UNSAFE_DEFAULT_LINUX_THREADS 200 #define UNSAFE_DEFAULT_LINUX_THREADS 200
...@@ -2115,7 +2265,7 @@ extern "C" sig_handler handle_segfault(int sig) ...@@ -2115,7 +2265,7 @@ extern "C" sig_handler handle_segfault(int sig)
*/ */
if (segfaulted) if (segfaulted)
{ {
fprintf(stderr, "Fatal signal %d while backtracing\n", sig); fprintf(stderr, "Fatal " SIGNAL_FMT " while backtracing\n", sig);
exit(1); exit(1);
} }
...@@ -2125,7 +2275,7 @@ extern "C" sig_handler handle_segfault(int sig) ...@@ -2125,7 +2275,7 @@ extern "C" sig_handler handle_segfault(int sig)
localtime_r(&curr_time, &tm); localtime_r(&curr_time, &tm);
fprintf(stderr,"\ fprintf(stderr,"\
%02d%02d%02d %2d:%02d:%02d - mysqld got signal %d;\n\ %02d%02d%02d %2d:%02d:%02d - mysqld got " SIGNAL_FMT " ;\n\
This could be because you hit a bug. It is also possible that this binary\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 against is corrupt, improperly built,\n\ or one of the libraries it was linked against is corrupt, improperly built,\n\
or misconfigured. This error can also be caused by malfunctioning hardware.\n", or misconfigured. This error can also be caused by malfunctioning hardware.\n",
...@@ -2166,6 +2316,10 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", ...@@ -2166,6 +2316,10 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n",
if (!(test_flags & TEST_NO_STACKTRACE)) if (!(test_flags & TEST_NO_STACKTRACE))
{ {
fprintf(stderr,"thd=%p\n",thd); fprintf(stderr,"thd=%p\n",thd);
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");
print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0, print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0,
thread_stack); thread_stack);
} }
...@@ -2214,15 +2368,22 @@ of those buggy OS calls. You should consider whether you really need the\n\ ...@@ -2214,15 +2368,22 @@ of those buggy OS calls. You should consider whether you really need the\n\
bugs.\n"); bugs.\n");
} }
#ifdef HAVE_WRITE_CORE
if (test_flags & TEST_CORE_ON_SIGNAL) if (test_flags & TEST_CORE_ON_SIGNAL)
{ {
fprintf(stderr, "Writing a core file\n"); fprintf(stderr, "Writing a core file\n");
fflush(stderr); fflush(stderr);
write_core(sig); write_core(sig);
} }
#endif
#ifndef __WIN__
/* On Windows, do not terminate, but pass control to exception filter */
exit(1); exit(1);
#endif
} }
#if !defined(__WIN__) && !defined(__NETWARE__) && !defined(__EMX__)
#ifndef SA_RESETHAND #ifndef SA_RESETHAND
#define SA_RESETHAND 0 #define SA_RESETHAND 0
#endif #endif
...@@ -2567,19 +2728,6 @@ static void my_str_free_mysqld(void *ptr) ...@@ -2567,19 +2728,6 @@ static void my_str_free_mysqld(void *ptr)
#ifdef __WIN__ #ifdef __WIN__
struct utsname
{
char nodename[FN_REFLEN];
};
int uname(struct utsname *a)
{
return -1;
}
pthread_handler_t handle_shutdown(void *arg) pthread_handler_t handle_shutdown(void *arg)
{ {
MSG msg; MSG msg;
...@@ -2593,18 +2741,6 @@ pthread_handler_t handle_shutdown(void *arg) ...@@ -2593,18 +2741,6 @@ pthread_handler_t handle_shutdown(void *arg)
kill_server(MYSQL_KILL_SIGNAL); kill_server(MYSQL_KILL_SIGNAL);
return 0; return 0;
} }
int STDCALL handle_kill(ulong ctrl_type)
{
if (ctrl_type == CTRL_CLOSE_EVENT ||
ctrl_type == CTRL_SHUTDOWN_EVENT)
{
kill_server(MYSQL_KILL_SIGNAL);
return TRUE;
}
return FALSE;
}
#endif #endif
...@@ -3633,11 +3769,6 @@ we force server id to 2, but this MySQL server will not act as a slave."); ...@@ -3633,11 +3769,6 @@ we force server id to 2, but this MySQL server will not act as a slave.");
freopen(log_error_file,"a+",stderr); freopen(log_error_file,"a+",stderr);
FreeConsole(); // Remove window FreeConsole(); // Remove window
} }
else
{
/* Don't show error dialog box when on foreground: it stops the server */
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
}
#endif #endif
/* /*
......
...@@ -98,22 +98,7 @@ static bool do_command(THD *thd); ...@@ -98,22 +98,7 @@ static bool do_command(THD *thd);
#endif // EMBEDDED_LIBRARY #endif // EMBEDDED_LIBRARY
#ifdef __WIN__ #ifdef __WIN__
static void test_signal(int sig_ptr) extern void win_install_sigabrt_handler(void);
{
#if !defined( DBUG_OFF)
MessageBox(NULL,"Test signal","DBUG",MB_OK);
#endif
#if defined(OS2)
fprintf(stderr, "Test signal %d\n", sig_ptr);
fflush(stderr);
#endif
}
static void init_signals(void)
{
int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
for (int i=0 ; i < 7 ; i++)
signal( signals[i], test_signal) ;
}
#endif #endif
static void unlock_locked_tables(THD *thd) static void unlock_locked_tables(THD *thd)
...@@ -1124,7 +1109,7 @@ pthread_handler_t handle_one_connection(void *arg) ...@@ -1124,7 +1109,7 @@ pthread_handler_t handle_one_connection(void *arg)
/* now that we've called my_thread_init(), it is safe to call DBUG_* */ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
#if defined(__WIN__) #if defined(__WIN__)
init_signals(); win_install_sigabrt_handler();
#elif !defined(OS2) && !defined(__NETWARE__) #elif !defined(OS2) && !defined(__NETWARE__)
sigset_t set; sigset_t set;
VOID(sigemptyset(&set)); // Get mask in use VOID(sigemptyset(&set)); // Get mask in use
......
...@@ -13,11 +13,15 @@ ...@@ -13,11 +13,15 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Workaround for Bug#32082: VOID redefinition on Win results in compile errors*/
#define DONT_DEFINE_VOID 1
#include <my_global.h> #include <my_global.h>
#include "stacktrace.h" #include "stacktrace.h"
#ifndef __WIN__
#include <signal.h> #include <signal.h>
#include <my_pthread.h> #include <my_pthread.h>
#ifdef HAVE_STACKTRACE #ifdef HAVE_STACKTRACE
#include <unistd.h> #include <unistd.h>
#include <strings.h> #include <strings.h>
...@@ -118,10 +122,7 @@ void print_stacktrace(gptr stack_bottom, ulong thread_stack) ...@@ -118,10 +122,7 @@ void print_stacktrace(gptr stack_bottom, ulong thread_stack)
#endif #endif
LINT_INIT(fp); 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__ #ifdef __i386__
__asm __volatile__ ("movl %%ebp,%0" __asm __volatile__ ("movl %%ebp,%0"
:"=r"(fp) :"=r"(fp)
...@@ -257,3 +258,267 @@ void write_core(int sig) ...@@ -257,3 +258,267 @@ void write_core(int sig)
#endif #endif
} }
#endif #endif
#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__*/
...@@ -29,20 +29,33 @@ extern char* heap_start; ...@@ -29,20 +29,33 @@ extern char* heap_start;
heap_start = (char*) &__bss_start; \ heap_start = (char*) &__bss_start; \
check_thread_lib(); \ check_thread_lib(); \
} while(0); } while(0);
void print_stacktrace(gptr stack_bottom, ulong thread_stack);
void safe_print_str(const char* name, const char* val, int max_len);
void check_thread_lib(void); void check_thread_lib(void);
#endif /* (defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */ #endif /* defined (__i386__) || (defined(__alpha__) && defined(__GNUC__))) */
#endif /* TARGET_OS_LINUX */ #elif defined (__WIN__)
#define HAVE_STACKTRACE
extern void set_exception_pointers(EXCEPTION_POINTERS *ep);
#define init_stacktrace() {}
#endif
#ifdef HAVE_STACKTRACE
void print_stacktrace(gptr stack_bottom, ulong thread_stack);
void safe_print_str(const char* name, const char* val, int max_len);
#else
/* Define empty prototypes for functions that are not implemented */ /* Define empty prototypes for functions that are not implemented */
#ifndef HAVE_STACKTRACE
#define init_stacktrace() {} #define init_stacktrace() {}
#define print_stacktrace(A,B) {} #define print_stacktrace(A,B) {}
#define safe_print_str(A,B,C) {} #define safe_print_str(A,B,C) {}
#endif /* HAVE_STACKTRACE */ #endif /* HAVE_STACKTRACE */
#if !defined(__NETWARE__)
#define HAVE_WRITE_CORE
#endif
#ifdef HAVE_WRITE_CORE
void write_core(int sig); void write_core(int sig);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment