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