/* Unit test for VEH on Windows * Copyright (C) 2019 Руслан Ижбулатов * * SPDX-License-Identifier: LicenseRef-old-glib-tests * * This work is provided "as is"; redistribution and modification * in whole or in part, in any medium, physical or electronic is * permitted without restriction. * * This work 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. * * In no event shall the authors or contributors be liable for any * direct, indirect, incidental, special, exemplary, or consequential * damages (including, but not limited to, procurement of substitute * goods or services; loss of use, data, or profits; or business * interruption) however caused and on any theory of liability, whether * in contract, strict liability, or tort (including negligence or * otherwise) arising in any way out of the use of this software, even * if advised of the possibility of such damage. */ #include "config.h" #include #include #include #include static char *argv0 = NULL; #include "../gwin32-private.c" static void test_subst_pid_and_event (void) { const wchar_t not_enough[] = L"too long when %e and %p are substituted"; wchar_t debugger_3[3]; wchar_t debugger_not_enough[G_N_ELEMENTS (not_enough)]; wchar_t debugger_enough[G_N_ELEMENTS (not_enough) + 1]; char *debugger_enough_utf8; wchar_t debugger_big[65535] = {0}; char *debugger_big_utf8; gchar *output; guintptr be = (guintptr) 0xFFFFFFFF; DWORD bp = G_MAXSIZE; /* %f is not valid */ g_assert_false (_g_win32_subst_pid_and_event_w (debugger_3, G_N_ELEMENTS (debugger_3), L"%f", 0, 0)); g_assert_false (_g_win32_subst_pid_and_event_w (debugger_3, G_N_ELEMENTS (debugger_3), L"string longer than 10", 0, 0)); /* 200 is longer than %e, so the string doesn't fit by 1 byte */ g_assert_false (_g_win32_subst_pid_and_event_w (debugger_not_enough, G_N_ELEMENTS (debugger_not_enough), not_enough, 10, 200)); /* This should fit */ g_assert_true (_g_win32_subst_pid_and_event_w (debugger_enough, G_N_ELEMENTS (debugger_enough), not_enough, 10, 200)); debugger_enough_utf8 = g_utf16_to_utf8 (debugger_enough, -1, NULL, NULL, NULL); g_assert_cmpstr (debugger_enough_utf8, ==, "too long when 200 and 10 are substituted"); g_free (debugger_enough_utf8); g_assert_true (_g_win32_subst_pid_and_event_w (debugger_big, G_N_ELEMENTS (debugger_big), L"multipl%e big %e %entries and %pids are %provided here", bp, be)); debugger_big_utf8 = g_utf16_to_utf8 (debugger_big, -1, NULL, NULL, NULL); output = g_strdup_printf ("multipl%llu big %llu %lluntries and %luids are %lurovided here", (guint64) be, (guint64) be, (guint64) be, bp, bp); g_assert_cmpstr (debugger_big_utf8, ==, output); g_free (debugger_big_utf8); g_free (output); } /* Crash with access violation */ static void test_access_violation (void) { int *integer = NULL; /* Use SEM_NOGPFAULTERRORBOX to prevent an error dialog * from being shown. */ DWORD dwMode = SetErrorMode (SEM_NOGPFAULTERRORBOX); SetErrorMode (dwMode | SEM_NOGPFAULTERRORBOX); *integer = 1; SetErrorMode (dwMode); } /* Crash with illegal instruction */ static void test_illegal_instruction (void) { DWORD dwMode = SetErrorMode (SEM_NOGPFAULTERRORBOX); SetErrorMode (dwMode | SEM_NOGPFAULTERRORBOX); RaiseException (EXCEPTION_ILLEGAL_INSTRUCTION, 0, 0, NULL); SetErrorMode (dwMode); } static void test_veh_crash_access_violation (void) { g_unsetenv ("G_DEBUGGER"); /* Run a test that crashes */ g_test_trap_subprocess ("/win32/subprocess/access_violation", 0, G_TEST_SUBPROCESS_DEFAULT); g_test_trap_assert_failed (); } static void test_veh_crash_illegal_instruction (void) { g_unsetenv ("G_DEBUGGER"); /* Run a test that crashes */ g_test_trap_subprocess ("/win32/subprocess/illegal_instruction", 0, G_TEST_SUBPROCESS_DEFAULT); g_test_trap_assert_failed (); } static void test_veh_debug (void) { /* Set up a debugger to be run on crash */ gchar *command = g_strdup_printf ("%s %s", argv0, "%p %e"); g_setenv ("G_DEBUGGER", command, TRUE); /* Because the "debugger" here is not really a debugger, * it can't write into stderr of this process, unless * we allow it to inherit our stderr. */ g_setenv ("G_DEBUGGER_OLD_CONSOLE", "1", TRUE); g_free (command); /* Run a test that crashes and runs a debugger */ g_test_trap_subprocess ("/win32/subprocess/debuggee", 0, G_TEST_SUBPROCESS_DEFAULT); g_test_trap_assert_failed (); g_test_trap_assert_stderr ("Debugger invoked, attaching to*"); } static void test_veh_debuggee (void) { /* Crash */ test_access_violation (); } static void veh_debugger (int argc, char *argv[]) { char *end; DWORD pid = strtoul (argv[1], &end, 10); guintptr event = (guintptr) _strtoui64 (argv[2], &end, 10); /* Unfreeze the debuggee and announce ourselves */ SetEvent ((HANDLE) event); CloseHandle ((HANDLE) event); g_fprintf (stderr, "Debugger invoked, attaching to %lu and signalling %" G_GUINTPTR_FORMAT, pid, event); } int main (int argc, char *argv[]) { argv0 = argv[0]; g_test_init (&argc, &argv, NULL); if (argc > 2) { veh_debugger (argc, argv); return 0; } g_test_add_func ("/win32/substitute-pid-and-event", test_subst_pid_and_event); g_test_add_func ("/win32/veh/access_violation", test_veh_crash_access_violation); g_test_add_func ("/win32/veh/illegal_instruction", test_veh_crash_illegal_instruction); g_test_add_func ("/win32/veh/debug", test_veh_debug); g_test_add_func ("/win32/subprocess/debuggee", test_veh_debuggee); g_test_add_func ("/win32/subprocess/access_violation", test_access_violation); g_test_add_func ("/win32/subprocess/illegal_instruction", test_illegal_instruction); return g_test_run(); }