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