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