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