xref: /haiku/src/bin/debug/strace/strace.cpp (revision 86036b7aeaec9cdc06a7ae6221e78c8cc2d9982f)
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 
287 
288 static void
289 init_syscalls()
290 {
291 	// init the syscall vector
292 	get_syscalls0(sSyscallVector);
293 	get_syscalls1(sSyscallVector);
294 	get_syscalls2(sSyscallVector);
295 	get_syscalls3(sSyscallVector);
296 	get_syscalls4(sSyscallVector);
297 	get_syscalls5(sSyscallVector);
298 	get_syscalls6(sSyscallVector);
299 	get_syscalls7(sSyscallVector);
300 	get_syscalls8(sSyscallVector);
301 	get_syscalls9(sSyscallVector);
302 	get_syscalls10(sSyscallVector);
303 	get_syscalls11(sSyscallVector);
304 	get_syscalls12(sSyscallVector);
305 	get_syscalls13(sSyscallVector);
306 	get_syscalls14(sSyscallVector);
307 	get_syscalls15(sSyscallVector);
308 	get_syscalls16(sSyscallVector);
309 	get_syscalls17(sSyscallVector);
310 	get_syscalls18(sSyscallVector);
311 	get_syscalls19(sSyscallVector);
312 
313 	// init the syscall map
314 	int32 count = sSyscallVector.size();
315 	for (int32 i = 0; i < count; i++) {
316 		Syscall *syscall = sSyscallVector[i];
317 		sSyscallMap[syscall->Name()] = syscall;
318 	}
319 
320 	patch_syscalls();
321 }
322 
323 
324 static void
325 record_syscall_stats(const Syscall& syscall, debug_post_syscall& message)
326 {
327 	syscall_stats& stats = sSyscallStats[syscall.Name()];
328 	stats.count++;
329 
330 	bigtime_t time = message.end_time - message.start_time;
331 	stats.time += time;
332 	sSyscallTime += time;
333 }
334 
335 
336 static void
337 print_buffer(FILE *outputFile, char* buffer, int32 length)
338 {
339 	// output either to file or serial debug line
340 	if (outputFile != NULL)
341 		fwrite(buffer, length, 1, outputFile);
342 	else
343 		_kern_debug_output(buffer);
344 }
345 
346 
347 static void
348 print_to_string(char **_buffer, int32 *_length, const char *format, ...)
349 {
350 	va_list list;
351 	va_start(list, format);
352 	ssize_t length = vsnprintf(*_buffer, *_length, format, list);
353 	va_end(list);
354 
355 	*_buffer += length;
356 	*_length -= length;
357 }
358 
359 
360 static void
361 print_syscall(FILE *outputFile, Syscall* syscall, debug_pre_syscall &message,
362 	MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags,
363 	bool colorize, bool decimal, thread_id &currentThreadID)
364 {
365 	char buffer[4096], *string = buffer;
366 	int32 length = (int32)sizeof(buffer);
367 
368 	Context ctx(syscall, (char *)message.args, memoryReader,
369 		    contentsFlags | Context::INPUT_VALUES, decimal);
370 
371 	if (currentThreadID != message.origin.thread) {
372 		if (currentThreadID != -1)
373 			print_to_string(&string, &length, " <unfinished ...>\n");
374 		currentThreadID = message.origin.thread;
375 	}
376 
377 	// print syscall name, without the "_kern_"
378 	if (colorize) {
379 		print_to_string(&string, &length, "[%6" B_PRId32 "] %s%s%s(",
380 			message.origin.thread, kTerminalTextBlue,
381 			syscall->Name().c_str() + 6, kTerminalTextNormal);
382 	} else {
383 		print_to_string(&string, &length, "[%6" B_PRId32 "] %s(",
384 			message.origin.thread, syscall->Name().c_str() + 6);
385 	}
386 
387 	// print arguments
388 	if (printArguments) {
389 		int32 count = syscall->CountParameters();
390 		for (int32 i = 0; i < count; i++) {
391 			// get the value
392 			Parameter *parameter = syscall->ParameterAt(i);
393 			if (parameter->Out())
394 				continue;
395 			TypeHandler *handler = parameter->Handler();
396 			::string value =
397 				handler->GetParameterValue(ctx, parameter,
398 						ctx.GetValue(parameter));
399 
400 			print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"),
401 				value.c_str());
402 		}
403 	}
404 
405 	print_to_string(&string, &length, ")");
406 
407 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
408 }
409 
410 
411 static void
412 print_syscall(FILE *outputFile, Syscall* syscall, debug_post_syscall &message,
413 	MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags,
414 	bool printReturnValue, bool colorize, bool decimal,
415 	thread_id &currentThreadID)
416 {
417 	char buffer[4096], *string = buffer;
418 	int32 length = (int32)sizeof(buffer);
419 	bool threadChanged = false;
420 
421 	Context ctx(syscall, (char *)message.args, memoryReader,
422 		    contentsFlags | Context::OUTPUT_VALUES, decimal, message.return_value);
423 
424 	if (currentThreadID != message.origin.thread) {
425 		if (currentThreadID != -1) {
426 			print_to_string(&string, &length, " <unfinished ...>\n");
427 		}
428 		threadChanged = true;
429 	}
430 	currentThreadID = -1;
431 
432 	// print return value
433 	if (printReturnValue) {
434 		if (threadChanged) {
435 			// print syscall name, without the "_kern_"
436 			if (colorize) {
437 				print_to_string(&string, &length, "[%6" B_PRId32 "] <... "
438 					"%s%s%s resumed> ", message.origin.thread, kTerminalTextBlue,
439 					syscall->Name().c_str() + 6, kTerminalTextNormal);
440 			} else {
441 				print_to_string(&string, &length, "[%6" B_PRId32 "] <... %s"
442 					" resumed> ", message.origin.thread,
443 					syscall->Name().c_str() + 6);
444 			}
445 		}
446 		Type *returnType = syscall->ReturnType();
447 		TypeHandler *handler = returnType->Handler();
448 		::string value = handler->GetReturnValue(ctx, message.return_value);
449 		if (value.length() > 0) {
450 			print_to_string(&string, &length, " = %s", value.c_str());
451 
452 			// if the return type is status_t or ssize_t, print human-readable
453 			// error codes
454 			if (returnType->TypeName() == "status_t"
455 				|| ((returnType->TypeName() == "ssize_t"
456 						|| returnType->TypeName() == "int")
457 					&& message.return_value < 0)) {
458 				print_to_string(&string, &length, " %s", strerror(message.return_value));
459 			}
460 		}
461 	}
462 
463 	// print arguments
464 	if (printArguments) {
465 		int32 count = syscall->CountParameters();
466 		int added = 0;
467 		print_to_string(&string, &length, " (");
468 		for (int32 i = 0; i < count; i++) {
469 			// get the value
470 			Parameter *parameter = syscall->ParameterAt(i);
471 			if (!parameter->InOut() && !parameter->Out())
472 				continue;
473 			TypeHandler *handler = parameter->Handler();
474 			::string value =
475 				handler->GetParameterValue(ctx, parameter,
476 						ctx.GetValue(parameter));
477 
478 			print_to_string(&string, &length, (added > 0 ? ", %s" : "%s"),
479 				value.c_str());
480 			added++;
481 		}
482 		print_to_string(&string, &length, ")");
483 	}
484 
485 	if (colorize) {
486 		print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta,
487 			message.end_time - message.start_time, kTerminalTextNormal);
488 	} else {
489 		print_to_string(&string, &length, " (%lld us)\n",
490 			message.end_time - message.start_time);
491 	}
492 
493 //for (int32 i = 0; i < 16; i++) {
494 //	if (i % 4 == 0) {
495 //		if (i > 0)
496 //			printf("\n");
497 //		printf("  ");
498 //	} else
499 //		printf(" ");
500 //	printf("%08lx", message.args[i]);
501 //}
502 //printf("\n");
503 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
504 }
505 
506 
507 static const char *
508 signal_name(int signal)
509 {
510 	if (signal >= 0 && signal <= SIGRESERVED2)
511 		return kSignalName[signal];
512 
513 	static char buffer[32];
514 	sprintf(buffer, "%d", signal);
515 	return buffer;
516 }
517 
518 
519 static void
520 print_signal(FILE *outputFile, debug_signal_received &message,
521 	bool colorize)
522 {
523 	char buffer[4096], *string = buffer;
524 	int32 length = (int32)sizeof(buffer);
525 	int signalNumber = message.signal;
526 
527 	// print signal name
528 	if (colorize) {
529 		print_to_string(&string, &length, "[%6" B_PRId32 "] --- %s%s (%s) %s---\n",
530 			message.origin.thread, kTerminalTextRed, signal_name(signalNumber),
531 			strsignal(signalNumber), kTerminalTextNormal);
532 	} else {
533 		print_to_string(&string, &length, "[%6" B_PRId32 "] --- %s (%s) ---\n",
534 			message.origin.thread, signal_name(signalNumber),
535 			strsignal(signalNumber));
536 	}
537 
538 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
539 }
540 
541 
542 static bool
543 compare_stats_by_time(
544 	const std::pair<const std::string*, const syscall_stats*>& a,
545 	const std::pair<const std::string*, const syscall_stats*>& b)
546 {
547 	return a.second->time > b.second->time;
548 }
549 
550 
551 static void
552 print_stats(FILE* outputFile)
553 {
554 	char buffer[4096], *string = buffer;
555 	int32 length = (int32)sizeof(buffer);
556 
557 	typedef std::vector<std::pair<const std::string*, const syscall_stats*> >
558 		StatsRefVector;
559 	StatsRefVector calls;
560 	StatsMap::const_iterator iterator = sSyscallStats.begin();
561 	for (; iterator != sSyscallStats.end(); iterator++)
562 		calls.push_back(std::make_pair(&iterator->first, &iterator->second));
563 
564 	// Sort calls by time spent
565 	std::sort(calls.begin(), calls.end(), compare_stats_by_time);
566 
567 	print_to_string(&string, &length, "\n%-6s %-10s %-7s %-10s Syscall\n",
568 		"Time %", "Usecs", "Calls", "Usecs/call");
569 	print_to_string(&string, &length, "------ ---------- ------- ---------- "
570 		"--------------------\n");
571 
572 	StatsRefVector::const_iterator callIterator = calls.begin();
573 	for (; callIterator != calls.end(); callIterator++) {
574 		const syscall_stats& stats = *callIterator->second;
575 		double percent = stats.time * 100.0 / sSyscallTime;
576 		bigtime_t perCall = stats.time / stats.count;
577 
578 		print_to_string(&string, &length, "%6.2f %10" B_PRIu64 " %7" B_PRIu32
579 			" %10" B_PRIu64 " %s\n", percent, stats.time, stats.count, perCall,
580 			callIterator->first->c_str());
581 	}
582 
583 	print_buffer(outputFile, buffer, sizeof(buffer) - length);
584 }
585 
586 
587 int
588 main(int argc, const char *const *argv)
589 {
590 	sArgc = argc;
591 	sArgv = argv;
592 
593 	// parameters
594 	const char *const *programArgs = NULL;
595 	int32 programArgCount = 0;
596 	bool printArguments = true;
597 	bool colorize = true;
598 	bool stats = false;
599 	bool trace = true;
600 	uint32 contentsFlags = 0;
601 	bool decimalFormat = false;
602 	bool fastMode = false;
603 	bool traceLoading = false;
604 	bool printReturnValues = true;
605 	bool traceChildThreads = false;
606 	bool traceTeam = false;
607 	bool traceChildTeams = false;
608 	bool traceSignal = true;
609 	bool traceFilter = false;
610 	FILE *outputFile = stdout;
611 
612 	// initialize our syscalls vector and map
613 	init_syscalls();
614 
615 	// parse arguments
616 	for (int argi = 1; argi < argc; argi++) {
617 		const char *arg = argv[argi];
618 		if (arg[0] == '-') {
619 			// ToDo: improve option parsing so that ie. "-rsf" would also work
620 			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
621 				print_usage_and_exit(false);
622 			} else if (strcmp(arg, "-a") == 0) {
623 				printArguments = false;
624 			} else if (strcmp(arg, "-c") == 0) {
625 				stats = true;
626 				trace = false;
627 			} else if (strcmp(arg, "-C") == 0) {
628 				stats = true;
629 			} else if (strcmp(arg, "--no-color") == 0) {
630 				colorize = false;
631 			} else if (strcmp(arg, "-d") == 0) {
632 				const char *what = NULL;
633 
634 				if (arg[2] == '\0'
635 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
636 					// next arg is what
637 					what = argv[++argi];
638 				} else
639 					print_usage_and_exit(true);
640 
641 				if (strcasecmp(what, "strings") == 0)
642 					contentsFlags |= Context::STRINGS;
643 				else if (strcasecmp(what, "enums") == 0)
644 					contentsFlags |= Context::ENUMERATIONS;
645 				else if (strcasecmp(what, "simple") == 0)
646 					contentsFlags |= Context::SIMPLE_STRUCTS;
647 				else if (strcasecmp(what, "complex") == 0)
648 					contentsFlags |= Context::COMPLEX_STRUCTS;
649 				else if (strcasecmp(what, "pointer_values") == 0)
650 					contentsFlags |= Context::POINTER_VALUES;
651 				else {
652 					fprintf(stderr, "%s: Unknown content filter `%s'\n",
653 						kCommandName, what);
654 					exit(1);
655 				}
656 			} else if (strcmp(arg, "-e") == 0) {
657 				traceFilter = true;
658 				// read filter string
659 				const char *filterString = NULL;
660 				if (arg[2] == '=') {
661 					// string follows
662 					filterString = arg + 3;
663 				} else if (arg[2] == '\0'
664 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
665 					// next arg is string
666 					filterString = argv[++argi];
667 				} else
668 					print_usage_and_exit(true);
669 				if (filterString != NULL) {
670 					char* copy = strdup(filterString);
671 					char *tok = strtok(copy, ",");
672 					while (tok != NULL) {
673 						if (tok[0] == '%') {
674 							tok++;
675 							// the following should be metadata in kernel/syscalls.h
676 							if (strcmp(tok, "memory") == 0) {
677 								sSyscallMap["clone_area"]->EnableTracing(true);
678 								sSyscallMap["create_area"]->EnableTracing(true);
679 								sSyscallMap["delete_area"]->EnableTracing(true);
680 								sSyscallMap["find_area"]->EnableTracing(true);
681 								sSyscallMap["resize_area"]->EnableTracing(true);
682 								sSyscallMap["transfer_area"]->EnableTracing(true);
683 								sSyscallMap["mlock"]->EnableTracing(true);
684 								sSyscallMap["munlock"]->EnableTracing(true);
685 								sSyscallMap["set_memory_protection"]->EnableTracing(true);
686 								sSyscallMap["get_memory_properties"]->EnableTracing(true);
687 								sSyscallMap["sync_memory"]->EnableTracing(true);
688 								sSyscallMap["unmap_memory"]->EnableTracing(true);
689 								sSyscallMap["memory_advice"]->EnableTracing(true);
690 								sSyscallMap["reserve_address_range"]->EnableTracing(true);
691 								sSyscallMap["unreserve_address_range"]->EnableTracing(true);
692 								sSyscallMap["set_area_protection"]->EnableTracing(true);
693 								sSyscallMap["map_file"]->EnableTracing(true);
694 							} else if (strcmp(tok, "network") == 0 || strcmp(tok, "net") == 0) {
695 								sSyscallMap["socket"]->EnableTracing(true);
696 								sSyscallMap["bind"]->EnableTracing(true);
697 								sSyscallMap["shutdown_socket"]->EnableTracing(true);
698 								sSyscallMap["connect"]->EnableTracing(true);
699 								sSyscallMap["listen"]->EnableTracing(true);
700 								sSyscallMap["accept"]->EnableTracing(true);
701 								sSyscallMap["recv"]->EnableTracing(true);
702 								sSyscallMap["recvfrom"]->EnableTracing(true);
703 								sSyscallMap["recvmsg"]->EnableTracing(true);
704 								sSyscallMap["send"]->EnableTracing(true);
705 								sSyscallMap["sendto"]->EnableTracing(true);
706 								sSyscallMap["sendmsg"]->EnableTracing(true);
707 								sSyscallMap["getsockopt"]->EnableTracing(true);
708 								sSyscallMap["setsockopt"]->EnableTracing(true);
709 								sSyscallMap["getpeername"]->EnableTracing(true);
710 								sSyscallMap["getsockname"]->EnableTracing(true);
711 								sSyscallMap["sockatmark"]->EnableTracing(true);
712 								sSyscallMap["socketpair"]->EnableTracing(true);
713 								sSyscallMap["get_next_socket_stat"]->EnableTracing(true);
714 							} else
715 								print_usage_and_exit(true);
716 						} else {
717 							char buffer[64];
718 							snprintf(buffer, sizeof(buffer), "_kern_%s", tok);
719 							Syscall* syscall = get_syscall(buffer);
720 							if (syscall == NULL)
721 								print_usage_and_exit(true);
722 							syscall->EnableTracing(true);
723 						}
724 					    tok = strtok(NULL, ",");
725 					}
726 					free(copy);
727 				}
728 			} else if (strcmp(arg, "-f") == 0) {
729 				fastMode = true;
730 			} else if (strcmp(arg, "-i") == 0) {
731 				decimalFormat = true;
732 			} else if (strcmp(arg, "-l") == 0) {
733 				traceLoading = true;
734 			} else if (strcmp(arg, "-r") == 0) {
735 				printReturnValues = false;
736 			} else if (strcmp(arg, "-s") == 0) {
737 				traceChildThreads = true;
738 			} else if (strcmp(arg, "-t") == 0) {
739 				traceChildTeams = true;
740 			} else if (strcmp(arg, "-T") == 0) {
741 				traceTeam = true;
742 			} else if (strcmp(arg, "-g") == 0) {
743 				traceSignal = false;
744 			} else if (strcmp(arg, "-S") == 0) {
745 				outputFile = NULL;
746 			} else if (strncmp(arg, "-o", 2) == 0) {
747 				// read filename
748 				const char *filename = NULL;
749 				if (arg[2] == '=') {
750 					// name follows
751 					filename = arg + 3;
752 				} else if (arg[2] == '\0'
753 					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
754 					// next arg is name
755 					filename = argv[++argi];
756 				} else
757 					print_usage_and_exit(true);
758 
759 				outputFile = fopen(filename, "w+");
760 				if (outputFile == NULL) {
761 					fprintf(stderr, "%s: Could not open `%s': %s\n",
762 						kCommandName, filename, strerror(errno));
763 					exit(1);
764 				}
765 			} else {
766 				print_usage_and_exit(true);
767 			}
768 		} else {
769 			programArgs = argv + argi;
770 			programArgCount = argc - argi;
771 			break;
772 		}
773 	}
774 
775 	// check parameters
776 	if (!programArgs)
777 		print_usage_and_exit(true);
778 
779 	if (fastMode)
780 		contentsFlags = 0;
781 	else if (contentsFlags == 0)
782 		contentsFlags = Context::ALL;
783 
784 	// don't colorize the output, if we don't have a terminal
785 	if (outputFile == stdout)
786 		colorize = colorize && isatty(STDOUT_FILENO);
787 	else if (outputFile)
788 		colorize = false;
789 
790 	// get thread/team to be debugged
791 	thread_id threadID = -1;
792 	team_id teamID = -1;
793 	if (programArgCount > 1
794 		|| !get_id(*programArgs, (traceTeam ? teamID : threadID))) {
795 		// we've been given an executable and need to load it
796 		threadID = load_program(programArgs, programArgCount, traceLoading);
797 		if (threadID < 0) {
798 			fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
799 				programArgs[0], strerror(threadID));
800 			exit(1);
801 		}
802 	}
803 
804 	// get the team ID, if we have none yet
805 	if (teamID < 0) {
806 		thread_info threadInfo;
807 		status_t error = get_thread_info(threadID, &threadInfo);
808 		if (error != B_OK) {
809 			fprintf(stderr, "%s: Failed to get info for thread %" B_PRId32
810 				": %s\n", kCommandName, threadID, strerror(error));
811 			exit(1);
812 		}
813 		teamID = threadInfo.team;
814 	}
815 
816 	// create a debugger port
817 	port_id debuggerPort = create_port(10, "debugger port");
818 	if (debuggerPort < 0) {
819 		fprintf(stderr, "%s: Failed to create debugger port: %s\n",
820 			kCommandName, strerror(debuggerPort));
821 		exit(1);
822 	}
823 
824 	// install ourselves as the team debugger
825 	typedef map<team_id, Team*> TeamMap;
826 	TeamMap debuggedTeams;
827 	port_id nubPort;
828 
829 	{
830 		Team* team = new Team(teamID);
831 		status_t error = team->InstallDebugger(debuggerPort, traceTeam,
832 			traceChildTeams, traceSignal);
833 		if (error != B_OK)
834 			exit(1);
835 
836 		debuggedTeams[team->ID()] = team;
837 
838 		nubPort = team->NubPort();
839 	}
840 
841 	// set thread debugging flags
842 	if (threadID >= 0) {
843 		int32 threadDebugFlags = 0;
844 		if (!traceTeam) {
845 			threadDebugFlags = B_THREAD_DEBUG_PRE_SYSCALL | B_THREAD_DEBUG_POST_SYSCALL
846 				| (traceChildThreads
847 					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
848 		}
849 		if (set_thread_debugging_flags(nubPort, threadID, threadDebugFlags)
850 				!= B_OK) {
851 			exit(1);
852 		}
853 
854 		// resume the target thread to be sure, it's running
855 		resume_thread(threadID);
856 	}
857 
858 	thread_id currentThreadID = -1;
859 
860 	// debug loop
861 	while (true) {
862 		bool quitLoop = false;
863 		int32 code;
864 		debug_debugger_message_data message;
865 		ssize_t messageSize = read_port(debuggerPort, &code, &message,
866 			sizeof(message));
867 
868 		if (messageSize < 0) {
869 			if (messageSize == B_INTERRUPTED)
870 				continue;
871 
872 			fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
873 				kCommandName, strerror(messageSize));
874 			exit(1);
875 		}
876 
877 		switch (code) {
878 			case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
879 			{
880 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
881 				if (it == debuggedTeams.end())
882 					break;
883 
884 				Team* team = it->second;
885 				MemoryReader& memoryReader = team->GetMemoryReader();
886 
887 				uint32 syscallNumber = message.pre_syscall.syscall;
888 				if (syscallNumber >= sSyscallVector.size()) {
889 					fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n",
890 						kCommandName, syscallNumber);
891 					break;
892 				}
893 				Syscall* syscall = sSyscallVector[syscallNumber];
894 
895 				if (trace) {
896 					if (traceFilter && !syscall->TracingEnabled())
897 						break;
898 					print_syscall(outputFile, syscall, message.pre_syscall,
899 						memoryReader, printArguments, contentsFlags,
900 						colorize, decimalFormat, currentThreadID);
901 				}
902 				break;
903 			}
904 
905 			case B_DEBUGGER_MESSAGE_POST_SYSCALL:
906 			{
907 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
908 				if (it == debuggedTeams.end())
909 					break;
910 
911 				Team* team = it->second;
912 				MemoryReader& memoryReader = team->GetMemoryReader();
913 
914 				uint32 syscallNumber = message.post_syscall.syscall;
915 				if (syscallNumber >= sSyscallVector.size()) {
916 					fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n",
917 						kCommandName, syscallNumber);
918 					break;
919 				}
920 				Syscall* syscall = sSyscallVector[syscallNumber];
921 
922 				if (stats)
923 					record_syscall_stats(*syscall, message.post_syscall);
924 
925 				if (trace) {
926 					if (traceFilter && !syscall->TracingEnabled())
927 						break;
928 					print_syscall(outputFile, syscall, message.post_syscall,
929 						memoryReader, printArguments, contentsFlags,
930 						printReturnValues, colorize, decimalFormat,
931 						currentThreadID);
932 				}
933 				break;
934 			}
935 
936 			case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
937 			{
938 				if (traceSignal && trace)
939 					print_signal(outputFile, message.signal_received, colorize);
940 				break;
941 			}
942 
943 			case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
944 			case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
945 			case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
946 			case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
947 			case B_DEBUGGER_MESSAGE_SINGLE_STEP:
948 			case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
949 			case B_DEBUGGER_MESSAGE_THREAD_CREATED:
950 			case B_DEBUGGER_MESSAGE_THREAD_DELETED:
951 			case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
952 			case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
953 				break;
954 
955 			case B_DEBUGGER_MESSAGE_TEAM_CREATED:
956 			{
957 				if (!traceChildTeams)
958 					break;
959 
960 				Team* team = new(std::nothrow) Team(
961 					message.team_created.new_team);
962 				if (team == NULL) {
963 					fprintf(stderr, "%s: Out of memory!\n", kCommandName);
964 					break;
965 				}
966 
967 				status_t error = team->InstallDebugger(debuggerPort, true, true,
968 					traceSignal);
969 				if (error != B_OK) {
970 					delete team;
971 					break;
972 				}
973 
974 				debuggedTeams[team->ID()] = team;
975 				break;
976 			}
977 
978 			case B_DEBUGGER_MESSAGE_TEAM_DELETED:
979 			{
980 				// a debugged team is gone
981 				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
982 				if (it == debuggedTeams.end())
983 					break;
984 
985 				Team* team = it->second;
986 				debuggedTeams.erase(it);
987 				delete team;
988 
989 				// if all debugged teams are gone, we're done
990 				quitLoop = debuggedTeams.empty();
991 				break;
992 			}
993 		}
994 
995 		if (quitLoop)
996 			break;
997 
998 		// tell the thread to continue (only when there is a thread and the
999 		// message was synchronous)
1000 		if (message.origin.thread >= 0 && message.origin.nub_port >= 0) {
1001 			if (continue_thread(message.origin.nub_port,
1002 					message.origin.thread) != B_OK) {
1003 				// the team can already be gone
1004 			}
1005 		}
1006 	}
1007 
1008 	if (stats) {
1009 		// Dump recorded statistics
1010 		print_stats(outputFile);
1011 	}
1012 
1013 	if (outputFile != NULL && outputFile != stdout)
1014 		fclose(outputFile);
1015 
1016 	return 0;
1017 }
1018