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