1 /* Unit test for VEH on Windows
2 * Copyright (C) 2019 Руслан Ижбулатов
3 *
4 * SPDX-License-Identifier: LicenseRef-old-glib-tests
5 *
6 * This work is provided "as is"; redistribution and modification
7 * in whole or in part, in any medium, physical or electronic is
8 * permitted without restriction.
9 *
10 * This work is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * In no event shall the authors or contributors be liable for any
15 * direct, indirect, incidental, special, exemplary, or consequential
16 * damages (including, but not limited to, procurement of substitute
17 * goods or services; loss of use, data, or profits; or business
18 * interruption) however caused and on any theory of liability, whether
19 * in contract, strict liability, or tort (including negligence or
20 * otherwise) arising in any way out of the use of this software, even
21 * if advised of the possibility of such damage.
22 */
23
24 #include "config.h"
25
26 #include <glib.h>
27 #include <gprintf.h>
28 #include <stdio.h>
29 #include <windows.h>
30
31 static char *argv0 = NULL;
32
33 #include "../gwin32-private.c"
34
35 static void
36 test_subst_pid_and_event (void)
37 {
38 const wchar_t not_enough[] = L"too long when %e and %p are substituted";
39 wchar_t debugger_3[3];
40 wchar_t debugger_not_enough[G_N_ELEMENTS (not_enough)];
41 wchar_t debugger_enough[G_N_ELEMENTS (not_enough) + 1];
42 char *debugger_enough_utf8;
43 wchar_t debugger_big[65535] = {0};
44 char *debugger_big_utf8;
45 gchar *output;
46 guintptr be = (guintptr) 0xFFFFFFFF;
47 DWORD bp = MAXDWORD;
48
49 /* %f is not valid */
50 g_assert_false (_g_win32_subst_pid_and_event_w (debugger_3, G_N_ELEMENTS (debugger_3),
51 L"%f", 0, 0));
52
53 g_assert_false (_g_win32_subst_pid_and_event_w (debugger_3, G_N_ELEMENTS (debugger_3),
54 L"string longer than 10", 0, 0));
55 /* 200 is longer than %e, so the string doesn't fit by 1 byte */
56 g_assert_false (_g_win32_subst_pid_and_event_w (debugger_not_enough, G_N_ELEMENTS (debugger_not_enough),
57 not_enough, 10, 200));
58
59 /* This should fit */
60 g_assert_true (_g_win32_subst_pid_and_event_w (debugger_enough, G_N_ELEMENTS (debugger_enough),
61 not_enough, 10, 200));
62 debugger_enough_utf8 = g_utf16_to_utf8 (debugger_enough, -1, NULL, NULL, NULL);
63 g_assert_cmpstr (debugger_enough_utf8, ==, "too long when 200 and 10 are substituted");
64 g_free (debugger_enough_utf8);
65
66 g_assert_true (_g_win32_subst_pid_and_event_w (debugger_big, G_N_ELEMENTS (debugger_big),
67 L"multipl%e big %e %entries and %pids are %provided here", bp, be));
68 debugger_big_utf8 = g_utf16_to_utf8 (debugger_big, -1, NULL, NULL, NULL);
69 output = g_strdup_printf ("multipl%llu big %llu %lluntries and %luids are %lurovided here", (guint64) be, (guint64) be, (guint64) be, bp, bp);
70 g_assert_cmpstr (debugger_big_utf8, ==, output);
71 g_free (debugger_big_utf8);
72 g_free (output);
73 }
74
75 /* Crash with access violation */
76 static void
77 test_access_violation (void)
78 {
79 int *integer = NULL;
80 /* Use SEM_NOGPFAULTERRORBOX to prevent an error dialog
81 * from being shown.
82 */
83 DWORD dwMode = SetErrorMode (SEM_NOGPFAULTERRORBOX);
84 SetErrorMode (dwMode | SEM_NOGPFAULTERRORBOX);
85 *integer = 1;
86 SetErrorMode (dwMode);
87 }
88
89 /* Crash with illegal instruction */
90 static void
91 test_illegal_instruction (void)
92 {
93 DWORD dwMode = SetErrorMode (SEM_NOGPFAULTERRORBOX);
94 SetErrorMode (dwMode | SEM_NOGPFAULTERRORBOX);
95 RaiseException (EXCEPTION_ILLEGAL_INSTRUCTION, 0, 0, NULL);
96 SetErrorMode (dwMode);
97 }
98
99 static void
100 test_veh_crash_access_violation (void)
101 {
102 g_unsetenv ("G_DEBUGGER");
103 /* Run a test that crashes */
104 g_test_trap_subprocess ("/win32/subprocess/access_violation", 0,
105 G_TEST_SUBPROCESS_DEFAULT);
106 g_test_trap_assert_failed ();
107 }
108
109 static void
110 test_veh_crash_illegal_instruction (void)
111 {
112 g_unsetenv ("G_DEBUGGER");
113 /* Run a test that crashes */
114 g_test_trap_subprocess ("/win32/subprocess/illegal_instruction", 0,
115 G_TEST_SUBPROCESS_DEFAULT);
116 g_test_trap_assert_failed ();
117 }
118
119 static void
120 test_veh_debug (void)
121 {
122 /* Set up a debugger to be run on crash */
123 gchar *command = g_strdup_printf ("%s %s", argv0, "%p %e");
124 g_setenv ("G_DEBUGGER", command, TRUE);
125 /* Because the "debugger" here is not really a debugger,
126 * it can't write into stderr of this process, unless
127 * we allow it to inherit our stderr.
128 */
129 g_setenv ("G_DEBUGGER_OLD_CONSOLE", "1", TRUE);
130 g_free (command);
131 /* Run a test that crashes and runs a debugger */
132 g_test_trap_subprocess ("/win32/subprocess/debuggee", 0,
133 G_TEST_SUBPROCESS_DEFAULT);
134 g_test_trap_assert_failed ();
135 g_test_trap_assert_stderr ("Debugger invoked, attaching to*");
136 }
137
138 static void
139 test_veh_debuggee (void)
140 {
141 /* Crash */
142 test_access_violation ();
143 }
144
145 static void
146 veh_debugger (int argc, char *argv[])
147 {
148 char *end;
149 DWORD pid = strtoul (argv[1], &end, 10);
150 guintptr event = (guintptr) _strtoui64 (argv[2], &end, 10);
151 /* Unfreeze the debuggee and announce ourselves */
152 SetEvent ((HANDLE) event);
153 CloseHandle ((HANDLE) event);
154 g_fprintf (stderr, "Debugger invoked, attaching to %lu and signalling %" G_GUINTPTR_FORMAT, pid, event);
155 }
156
157 int
158 main (int argc,
159 char *argv[])
160 {
161 argv0 = argv[0];
162
163 g_test_init (&argc, &argv, NULL);
164
165 if (argc > 2)
166 {
167 veh_debugger (argc, argv);
168 return 0;
169 }
170
171 g_test_add_func ("/win32/substitute-pid-and-event", test_subst_pid_and_event);
172
173 g_test_add_func ("/win32/veh/access_violation", test_veh_crash_access_violation);
174 g_test_add_func ("/win32/veh/illegal_instruction", test_veh_crash_illegal_instruction);
175 g_test_add_func ("/win32/veh/debug", test_veh_debug);
176
177 g_test_add_func ("/win32/subprocess/debuggee", test_veh_debuggee);
178 g_test_add_func ("/win32/subprocess/access_violation", test_access_violation);
179 g_test_add_func ("/win32/subprocess/illegal_instruction", test_illegal_instruction);
180
181 return g_test_run();
182 }