xref: /haiku/src/bin/debug/strace/strace.cpp (revision 12dba4e70f831d6d27a7f769cc9dab19c19a155d)
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 // command line args
114 static int sArgc;
115 static const char *const *sArgv;
116 
117 // syscalls
118 static vector<Syscall*>			sSyscallVector;
119 static map<string, Syscall*>	sSyscallMap;
120 
121 // statistics
122 typedef map<string, syscall_stats> StatsMap;
123 static StatsMap sSyscallStats;
124 static bigtime_t sSyscallTime;
125 
126 
127 struct Team {
128 	Team(team_id id)
129 		:
130 		fID(id),
131 		fNubPort(-1)
132 	{
133 	}
134 
135 	team_id ID() const
136 	{
137 		return fID;
138 	}
139 
140 	port_id NubPort() const
141 	{
142 		return fNubPort;
143 	}
144 
145 	MemoryReader& GetMemoryReader()
146 	{
147 		return fMemoryReader;
148 	}
149 
150 	status_t InstallDebugger(port_id debuggerPort, bool traceTeam,
151 		bool traceChildTeams, bool traceSignal)
152 	{
153 		fNubPort = install_team_debugger(fID, debuggerPort);
154 		if (fNubPort < 0) {
155 			fprintf(stderr, "%s: Failed to install team debugger: %s\n",
156 				kCommandName, strerror(fNubPort));
157 			return fNubPort;
158 		}
159 
160 		// set team debugging flags
161 		int32 teamDebugFlags = (traceTeam ? B_TEAM_DEBUG_POST_SYSCALL : 0)
162 			| (traceChildTeams ? B_TEAM_DEBUG_TEAM_CREATION : 0)
163 			| (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0);
164 		if (set_team_debugging_flags(fNubPort, teamDebugFlags) != B_OK)
165 			exit(1);
166 
167 		return fMemoryReader.Init(fNubPort);
168 	}
169 
170 private:
171 	team_id			fID;
172 	port_id			fNubPort;
173 	MemoryReader	fMemoryReader;
174 };
175 
176 
177 static void
178 print_usage(bool error)
179 {
180 	// print usage
181 	fprintf((error ? stderr : stdout), kUsage, kCommandName);
182 }
183 
184 
185 static void
186 print_usage_and_exit(bool error)
187 {
188 	print_usage(error);
189 	exit(error ? 1 : 0);
190 }
191 
192 
193 static bool
194 get_id(const char *str, int32 &id)
195 {
196 	int32 len = strlen(str);
197 	for (int32 i = 0; i < len; i++) {
198 		if (!isdigit(str[i]))
199 			return false;
200 	}
201 
202 	id = atol(str);
203 	return true;
204 }
205 
206 
207 Syscall *
208 get_syscall(const char *name)
209 {
210 	map<string, Syscall *>::const_iterator i = sSyscallMap.find(name);
211 	if (i == sSyscallMap.end())
212 		return NULL;
213 
214 	return i->second;
215 }
216 
217 
218 static void
219 patch_syscalls()
220 {
221 	// instead of having this done here manually we should either add the
222 	// patching step to gensyscalls also manually or add metadata to
223 	// kernel/syscalls.h and have it parsed automatically
224 	extern void patch_ioctl();
225 
226 	patch_ioctl();
227 }
228 
229 
230 static void
231 init_syscalls()
232 {
233 	// init the syscall vector
234 	get_syscalls0(sSyscallVector);
235 	get_syscalls1(sSyscallVector);
236 	get_syscalls2(sSyscallVector);
237 	get_syscalls3(sSyscallVector);
238 	get_syscalls4(sSyscallVector);
239 	get_syscalls5(sSyscallVector);
240 	get_syscalls6(sSyscallVector);
241 	get_syscalls7(sSyscallVector);
242 	get_syscalls8(sSyscallVector);
243 	get_syscalls9(sSyscallVector);
244 	get_syscalls10(sSyscallVector);
245 	get_syscalls11(sSyscallVector);
246 	get_syscalls12(sSyscallVector);
247 	get_syscalls13(sSyscallVector);
248 	get_syscalls14(sSyscallVector);
249 	get_syscalls15(sSyscallVector);
250 	get_syscalls16(sSyscallVector);
251 	get_syscalls17(sSyscallVector);
252 	get_syscalls18(sSyscallVector);
253 	get_syscalls19(sSyscallVector);
254 
255 	// init the syscall map
256 	int32 count = sSyscallVector.size();
257 	for (int32 i = 0; i < count; i++) {
258 		Syscall *syscall = sSyscallVector[i];
259 		sSyscallMap[syscall->Name()] = syscall;
260 	}
261 
262 	patch_syscalls();
263 }
264 
265 
266 static void
267 record_syscall_stats(const Syscall& syscall, debug_post_syscall& message)
268 {
269 	syscall_stats& stats = sSyscallStats[syscall.Name()];
270 	stats.count++;
271 
272 	bigtime_t time = message.end_time - message.start_time;
273 	stats.time += time;
274 	sSyscallTime += time;
275 }
276 
277 
278 static void
279 print_buffer(FILE *outputFile, char* buffer, int32 length)
280 {
281 	// output either to file or serial debug line
282 	if (outputFile != NULL)
283 		fwrite(buffer, length, 1, outputFile);
284 	else
285 		_kern_debug_output(buffer);
286 }
287 
288 
289 static void
290 print_to_string(char **_buffer, int32 *_length, const char *format, ...)
291 {
292 	va_list list;
293 	va_start(list, format);
294 	ssize_t length = vsnprintf(*_buffer, *_length, format, list);
295 	va_end(list);
296 
297 	*_buffer += length;
298 	*_length -= length;
299 }
300 
301 
302 static void
303 print_syscall(FILE *outputFile, Syscall* syscall, debug_post_syscall &message,
304 	MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags,
305 	bool printReturnValue, bool colorize, bool decimal)
306 {
307 	char buffer[4096], *string = buffer;
308 	int32 length = (int32)sizeof(buffer);
309 
310 	Context ctx(syscall, (char *)message.args, memoryReader,
311 		    contentsFlags, decimal);
312 
313 	// print syscall name
314 	if (colorize) {
315 		print_to_string(&string, &length, "[%6ld] %s%s%s(",
316 			message.origin.thread, kTerminalTextBlue, syscall->Name().c_str(),
317 			kTerminalTextNormal);
318 	} else {
319 		print_to_string(&string, &length, "[%6ld] %s(",
320 			message.origin.thread, syscall->Name().c_str());
321 	}
322 
323 	// print arguments
324 	if (printArguments) {
325 		int32 count = syscall->CountParameters();
326 		for (int32 i = 0; i < count; i++) {
327 			// get the value
328 			Parameter *parameter = syscall->ParameterAt(i);
329 			TypeHandler *handler = parameter->Handler();
330 			::string value =
331 				handler->GetParameterValue(ctx, parameter,
332 						ctx.GetValue(parameter));
333 
334 			print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"),
335 				value.c_str());
336 		}
337 	}
338 
339 	print_to_string(&string, &length, ")");
340 
341 	// print return value
342 	if (printReturnValue) {
343 		Type *returnType = syscall->ReturnType();
344 		TypeHandler *handler = returnType->Handler();
345 		::string value = handler->GetReturnValue(ctx, message.return_value);
346 		if (value.length() > 0) {
347 			print_to_string(&string, &length, " = %s", value.c_str());
348 
349 			// if the return type is status_t or ssize_t, print human-readable
350 			// error codes
351 			if (returnType->TypeName() == "status_t"
352 				|| ((returnType->TypeName() == "ssize_t"
353 						|| returnType->TypeName() == "int")
354 					&& message.return_value < 0)) {
355 				print_to_string(&string, &length, " %s", strerror(message.return_value));
356 			}
357 		}
358 	}
359 
360 	if (colorize) {
361 		print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta,
362 			message.end_time - message.start_time, kTerminalTextNormal);
363 	} else {
364 		print_to_string(&string, &length, " (%lld us)\n",
365 			message.end_time - message.start_time);
366 	}
367 
368 //for (int32 i = 0; i < 16; i++) {
369 //	if (i % 4 == 0) {
370 //		if (i > 0)
371 //			printf("\n");
372 //		printf("  ");
373 //	} else
374 //		printf(" ");
375 //	printf("%08lx", message.args[i]);
376 //}
377 //printf("\n");
378 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
379 }
380 
381 
382 static const char *
383 signal_name(int signal)
384 {
385 	if (signal >= 0 && signal < NSIG)
386 		return strsignal(signal);
387 
388 	static char buffer[32];
389 	sprintf(buffer, "%d", signal);
390 	return buffer;
391 }
392 
393 
394 static void
395 print_signal(FILE *outputFile, debug_signal_received &message,
396 	bool colorize)
397 {
398 	char buffer[4096], *string = buffer;
399 	int32 length = (int32)sizeof(buffer);
400 	int signalNumber = message.signal;
401 
402 	// print signal name
403 	if (colorize) {
404 		print_to_string(&string, &length, "[%6ld] --- %s%s (%s) %s---\n",
405 			message.origin.thread, kTerminalTextRed, signal_name(signalNumber),
406 			strsignal(signalNumber), kTerminalTextNormal);
407 	} else {
408 		print_to_string(&string, &length, "[%6ld] --- %s (%s) ---\n",
409 			message.origin.thread, signal_name(signalNumber),
410 			strsignal(signalNumber));
411 	}
412 
413 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
414 }
415 
416 
417 static bool
418 compare_stats_by_time(
419 	const std::pair<const std::string*, const syscall_stats*>& a,
420 	const std::pair<const std::string*, const syscall_stats*>& b)
421 {
422 	return a.second->time > b.second->time;
423 }
424 
425 
426 static void
427 print_stats(FILE* outputFile)
428 {
429 	char buffer[4096], *string = buffer;
430 	int32 length = (int32)sizeof(buffer);
431 
432 	typedef std::vector<std::pair<const std::string*, const syscall_stats*> >
433 		StatsRefVector;
434 	StatsRefVector calls;
435 	StatsMap::const_iterator iterator = sSyscallStats.begin();
436 	for (; iterator != sSyscallStats.end(); iterator++)
437 		calls.push_back(std::make_pair(&iterator->first, &iterator->second));
438 
439 	// Sort calls by time spent
440 	std::sort(calls.begin(), calls.end(), compare_stats_by_time);
441 
442 	print_to_string(&string, &length, "\n%-6s %-10s %-7s %-10s Syscall\n",
443 		"Time %", "Usecs", "Calls", "Usecs/call");
444 	print_to_string(&string, &length, "------ ---------- ------- ---------- "
445 		"--------------------\n");
446 
447 	StatsRefVector::const_iterator callIterator = calls.begin();
448 	for (; callIterator != calls.end(); callIterator++) {
449 		const syscall_stats& stats = *callIterator->second;
450 		double percent = stats.time * 100.0 / sSyscallTime;
451 		bigtime_t perCall = stats.time / stats.count;
452 
453 		print_to_string(&string, &length, "%6.2f %10" B_PRIu64 " %7" B_PRIu32
454 			" %10" B_PRIu64 " %s\n", percent, stats.time, stats.count, perCall,
455 			callIterator->first->c_str());
456 	}
457 
458 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
459 }
460 
461 
462 int
463 main(int argc, const char *const *argv)
464 {
465 	sArgc = argc;
466 	sArgv = argv;
467 
468 	// parameters
469 	const char *const *programArgs = NULL;
470 	int32 programArgCount = 0;
471 	bool printArguments = true;
472 	bool colorize = true;
473 	bool stats = false;
474 	bool trace = true;
475 	uint32 contentsFlags = 0;
476 	bool decimalFormat = false;
477 	bool fastMode = false;
478 	bool traceLoading = false;
479 	bool printReturnValues = true;
480 	bool traceChildThreads = false;
481 	bool traceTeam = false;
482 	bool traceChildTeams = false;
483 	bool traceSignal = true;
484 	bool serialOutput = false;
485 	FILE *outputFile = stdout;
486 
487 	// parse arguments
488 	for (int argi = 1; argi < argc; argi++) {
489 		const char *arg = argv[argi];
490 		if (arg[0] == '-') {
491 			// ToDo: improve option parsing so that ie. "-rsf" would also work
492 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
493 				print_usage_and_exit(false);
494 			} else if (strcmp(arg, "-a") == 0) {
495 				printArguments = false;
496 			} else if (strcmp(arg, "-c") == 0) {
497 				stats = true;
498 				trace = false;
499 			} else if (strcmp(arg, "-C") == 0) {
500 				stats = true;
501 			} else if (strcmp(arg, "--no-color") == 0) {
502 				colorize = false;
503 			} else if (strcmp(arg, "-d") == 0) {
504 				const char *what = NULL;
505 
506 				if (arg[2] == '\0'
507 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
508 					// next arg is what
509 					what = argv[++argi];
510 				} else
511 					print_usage_and_exit(true);
512 
513 				if (strcasecmp(what, "strings") == 0)
514 					contentsFlags |= Context::STRINGS;
515 				else if (strcasecmp(what, "enums") == 0)
516 					contentsFlags |= Context::ENUMERATIONS;
517 				else if (strcasecmp(what, "simple") == 0)
518 					contentsFlags |= Context::SIMPLE_STRUCTS;
519 				else if (strcasecmp(what, "complex") == 0)
520 					contentsFlags |= Context::COMPLEX_STRUCTS;
521 				else if (strcasecmp(what, "pointer_values") == 0)
522 					contentsFlags |= Context::POINTER_VALUES;
523 				else {
524 					fprintf(stderr, "%s: Unknown content filter `%s'\n",
525 						kCommandName, what);
526 					exit(1);
527 				}
528 			} else if (strcmp(arg, "-f") == 0) {
529 				fastMode = true;
530 			} else if (strcmp(arg, "-i") == 0) {
531 				decimalFormat = true;
532 			} else if (strcmp(arg, "-l") == 0) {
533 				traceLoading = true;
534 			} else if (strcmp(arg, "-r") == 0) {
535 				printReturnValues = false;
536 			} else if (strcmp(arg, "-s") == 0) {
537 				traceChildThreads = true;
538 			} else if (strcmp(arg, "-t") == 0) {
539 				traceChildTeams = true;
540 			} else if (strcmp(arg, "-T") == 0) {
541 				traceTeam = true;
542 			} else if (strcmp(arg, "-g") == 0) {
543 				traceSignal = false;
544 			} else if (strcmp(arg, "-S") == 0) {
545 				serialOutput = true;
546 				outputFile = NULL;
547 			} else if (strncmp(arg, "-o", 2) == 0) {
548 				// read filename
549 				const char *filename = NULL;
550 				if (arg[2] == '=') {
551 					// name follows
552 					filename = arg + 3;
553 				} else if (arg[2] == '\0'
554 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
555 					// next arg is name
556 					filename = argv[++argi];
557 				} else
558 					print_usage_and_exit(true);
559 
560 				outputFile = fopen(filename, "w+");
561 				if (outputFile == NULL) {
562 					fprintf(stderr, "%s: Could not open `%s': %s\n",
563 						kCommandName, filename, strerror(errno));
564 					exit(1);
565 				}
566 			} else {
567 				print_usage_and_exit(true);
568 			}
569 		} else {
570 			programArgs = argv + argi;
571 			programArgCount = argc - argi;
572 			break;
573 		}
574 	}
575 
576 	// check parameters
577 	if (!programArgs)
578 		print_usage_and_exit(true);
579 
580 	if (fastMode)
581 		contentsFlags = 0;
582 	else if (contentsFlags == 0)
583 		contentsFlags = Context::ALL;
584 
585 	// initialize our syscalls vector and map
586 	init_syscalls();
587 
588 	// don't colorize the output, if we don't have a terminal
589 	if (outputFile == stdout)
590 		colorize = colorize && isatty(STDOUT_FILENO);
591 	else if (outputFile)
592 		colorize = false;
593 
594 	// get thread/team to be debugged
595 	thread_id threadID = -1;
596 	team_id teamID = -1;
597 	if (programArgCount > 1
598 		|| !get_id(*programArgs, (traceTeam ? teamID : threadID))) {
599 		// we've been given an executable and need to load it
600 		threadID = load_program(programArgs, programArgCount, traceLoading);
601 		if (threadID < 0) {
602 			fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
603 				programArgs[0], strerror(threadID));
604 			exit(1);
605 		}
606 	}
607 
608 	// get the team ID, if we have none yet
609 	if (teamID < 0) {
610 		thread_info threadInfo;
611 		status_t error = get_thread_info(threadID, &threadInfo);
612 		if (error != B_OK) {
613 			fprintf(stderr, "%s: Failed to get info for thread %" B_PRId32
614 				": %s\n", kCommandName, threadID, strerror(error));
615 			exit(1);
616 		}
617 		teamID = threadInfo.team;
618 	}
619 
620 	// create a debugger port
621 	port_id debuggerPort = create_port(10, "debugger port");
622 	if (debuggerPort < 0) {
623 		fprintf(stderr, "%s: Failed to create debugger port: %s\n",
624 			kCommandName, strerror(debuggerPort));
625 		exit(1);
626 	}
627 
628 	// install ourselves as the team debugger
629 	typedef map<team_id, Team*> TeamMap;
630 	TeamMap debuggedTeams;
631 	port_id nubPort;
632 
633 	{
634 		Team* team = new Team(teamID);
635 		status_t error = team->InstallDebugger(debuggerPort, traceTeam,
636 			traceChildTeams, traceSignal);
637 		if (error != B_OK)
638 			exit(1);
639 
640 		debuggedTeams[team->ID()] = team;
641 
642 		nubPort = team->NubPort();
643 	}
644 
645 	// set thread debugging flags
646 	if (threadID >= 0) {
647 		int32 threadDebugFlags = 0;
648 		if (!traceTeam) {
649 			threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
650 				| (traceChildThreads
651 					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
652 		}
653 		if (set_thread_debugging_flags(nubPort, threadID, threadDebugFlags)
654 				!= B_OK) {
655 			exit(1);
656 		}
657 
658 		// resume the target thread to be sure, it's running
659 		resume_thread(threadID);
660 	}
661 
662 	// debug loop
663 	while (true) {
664 		bool quitLoop = false;
665 		int32 code;
666 		debug_debugger_message_data message;
667 		ssize_t messageSize = read_port(debuggerPort, &code, &message,
668 			sizeof(message));
669 
670 		if (messageSize < 0) {
671 			if (messageSize == B_INTERRUPTED)
672 				continue;
673 
674 			fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
675 				kCommandName, strerror(messageSize));
676 			exit(1);
677 		}
678 
679 		switch (code) {
680 			case B_DEBUGGER_MESSAGE_POST_SYSCALL:
681 			{
682 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
683 				if (it == debuggedTeams.end())
684 					break;
685 
686 				Team* team = it->second;
687 				MemoryReader& memoryReader = team->GetMemoryReader();
688 
689 				uint32 syscallNumber = message.post_syscall.syscall;
690 				if (syscallNumber >= sSyscallVector.size()) {
691 					fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n",
692 						kCommandName, syscallNumber);
693 					break;
694 				}
695 				Syscall* syscall = sSyscallVector[syscallNumber];
696 
697 				if (stats)
698 					record_syscall_stats(*syscall, message.post_syscall);
699 
700 				if (trace) {
701 					print_syscall(outputFile, syscall, message.post_syscall,
702 						memoryReader, printArguments, contentsFlags,
703 						printReturnValues, colorize, decimalFormat);
704 				}
705 				break;
706 			}
707 
708 			case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
709 			{
710 				if (traceSignal && trace)
711 					print_signal(outputFile, message.signal_received, colorize);
712 				break;
713 			}
714 
715 			case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
716 			case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
717 			case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
718 			case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
719 			case B_DEBUGGER_MESSAGE_SINGLE_STEP:
720 			case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
721 			case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
722 			case B_DEBUGGER_MESSAGE_THREAD_CREATED:
723 			case B_DEBUGGER_MESSAGE_THREAD_DELETED:
724 			case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
725 			case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
726 				break;
727 
728 			case B_DEBUGGER_MESSAGE_TEAM_CREATED:
729 			{
730 				if (!traceChildTeams)
731 					break;
732 
733 				Team* team = new(std::nothrow) Team(
734 					message.team_created.new_team);
735 				if (team == NULL) {
736 					fprintf(stderr, "%s: Out of memory!\n", kCommandName);
737 					break;
738 				}
739 
740 				status_t error = team->InstallDebugger(debuggerPort, true, true,
741 					traceSignal);
742 				if (error != B_OK) {
743 					delete team;
744 					break;
745 				}
746 
747 				debuggedTeams[team->ID()] = team;
748 				break;
749 			}
750 
751 			case B_DEBUGGER_MESSAGE_TEAM_DELETED:
752 			{
753 				// a debugged team is gone
754 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
755 				if (it == debuggedTeams.end())
756 					break;
757 
758 				Team* team = it->second;
759 				debuggedTeams.erase(it);
760 				delete team;
761 
762 				// if all debugged teams are gone, we're done
763 				quitLoop = debuggedTeams.empty();
764 				break;
765 			}
766 		}
767 
768 		if (quitLoop)
769 			break;
770 
771 		// tell the thread to continue (only when there is a thread and the
772 		// message was synchronous)
773 		if (message.origin.thread >= 0 && message.origin.nub_port >= 0) {
774 			if (continue_thread(message.origin.nub_port,
775 					message.origin.thread) != B_OK) {
776 				exit(1);
777 			}
778 		}
779 	}
780 
781 	if (stats) {
782 		// Dump recorded statistics
783 		print_stats(outputFile);
784 	}
785 
786 	if (outputFile != NULL && outputFile != stdout)
787 		fclose(outputFile);
788 
789 	return 0;
790 }
791