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