xref: /haiku/src/bin/debug/strace/strace.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2  * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2013, Rene Gollent, rene@gollent.com.
4  * Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include <ctype.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <strings.h>
14 #include <errno.h>
15 #include <signal.h>
16 
17 #include <algorithm>
18 #include <map>
19 #include <string>
20 #include <vector>
21 
22 #include <debugger.h>
23 #include <image.h>
24 #include <syscalls.h>
25 
26 #include "debug_utils.h"
27 
28 #include "Context.h"
29 #include "MemoryReader.h"
30 #include "Syscall.h"
31 #include "TypeHandler.h"
32 
33 
34 using std::map;
35 using std::string;
36 using std::vector;
37 
38 
39 struct syscall_stats {
40 	bigtime_t	time;
41 	uint32		count;
42 };
43 
44 
45 extern void get_syscalls0(vector<Syscall*> &syscalls);
46 extern void get_syscalls1(vector<Syscall*> &syscalls);
47 extern void get_syscalls2(vector<Syscall*> &syscalls);
48 extern void get_syscalls3(vector<Syscall*> &syscalls);
49 extern void get_syscalls4(vector<Syscall*> &syscalls);
50 extern void get_syscalls5(vector<Syscall*> &syscalls);
51 extern void get_syscalls6(vector<Syscall*> &syscalls);
52 extern void get_syscalls7(vector<Syscall*> &syscalls);
53 extern void get_syscalls8(vector<Syscall*> &syscalls);
54 extern void get_syscalls9(vector<Syscall*> &syscalls);
55 extern void get_syscalls10(vector<Syscall*> &syscalls);
56 extern void get_syscalls11(vector<Syscall*> &syscalls);
57 extern void get_syscalls12(vector<Syscall*> &syscalls);
58 extern void get_syscalls13(vector<Syscall*> &syscalls);
59 extern void get_syscalls14(vector<Syscall*> &syscalls);
60 extern void get_syscalls15(vector<Syscall*> &syscalls);
61 extern void get_syscalls16(vector<Syscall*> &syscalls);
62 extern void get_syscalls17(vector<Syscall*> &syscalls);
63 extern void get_syscalls18(vector<Syscall*> &syscalls);
64 extern void get_syscalls19(vector<Syscall*> &syscalls);
65 
66 
67 extern const char *__progname;
68 static const char *kCommandName = __progname;
69 
70 
71 // usage
72 static const char *kUsage =
73 "Usage: %s [ <options> ] [ <thread or team ID> | <executable with args> ]\n"
74 "\n"
75 "Traces the syscalls of a thread or a team. If an executable with\n"
76 "arguments is supplied, it is loaded and it's main thread traced.\n"
77 "\n"
78 "Options:\n"
79 "  -a             - Don't print syscall arguments.\n"
80 "  -c             - Record and dump syscall usage statistics.\n"
81 "  -C             - Same as -c, but also print syscalls as usual.\n"
82 "  -d <name>      - Filter the types that have their contents retrieved.\n"
83 "                   <name> is one of: strings, enums, simple, complex or\n"
84 "                                     pointer_values\n"
85 "  -f             - Fast mode. Syscall arguments contents aren't retrieved.\n"
86 "  -h, --help     - Print this text.\n"
87 "  -i             - Print integers in decimal format instead of hexadecimal.\n"
88 "  -l             - Also trace loading the executable. Only considered when\n"
89 "                   an executable is provided.\n"
90 "  --no-color     - Don't colorize output.\n"
91 "  -r             - Don't print syscall return values.\n"
92 "  -s             - Also trace all threads spawned by the supplied thread,\n"
93 "                   respectively the loaded executable's main thread.\n"
94 "  -t             - Also recursively trace all teams created by a traced\n"
95 "                   thread or team.\n"
96 "  -T             - Trace all threads of the supplied or loaded executable's\n"
97 "                   team. If an ID is supplied, it is interpreted as a team\n"
98 "                   ID.\n"
99 "  -o <file>      - directs output into the specified file.\n"
100 "  -S             - prints output to serial debug line.\n"
101 "  -g             - turns off signal tracing.\n"
102 ;
103 
104 
105 // terminal color escape sequences
106 // (http://www.dee.ufcg.edu.br/~rrbrandt/tools/ansi.html)
107 static const char *kTerminalTextNormal	= "\33[0m";
108 static const char *kTerminalTextRed		= "\33[31m";
109 static const char *kTerminalTextMagenta	= "\33[35m";
110 static const char *kTerminalTextBlue	= "\33[34m";
111 
112 
113 // signal names
114 static const char *kSignalName[] = {
115 	/*  0 */ "SIG0",
116 	/*  1 */ "SIGHUP",
117 	/*  2 */ "SIGINT",
118 	/*  3 */ "SIGQUIT",
119 	/*  4 */ "SIGILL",
120 	/*  5 */ "SIGCHLD",
121 	/*  6 */ "SIGABRT",
122 	/*  7 */ "SIGPIPE",
123 	/*  8 */ "SIGFPE",
124 	/*  9 */ "SIGKILL",
125 	/* 10 */ "SIGSTOP",
126 	/* 11 */ "SIGSEGV",
127 	/* 12 */ "SIGCONT",
128 	/* 13 */ "SIGTSTP",
129 	/* 14 */ "SIGALRM",
130 	/* 15 */ "SIGTERM",
131 	/* 16 */ "SIGTTIN",
132 	/* 17 */ "SIGTTOU",
133 	/* 18 */ "SIGUSR1",
134 	/* 19 */ "SIGUSR2",
135 	/* 20 */ "SIGWINCH",
136 	/* 21 */ "SIGKILLTHR",
137 	/* 22 */ "SIGTRAP",
138 	/* 23 */ "SIGPOLL",
139 	/* 24 */ "SIGPROF",
140 	/* 25 */ "SIGSYS",
141 	/* 26 */ "SIGURG",
142 	/* 27 */ "SIGVTALRM",
143 	/* 28 */ "SIGXCPU",
144 	/* 29 */ "SIGXFSZ",
145 	/* 30 */ "SIGBUS",
146 	/* 31 */ "SIGRESERVED1",
147 	/* 32 */ "SIGRESERVED2",
148 };
149 
150 
151 // command line args
152 static int sArgc;
153 static const char *const *sArgv;
154 
155 // syscalls
156 static vector<Syscall*>			sSyscallVector;
157 static map<string, Syscall*>	sSyscallMap;
158 
159 // statistics
160 typedef map<string, syscall_stats> StatsMap;
161 static StatsMap sSyscallStats;
162 static bigtime_t sSyscallTime;
163 
164 
165 struct Team {
166 	Team(team_id id)
167 		:
168 		fID(id),
169 		fNubPort(-1)
170 	{
171 	}
172 
173 	team_id ID() const
174 	{
175 		return fID;
176 	}
177 
178 	port_id NubPort() const
179 	{
180 		return fNubPort;
181 	}
182 
183 	MemoryReader& GetMemoryReader()
184 	{
185 		return fMemoryReader;
186 	}
187 
188 	status_t InstallDebugger(port_id debuggerPort, bool traceTeam,
189 		bool traceChildTeams, bool traceSignal)
190 	{
191 		fNubPort = install_team_debugger(fID, debuggerPort);
192 		if (fNubPort < 0) {
193 			fprintf(stderr, "%s: Failed to install team debugger: %s\n",
194 				kCommandName, strerror(fNubPort));
195 			return fNubPort;
196 		}
197 
198 		// set team debugging flags
199 		int32 teamDebugFlags =
200 			(traceTeam ? B_TEAM_DEBUG_PRE_SYSCALL | B_TEAM_DEBUG_POST_SYSCALL : 0)
201 			| (traceChildTeams ? B_TEAM_DEBUG_TEAM_CREATION : 0)
202 			| (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0);
203 		if (set_team_debugging_flags(fNubPort, teamDebugFlags) != B_OK)
204 			exit(1);
205 
206 		return fMemoryReader.Init(fNubPort);
207 	}
208 
209 private:
210 	team_id			fID;
211 	port_id			fNubPort;
212 	MemoryReader	fMemoryReader;
213 };
214 
215 
216 static void
217 print_usage(bool error)
218 {
219 	// print usage
220 	fprintf((error ? stderr : stdout), kUsage, kCommandName);
221 }
222 
223 
224 static void
225 print_usage_and_exit(bool error)
226 {
227 	print_usage(error);
228 	exit(error ? 1 : 0);
229 }
230 
231 
232 static bool
233 get_id(const char *str, int32 &id)
234 {
235 	int32 len = strlen(str);
236 	for (int32 i = 0; i < len; i++) {
237 		if (!isdigit(str[i]))
238 			return false;
239 	}
240 
241 	id = atol(str);
242 	return true;
243 }
244 
245 
246 Syscall *
247 get_syscall(const char *name)
248 {
249 	map<string, Syscall *>::const_iterator i = sSyscallMap.find(name);
250 	if (i == sSyscallMap.end())
251 		return NULL;
252 
253 	return i->second;
254 }
255 
256 
257 static void
258 patch_syscalls()
259 {
260 	// instead of having this done here manually we should either add the
261 	// patching step to gensyscalls also manually or add metadata to
262 	// kernel/syscalls.h and have it parsed automatically
263 	extern void patch_fcntl();
264 	extern void patch_ioctl();
265 
266 	patch_fcntl();
267 	patch_ioctl();
268 
269 	Syscall *poll = get_syscall("_kern_poll");
270 	poll->ParameterAt(0)->SetInOut(true);
271 
272 	Syscall *select = get_syscall("_kern_select");
273 	select->ParameterAt(1)->SetInOut(true);
274 	select->ParameterAt(2)->SetInOut(true);
275 	select->ParameterAt(3)->SetInOut(true);
276 
277 	Syscall *wait = get_syscall("_kern_wait_for_child");
278 	wait->ParameterAt(2)->SetOut(true);
279 	wait->ParameterAt(3)->SetOut(true);
280 }
281 
282 
283 static void
284 init_syscalls()
285 {
286 	// init the syscall vector
287 	get_syscalls0(sSyscallVector);
288 	get_syscalls1(sSyscallVector);
289 	get_syscalls2(sSyscallVector);
290 	get_syscalls3(sSyscallVector);
291 	get_syscalls4(sSyscallVector);
292 	get_syscalls5(sSyscallVector);
293 	get_syscalls6(sSyscallVector);
294 	get_syscalls7(sSyscallVector);
295 	get_syscalls8(sSyscallVector);
296 	get_syscalls9(sSyscallVector);
297 	get_syscalls10(sSyscallVector);
298 	get_syscalls11(sSyscallVector);
299 	get_syscalls12(sSyscallVector);
300 	get_syscalls13(sSyscallVector);
301 	get_syscalls14(sSyscallVector);
302 	get_syscalls15(sSyscallVector);
303 	get_syscalls16(sSyscallVector);
304 	get_syscalls17(sSyscallVector);
305 	get_syscalls18(sSyscallVector);
306 	get_syscalls19(sSyscallVector);
307 
308 	// init the syscall map
309 	int32 count = sSyscallVector.size();
310 	for (int32 i = 0; i < count; i++) {
311 		Syscall *syscall = sSyscallVector[i];
312 		sSyscallMap[syscall->Name()] = syscall;
313 	}
314 
315 	patch_syscalls();
316 }
317 
318 
319 static void
320 record_syscall_stats(const Syscall& syscall, debug_post_syscall& message)
321 {
322 	syscall_stats& stats = sSyscallStats[syscall.Name()];
323 	stats.count++;
324 
325 	bigtime_t time = message.end_time - message.start_time;
326 	stats.time += time;
327 	sSyscallTime += time;
328 }
329 
330 
331 static void
332 print_buffer(FILE *outputFile, char* buffer, int32 length)
333 {
334 	// output either to file or serial debug line
335 	if (outputFile != NULL)
336 		fwrite(buffer, length, 1, outputFile);
337 	else
338 		_kern_debug_output(buffer);
339 }
340 
341 
342 static void
343 print_to_string(char **_buffer, int32 *_length, const char *format, ...)
344 {
345 	va_list list;
346 	va_start(list, format);
347 	ssize_t length = vsnprintf(*_buffer, *_length, format, list);
348 	va_end(list);
349 
350 	*_buffer += length;
351 	*_length -= length;
352 }
353 
354 
355 static void
356 print_syscall(FILE *outputFile, Syscall* syscall, debug_pre_syscall &message,
357 	MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags,
358 	bool colorize, bool decimal, thread_id &currentThreadID)
359 {
360 	char buffer[4096], *string = buffer;
361 	int32 length = (int32)sizeof(buffer);
362 
363 	Context ctx(syscall, (char *)message.args, memoryReader,
364 		    contentsFlags | Context::INPUT_VALUES, decimal);
365 
366 	if (currentThreadID != message.origin.thread) {
367 		if (currentThreadID != -1)
368 			print_to_string(&string, &length, " <unfinished ...>\n");
369 		currentThreadID = message.origin.thread;
370 	}
371 
372 	// print syscall name, without the "_kern_"
373 	if (colorize) {
374 		print_to_string(&string, &length, "[%6" B_PRId32 "] %s%s%s(",
375 			message.origin.thread, kTerminalTextBlue,
376 			syscall->Name().c_str() + 6, kTerminalTextNormal);
377 	} else {
378 		print_to_string(&string, &length, "[%6" B_PRId32 "] %s(",
379 			message.origin.thread, syscall->Name().c_str() + 6);
380 	}
381 
382 	// print arguments
383 	if (printArguments) {
384 		int32 count = syscall->CountParameters();
385 		for (int32 i = 0; i < count; i++) {
386 			// get the value
387 			Parameter *parameter = syscall->ParameterAt(i);
388 			if (parameter->Out())
389 				continue;
390 			TypeHandler *handler = parameter->Handler();
391 			::string value =
392 				handler->GetParameterValue(ctx, parameter,
393 						ctx.GetValue(parameter));
394 
395 			print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"),
396 				value.c_str());
397 		}
398 	}
399 
400 	print_to_string(&string, &length, ")");
401 
402 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
403 }
404 
405 
406 static void
407 print_syscall(FILE *outputFile, Syscall* syscall, debug_post_syscall &message,
408 	MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags,
409 	bool printReturnValue, bool colorize, bool decimal,
410 	thread_id &currentThreadID)
411 {
412 	char buffer[4096], *string = buffer;
413 	int32 length = (int32)sizeof(buffer);
414 	bool threadChanged = false;
415 
416 	Context ctx(syscall, (char *)message.args, memoryReader,
417 		    contentsFlags | Context::OUTPUT_VALUES, decimal, message.return_value);
418 
419 	if (currentThreadID != message.origin.thread) {
420 		if (currentThreadID != -1) {
421 			print_to_string(&string, &length, " <unfinished ...>\n");
422 		}
423 		threadChanged = true;
424 	}
425 	currentThreadID = -1;
426 
427 	// print return value
428 	if (printReturnValue) {
429 		if (threadChanged) {
430 			// print syscall name, without the "_kern_"
431 			if (colorize) {
432 				print_to_string(&string, &length, "[%6" B_PRId32 "] <... "
433 					"%s%s%s resumed> ", message.origin.thread, kTerminalTextBlue,
434 					syscall->Name().c_str() + 6, kTerminalTextNormal);
435 			} else {
436 				print_to_string(&string, &length, "[%6" B_PRId32 "] <... %s"
437 					" resumed> ", message.origin.thread,
438 					syscall->Name().c_str() + 6);
439 			}
440 		}
441 		Type *returnType = syscall->ReturnType();
442 		TypeHandler *handler = returnType->Handler();
443 		::string value = handler->GetReturnValue(ctx, message.return_value);
444 		if (value.length() > 0) {
445 			print_to_string(&string, &length, " = %s", value.c_str());
446 
447 			// if the return type is status_t or ssize_t, print human-readable
448 			// error codes
449 			if (returnType->TypeName() == "status_t"
450 				|| ((returnType->TypeName() == "ssize_t"
451 						|| returnType->TypeName() == "int")
452 					&& message.return_value < 0)) {
453 				print_to_string(&string, &length, " %s", strerror(message.return_value));
454 			}
455 		}
456 	}
457 
458 	// print arguments
459 	if (printArguments) {
460 		int32 count = syscall->CountParameters();
461 		int added = 0;
462 		print_to_string(&string, &length, " (");
463 		for (int32 i = 0; i < count; i++) {
464 			// get the value
465 			Parameter *parameter = syscall->ParameterAt(i);
466 			if (!parameter->InOut() && !parameter->Out())
467 				continue;
468 			TypeHandler *handler = parameter->Handler();
469 			::string value =
470 				handler->GetParameterValue(ctx, parameter,
471 						ctx.GetValue(parameter));
472 
473 			print_to_string(&string, &length, (added > 0 ? ", %s" : "%s"),
474 				value.c_str());
475 			added++;
476 		}
477 		print_to_string(&string, &length, ")");
478 	}
479 
480 	if (colorize) {
481 		print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta,
482 			message.end_time - message.start_time, kTerminalTextNormal);
483 	} else {
484 		print_to_string(&string, &length, " (%lld us)\n",
485 			message.end_time - message.start_time);
486 	}
487 
488 //for (int32 i = 0; i < 16; i++) {
489 //	if (i % 4 == 0) {
490 //		if (i > 0)
491 //			printf("\n");
492 //		printf("  ");
493 //	} else
494 //		printf(" ");
495 //	printf("%08lx", message.args[i]);
496 //}
497 //printf("\n");
498 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
499 }
500 
501 
502 static const char *
503 signal_name(int signal)
504 {
505 	if (signal >= 0 && signal <= SIGRESERVED2)
506 		return kSignalName[signal];
507 
508 	static char buffer[32];
509 	sprintf(buffer, "%d", signal);
510 	return buffer;
511 }
512 
513 
514 static void
515 print_signal(FILE *outputFile, debug_signal_received &message,
516 	bool colorize)
517 {
518 	char buffer[4096], *string = buffer;
519 	int32 length = (int32)sizeof(buffer);
520 	int signalNumber = message.signal;
521 
522 	// print signal name
523 	if (colorize) {
524 		print_to_string(&string, &length, "[%6" B_PRId32 "] --- %s%s (%s) %s---\n",
525 			message.origin.thread, kTerminalTextRed, signal_name(signalNumber),
526 			strsignal(signalNumber), kTerminalTextNormal);
527 	} else {
528 		print_to_string(&string, &length, "[%6" B_PRId32 "] --- %s (%s) ---\n",
529 			message.origin.thread, signal_name(signalNumber),
530 			strsignal(signalNumber));
531 	}
532 
533 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
534 }
535 
536 
537 static bool
538 compare_stats_by_time(
539 	const std::pair<const std::string*, const syscall_stats*>& a,
540 	const std::pair<const std::string*, const syscall_stats*>& b)
541 {
542 	return a.second->time > b.second->time;
543 }
544 
545 
546 static void
547 print_stats(FILE* outputFile)
548 {
549 	char buffer[4096], *string = buffer;
550 	int32 length = (int32)sizeof(buffer);
551 
552 	typedef std::vector<std::pair<const std::string*, const syscall_stats*> >
553 		StatsRefVector;
554 	StatsRefVector calls;
555 	StatsMap::const_iterator iterator = sSyscallStats.begin();
556 	for (; iterator != sSyscallStats.end(); iterator++)
557 		calls.push_back(std::make_pair(&iterator->first, &iterator->second));
558 
559 	// Sort calls by time spent
560 	std::sort(calls.begin(), calls.end(), compare_stats_by_time);
561 
562 	print_to_string(&string, &length, "\n%-6s %-10s %-7s %-10s Syscall\n",
563 		"Time %", "Usecs", "Calls", "Usecs/call");
564 	print_to_string(&string, &length, "------ ---------- ------- ---------- "
565 		"--------------------\n");
566 
567 	StatsRefVector::const_iterator callIterator = calls.begin();
568 	for (; callIterator != calls.end(); callIterator++) {
569 		const syscall_stats& stats = *callIterator->second;
570 		double percent = stats.time * 100.0 / sSyscallTime;
571 		bigtime_t perCall = stats.time / stats.count;
572 
573 		print_to_string(&string, &length, "%6.2f %10" B_PRIu64 " %7" B_PRIu32
574 			" %10" B_PRIu64 " %s\n", percent, stats.time, stats.count, perCall,
575 			callIterator->first->c_str());
576 	}
577 
578 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
579 }
580 
581 
582 int
583 main(int argc, const char *const *argv)
584 {
585 	sArgc = argc;
586 	sArgv = argv;
587 
588 	// parameters
589 	const char *const *programArgs = NULL;
590 	int32 programArgCount = 0;
591 	bool printArguments = true;
592 	bool colorize = true;
593 	bool stats = false;
594 	bool trace = true;
595 	uint32 contentsFlags = 0;
596 	bool decimalFormat = false;
597 	bool fastMode = false;
598 	bool traceLoading = false;
599 	bool printReturnValues = true;
600 	bool traceChildThreads = false;
601 	bool traceTeam = false;
602 	bool traceChildTeams = false;
603 	bool traceSignal = true;
604 	bool serialOutput = false;
605 	FILE *outputFile = stdout;
606 
607 	// parse arguments
608 	for (int argi = 1; argi < argc; argi++) {
609 		const char *arg = argv[argi];
610 		if (arg[0] == '-') {
611 			// ToDo: improve option parsing so that ie. "-rsf" would also work
612 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
613 				print_usage_and_exit(false);
614 			} else if (strcmp(arg, "-a") == 0) {
615 				printArguments = false;
616 			} else if (strcmp(arg, "-c") == 0) {
617 				stats = true;
618 				trace = false;
619 			} else if (strcmp(arg, "-C") == 0) {
620 				stats = true;
621 			} else if (strcmp(arg, "--no-color") == 0) {
622 				colorize = false;
623 			} else if (strcmp(arg, "-d") == 0) {
624 				const char *what = NULL;
625 
626 				if (arg[2] == '\0'
627 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
628 					// next arg is what
629 					what = argv[++argi];
630 				} else
631 					print_usage_and_exit(true);
632 
633 				if (strcasecmp(what, "strings") == 0)
634 					contentsFlags |= Context::STRINGS;
635 				else if (strcasecmp(what, "enums") == 0)
636 					contentsFlags |= Context::ENUMERATIONS;
637 				else if (strcasecmp(what, "simple") == 0)
638 					contentsFlags |= Context::SIMPLE_STRUCTS;
639 				else if (strcasecmp(what, "complex") == 0)
640 					contentsFlags |= Context::COMPLEX_STRUCTS;
641 				else if (strcasecmp(what, "pointer_values") == 0)
642 					contentsFlags |= Context::POINTER_VALUES;
643 				else {
644 					fprintf(stderr, "%s: Unknown content filter `%s'\n",
645 						kCommandName, what);
646 					exit(1);
647 				}
648 			} else if (strcmp(arg, "-f") == 0) {
649 				fastMode = true;
650 			} else if (strcmp(arg, "-i") == 0) {
651 				decimalFormat = true;
652 			} else if (strcmp(arg, "-l") == 0) {
653 				traceLoading = true;
654 			} else if (strcmp(arg, "-r") == 0) {
655 				printReturnValues = false;
656 			} else if (strcmp(arg, "-s") == 0) {
657 				traceChildThreads = true;
658 			} else if (strcmp(arg, "-t") == 0) {
659 				traceChildTeams = true;
660 			} else if (strcmp(arg, "-T") == 0) {
661 				traceTeam = true;
662 			} else if (strcmp(arg, "-g") == 0) {
663 				traceSignal = false;
664 			} else if (strcmp(arg, "-S") == 0) {
665 				serialOutput = true;
666 				outputFile = NULL;
667 			} else if (strncmp(arg, "-o", 2) == 0) {
668 				// read filename
669 				const char *filename = NULL;
670 				if (arg[2] == '=') {
671 					// name follows
672 					filename = arg + 3;
673 				} else if (arg[2] == '\0'
674 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
675 					// next arg is name
676 					filename = argv[++argi];
677 				} else
678 					print_usage_and_exit(true);
679 
680 				outputFile = fopen(filename, "w+");
681 				if (outputFile == NULL) {
682 					fprintf(stderr, "%s: Could not open `%s': %s\n",
683 						kCommandName, filename, strerror(errno));
684 					exit(1);
685 				}
686 			} else {
687 				print_usage_and_exit(true);
688 			}
689 		} else {
690 			programArgs = argv + argi;
691 			programArgCount = argc - argi;
692 			break;
693 		}
694 	}
695 
696 	// check parameters
697 	if (!programArgs)
698 		print_usage_and_exit(true);
699 
700 	if (fastMode)
701 		contentsFlags = 0;
702 	else if (contentsFlags == 0)
703 		contentsFlags = Context::ALL;
704 
705 	// initialize our syscalls vector and map
706 	init_syscalls();
707 
708 	// don't colorize the output, if we don't have a terminal
709 	if (outputFile == stdout)
710 		colorize = colorize && isatty(STDOUT_FILENO);
711 	else if (outputFile)
712 		colorize = false;
713 
714 	// get thread/team to be debugged
715 	thread_id threadID = -1;
716 	team_id teamID = -1;
717 	if (programArgCount > 1
718 		|| !get_id(*programArgs, (traceTeam ? teamID : threadID))) {
719 		// we've been given an executable and need to load it
720 		threadID = load_program(programArgs, programArgCount, traceLoading);
721 		if (threadID < 0) {
722 			fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
723 				programArgs[0], strerror(threadID));
724 			exit(1);
725 		}
726 	}
727 
728 	// get the team ID, if we have none yet
729 	if (teamID < 0) {
730 		thread_info threadInfo;
731 		status_t error = get_thread_info(threadID, &threadInfo);
732 		if (error != B_OK) {
733 			fprintf(stderr, "%s: Failed to get info for thread %" B_PRId32
734 				": %s\n", kCommandName, threadID, strerror(error));
735 			exit(1);
736 		}
737 		teamID = threadInfo.team;
738 	}
739 
740 	// create a debugger port
741 	port_id debuggerPort = create_port(10, "debugger port");
742 	if (debuggerPort < 0) {
743 		fprintf(stderr, "%s: Failed to create debugger port: %s\n",
744 			kCommandName, strerror(debuggerPort));
745 		exit(1);
746 	}
747 
748 	// install ourselves as the team debugger
749 	typedef map<team_id, Team*> TeamMap;
750 	TeamMap debuggedTeams;
751 	port_id nubPort;
752 
753 	{
754 		Team* team = new Team(teamID);
755 		status_t error = team->InstallDebugger(debuggerPort, traceTeam,
756 			traceChildTeams, traceSignal);
757 		if (error != B_OK)
758 			exit(1);
759 
760 		debuggedTeams[team->ID()] = team;
761 
762 		nubPort = team->NubPort();
763 	}
764 
765 	// set thread debugging flags
766 	if (threadID >= 0) {
767 		int32 threadDebugFlags = 0;
768 		if (!traceTeam) {
769 			threadDebugFlags = B_THREAD_DEBUG_PRE_SYSCALL | B_THREAD_DEBUG_POST_SYSCALL
770 				| (traceChildThreads
771 					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
772 		}
773 		if (set_thread_debugging_flags(nubPort, threadID, threadDebugFlags)
774 				!= B_OK) {
775 			exit(1);
776 		}
777 
778 		// resume the target thread to be sure, it's running
779 		resume_thread(threadID);
780 	}
781 
782 	thread_id currentThreadID = -1;
783 
784 	// debug loop
785 	while (true) {
786 		bool quitLoop = false;
787 		int32 code;
788 		debug_debugger_message_data message;
789 		ssize_t messageSize = read_port(debuggerPort, &code, &message,
790 			sizeof(message));
791 
792 		if (messageSize < 0) {
793 			if (messageSize == B_INTERRUPTED)
794 				continue;
795 
796 			fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
797 				kCommandName, strerror(messageSize));
798 			exit(1);
799 		}
800 
801 		switch (code) {
802 			case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
803 			{
804 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
805 				if (it == debuggedTeams.end())
806 					break;
807 
808 				Team* team = it->second;
809 				MemoryReader& memoryReader = team->GetMemoryReader();
810 
811 				uint32 syscallNumber = message.pre_syscall.syscall;
812 				if (syscallNumber >= sSyscallVector.size()) {
813 					fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n",
814 						kCommandName, syscallNumber);
815 					break;
816 				}
817 				Syscall* syscall = sSyscallVector[syscallNumber];
818 
819 				if (trace) {
820 					print_syscall(outputFile, syscall, message.pre_syscall,
821 						memoryReader, printArguments, contentsFlags,
822 						colorize, decimalFormat, currentThreadID);
823 				}
824 				break;
825 			}
826 
827 			case B_DEBUGGER_MESSAGE_POST_SYSCALL:
828 			{
829 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
830 				if (it == debuggedTeams.end())
831 					break;
832 
833 				Team* team = it->second;
834 				MemoryReader& memoryReader = team->GetMemoryReader();
835 
836 				uint32 syscallNumber = message.post_syscall.syscall;
837 				if (syscallNumber >= sSyscallVector.size()) {
838 					fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n",
839 						kCommandName, syscallNumber);
840 					break;
841 				}
842 				Syscall* syscall = sSyscallVector[syscallNumber];
843 
844 				if (stats)
845 					record_syscall_stats(*syscall, message.post_syscall);
846 
847 				if (trace) {
848 					print_syscall(outputFile, syscall, message.post_syscall,
849 						memoryReader, printArguments, contentsFlags,
850 						printReturnValues, colorize, decimalFormat,
851 						currentThreadID);
852 				}
853 				break;
854 			}
855 
856 			case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
857 			{
858 				if (traceSignal && trace)
859 					print_signal(outputFile, message.signal_received, colorize);
860 				break;
861 			}
862 
863 			case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
864 			case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
865 			case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
866 			case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
867 			case B_DEBUGGER_MESSAGE_SINGLE_STEP:
868 			case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
869 			case B_DEBUGGER_MESSAGE_THREAD_CREATED:
870 			case B_DEBUGGER_MESSAGE_THREAD_DELETED:
871 			case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
872 			case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
873 				break;
874 
875 			case B_DEBUGGER_MESSAGE_TEAM_CREATED:
876 			{
877 				if (!traceChildTeams)
878 					break;
879 
880 				Team* team = new(std::nothrow) Team(
881 					message.team_created.new_team);
882 				if (team == NULL) {
883 					fprintf(stderr, "%s: Out of memory!\n", kCommandName);
884 					break;
885 				}
886 
887 				status_t error = team->InstallDebugger(debuggerPort, true, true,
888 					traceSignal);
889 				if (error != B_OK) {
890 					delete team;
891 					break;
892 				}
893 
894 				debuggedTeams[team->ID()] = team;
895 				break;
896 			}
897 
898 			case B_DEBUGGER_MESSAGE_TEAM_DELETED:
899 			{
900 				// a debugged team is gone
901 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
902 				if (it == debuggedTeams.end())
903 					break;
904 
905 				Team* team = it->second;
906 				debuggedTeams.erase(it);
907 				delete team;
908 
909 				// if all debugged teams are gone, we're done
910 				quitLoop = debuggedTeams.empty();
911 				break;
912 			}
913 		}
914 
915 		if (quitLoop)
916 			break;
917 
918 		// tell the thread to continue (only when there is a thread and the
919 		// message was synchronous)
920 		if (message.origin.thread >= 0 && message.origin.nub_port >= 0) {
921 			if (continue_thread(message.origin.nub_port,
922 					message.origin.thread) != B_OK) {
923 				// the team can already be gone
924 			}
925 		}
926 	}
927 
928 	if (stats) {
929 		// Dump recorded statistics
930 		print_stats(outputFile);
931 	}
932 
933 	if (outputFile != NULL && outputFile != stdout)
934 		fclose(outputFile);
935 
936 	return 0;
937 }
938