W32: Initialize debugger stuff in advance

Since VEH is invoked when an exception occurs (which, for us,
is mostly when the program is already crashing), we should
try to avoid doing much processing at that point. Since these
things (debugger commandline, a list of extra exceptions to catch)
are known in advance, set them up during initialization.
This commit is contained in:
Руслан Ижбулатов 2021-04-06 09:35:39 +00:00
parent 1955ede43b
commit 891e3a0bba

View File

@ -1035,7 +1035,17 @@ g_console_win32_init (void)
* it will be non-NULL. Only used to later de-install the handler * it will be non-NULL. Only used to later de-install the handler
* on library de-initialization. * on library de-initialization.
*/ */
static void *WinVEH_handle = NULL; static void *WinVEH_handle = NULL;
#define DEBUGGER_BUFFER_SIZE (MAX_PATH + 1)
/* This is the debugger that we'll run on crash */
static char debugger[DEBUGGER_BUFFER_SIZE];
static gsize number_of_exceptions_to_catch = 0;
static DWORD *exceptions_to_catch = NULL;
static HANDLE debugger_wakeup_event = 0;
static DWORD debugger_spawn_flags = 0;
#include "gwin32-private.c" #include "gwin32-private.c"
@ -1079,18 +1089,9 @@ static LONG __stdcall
g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo) g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
{ {
EXCEPTION_RECORD *er; EXCEPTION_RECORD *er;
const gsize debugger_buffer_size = MAX_PATH + 1; gsize i;
char debugger[debugger_buffer_size];
char debugger_env[debugger_buffer_size];
const gsize catch_buffer_size = 1024;
char catch_buffer[catch_buffer_size];
char *catch_list;
gboolean catch = FALSE;
STARTUPINFOA si; STARTUPINFOA si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
HANDLE event;
SECURITY_ATTRIBUTES sa;
DWORD flags;
if (ExceptionInfo == NULL || if (ExceptionInfo == NULL ||
ExceptionInfo->ExceptionRecord == NULL || ExceptionInfo->ExceptionRecord == NULL ||
@ -1106,33 +1107,14 @@ g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_ILLEGAL_INSTRUCTION:
break; break;
default: default:
catch_buffer[0] = 0; for (i = 0; i < number_of_exceptions_to_catch; i++)
if (!GetEnvironmentVariableA ("G_VEH_CATCH", catch_buffer, catch_buffer_size)) if (exceptions_to_catch[i] == er->ExceptionCode)
break; break;
catch_list = catch_buffer; if (i == number_of_exceptions_to_catch)
return EXCEPTION_CONTINUE_SEARCH;
while (!catch && break;
catch_list != NULL &&
catch_list[0] != 0)
{
unsigned long catch_code;
char *end;
errno = 0;
catch_code = strtoul (catch_list, &end, 16);
if (errno != NO_ERROR)
break;
catch_list = end;
if (catch_list != NULL && catch_list[0] == ',')
catch_list++;
if (catch_code == er->ExceptionCode)
catch = TRUE;
}
if (catch)
break;
return EXCEPTION_CONTINUE_SEARCH;
} }
fprintf_s (stderr, fprintf_s (stderr,
@ -1169,36 +1151,15 @@ g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
fflush (stderr); fflush (stderr);
debugger_env[0] = 0; if (debugger[0] == 0)
if (!GetEnvironmentVariableA ("G_DEBUGGER", debugger_env, debugger_buffer_size))
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
/* Create an inheritable event */
memset (&si, 0, sizeof (si)); memset (&si, 0, sizeof (si));
memset (&pi, 0, sizeof (pi)); memset (&pi, 0, sizeof (pi));
memset (&sa, 0, sizeof (sa));
si.cb = sizeof (si); si.cb = sizeof (si);
sa.nLength = sizeof (sa);
sa.bInheritHandle = TRUE;
event = CreateEvent (&sa, FALSE, FALSE, NULL);
/* Put process ID and event handle into debugger commandline */
if (!_g_win32_subst_pid_and_event (debugger, G_N_ELEMENTS (debugger),
debugger_env, GetCurrentProcessId (),
(guintptr) event))
{
CloseHandle (event);
return EXCEPTION_CONTINUE_SEARCH;
}
debugger[MAX_PATH] = '\0';
if (GetEnvironmentVariableA ("G_DEBUGGER_OLD_CONSOLE", (char *) &flags, 1))
flags = 0;
else
flags = CREATE_NEW_CONSOLE;
/* Run the debugger */ /* Run the debugger */
if (0 != CreateProcessA (NULL, debugger, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi)) if (0 != CreateProcessA (NULL, debugger, NULL, NULL, TRUE, debugger_spawn_flags, NULL, NULL, &si, &pi))
{ {
CloseHandle (pi.hProcess); CloseHandle (pi.hProcess);
CloseHandle (pi.hThread); CloseHandle (pi.hThread);
@ -1208,11 +1169,9 @@ g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
* up forever in case the debugger does not support * up forever in case the debugger does not support
* event signalling. * event signalling.
*/ */
WaitForSingleObject (event, 60000); WaitForSingleObject (debugger_wakeup_event, 60000);
} }
CloseHandle (event);
/* Now the debugger is present, and we can try /* Now the debugger is present, and we can try
* resuming execution, re-triggering the exception, * resuming execution, re-triggering the exception,
* which will be caught by debugger this time around. * which will be caught by debugger this time around.
@ -1223,10 +1182,41 @@ g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
static gsize
parse_catch_list (const char *catch_buffer,
DWORD *exceptions,
gsize num_exceptions)
{
const char *catch_list = catch_buffer;
gsize result = 0;
gsize i = 0;
while (catch_list != NULL &&
catch_list[0] != 0)
{
unsigned long catch_code;
char *end;
errno = 0;
catch_code = strtoul (catch_list, &end, 16);
if (errno != NO_ERROR)
break;
catch_list = end;
if (catch_list != NULL && catch_list[0] == ',')
catch_list++;
if (exceptions && i < num_exceptions)
exceptions[i++] = catch_code;
}
return result;
}
void void
g_crash_handler_win32_init (void) g_crash_handler_win32_init (void)
{ {
char tmp; char debugger_env[DEBUGGER_BUFFER_SIZE];
#define CATCH_BUFFER_SIZE 1024
char catch_buffer[CATCH_BUFFER_SIZE];
SECURITY_ATTRIBUTES sa;
if (WinVEH_handle != NULL) if (WinVEH_handle != NULL)
return; return;
@ -1234,11 +1224,46 @@ g_crash_handler_win32_init (void)
/* Do not register an exception handler if we're not supposed to catch any /* Do not register an exception handler if we're not supposed to catch any
* exceptions. Exception handlers are considered dangerous to use, and can * exceptions. Exception handlers are considered dangerous to use, and can
* break advanced exception handling such as in CLRs like C# or other managed * break advanced exception handling such as in CLRs like C# or other managed
* code. See: https://blogs.msdn.microsoft.com/jmstall/2006/05/24/beware-of-the-vectored-exception-handler-and-managed-code/ * code. See: http://www.windows-tech.info/13/785f590867bd6316.php
*/ */
if (!GetEnvironmentVariableA ("G_DEBUGGER", (char *) &tmp, 1)) debugger_env[0] = 0;
if (!GetEnvironmentVariableA ("G_DEBUGGER", debugger_env, DEBUGGER_BUFFER_SIZE))
return; return;
/* Create an inheritable event */
memset (&sa, 0, sizeof (sa));
sa.nLength = sizeof (sa);
sa.bInheritHandle = TRUE;
debugger_wakeup_event = CreateEvent (&sa, FALSE, FALSE, NULL);
/* Put process ID and event handle into debugger commandline */
if (!_g_win32_subst_pid_and_event (debugger, G_N_ELEMENTS (debugger),
debugger_env, GetCurrentProcessId (),
(guintptr) debugger_wakeup_event))
{
CloseHandle (debugger_wakeup_event);
debugger_wakeup_event = 0;
debugger[0] = 0;
return;
}
debugger[MAX_PATH] = '\0';
catch_buffer[0] = 0;
if (GetEnvironmentVariableA ("G_VEH_CATCH", catch_buffer, CATCH_BUFFER_SIZE))
{
number_of_exceptions_to_catch = parse_catch_list (catch_buffer, NULL, 0);
if (number_of_exceptions_to_catch > 0)
{
exceptions_to_catch = g_new0 (DWORD, number_of_exceptions_to_catch);
parse_catch_list (catch_buffer, exceptions_to_catch, number_of_exceptions_to_catch);
}
}
if (GetEnvironmentVariableA ("G_DEBUGGER_OLD_CONSOLE", (char *) &debugger_spawn_flags, 1))
debugger_spawn_flags = 0;
else
debugger_spawn_flags = CREATE_NEW_CONSOLE;
WinVEH_handle = AddVectoredExceptionHandler (0, &g_win32_veh_handler); WinVEH_handle = AddVectoredExceptionHandler (0, &g_win32_veh_handler);
} }