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
34using std::map;
35using std::string;
36using std::vector;
37
38
39struct syscall_stats {
40	bigtime_t	time;
41	uint32		count;
42};
43
44
45extern void get_syscalls0(vector<Syscall*> &syscalls);
46extern void get_syscalls1(vector<Syscall*> &syscalls);
47extern void get_syscalls2(vector<Syscall*> &syscalls);
48extern void get_syscalls3(vector<Syscall*> &syscalls);
49extern void get_syscalls4(vector<Syscall*> &syscalls);
50extern void get_syscalls5(vector<Syscall*> &syscalls);
51extern void get_syscalls6(vector<Syscall*> &syscalls);
52extern void get_syscalls7(vector<Syscall*> &syscalls);
53extern void get_syscalls8(vector<Syscall*> &syscalls);
54extern void get_syscalls9(vector<Syscall*> &syscalls);
55extern void get_syscalls10(vector<Syscall*> &syscalls);
56extern void get_syscalls11(vector<Syscall*> &syscalls);
57extern void get_syscalls12(vector<Syscall*> &syscalls);
58extern void get_syscalls13(vector<Syscall*> &syscalls);
59extern void get_syscalls14(vector<Syscall*> &syscalls);
60extern void get_syscalls15(vector<Syscall*> &syscalls);
61extern void get_syscalls16(vector<Syscall*> &syscalls);
62extern void get_syscalls17(vector<Syscall*> &syscalls);
63extern void get_syscalls18(vector<Syscall*> &syscalls);
64extern void get_syscalls19(vector<Syscall*> &syscalls);
65
66
67extern const char *__progname;
68static const char *kCommandName = __progname;
69
70
71// usage
72static 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"  -f             - Fast mode. Syscall arguments contents aren't retrieved.\n"
86"  -h, --help     - Print this text.\n"
87"  -i             - Print integers in decimal format instead of hexadecimal.\n"
88"  -l             - Also trace loading the executable. Only considered when\n"
89"                   an executable is provided.\n"
90"  --no-color     - Don't colorize output.\n"
91"  -r             - Don't print syscall return values.\n"
92"  -s             - Also trace all threads spawned by the supplied thread,\n"
93"                   respectively the loaded executable's main thread.\n"
94"  -t             - Also recursively trace all teams created by a traced\n"
95"                   thread or team.\n"
96"  -T             - Trace all threads of the supplied or loaded executable's\n"
97"                   team. If an ID is supplied, it is interpreted as a team\n"
98"                   ID.\n"
99"  -o <file>      - directs output into the specified file.\n"
100"  -S             - prints output to serial debug line.\n"
101"  -g             - turns off signal tracing.\n"
102;
103
104
105// terminal color escape sequences
106// (http://www.dee.ufcg.edu.br/~rrbrandt/tools/ansi.html)
107static const char *kTerminalTextNormal	= "\33[0m";
108static const char *kTerminalTextRed		= "\33[31m";
109static const char *kTerminalTextMagenta	= "\33[35m";
110static const char *kTerminalTextBlue	= "\33[34m";
111
112
113// command line args
114static int sArgc;
115static const char *const *sArgv;
116
117// syscalls
118static vector<Syscall*>			sSyscallVector;
119static map<string, Syscall*>	sSyscallMap;
120
121// statistics
122typedef map<string, syscall_stats> StatsMap;
123static StatsMap sSyscallStats;
124static bigtime_t sSyscallTime;
125
126
127struct Team {
128	Team(team_id id)
129		:
130		fID(id),
131		fNubPort(-1)
132	{
133	}
134
135	team_id ID() const
136	{
137		return fID;
138	}
139
140	port_id NubPort() const
141	{
142		return fNubPort;
143	}
144
145	MemoryReader& GetMemoryReader()
146	{
147		return fMemoryReader;
148	}
149
150	status_t InstallDebugger(port_id debuggerPort, bool traceTeam,
151		bool traceChildTeams, bool traceSignal)
152	{
153		fNubPort = install_team_debugger(fID, debuggerPort);
154		if (fNubPort < 0) {
155			fprintf(stderr, "%s: Failed to install team debugger: %s\n",
156				kCommandName, strerror(fNubPort));
157			return fNubPort;
158		}
159
160		// set team debugging flags
161		int32 teamDebugFlags = (traceTeam ? B_TEAM_DEBUG_POST_SYSCALL : 0)
162			| (traceChildTeams ? B_TEAM_DEBUG_TEAM_CREATION : 0)
163			| (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0);
164		if (set_team_debugging_flags(fNubPort, teamDebugFlags) != B_OK)
165			exit(1);
166
167		return fMemoryReader.Init(fNubPort);
168	}
169
170private:
171	team_id			fID;
172	port_id			fNubPort;
173	MemoryReader	fMemoryReader;
174};
175
176
177static void
178print_usage(bool error)
179{
180	// print usage
181	fprintf((error ? stderr : stdout), kUsage, kCommandName);
182}
183
184
185static void
186print_usage_and_exit(bool error)
187{
188	print_usage(error);
189	exit(error ? 1 : 0);
190}
191
192
193static bool
194get_id(const char *str, int32 &id)
195{
196	int32 len = strlen(str);
197	for (int32 i = 0; i < len; i++) {
198		if (!isdigit(str[i]))
199			return false;
200	}
201
202	id = atol(str);
203	return true;
204}
205
206
207Syscall *
208get_syscall(const char *name)
209{
210	map<string, Syscall *>::const_iterator i = sSyscallMap.find(name);
211	if (i == sSyscallMap.end())
212		return NULL;
213
214	return i->second;
215}
216
217
218static void
219patch_syscalls()
220{
221	// instead of having this done here manually we should either add the
222	// patching step to gensyscalls also manually or add metadata to
223	// kernel/syscalls.h and have it parsed automatically
224	extern void patch_ioctl();
225
226	patch_ioctl();
227}
228
229
230static void
231init_syscalls()
232{
233	// init the syscall vector
234	get_syscalls0(sSyscallVector);
235	get_syscalls1(sSyscallVector);
236	get_syscalls2(sSyscallVector);
237	get_syscalls3(sSyscallVector);
238	get_syscalls4(sSyscallVector);
239	get_syscalls5(sSyscallVector);
240	get_syscalls6(sSyscallVector);
241	get_syscalls7(sSyscallVector);
242	get_syscalls8(sSyscallVector);
243	get_syscalls9(sSyscallVector);
244	get_syscalls10(sSyscallVector);
245	get_syscalls11(sSyscallVector);
246	get_syscalls12(sSyscallVector);
247	get_syscalls13(sSyscallVector);
248	get_syscalls14(sSyscallVector);
249	get_syscalls15(sSyscallVector);
250	get_syscalls16(sSyscallVector);
251	get_syscalls17(sSyscallVector);
252	get_syscalls18(sSyscallVector);
253	get_syscalls19(sSyscallVector);
254
255	// init the syscall map
256	int32 count = sSyscallVector.size();
257	for (int32 i = 0; i < count; i++) {
258		Syscall *syscall = sSyscallVector[i];
259		sSyscallMap[syscall->Name()] = syscall;
260	}
261
262	patch_syscalls();
263}
264
265
266static void
267record_syscall_stats(const Syscall& syscall, debug_post_syscall& message)
268{
269	syscall_stats& stats = sSyscallStats[syscall.Name()];
270	stats.count++;
271
272	bigtime_t time = message.end_time - message.start_time;
273	stats.time += time;
274	sSyscallTime += time;
275}
276
277
278static void
279print_buffer(FILE *outputFile, char* buffer, int32 length)
280{
281	// output either to file or serial debug line
282	if (outputFile != NULL)
283		fwrite(buffer, length, 1, outputFile);
284	else
285		_kern_debug_output(buffer);
286}
287
288
289static void
290print_to_string(char **_buffer, int32 *_length, const char *format, ...)
291{
292	va_list list;
293	va_start(list, format);
294	ssize_t length = vsnprintf(*_buffer, *_length, format, list);
295	va_end(list);
296
297	*_buffer += length;
298	*_length -= length;
299}
300
301
302static void
303print_syscall(FILE *outputFile, Syscall* syscall, debug_post_syscall &message,
304	MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags,
305	bool printReturnValue, bool colorize, bool decimal)
306{
307	char buffer[4096], *string = buffer;
308	int32 length = (int32)sizeof(buffer);
309
310	Context ctx(syscall, (char *)message.args, memoryReader,
311		    contentsFlags, decimal);
312
313	// print syscall name, without the "_kern_"
314	if (colorize) {
315		print_to_string(&string, &length, "[%6ld] %s%s%s(",
316			message.origin.thread, kTerminalTextBlue,
317			syscall->Name().c_str() + 6, kTerminalTextNormal);
318	} else {
319		print_to_string(&string, &length, "[%6ld] %s(",
320			message.origin.thread, syscall->Name().c_str() + 6);
321	}
322
323	// print arguments
324	if (printArguments) {
325		int32 count = syscall->CountParameters();
326		for (int32 i = 0; i < count; i++) {
327			// get the value
328			Parameter *parameter = syscall->ParameterAt(i);
329			TypeHandler *handler = parameter->Handler();
330			::string value =
331				handler->GetParameterValue(ctx, parameter,
332						ctx.GetValue(parameter));
333
334			print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"),
335				value.c_str());
336		}
337	}
338
339	print_to_string(&string, &length, ")");
340
341	// print return value
342	if (printReturnValue) {
343		Type *returnType = syscall->ReturnType();
344		TypeHandler *handler = returnType->Handler();
345		::string value = handler->GetReturnValue(ctx, message.return_value);
346		if (value.length() > 0) {
347			print_to_string(&string, &length, " = %s", value.c_str());
348
349			// if the return type is status_t or ssize_t, print human-readable
350			// error codes
351			if (returnType->TypeName() == "status_t"
352				|| ((returnType->TypeName() == "ssize_t"
353						|| returnType->TypeName() == "int")
354					&& message.return_value < 0)) {
355				print_to_string(&string, &length, " %s", strerror(message.return_value));
356			}
357		}
358	}
359
360	if (colorize) {
361		print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta,
362			message.end_time - message.start_time, kTerminalTextNormal);
363	} else {
364		print_to_string(&string, &length, " (%lld us)\n",
365			message.end_time - message.start_time);
366	}
367
368//for (int32 i = 0; i < 16; i++) {
369//	if (i % 4 == 0) {
370//		if (i > 0)
371//			printf("\n");
372//		printf("  ");
373//	} else
374//		printf(" ");
375//	printf("%08lx", message.args[i]);
376//}
377//printf("\n");
378	print_buffer(outputFile, buffer, sizeof(buffer) - length);
379}
380
381
382static const char *
383signal_name(int signal)
384{
385	if (signal >= 0 && signal < NSIG)
386		return strsignal(signal);
387
388	static char buffer[32];
389	sprintf(buffer, "%d", signal);
390	return buffer;
391}
392
393
394static void
395print_signal(FILE *outputFile, debug_signal_received &message,
396	bool colorize)
397{
398	char buffer[4096], *string = buffer;
399	int32 length = (int32)sizeof(buffer);
400	int signalNumber = message.signal;
401
402	// print signal name
403	if (colorize) {
404		print_to_string(&string, &length, "[%6ld] --- %s%s (%s) %s---\n",
405			message.origin.thread, kTerminalTextRed, signal_name(signalNumber),
406			strsignal(signalNumber), kTerminalTextNormal);
407	} else {
408		print_to_string(&string, &length, "[%6ld] --- %s (%s) ---\n",
409			message.origin.thread, signal_name(signalNumber),
410			strsignal(signalNumber));
411	}
412
413	print_buffer(outputFile, buffer, sizeof(buffer) - length);
414}
415
416
417static bool
418compare_stats_by_time(
419	const std::pair<const std::string*, const syscall_stats*>& a,
420	const std::pair<const std::string*, const syscall_stats*>& b)
421{
422	return a.second->time > b.second->time;
423}
424
425
426static void
427print_stats(FILE* outputFile)
428{
429	char buffer[4096], *string = buffer;
430	int32 length = (int32)sizeof(buffer);
431
432	typedef std::vector<std::pair<const std::string*, const syscall_stats*> >
433		StatsRefVector;
434	StatsRefVector calls;
435	StatsMap::const_iterator iterator = sSyscallStats.begin();
436	for (; iterator != sSyscallStats.end(); iterator++)
437		calls.push_back(std::make_pair(&iterator->first, &iterator->second));
438
439	// Sort calls by time spent
440	std::sort(calls.begin(), calls.end(), compare_stats_by_time);
441
442	print_to_string(&string, &length, "\n%-6s %-10s %-7s %-10s Syscall\n",
443		"Time %", "Usecs", "Calls", "Usecs/call");
444	print_to_string(&string, &length, "------ ---------- ------- ---------- "
445		"--------------------\n");
446
447	StatsRefVector::const_iterator callIterator = calls.begin();
448	for (; callIterator != calls.end(); callIterator++) {
449		const syscall_stats& stats = *callIterator->second;
450		double percent = stats.time * 100.0 / sSyscallTime;
451		bigtime_t perCall = stats.time / stats.count;
452
453		print_to_string(&string, &length, "%6.2f %10" B_PRIu64 " %7" B_PRIu32
454			" %10" B_PRIu64 " %s\n", percent, stats.time, stats.count, perCall,
455			callIterator->first->c_str());
456	}
457
458	print_buffer(outputFile, buffer, sizeof(buffer) - length);
459}
460
461
462int
463main(int argc, const char *const *argv)
464{
465	sArgc = argc;
466	sArgv = argv;
467
468	// parameters
469	const char *const *programArgs = NULL;
470	int32 programArgCount = 0;
471	bool printArguments = true;
472	bool colorize = true;
473	bool stats = false;
474	bool trace = true;
475	uint32 contentsFlags = 0;
476	bool decimalFormat = false;
477	bool fastMode = false;
478	bool traceLoading = false;
479	bool printReturnValues = true;
480	bool traceChildThreads = false;
481	bool traceTeam = false;
482	bool traceChildTeams = false;
483	bool traceSignal = true;
484	bool serialOutput = false;
485	FILE *outputFile = stdout;
486
487	// parse arguments
488	for (int argi = 1; argi < argc; argi++) {
489		const char *arg = argv[argi];
490		if (arg[0] == '-') {
491			// ToDo: improve option parsing so that ie. "-rsf" would also work
492			if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
493				print_usage_and_exit(false);
494			} else if (strcmp(arg, "-a") == 0) {
495				printArguments = false;
496			} else if (strcmp(arg, "-c") == 0) {
497				stats = true;
498				trace = false;
499			} else if (strcmp(arg, "-C") == 0) {
500				stats = true;
501			} else if (strcmp(arg, "--no-color") == 0) {
502				colorize = false;
503			} else if (strcmp(arg, "-d") == 0) {
504				const char *what = NULL;
505
506				if (arg[2] == '\0'
507					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
508					// next arg is what
509					what = argv[++argi];
510				} else
511					print_usage_and_exit(true);
512
513				if (strcasecmp(what, "strings") == 0)
514					contentsFlags |= Context::STRINGS;
515				else if (strcasecmp(what, "enums") == 0)
516					contentsFlags |= Context::ENUMERATIONS;
517				else if (strcasecmp(what, "simple") == 0)
518					contentsFlags |= Context::SIMPLE_STRUCTS;
519				else if (strcasecmp(what, "complex") == 0)
520					contentsFlags |= Context::COMPLEX_STRUCTS;
521				else if (strcasecmp(what, "pointer_values") == 0)
522					contentsFlags |= Context::POINTER_VALUES;
523				else {
524					fprintf(stderr, "%s: Unknown content filter `%s'\n",
525						kCommandName, what);
526					exit(1);
527				}
528			} else if (strcmp(arg, "-f") == 0) {
529				fastMode = true;
530			} else if (strcmp(arg, "-i") == 0) {
531				decimalFormat = true;
532			} else if (strcmp(arg, "-l") == 0) {
533				traceLoading = true;
534			} else if (strcmp(arg, "-r") == 0) {
535				printReturnValues = false;
536			} else if (strcmp(arg, "-s") == 0) {
537				traceChildThreads = true;
538			} else if (strcmp(arg, "-t") == 0) {
539				traceChildTeams = true;
540			} else if (strcmp(arg, "-T") == 0) {
541				traceTeam = true;
542			} else if (strcmp(arg, "-g") == 0) {
543				traceSignal = false;
544			} else if (strcmp(arg, "-S") == 0) {
545				serialOutput = true;
546				outputFile = NULL;
547			} else if (strncmp(arg, "-o", 2) == 0) {
548				// read filename
549				const char *filename = NULL;
550				if (arg[2] == '=') {
551					// name follows
552					filename = arg + 3;
553				} else if (arg[2] == '\0'
554					&& argi + 1 < argc && argv[argi + 1][0] != '-') {
555					// next arg is name
556					filename = argv[++argi];
557				} else
558					print_usage_and_exit(true);
559
560				outputFile = fopen(filename, "w+");
561				if (outputFile == NULL) {
562					fprintf(stderr, "%s: Could not open `%s': %s\n",
563						kCommandName, filename, strerror(errno));
564					exit(1);
565				}
566			} else {
567				print_usage_and_exit(true);
568			}
569		} else {
570			programArgs = argv + argi;
571			programArgCount = argc - argi;
572			break;
573		}
574	}
575
576	// check parameters
577	if (!programArgs)
578		print_usage_and_exit(true);
579
580	if (fastMode)
581		contentsFlags = 0;
582	else if (contentsFlags == 0)
583		contentsFlags = Context::ALL;
584
585	// initialize our syscalls vector and map
586	init_syscalls();
587
588	// don't colorize the output, if we don't have a terminal
589	if (outputFile == stdout)
590		colorize = colorize && isatty(STDOUT_FILENO);
591	else if (outputFile)
592		colorize = false;
593
594	// get thread/team to be debugged
595	thread_id threadID = -1;
596	team_id teamID = -1;
597	if (programArgCount > 1
598		|| !get_id(*programArgs, (traceTeam ? teamID : threadID))) {
599		// we've been given an executable and need to load it
600		threadID = load_program(programArgs, programArgCount, traceLoading);
601		if (threadID < 0) {
602			fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
603				programArgs[0], strerror(threadID));
604			exit(1);
605		}
606	}
607
608	// get the team ID, if we have none yet
609	if (teamID < 0) {
610		thread_info threadInfo;
611		status_t error = get_thread_info(threadID, &threadInfo);
612		if (error != B_OK) {
613			fprintf(stderr, "%s: Failed to get info for thread %" B_PRId32
614				": %s\n", kCommandName, threadID, strerror(error));
615			exit(1);
616		}
617		teamID = threadInfo.team;
618	}
619
620	// create a debugger port
621	port_id debuggerPort = create_port(10, "debugger port");
622	if (debuggerPort < 0) {
623		fprintf(stderr, "%s: Failed to create debugger port: %s\n",
624			kCommandName, strerror(debuggerPort));
625		exit(1);
626	}
627
628	// install ourselves as the team debugger
629	typedef map<team_id, Team*> TeamMap;
630	TeamMap debuggedTeams;
631	port_id nubPort;
632
633	{
634		Team* team = new Team(teamID);
635		status_t error = team->InstallDebugger(debuggerPort, traceTeam,
636			traceChildTeams, traceSignal);
637		if (error != B_OK)
638			exit(1);
639
640		debuggedTeams[team->ID()] = team;
641
642		nubPort = team->NubPort();
643	}
644
645	// set thread debugging flags
646	if (threadID >= 0) {
647		int32 threadDebugFlags = 0;
648		if (!traceTeam) {
649			threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
650				| (traceChildThreads
651					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
652		}
653		if (set_thread_debugging_flags(nubPort, threadID, threadDebugFlags)
654				!= B_OK) {
655			exit(1);
656		}
657
658		// resume the target thread to be sure, it's running
659		resume_thread(threadID);
660	}
661
662	// debug loop
663	while (true) {
664		bool quitLoop = false;
665		int32 code;
666		debug_debugger_message_data message;
667		ssize_t messageSize = read_port(debuggerPort, &code, &message,
668			sizeof(message));
669
670		if (messageSize < 0) {
671			if (messageSize == B_INTERRUPTED)
672				continue;
673
674			fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
675				kCommandName, strerror(messageSize));
676			exit(1);
677		}
678
679		switch (code) {
680			case B_DEBUGGER_MESSAGE_POST_SYSCALL:
681			{
682				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
683				if (it == debuggedTeams.end())
684					break;
685
686				Team* team = it->second;
687				MemoryReader& memoryReader = team->GetMemoryReader();
688
689				uint32 syscallNumber = message.post_syscall.syscall;
690				if (syscallNumber >= sSyscallVector.size()) {
691					fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n",
692						kCommandName, syscallNumber);
693					break;
694				}
695				Syscall* syscall = sSyscallVector[syscallNumber];
696
697				if (stats)
698					record_syscall_stats(*syscall, message.post_syscall);
699
700				if (trace) {
701					print_syscall(outputFile, syscall, message.post_syscall,
702						memoryReader, printArguments, contentsFlags,
703						printReturnValues, colorize, decimalFormat);
704				}
705				break;
706			}
707
708			case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
709			{
710				if (traceSignal && trace)
711					print_signal(outputFile, message.signal_received, colorize);
712				break;
713			}
714
715			case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
716			case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
717			case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
718			case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
719			case B_DEBUGGER_MESSAGE_SINGLE_STEP:
720			case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
721			case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
722			case B_DEBUGGER_MESSAGE_THREAD_CREATED:
723			case B_DEBUGGER_MESSAGE_THREAD_DELETED:
724			case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
725			case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
726				break;
727
728			case B_DEBUGGER_MESSAGE_TEAM_CREATED:
729			{
730				if (!traceChildTeams)
731					break;
732
733				Team* team = new(std::nothrow) Team(
734					message.team_created.new_team);
735				if (team == NULL) {
736					fprintf(stderr, "%s: Out of memory!\n", kCommandName);
737					break;
738				}
739
740				status_t error = team->InstallDebugger(debuggerPort, true, true,
741					traceSignal);
742				if (error != B_OK) {
743					delete team;
744					break;
745				}
746
747				debuggedTeams[team->ID()] = team;
748				break;
749			}
750
751			case B_DEBUGGER_MESSAGE_TEAM_DELETED:
752			{
753				// a debugged team is gone
754				TeamMap::iterator it = debuggedTeams.find(message.origin.team);
755				if (it == debuggedTeams.end())
756					break;
757
758				Team* team = it->second;
759				debuggedTeams.erase(it);
760				delete team;
761
762				// if all debugged teams are gone, we're done
763				quitLoop = debuggedTeams.empty();
764				break;
765			}
766		}
767
768		if (quitLoop)
769			break;
770
771		// tell the thread to continue (only when there is a thread and the
772		// message was synchronous)
773		if (message.origin.thread >= 0 && message.origin.nub_port >= 0) {
774			if (continue_thread(message.origin.nub_port,
775					message.origin.thread) != B_OK) {
776				exit(1);
777			}
778		}
779	}
780
781	if (stats) {
782		// Dump recorded statistics
783		print_stats(outputFile);
784	}
785
786	if (outputFile != NULL && outputFile != stdout)
787		fclose(outputFile);
788
789	return 0;
790}
791