From 1005b4b98a43c4aea2ac8f01a5743b6b38bdd964 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 2 Nov 2012 16:00:45 +0200 Subject: [PATCH] Implement backtrace output for fatal errors on MS-Windows. src/w32fns.c (CaptureStackBackTrace_proc): New typedef. (BACKTRACE_LIMIT_MAX): New macro. (w32_backtrace): New function. (emacs_abort): Use w32_backtrace when the user chooses not to attach a debugger. Update the text of the abort dialog. --- src/ChangeLog | 9 ++++++ src/w32fns.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 25cb1e76fa5..5739b5d9125 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2012-11-02 Eli Zaretskii + + Implement backtrace output for fatal errors on MS-Windows. + * w32fns.c (CaptureStackBackTrace_proc): New typedef. + (BACKTRACE_LIMIT_MAX): New macro. + (w32_backtrace): New function. + (emacs_abort): Use w32_backtrace when the user chooses not to + attach a debugger. Update the text of the abort dialog. + 2012-11-02 Dmitry Antipov Window-related stuff cleanup here and there. diff --git a/src/w32fns.c b/src/w32fns.c index aa120d59ce5..7459c4a31db 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -26,6 +26,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include "lisp.h" #include "w32term.h" @@ -7697,6 +7698,30 @@ globals_of_w32fns (void) syms_of_w32uniscribe (); } +typedef USHORT (WINAPI * CaptureStackBackTrace_proc) (ULONG, ULONG, PVOID *, + PULONG); + +#define BACKTRACE_LIMIT_MAX 62 + +int +w32_backtrace (void **buffer, int limit) +{ + static CaptureStackBackTrace_proc s_pfn_CaptureStackBackTrace = NULL; + HMODULE hm_kernel32 = NULL; + + if (!s_pfn_CaptureStackBackTrace) + { + hm_kernel32 = LoadLibrary ("Kernel32.dll"); + s_pfn_CaptureStackBackTrace = + (CaptureStackBackTrace_proc) GetProcAddress (hm_kernel32, + "RtlCaptureStackBackTrace"); + } + if (s_pfn_CaptureStackBackTrace) + return s_pfn_CaptureStackBackTrace (0, min (BACKTRACE_LIMIT_MAX, limit), + buffer, NULL); + return 0; +} + void emacs_abort (void) { @@ -7704,7 +7729,10 @@ emacs_abort (void) button = MessageBox (NULL, "A fatal error has occurred!\n\n" "Would you like to attach a debugger?\n\n" - "Select YES to debug, NO to abort Emacs" + "Select:\n" + "YES -- to debug Emacs, or\n" + "NO -- to abort Emacs and produce a backtrace\n" + " (emacs_backtrace.txt in current directory)." #if __GNUC__ "\n\n(type \"gdb -p \" and\n" "\"continue\" inside GDB before clicking YES.)" @@ -7719,7 +7747,52 @@ emacs_abort (void) exit (2); /* tell the compiler we will never return */ case IDNO: default: - abort (); - break; + { + void *stack[BACKTRACE_LIMIT_MAX + 1]; + int i = w32_backtrace (stack, BACKTRACE_LIMIT_MAX + 1); + + if (i) + { + HANDLE errout = GetStdHandle (STD_ERROR_HANDLE); + int stderr_fd = -1, errfile_fd = -1; + int j; + + if (errout && errout != INVALID_HANDLE_VALUE) + stderr_fd = _open_osfhandle ((intptr_t)errout, O_APPEND | O_BINARY); + if (stderr_fd >= 0) + write (stderr_fd, "\r\nBacktrace:\r\n", 14); + errfile_fd = _open ("emacs_backtrace.txt", O_RDWR | O_CREAT | O_BINARY, S_IREAD | S_IWRITE); + if (errfile_fd >= 0) + { + lseek (errfile_fd, 0L, SEEK_END); + 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", stack[j] - sizeof(void *)); + if (stderr_fd >= 0) + write (stderr_fd, buf, strlen (buf)); + if (errfile_fd >= 0) + write (errfile_fd, buf, strlen (buf)); + } + if (i == BACKTRACE_LIMIT_MAX) + { + if (stderr_fd >= 0) + write (stderr_fd, "...\r\n", 5); + if (errfile_fd >= 0) + write (errfile_fd, "...\r\n", 5); + } + if (errfile_fd >= 0) + close (errfile_fd); + } + abort (); + break; + } } } -- 2.39.5