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.
26 #include "debug_utils.h"
29 #include "MemoryReader.h"
31 #include "TypeHandler.h"
39 struct syscall_stats
{
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
);
67 extern const char *__progname
;
68 static const char *kCommandName
= __progname
;
72 static const char *kUsage
=
73 "Usage: %s [ <options> ] [ <thread or team ID> | <executable with args> ]\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"
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"
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"
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"
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"
105 // terminal color escape sequences
106 // (http://www.dee.ufcg.edu.br/~rrbrandt/tools/ansi.html)
107 static const char *kTerminalTextNormal
= "\33[0m";
108 static const char *kTerminalTextRed
= "\33[31m";
109 static const char *kTerminalTextMagenta
= "\33[35m";
110 static const char *kTerminalTextBlue
= "\33[34m";
115 static const char *const *sArgv
;
118 static vector
<Syscall
*> sSyscallVector
;
119 static map
<string
, Syscall
*> sSyscallMap
;
122 typedef map
<string
, syscall_stats
> StatsMap
;
123 static StatsMap sSyscallStats
;
124 static bigtime_t sSyscallTime
;
140 port_id
NubPort() const
145 MemoryReader
& GetMemoryReader()
147 return fMemoryReader
;
150 status_t
InstallDebugger(port_id debuggerPort
, bool traceTeam
,
151 bool traceChildTeams
, bool traceSignal
)
153 fNubPort
= install_team_debugger(fID
, debuggerPort
);
155 fprintf(stderr
, "%s: Failed to install team debugger: %s\n",
156 kCommandName
, strerror(fNubPort
));
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
)
167 return fMemoryReader
.Init(fNubPort
);
173 MemoryReader fMemoryReader
;
178 print_usage(bool error
)
181 fprintf((error
? stderr
: stdout
), kUsage
, kCommandName
);
186 print_usage_and_exit(bool error
)
194 get_id(const char *str
, int32
&id
)
196 int32 len
= strlen(str
);
197 for (int32 i
= 0; i
< len
; i
++) {
198 if (!isdigit(str
[i
]))
208 get_syscall(const char *name
)
210 map
<string
, Syscall
*>::const_iterator i
= sSyscallMap
.find(name
);
211 if (i
== sSyscallMap
.end())
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();
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
);
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
;
267 record_syscall_stats(const Syscall
& syscall
, debug_post_syscall
& message
)
269 syscall_stats
& stats
= sSyscallStats
[syscall
.Name()];
272 bigtime_t time
= message
.end_time
- message
.start_time
;
274 sSyscallTime
+= time
;
279 print_buffer(FILE *outputFile
, char* buffer
, int32 length
)
281 // output either to file or serial debug line
282 if (outputFile
!= NULL
)
283 fwrite(buffer
, length
, 1, outputFile
);
285 _kern_debug_output(buffer
);
290 print_to_string(char **_buffer
, int32
*_length
, const char *format
, ...)
293 va_start(list
, format
);
294 ssize_t length
= vsnprintf(*_buffer
, *_length
, format
, list
);
303 print_syscall(FILE *outputFile
, Syscall
* syscall
, debug_post_syscall
&message
,
304 MemoryReader
&memoryReader
, bool printArguments
, uint32 contentsFlags
,
305 bool printReturnValue
, bool colorize
, bool decimal
)
307 char buffer
[4096], *string
= buffer
;
308 int32 length
= (int32
)sizeof(buffer
);
310 Context
ctx(syscall
, (char *)message
.args
, memoryReader
,
311 contentsFlags
, decimal
);
313 // print syscall name
315 print_to_string(&string
, &length
, "[%6ld] %s%s%s(",
316 message
.origin
.thread
, kTerminalTextBlue
, syscall
->Name().c_str(),
317 kTerminalTextNormal
);
319 print_to_string(&string
, &length
, "[%6ld] %s(",
320 message
.origin
.thread
, syscall
->Name().c_str());
324 if (printArguments
) {
325 int32 count
= syscall
->CountParameters();
326 for (int32 i
= 0; i
< count
; i
++) {
328 Parameter
*parameter
= syscall
->ParameterAt(i
);
329 TypeHandler
*handler
= parameter
->Handler();
331 handler
->GetParameterValue(ctx
, parameter
,
332 ctx
.GetValue(parameter
));
334 print_to_string(&string
, &length
, (i
> 0 ? ", %s" : "%s"),
339 print_to_string(&string
, &length
, ")");
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());
349 // if the return type is status_t or ssize_t, print human-readable
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
));
361 print_to_string(&string
, &length
, " %s(%lld us)%s\n", kTerminalTextMagenta
,
362 message
.end_time
- message
.start_time
, kTerminalTextNormal
);
364 print_to_string(&string
, &length
, " (%lld us)\n",
365 message
.end_time
- message
.start_time
);
368 //for (int32 i = 0; i < 16; i++) {
375 // printf("%08lx", message.args[i]);
378 print_buffer(outputFile
, buffer
, sizeof(buffer
) - length
);
383 signal_name(int signal
)
385 if (signal
>= 0 && signal
< NSIG
)
386 return strsignal(signal
);
388 static char buffer
[32];
389 sprintf(buffer
, "%d", signal
);
395 print_signal(FILE *outputFile
, debug_signal_received
&message
,
398 char buffer
[4096], *string
= buffer
;
399 int32 length
= (int32
)sizeof(buffer
);
400 int signalNumber
= message
.signal
;
404 print_to_string(&string
, &length
, "[%6ld] --- %s%s (%s) %s---\n",
405 message
.origin
.thread
, kTerminalTextRed
, signal_name(signalNumber
),
406 strsignal(signalNumber
), kTerminalTextNormal
);
408 print_to_string(&string
, &length
, "[%6ld] --- %s (%s) ---\n",
409 message
.origin
.thread
, signal_name(signalNumber
),
410 strsignal(signalNumber
));
413 print_buffer(outputFile
, buffer
, sizeof(buffer
) - length
);
418 compare_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
)
422 return a
.second
->time
> b
.second
->time
;
427 print_stats(FILE* outputFile
)
429 char buffer
[4096], *string
= buffer
;
430 int32 length
= (int32
)sizeof(buffer
);
432 typedef std::vector
<std::pair
<const std::string
*, const syscall_stats
*> >
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
));
439 // Sort calls by time spent
440 std::sort(calls
.begin(), calls
.end(), compare_stats_by_time
);
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");
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
;
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());
458 print_buffer(outputFile
, buffer
, sizeof(buffer
) - length
);
463 main(int argc
, const char *const *argv
)
469 const char *const *programArgs
= NULL
;
470 int32 programArgCount
= 0;
471 bool printArguments
= true;
472 bool colorize
= 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
;
488 for (int argi
= 1; argi
< argc
; argi
++) {
489 const char *arg
= argv
[argi
];
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) {
499 } else if (strcmp(arg
, "-C") == 0) {
501 } else if (strcmp(arg
, "--no-color") == 0) {
503 } else if (strcmp(arg
, "-d") == 0) {
504 const char *what
= NULL
;
507 && argi
+ 1 < argc
&& argv
[argi
+ 1][0] != '-') {
511 print_usage_and_exit(true);
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
;
524 fprintf(stderr
, "%s: Unknown content filter `%s'\n",
528 } else if (strcmp(arg
, "-f") == 0) {
530 } else if (strcmp(arg
, "-i") == 0) {
531 decimalFormat
= true;
532 } else if (strcmp(arg
, "-l") == 0) {
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) {
542 } else if (strcmp(arg
, "-g") == 0) {
544 } else if (strcmp(arg
, "-S") == 0) {
547 } else if (strncmp(arg
, "-o", 2) == 0) {
549 const char *filename
= NULL
;
553 } else if (arg
[2] == '\0'
554 && argi
+ 1 < argc
&& argv
[argi
+ 1][0] != '-') {
556 filename
= argv
[++argi
];
558 print_usage_and_exit(true);
560 outputFile
= fopen(filename
, "w+");
561 if (outputFile
== NULL
) {
562 fprintf(stderr
, "%s: Could not open `%s': %s\n",
563 kCommandName
, filename
, strerror(errno
));
567 print_usage_and_exit(true);
570 programArgs
= argv
+ argi
;
571 programArgCount
= argc
- argi
;
578 print_usage_and_exit(true);
582 else if (contentsFlags
== 0)
583 contentsFlags
= Context::ALL
;
585 // initialize our syscalls vector and map
588 // don't colorize the output, if we don't have a terminal
589 if (outputFile
== stdout
)
590 colorize
= colorize
&& isatty(STDOUT_FILENO
);
594 // get thread/team to be debugged
595 thread_id threadID
= -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
);
602 fprintf(stderr
, "%s: Failed to start `%s': %s\n", kCommandName
,
603 programArgs
[0], strerror(threadID
));
608 // get the team ID, if we have none yet
610 thread_info threadInfo
;
611 status_t error
= get_thread_info(threadID
, &threadInfo
);
613 fprintf(stderr
, "%s: Failed to get info for thread %" B_PRId32
614 ": %s\n", kCommandName
, threadID
, strerror(error
));
617 teamID
= threadInfo
.team
;
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
));
628 // install ourselves as the team debugger
629 typedef map
<team_id
, Team
*> TeamMap
;
630 TeamMap debuggedTeams
;
634 Team
* team
= new Team(teamID
);
635 status_t error
= team
->InstallDebugger(debuggerPort
, traceTeam
,
636 traceChildTeams
, traceSignal
);
640 debuggedTeams
[team
->ID()] = team
;
642 nubPort
= team
->NubPort();
645 // set thread debugging flags
647 int32 threadDebugFlags
= 0;
649 threadDebugFlags
= B_THREAD_DEBUG_POST_SYSCALL
651 ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS
: 0);
653 if (set_thread_debugging_flags(nubPort
, threadID
, threadDebugFlags
)
658 // resume the target thread to be sure, it's running
659 resume_thread(threadID
);
664 bool quitLoop
= false;
666 debug_debugger_message_data message
;
667 ssize_t messageSize
= read_port(debuggerPort
, &code
, &message
,
670 if (messageSize
< 0) {
671 if (messageSize
== B_INTERRUPTED
)
674 fprintf(stderr
, "%s: Reading from debugger port failed: %s\n",
675 kCommandName
, strerror(messageSize
));
680 case B_DEBUGGER_MESSAGE_POST_SYSCALL
:
682 TeamMap::iterator it
= debuggedTeams
.find(message
.origin
.team
);
683 if (it
== debuggedTeams
.end())
686 Team
* team
= it
->second
;
687 MemoryReader
& memoryReader
= team
->GetMemoryReader();
689 int32 syscallNumber
= message
.post_syscall
.syscall
;
690 Syscall
* syscall
= sSyscallVector
[syscallNumber
];
693 record_syscall_stats(*syscall
, message
.post_syscall
);
696 print_syscall(outputFile
, syscall
, message
.post_syscall
,
697 memoryReader
, printArguments
, contentsFlags
,
698 printReturnValues
, colorize
, decimalFormat
);
703 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED
:
705 if (traceSignal
&& trace
)
706 print_signal(outputFile
, message
.signal_received
, colorize
);
710 case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED
:
711 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL
:
712 case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT
:
713 case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT
:
714 case B_DEBUGGER_MESSAGE_SINGLE_STEP
:
715 case B_DEBUGGER_MESSAGE_PRE_SYSCALL
:
716 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED
:
717 case B_DEBUGGER_MESSAGE_THREAD_CREATED
:
718 case B_DEBUGGER_MESSAGE_THREAD_DELETED
:
719 case B_DEBUGGER_MESSAGE_IMAGE_CREATED
:
720 case B_DEBUGGER_MESSAGE_IMAGE_DELETED
:
723 case B_DEBUGGER_MESSAGE_TEAM_CREATED
:
725 if (!traceChildTeams
)
728 Team
* team
= new(std::nothrow
) Team(
729 message
.team_created
.new_team
);
731 fprintf(stderr
, "%s: Out of memory!\n", kCommandName
);
735 status_t error
= team
->InstallDebugger(debuggerPort
, true, true,
742 debuggedTeams
[team
->ID()] = team
;
746 case B_DEBUGGER_MESSAGE_TEAM_DELETED
:
748 // a debugged team is gone
749 TeamMap::iterator it
= debuggedTeams
.find(message
.origin
.team
);
750 if (it
== debuggedTeams
.end())
753 Team
* team
= it
->second
;
754 debuggedTeams
.erase(it
);
757 // if all debugged teams are gone, we're done
758 quitLoop
= debuggedTeams
.empty();
766 // tell the thread to continue (only when there is a thread and the
767 // message was synchronous)
768 if (message
.origin
.thread
>= 0 && message
.origin
.nub_port
>= 0) {
769 if (continue_thread(message
.origin
.nub_port
,
770 message
.origin
.thread
) != B_OK
) {
777 // Dump recorded statistics
778 print_stats(outputFile
);
781 if (outputFile
!= NULL
&& outputFile
!= stdout
)