From: Eli Zaretskii Date: Mon, 2 Dec 2013 17:28:17 +0000 (+0200) Subject: Improve reporting of fatal exception on MS-Windows, to aid debugging #15994. X-Git-Tag: emacs-24.3.90~173^2^2~42^2~45^2~387^2~584 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=21bf394d7d1065a886c486f00c614ca905db1ed5;p=emacs.git Improve reporting of fatal exception on MS-Windows, to aid debugging #15994. src/w32fns.c (my_exception_handler): New function. (globals_of_w32fns): Set it up as the unhandled exception handler. Initialize exception code and address to zeros. (emacs_abort): If the exception code and address are available, print them at the beginning of the backtrace. Fix the format of printing addresses (was producing 0x0x12345678 on XP). --- diff --git a/src/ChangeLog b/src/ChangeLog index db311bdcba5..a5c6668c551 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2013-12-02 Eli Zaretskii + + Improve reporting of fatal exception on MS-Windows. + * w32fns.c (my_exception_handler): New function. + (globals_of_w32fns): Set it up as the unhandled exception + handler. Initialize exception code and address to zeros. + (emacs_abort): If the exception code and address are available, + print them at the beginning of the backtrace. Fix the format of + printing addresses (was producing 0x0x12345678 on XP). + (Bug#15994) + 2013-12-02 Helmut Eller * eval.c (Fbacktrace__locals): New function. diff --git a/src/w32fns.c b/src/w32fns.c index f574a86b1e1..79011f9afc2 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -7969,61 +7969,35 @@ only be necessary if the default setting causes problems. */); #endif } + -/* - globals_of_w32fns is used to initialize those global variables that - must always be initialized on startup even when the global variable - initialized is non zero (see the function main in emacs.c). - globals_of_w32fns is called from syms_of_w32fns when the global - variable initialized is 0 and directly from main when initialized - is non zero. - */ -void -globals_of_w32fns (void) -{ - HMODULE user32_lib = GetModuleHandle ("user32.dll"); - /* - TrackMouseEvent not available in all versions of Windows, so must load - it dynamically. Do it once, here, instead of every time it is used. - */ - track_mouse_event_fn = (TrackMouseEvent_Proc) - GetProcAddress (user32_lib, "TrackMouseEvent"); - - monitor_from_point_fn = (MonitorFromPoint_Proc) - GetProcAddress (user32_lib, "MonitorFromPoint"); - get_monitor_info_fn = (GetMonitorInfo_Proc) - GetProcAddress (user32_lib, "GetMonitorInfoA"); - monitor_from_window_fn = (MonitorFromWindow_Proc) - GetProcAddress (user32_lib, "MonitorFromWindow"); - enum_display_monitors_fn = (EnumDisplayMonitors_Proc) - GetProcAddress (user32_lib, "EnumDisplayMonitors"); - - { - HMODULE imm32_lib = GetModuleHandle ("imm32.dll"); - get_composition_string_fn = (ImmGetCompositionString_Proc) - GetProcAddress (imm32_lib, "ImmGetCompositionStringW"); - get_ime_context_fn = (ImmGetContext_Proc) - GetProcAddress (imm32_lib, "ImmGetContext"); - release_ime_context_fn = (ImmReleaseContext_Proc) - GetProcAddress (imm32_lib, "ImmReleaseContext"); - set_ime_composition_window_fn = (ImmSetCompositionWindow_Proc) - GetProcAddress (imm32_lib, "ImmSetCompositionWindow"); - } - DEFVAR_INT ("w32-ansi-code-page", - w32_ansi_code_page, - doc: /* The ANSI code page used by the system. */); - w32_ansi_code_page = GetACP (); - - if (os_subtype == OS_NT) - w32_unicode_gui = 1; - else - w32_unicode_gui = 0; +/* Crashing and reporting backtrace. */ - /* MessageBox does not work without this when linked to comctl32.dll 6.0. */ - InitCommonControls (); +#ifndef CYGWIN +static LONG CALLBACK my_exception_handler (EXCEPTION_POINTERS *); +static LPTOP_LEVEL_EXCEPTION_FILTER prev_exception_handler; +#endif +static DWORD except_code; +static PVOID except_addr; - syms_of_w32uniscribe (); +#ifndef CYGWIN +/* This handler records the exception code and the address where it + was triggered so that this info could be included in the backtrace. + Without that, the backtrace in some cases has no information + whatsoever about the offending code, and looks as if the top-level + exception handler in the MinGW startup code di the one that + crashed. */ +static LONG CALLBACK +my_exception_handler (EXCEPTION_POINTERS * exception_data) +{ + except_code = exception_data->ExceptionRecord->ExceptionCode; + except_addr = exception_data->ExceptionRecord->ExceptionAddress; + + if (prev_exception_handler) + return prev_exception_handler (exception_data); + return EXCEPTION_EXECUTE_HANDLER; } +#endif typedef USHORT (WINAPI * CaptureStackBackTrace_proc) (ULONG, ULONG, PVOID *, PULONG); @@ -8080,21 +8054,32 @@ emacs_abort (void) if (i) { + int errfile_fd = -1; + int j; + char buf[sizeof ("\r\nException at this address:\r\n\r\n") + + 2 * INT_BUFSIZE_BOUND (void *)]; #ifdef CYGWIN int stderr_fd = 2; #else HANDLE errout = GetStdHandle (STD_ERROR_HANDLE); int stderr_fd = -1; -#endif - int errfile_fd = -1; - int j; -#ifndef CYGWIN if (errout && errout != INVALID_HANDLE_VALUE) stderr_fd = _open_osfhandle ((intptr_t)errout, O_APPEND | O_BINARY); #endif + + /* We use %p, not 0x%p, as %p produces a leading "0x" on XP, + but not on Windows 7. addr2line doesn't mind a missing + "0x", but will be confused by an extra one. */ + if (except_addr) + sprintf (buf, "\r\nException 0x%lx at this address:\r\n%p\r\n", + except_code, except_addr); if (stderr_fd >= 0) - write (stderr_fd, "\r\nBacktrace:\r\n", 14); + { + if (except_addr) + write (stderr_fd, buf, strlen (buf)); + write (stderr_fd, "\r\nBacktrace:\r\n", 14); + } #ifdef CYGWIN #define _open open #endif @@ -8102,17 +8087,17 @@ emacs_abort (void) if (errfile_fd >= 0) { lseek (errfile_fd, 0L, SEEK_END); + if (except_addr) + write (errfile_fd, buf, strlen (buf)); write (errfile_fd, "\r\nBacktrace:\r\n", 14); } for (j = 0; j < i; j++) { - char buf[INT_BUFSIZE_BOUND (void *)]; - /* stack[] gives the return addresses, whereas we want the address of the call, so decrease each address by approximate size of 1 CALL instruction. */ - sprintf (buf, "0x%p\r\n", (char *)stack[j] - sizeof(void *)); + sprintf (buf, "%p\r\n", (char *)stack[j] - sizeof(void *)); if (stderr_fd >= 0) write (stderr_fd, buf, strlen (buf)); if (errfile_fd >= 0) @@ -8134,6 +8119,72 @@ emacs_abort (void) } } + + +/* Initialization. */ + +/* + globals_of_w32fns is used to initialize those global variables that + must always be initialized on startup even when the global variable + initialized is non zero (see the function main in emacs.c). + globals_of_w32fns is called from syms_of_w32fns when the global + variable initialized is 0 and directly from main when initialized + is non zero. + */ +void +globals_of_w32fns (void) +{ + HMODULE user32_lib = GetModuleHandle ("user32.dll"); + /* + TrackMouseEvent not available in all versions of Windows, so must load + it dynamically. Do it once, here, instead of every time it is used. + */ + track_mouse_event_fn = (TrackMouseEvent_Proc) + GetProcAddress (user32_lib, "TrackMouseEvent"); + + monitor_from_point_fn = (MonitorFromPoint_Proc) + GetProcAddress (user32_lib, "MonitorFromPoint"); + get_monitor_info_fn = (GetMonitorInfo_Proc) + GetProcAddress (user32_lib, "GetMonitorInfoA"); + monitor_from_window_fn = (MonitorFromWindow_Proc) + GetProcAddress (user32_lib, "MonitorFromWindow"); + enum_display_monitors_fn = (EnumDisplayMonitors_Proc) + GetProcAddress (user32_lib, "EnumDisplayMonitors"); + + { + HMODULE imm32_lib = GetModuleHandle ("imm32.dll"); + get_composition_string_fn = (ImmGetCompositionString_Proc) + GetProcAddress (imm32_lib, "ImmGetCompositionStringW"); + get_ime_context_fn = (ImmGetContext_Proc) + GetProcAddress (imm32_lib, "ImmGetContext"); + release_ime_context_fn = (ImmReleaseContext_Proc) + GetProcAddress (imm32_lib, "ImmReleaseContext"); + set_ime_composition_window_fn = (ImmSetCompositionWindow_Proc) + GetProcAddress (imm32_lib, "ImmSetCompositionWindow"); + } + + except_code = 0; + except_addr = 0; +#ifndef CYGWIN + prev_exception_handler = SetUnhandledExceptionFilter (my_exception_handler); +#endif + + DEFVAR_INT ("w32-ansi-code-page", + w32_ansi_code_page, + doc: /* The ANSI code page used by the system. */); + w32_ansi_code_page = GetACP (); + + if (os_subtype == OS_NT) + w32_unicode_gui = 1; + else + w32_unicode_gui = 0; + + /* MessageBox does not work without this when linked to comctl32.dll 6.0. */ + InitCommonControls (); + + syms_of_w32uniscribe (); +} + #ifdef NTGUI_UNICODE Lisp_Object