50行straceもどき


すこし前に、straceコマンドもどきを50行くらいで書いてみたことがあるので、それを貼ってみまーす。いきなりコード。あ、C99です。

// strace_modoki.c:  Linux/x86専用です。x86_64カーネルでは-m32でコンパイルしても動きません。

#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <asm/user.h>
#include <asm/ptrace.h>

int main() {
  int i;
  static const char* syscallstr[1000] = {0};
  for(i = 0; i < 1000; ++i) syscallstr[i] = "???";

  syscallstr[0] = "restart_syscall";
  syscallstr[1] = "exit";
  syscallstr[2] = "fork";
  /* 略 */
  syscallstr[321] = "signalfd";
  syscallstr[322] = "timerfd";
  syscallstr[323] = "eventfd";

  if (!fork()) {
    // child
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execve("/bin/echo",
           (char *const []){ "/bin/echo", "123", NULL },
           (char *const []){ NULL });
    _exit(-1);
  } else {
    int st, in_syscall = 1; // うまく動かなかったら1を0にしてみてください :-)
    if (in_syscall) printf("enter execve() ");
    wait(&st); // wait for exec
    while (ptrace(PTRACE_SYSCALL, p, 0, 0) == 0) { // cont until syscall enter/leave
      wait(&st); // wait for syscall enter/leave
      if(WIFEXITED(st)) {
        puts("= ?"); break;
      }
      long orig_eax = ptrace(PTRACE_PEEKUSER, p, 4 * ORIG_EAX, 0);
      struct user_regs_struct regs;
      ptrace(PTRACE_GETREGS, p, 0, &regs);
      if (in_syscall == 0) {
        in_syscall = 1;
        printf("enter %s(0x%lx) ",
               (orig_eax >= 0 && orig_eax < 1000) ? syscallstr[orig_eax] : "???", regs.ebx);
      } else {
        in_syscall = 0;
        printf("= %ld\n", regs.eax);
      }
      fflush(stdout);
    }
  }
  return 0;
}

冒頭の syscallstr[nnn] = "xxx"; の部分は、/usr/include/asm/unistd.h をもとに、適当に変形して生成してください。余談ですがこのファイルをたまにのぞくと新規に追加されたシステムコールがわかって楽しいです。signalfdとか。エラーチェック他全部省略の手抜きコードです。うまく動かなかったらゴメンナサイ。


これを実行すると、まずfork()して子プロセスで /bin/echo 123 を実行し、親プロセスがその子の様子をptraceで観察します。出力はこんな感じになります。

% gcc -Wall -W strace_modoki.c
% ./a.out 
enter execve() = 0
enter brk(0x0) = 164818944
enter access(0x4bee8f) = -2
enter open(0x4bf077) = 3
enter fstat64(0x3) = 0
enter mmap2(0x0) = -1208172544
enter close(0x3) = 0
enter open(0xb7fd7a08) = 3
enter read(0x3) = 512
enter fstat64(0x3) = 0
enter mmap2(0x0) = -1208176640
enter mmap2(0x4c5000) = 5001216
enter mmap2(0x5ff000) = 6287360
enter mmap2(0x602000) = 6299648
enter close(0x3) = 0
enter mmap2(0x0) = -1208180736
enter set_thread_area(0xbfac95c4) = 0
enter mprotect(0x5ff000) = 0
enter mprotect(0x4c1000) = 0
enter munmap(0xb7fcc000) = 0
enter brk(0x0) = 164818944
enter brk(0x9d50000) = 164954112
enter fstat64(0x1) = 0
enter mmap2(0x0) = -1208119296
enter write(0x1) 123
= 4
enter close(0x1) = 0
enter munmap(0xb7fd9000) = 0
enter exit_group(0x0) = ?
% 

全くpretty printされていませんが、一応straceっぽい出力が得られています。ptraceの詳細については、man 2 ptraceや、2002年のこの記事あたりが参考になると思います。