strace.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. static int g_pid = -1;
  2. static void handle_sigint(int)
  3. {
  4. if (g_pid == -1)
  5. return;
  6. if (ptrace(PT_DETACH, g_pid, 0, 0) == -1) {
  7. perror("detach");
  8. }
  9. }
  10. int main(int argc, char** argv)
  11. {
  12. if (pledge("stdio wpath cpath proc exec ptrace sigaction", nullptr) < 0) {
  13. perror("pledge");
  14. return 1;
  15. }
  16. Vector<const char*> child_argv;
  17. const char* output_filename = nullptr;
  18. auto trace_file_or_error = Core::File::standard_error();
  19. if (trace_file_or_error.is_error()) {
  20. outln(stderr, "Failed to open stderr: {}", trace_file_or_error.error());
  21. return 1;
  22. }
  23. auto trace_file = trace_file_or_error.release_value();
  24. Core::ArgsParser parser;
  25. parser.set_general_help(
  26. "Trace all syscalls and their result.");
  27. parser.add_option(g_pid, "Trace the given PID", "pid", 'p', "pid");
  28. parser.add_option(output_filename, "Filename to write output to", "output", 'o', "output");
  29. parser.add_positional_argument(child_argv, "Arguments to exec", "argument", Core::ArgsParser::Required::No);
  30. parser.parse(argc, argv);
  31. if (output_filename != nullptr) {
  32. auto open_result = Core::File::open(output_filename, Core::File::OpenMode::Write);
  33. if (open_result.is_error()) {
  34. outln(stderr, "Failed to open output file: {}", open_result.error());
  35. return 1;
  36. }
  37. trace_file = open_result.release_value();
  38. }
  39. if (pledge("stdio proc exec ptrace sigaction", nullptr) < 0) {
  40. perror("pledge");
  41. return 1;
  42. }
  43. int status;
  44. if (g_pid == -1) {
  45. if (child_argv.is_empty()) {
  46. outln(stderr, "strace: Expected either a pid or some arguments\n");
  47. return 1;
  48. }
  49. child_argv.append(nullptr);
  50. int pid = fork();
  51. if (pid < 0) {
  52. perror("fork");
  53. return 1;
  54. }
  55. if (!pid) {
  56. if (ptrace(PT_TRACE_ME, 0, 0, 0) == -1) {
  57. perror("traceme");
  58. return 1;
  59. }
  60. int rc = execvp(child_argv.first(), const_cast<char**>(child_argv.data()));
  61. if (rc < 0) {
  62. perror("execvp");
  63. exit(1);
  64. }
  65. VERIFY_NOT_REACHED();
  66. }
  67. g_pid = pid;
  68. if (waitpid(pid, &status, WSTOPPED | WEXITED) != pid || !WIFSTOPPED(status)) {
  69. perror("waitpid");
  70. return 1;
  71. }
  72. }
  73. struct sigaction sa;
  74. memset(&sa, 0, sizeof(struct sigaction));
  75. sa.sa_handler = handle_sigint;
  76. sigaction(SIGINT, &sa, nullptr);
  77. if (ptrace(PT_ATTACH, g_pid, 0, 0) == -1) {
  78. perror("attach");
  79. return 1;
  80. }
  81. if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
  82. perror("waitpid");
  83. return 1;
  84. }
  85. for (;;) {
  86. if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) {
  87. perror("syscall");
  88. return 1;
  89. }
  90. if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
  91. perror("wait_pid");
  92. return 1;
  93. }
  94. PtraceRegisters regs = {};
  95. if (ptrace(PT_GETREGS, g_pid, &regs, 0) == -1) {
  96. perror("getregs");
  97. return 1;
  98. }
  99. u32 syscall_index = regs.eax;
  100. u32 arg1 = regs.edx;
  101. u32 arg2 = regs.ecx;
  102. u32 arg3 = regs.ebx;
  103. if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) {
  104. perror("syscall");
  105. return 1;
  106. }
  107. if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
  108. perror("wait_pid");
  109. return 1;
  110. }
  111. if (ptrace(PT_GETREGS, g_pid, &regs, 0) == -1) {
  112. perror("getregs");
  113. return 1;
  114. }
  115. u32 res = regs.eax;
  116. auto string = String::formatted("{}({:#08x}, {:#08x}, {:#08x})\t={}\n",
  117. Syscall::to_string((Syscall::Function)syscall_index),
  118. arg1,
  119. arg2,
  120. arg3,
  121. res);
  122. auto result = trace_file->write_value(string);
  123. if (result.is_error()) {
  124. warnln("write: {}", result->error());
  125. return 1;
  126. }
  127. }
  128. return 0;
  129. }