xref: /haiku/src/tests/servers/debug/crashing_app.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2  * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #undef NDEBUG
8 
9 #include <assert.h>
10 #include <errno.h>
11 #include <signal.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 
17 #include <OS.h>
18 
19 extern const char *__progname;
20 
21 // usage
22 static const char *kUsage =
23 	"%s <options> [ <mode> ]\n"
24 	"Crashes in more or less inovative ways.\n"
25 	"\n"
26 	"Options:\n"
27 	"  -d                   - call disable_debugger() first\n"
28 	"  -f, --fork           - fork() and continue in the child\n"
29 	"  -h, --help           - print this info text\n"
30 	"  --multi              - crash in multiple threads\n"
31 	"  --signal             - crash in a signal handler\n"
32 	"  --thread             - crash in a separate thread\n"
33 	"\n"
34 	"Modes:\n"
35 	"[general]\n"
36 	"  segv                 - dereferences a null pointer (default)\n"
37 	"  segv2                - strcmp() using a 0x1 pointer\n"
38 	"  div                  - executes a division by zero\n"
39 	"  debugger             - invokes debugger()\n"
40 	"  assert               - failed assert(), which should invoke the\n"
41 	"                         debugger\n"
42 	"\n"
43 	"[x86 specific]\n"
44 	"  int3                 - executes the int3 (breakpoint) instruction\n"
45 	"  protection           - executes an instruction that causes a general\n"
46 	"                         protection exception\n";
47 
48 // application name
49 const char *kAppName = __progname;
50 
51 static void
52 print_usage(bool error)
53 {
54 	fprintf(error ? stderr : stdout, kUsage, kAppName);
55 }
56 
57 
58 static void
59 print_usage_and_exit(bool error)
60 {
61 	print_usage(error);
62 	exit(error ? 0 : 1);
63 }
64 
65 
66 static int
67 crash_segv()
68 {
69 	int *a = 0;
70 	*a = 0;
71 	return 0;
72 }
73 
74 static int
75 crash_segv2()
76 {
77 	const char *str = (const char*)0x1;
78 	return strcmp(str, "Test");
79 }
80 
81 static int
82 crash_div()
83 {
84 	int i = 0;
85 	i = 1 / i;
86 	return i;
87 }
88 
89 static int
90 crash_debugger()
91 {
92 	debugger("crashing_app() invoked debugger()");
93 	return 0;
94 }
95 
96 
97 static int
98 crash_assert()
99 {
100 	assert(0 > 1);
101 	return 0;
102 }
103 
104 
105 #if __i386__
106 
107 static int
108 crash_int3()
109 {
110 	asm("int3");
111 	return 0;
112 }
113 
114 static int
115 crash_protection()
116 {
117 	asm("movl %0, %%dr7" : : "r"(0));
118 	return 0;
119 }
120 
121 #endif	// __i386__
122 
123 
124 typedef int crash_function_t();
125 
126 
127 struct Options {
128 	Options()
129 		:
130 		inThread(false),
131 		multipleThreads(false),
132 		inSignalHandler(false),
133 		disableDebugger(false),
134 		fork(false)
135 	{
136 	}
137 
138 	crash_function_t*	function;
139 	bool				inThread;
140 	bool				multipleThreads;
141 	bool				inSignalHandler;
142 	bool				disableDebugger;
143 	bool				fork;
144 };
145 
146 static Options sOptions;
147 
148 
149 static crash_function_t*
150 get_crash_function(const char* mode)
151 {
152 	if (strcmp(mode, "segv") == 0) {
153 		return crash_segv;
154 	} else if (strcmp(mode, "segv2") == 0) {
155 		return crash_segv2;
156 	} else if (strcmp(mode, "div") == 0) {
157 		return (crash_function_t*)crash_div;
158 	} else if (strcmp(mode, "debugger") == 0) {
159 		return crash_debugger;
160 	} else if (strcmp(mode, "assert") == 0) {
161 		return crash_assert;
162 #if __i386__
163 	} else if (strcmp(mode, "int3") == 0) {
164 		return crash_int3;
165 	} else if (strcmp(mode, "protection") == 0) {
166 		return crash_protection;
167 #endif	// __i386__
168 	}
169 
170 	return NULL;
171 }
172 
173 
174 static void
175 signal_handler(int signal)
176 {
177 	sOptions.function();
178 }
179 
180 
181 static void
182 do_crash()
183 {
184 	if (sOptions.inSignalHandler) {
185 		signal(SIGUSR1, &signal_handler);
186 		send_signal(find_thread(NULL), SIGUSR1);
187 	} else
188 		sOptions.function();
189 }
190 
191 
192 static status_t
193 crashing_thread(void* data)
194 {
195 	snooze(100000);
196 	do_crash();
197 	return 0;
198 }
199 
200 
201 int
202 main(int argc, const char* const* argv)
203 {
204 	const char* mode = "segv";
205 
206 	// parse args
207 	int argi = 1;
208 	while (argi < argc) {
209 		const char *arg = argv[argi++];
210 
211 		if (arg[0] == '-') {
212 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
213 				print_usage_and_exit(false);
214 			} else if (strcmp(arg, "-d") == 0) {
215 				sOptions.disableDebugger = true;
216 			} else if (strcmp(arg, "-f") == 0 || strcmp(arg, "--fork") == 0) {
217 				sOptions.fork = true;
218 			} else if (strcmp(arg, "--multi") == 0) {
219 				sOptions.inThread = true;
220 				sOptions.multipleThreads = true;
221 			} else if (strcmp(arg, "--signal") == 0) {
222 				sOptions.inSignalHandler = true;
223 			} else if (strcmp(arg, "--thread") == 0) {
224 				sOptions.inThread = true;
225 			} else {
226 				fprintf(stderr, "Invalid option \"%s\"\n", arg);
227 				print_usage_and_exit(true);
228 			}
229 		} else {
230 			mode = arg;
231 		}
232 	}
233 
234 	sOptions.function = get_crash_function(mode);
235 	if (sOptions.function == NULL) {
236 		fprintf(stderr, "Invalid mode \"%s\"\n", mode);
237 		print_usage_and_exit(true);
238 	}
239 
240 	if (sOptions.disableDebugger)
241 		disable_debugger(true);
242 
243 	if (sOptions.fork) {
244 		pid_t child = fork();
245 		if (child < 0) {
246 			fprintf(stderr, "fork() failed: %s\n", strerror(errno));
247 			exit(1);
248 		}
249 
250 		if (child > 0) {
251 			// the parent exits
252 			exit(1);
253 		}
254 
255 		// the child continues...
256 	}
257 
258 	if (sOptions.inThread) {
259 		thread_id thread = spawn_thread(crashing_thread, "crashing thread",
260 			B_NORMAL_PRIORITY, NULL);
261 		if (thread < 0) {
262 			fprintf(stderr, "Error: Failed to spawn thread: %s\n",
263 				strerror(thread));
264 			exit(1);
265 		}
266 
267 		resume_thread(thread);
268 
269 		if (sOptions.multipleThreads) {
270 			snooze(200000);
271 			do_crash();
272 		} else {
273 			status_t result;
274 			while (wait_for_thread(thread, &result) == B_INTERRUPTED) {
275 			}
276 		}
277 	} else {
278 		do_crash();
279 	}
280 
281 	return 0;
282 }
283