vfs: check userland buffers before reading them.
[haiku.git] / src / bin / debug / strace / strace.cpp
bloba19fe0c9e604e306c0750dea68d9310beefed008
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 */
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>
17 #include <algorithm>
18 #include <map>
19 #include <string>
20 #include <vector>
22 #include <debugger.h>
23 #include <image.h>
24 #include <syscalls.h>
26 #include "debug_utils.h"
28 #include "Context.h"
29 #include "MemoryReader.h"
30 #include "Syscall.h"
31 #include "TypeHandler.h"
34 using std::map;
35 using std::string;
36 using std::vector;
39 struct syscall_stats {
40 bigtime_t time;
41 uint32 count;
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;
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 " -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"
95 " thread or team.\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"
98 " ID.\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";
113 // command line args
114 static int sArgc;
115 static const char *const *sArgv;
117 // syscalls
118 static vector<Syscall*> sSyscallVector;
119 static map<string, Syscall*> sSyscallMap;
121 // statistics
122 typedef map<string, syscall_stats> StatsMap;
123 static StatsMap sSyscallStats;
124 static bigtime_t sSyscallTime;
127 struct Team {
128 Team(team_id id)
130 fID(id),
131 fNubPort(-1)
135 team_id ID() const
137 return fID;
140 port_id NubPort() const
142 return fNubPort;
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);
154 if (fNubPort < 0) {
155 fprintf(stderr, "%s: Failed to install team debugger: %s\n",
156 kCommandName, strerror(fNubPort));
157 return 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)
165 exit(1);
167 return fMemoryReader.Init(fNubPort);
170 private:
171 team_id fID;
172 port_id fNubPort;
173 MemoryReader fMemoryReader;
177 static void
178 print_usage(bool error)
180 // print usage
181 fprintf((error ? stderr : stdout), kUsage, kCommandName);
185 static void
186 print_usage_and_exit(bool error)
188 print_usage(error);
189 exit(error ? 1 : 0);
193 static bool
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]))
199 return false;
202 id = atol(str);
203 return true;
207 Syscall *
208 get_syscall(const char *name)
210 map<string, Syscall *>::const_iterator i = sSyscallMap.find(name);
211 if (i == sSyscallMap.end())
212 return NULL;
214 return i->second;
218 static void
219 patch_syscalls()
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();
226 patch_ioctl();
230 static void
231 init_syscalls()
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;
262 patch_syscalls();
266 static void
267 record_syscall_stats(const Syscall& syscall, debug_post_syscall& message)
269 syscall_stats& stats = sSyscallStats[syscall.Name()];
270 stats.count++;
272 bigtime_t time = message.end_time - message.start_time;
273 stats.time += time;
274 sSyscallTime += time;
278 static void
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);
284 else
285 _kern_debug_output(buffer);
289 static void
290 print_to_string(char **_buffer, int32 *_length, const char *format, ...)
292 va_list list;
293 va_start(list, format);
294 ssize_t length = vsnprintf(*_buffer, *_length, format, list);
295 va_end(list);
297 *_buffer += length;
298 *_length -= length;
302 static void
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
314 if (colorize) {
315 print_to_string(&string, &length, "[%6ld] %s%s%s(",
316 message.origin.thread, kTerminalTextBlue, syscall->Name().c_str(),
317 kTerminalTextNormal);
318 } else {
319 print_to_string(&string, &length, "[%6ld] %s(",
320 message.origin.thread, syscall->Name().c_str());
323 // print arguments
324 if (printArguments) {
325 int32 count = syscall->CountParameters();
326 for (int32 i = 0; i < count; i++) {
327 // get the value
328 Parameter *parameter = syscall->ParameterAt(i);
329 TypeHandler *handler = parameter->Handler();
330 ::string value =
331 handler->GetParameterValue(ctx, parameter,
332 ctx.GetValue(parameter));
334 print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"),
335 value.c_str());
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
350 // error codes
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));
360 if (colorize) {
361 print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta,
362 message.end_time - message.start_time, kTerminalTextNormal);
363 } else {
364 print_to_string(&string, &length, " (%lld us)\n",
365 message.end_time - message.start_time);
368 //for (int32 i = 0; i < 16; i++) {
369 // if (i % 4 == 0) {
370 // if (i > 0)
371 // printf("\n");
372 // printf(" ");
373 // } else
374 // printf(" ");
375 // printf("%08lx", message.args[i]);
377 //printf("\n");
378 print_buffer(outputFile, buffer, sizeof(buffer) - length);
382 static const char *
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);
390 return buffer;
394 static void
395 print_signal(FILE *outputFile, debug_signal_received &message,
396 bool colorize)
398 char buffer[4096], *string = buffer;
399 int32 length = (int32)sizeof(buffer);
400 int signalNumber = message.signal;
402 // print signal name
403 if (colorize) {
404 print_to_string(&string, &length, "[%6ld] --- %s%s (%s) %s---\n",
405 message.origin.thread, kTerminalTextRed, signal_name(signalNumber),
406 strsignal(signalNumber), kTerminalTextNormal);
407 } else {
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);
417 static bool
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;
426 static void
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*> >
433 StatsRefVector;
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)
465 sArgc = argc;
466 sArgv = argv;
468 // parameters
469 const char *const *programArgs = NULL;
470 int32 programArgCount = 0;
471 bool printArguments = true;
472 bool colorize = true;
473 bool stats = false;
474 bool trace = 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;
487 // parse arguments
488 for (int argi = 1; argi < argc; argi++) {
489 const char *arg = argv[argi];
490 if (arg[0] == '-') {
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) {
497 stats = true;
498 trace = false;
499 } else if (strcmp(arg, "-C") == 0) {
500 stats = true;
501 } else if (strcmp(arg, "--no-color") == 0) {
502 colorize = false;
503 } else if (strcmp(arg, "-d") == 0) {
504 const char *what = NULL;
506 if (arg[2] == '\0'
507 && argi + 1 < argc && argv[argi + 1][0] != '-') {
508 // next arg is what
509 what = argv[++argi];
510 } else
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;
523 else {
524 fprintf(stderr, "%s: Unknown content filter `%s'\n",
525 kCommandName, what);
526 exit(1);
528 } else if (strcmp(arg, "-f") == 0) {
529 fastMode = true;
530 } else if (strcmp(arg, "-i") == 0) {
531 decimalFormat = true;
532 } else if (strcmp(arg, "-l") == 0) {
533 traceLoading = true;
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) {
541 traceTeam = true;
542 } else if (strcmp(arg, "-g") == 0) {
543 traceSignal = false;
544 } else if (strcmp(arg, "-S") == 0) {
545 serialOutput = true;
546 outputFile = NULL;
547 } else if (strncmp(arg, "-o", 2) == 0) {
548 // read filename
549 const char *filename = NULL;
550 if (arg[2] == '=') {
551 // name follows
552 filename = arg + 3;
553 } else if (arg[2] == '\0'
554 && argi + 1 < argc && argv[argi + 1][0] != '-') {
555 // next arg is name
556 filename = argv[++argi];
557 } else
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));
564 exit(1);
566 } else {
567 print_usage_and_exit(true);
569 } else {
570 programArgs = argv + argi;
571 programArgCount = argc - argi;
572 break;
576 // check parameters
577 if (!programArgs)
578 print_usage_and_exit(true);
580 if (fastMode)
581 contentsFlags = 0;
582 else if (contentsFlags == 0)
583 contentsFlags = Context::ALL;
585 // initialize our syscalls vector and map
586 init_syscalls();
588 // don't colorize the output, if we don't have a terminal
589 if (outputFile == stdout)
590 colorize = colorize && isatty(STDOUT_FILENO);
591 else if (outputFile)
592 colorize = false;
594 // get thread/team to be debugged
595 thread_id threadID = -1;
596 team_id teamID = -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);
601 if (threadID < 0) {
602 fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
603 programArgs[0], strerror(threadID));
604 exit(1);
608 // get the team ID, if we have none yet
609 if (teamID < 0) {
610 thread_info threadInfo;
611 status_t error = get_thread_info(threadID, &threadInfo);
612 if (error != B_OK) {
613 fprintf(stderr, "%s: Failed to get info for thread %" B_PRId32
614 ": %s\n", kCommandName, threadID, strerror(error));
615 exit(1);
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));
625 exit(1);
628 // install ourselves as the team debugger
629 typedef map<team_id, Team*> TeamMap;
630 TeamMap debuggedTeams;
631 port_id nubPort;
634 Team* team = new Team(teamID);
635 status_t error = team->InstallDebugger(debuggerPort, traceTeam,
636 traceChildTeams, traceSignal);
637 if (error != B_OK)
638 exit(1);
640 debuggedTeams[team->ID()] = team;
642 nubPort = team->NubPort();
645 // set thread debugging flags
646 if (threadID >= 0) {
647 int32 threadDebugFlags = 0;
648 if (!traceTeam) {
649 threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
650 | (traceChildThreads
651 ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
653 if (set_thread_debugging_flags(nubPort, threadID, threadDebugFlags)
654 != B_OK) {
655 exit(1);
658 // resume the target thread to be sure, it's running
659 resume_thread(threadID);
662 // debug loop
663 while (true) {
664 bool quitLoop = false;
665 int32 code;
666 debug_debugger_message_data message;
667 ssize_t messageSize = read_port(debuggerPort, &code, &message,
668 sizeof(message));
670 if (messageSize < 0) {
671 if (messageSize == B_INTERRUPTED)
672 continue;
674 fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
675 kCommandName, strerror(messageSize));
676 exit(1);
679 switch (code) {
680 case B_DEBUGGER_MESSAGE_POST_SYSCALL:
682 TeamMap::iterator it = debuggedTeams.find(message.origin.team);
683 if (it == debuggedTeams.end())
684 break;
686 Team* team = it->second;
687 MemoryReader& memoryReader = team->GetMemoryReader();
689 int32 syscallNumber = message.post_syscall.syscall;
690 Syscall* syscall = sSyscallVector[syscallNumber];
692 if (stats)
693 record_syscall_stats(*syscall, message.post_syscall);
695 if (trace) {
696 print_syscall(outputFile, syscall, message.post_syscall,
697 memoryReader, printArguments, contentsFlags,
698 printReturnValues, colorize, decimalFormat);
700 break;
703 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
705 if (traceSignal && trace)
706 print_signal(outputFile, message.signal_received, colorize);
707 break;
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:
721 break;
723 case B_DEBUGGER_MESSAGE_TEAM_CREATED:
725 if (!traceChildTeams)
726 break;
728 Team* team = new(std::nothrow) Team(
729 message.team_created.new_team);
730 if (team == NULL) {
731 fprintf(stderr, "%s: Out of memory!\n", kCommandName);
732 break;
735 status_t error = team->InstallDebugger(debuggerPort, true, true,
736 traceSignal);
737 if (error != B_OK) {
738 delete team;
739 break;
742 debuggedTeams[team->ID()] = team;
743 break;
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())
751 break;
753 Team* team = it->second;
754 debuggedTeams.erase(it);
755 delete team;
757 // if all debugged teams are gone, we're done
758 quitLoop = debuggedTeams.empty();
759 break;
763 if (quitLoop)
764 break;
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) {
771 exit(1);
776 if (stats) {
777 // Dump recorded statistics
778 print_stats(outputFile);
781 if (outputFile != NULL && outputFile != stdout)
782 fclose(outputFile);
784 return 0;