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.
24 #include "debug_utils.h"
27 #include "MemoryReader.h"
29 #include "TypeHandler.h"
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
;
64 static const char *kUsage
=
65 "Usage: %s [ <options> ] [ <thread or team ID> | <executable with args> ]\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"
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"
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"
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"
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";
105 static const char *const *sArgv
;
108 static vector
<Syscall
*> sSyscallVector
;
109 static map
<string
, Syscall
*> sSyscallMap
;
125 port_id
NubPort() const
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
);
140 fprintf(stderr
, "%s: Failed to install team debugger: %s\n",
141 kCommandName
, strerror(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
)
152 return fMemoryReader
.Init(fNubPort
);
158 MemoryReader fMemoryReader
;
163 print_usage(bool error
)
166 fprintf((error
? stderr
: stdout
), kUsage
, kCommandName
);
171 print_usage_and_exit(bool error
)
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
]))
193 get_syscall(const char *name
)
195 map
<string
, Syscall
*>::const_iterator i
= sSyscallMap
.find(name
);
196 if (i
== sSyscallMap
.end())
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();
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
;
252 print_to_string(char **_buffer
, int32
*_length
, const char *format
, ...)
255 va_start(list
, format
);
256 ssize_t length
= vsnprintf(*_buffer
, *_length
, format
, list
);
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
279 print_to_string(&string
, &length
, "[%6ld] %s%s%s(",
280 message
.origin
.thread
, kTerminalTextBlue
, syscall
->Name().c_str(),
281 kTerminalTextNormal
);
283 print_to_string(&string
, &length
, "[%6ld] %s(",
284 message
.origin
.thread
, syscall
->Name().c_str());
288 if (printArguments
) {
289 int32 count
= syscall
->CountParameters();
290 for (int32 i
= 0; i
< count
; i
++) {
292 Parameter
*parameter
= syscall
->ParameterAt(i
);
293 TypeHandler
*handler
= parameter
->Handler();
295 handler
->GetParameterValue(ctx
, parameter
,
296 ctx
.GetValue(parameter
));
298 print_to_string(&string
, &length
, (i
> 0 ? ", %s" : "%s"),
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
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
));
325 print_to_string(&string
, &length
, " %s(%lld us)%s\n", kTerminalTextMagenta
,
326 message
.end_time
- message
.start_time
, kTerminalTextNormal
);
328 print_to_string(&string
, &length
, " (%lld us)\n",
329 message
.end_time
- message
.start_time
);
332 //for (int32 i = 0; i < 16; i++) {
339 // printf("%08lx", message.args[i]);
343 // output either to file or serial debug line
344 if (outputFile
!= NULL
)
345 fwrite(buffer
, sizeof(buffer
) - length
, 1, outputFile
);
347 _kern_debug_output(buffer
);
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
);
364 print_signal(FILE *outputFile
, debug_signal_received
&message
,
367 char buffer
[4096], *string
= buffer
;
368 int32 length
= (int32
)sizeof(buffer
);
369 int signalNumber
= message
.signal
;
373 print_to_string(&string
, &length
, "[%6ld] --- %s%s (%s) %s---\n",
374 message
.origin
.thread
, kTerminalTextRed
, signal_name(signalNumber
),
375 strsignal(signalNumber
), kTerminalTextNormal
);
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
);
386 _kern_debug_output(buffer
);
391 main(int argc
, const char *const *argv
)
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
;
414 for (int argi
= 1; argi
< argc
; argi
++) {
415 const char *arg
= argv
[argi
];
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) {
424 } else if (strcmp(arg
, "-d") == 0) {
425 const char *what
= NULL
;
428 && argi
+ 1 < argc
&& argv
[argi
+ 1][0] != '-') {
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
;
445 fprintf(stderr
, "%s: Unknown content filter `%s'\n",
449 } else if (strcmp(arg
, "-f") == 0) {
451 } else if (strcmp(arg
, "-i") == 0) {
452 decimalFormat
= true;
453 } else if (strcmp(arg
, "-l") == 0) {
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) {
463 } else if (strcmp(arg
, "-g") == 0) {
465 } else if (strcmp(arg
, "-S") == 0) {
468 } else if (strncmp(arg
, "-o", 2) == 0) {
470 const char *filename
= NULL
;
474 } else if (arg
[2] == '\0'
475 && argi
+ 1 < argc
&& argv
[argi
+ 1][0] != '-') {
477 filename
= argv
[++argi
];
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
));
488 print_usage_and_exit(true);
491 programArgs
= argv
+ argi
;
492 programArgCount
= argc
- argi
;
499 print_usage_and_exit(true);
503 else if (contentsFlags
== 0)
504 contentsFlags
= Context::ALL
;
506 // initialize our syscalls vector and map
509 // don't colorize the output, if we don't have a terminal
510 if (outputFile
== stdout
)
511 colorize
= colorize
&& isatty(STDOUT_FILENO
);
515 // get thread/team to be debugged
516 thread_id threadID
= -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
);
523 fprintf(stderr
, "%s: Failed to start `%s': %s\n", kCommandName
,
524 programArgs
[0], strerror(threadID
));
529 // get the team ID, if we have none yet
531 thread_info threadInfo
;
532 status_t error
= get_thread_info(threadID
, &threadInfo
);
534 fprintf(stderr
, "%s: Failed to get info for thread %" B_PRId32
535 ": %s\n", kCommandName
, threadID
, strerror(error
));
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
));
549 // install ourselves as the team debugger
550 typedef map
<team_id
, Team
*> TeamMap
;
551 TeamMap debuggedTeams
;
555 Team
* team
= new Team(teamID
);
556 status_t error
= team
->InstallDebugger(debuggerPort
, traceTeam
,
557 traceChildTeams
, traceSignal
);
561 debuggedTeams
[team
->ID()] = team
;
563 nubPort
= team
->NubPort();
566 // set thread debugging flags
568 int32 threadDebugFlags
= 0;
570 threadDebugFlags
= B_THREAD_DEBUG_POST_SYSCALL
572 ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS
: 0);
574 if (set_thread_debugging_flags(nubPort
, threadID
, threadDebugFlags
)
579 // resume the target thread to be sure, it's running
580 resume_thread(threadID
);
585 bool quitLoop
= false;
587 debug_debugger_message_data message
;
588 ssize_t messageSize
= read_port(debuggerPort
, &code
, &message
,
591 if (messageSize
< 0) {
592 if (messageSize
== B_INTERRUPTED
)
595 fprintf(stderr
, "%s: Reading from debugger port failed: %s\n",
596 kCommandName
, strerror(messageSize
));
601 case B_DEBUGGER_MESSAGE_POST_SYSCALL
:
603 TeamMap::iterator it
= debuggedTeams
.find(message
.origin
.team
);
604 if (it
== debuggedTeams
.end())
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
);
616 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED
:
619 print_signal(outputFile
, message
.signal_received
, colorize
);
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
:
636 case B_DEBUGGER_MESSAGE_TEAM_CREATED
:
638 if (!traceChildTeams
)
641 Team
* team
= new(std::nothrow
) Team(
642 message
.team_created
.new_team
);
644 fprintf(stderr
, "%s: Out of memory!\n", kCommandName
);
648 status_t error
= team
->InstallDebugger(debuggerPort
, true, true,
655 debuggedTeams
[team
->ID()] = team
;
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())
666 Team
* team
= it
->second
;
667 debuggedTeams
.erase(it
);
670 // if all debugged teams are gone, we're done
671 quitLoop
= debuggedTeams
.empty();
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
) {
689 if (outputFile
!= NULL
&& outputFile
!= stdout
)