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