2011-08-31 41 views
3

gdbのようにbacktraceのような出力を得たいです。しかし、私はこれを直接ptrace()経由でしたいと思います。私のプラットフォームはLinux、x86です。また、x86_64以降。ptrace(linux、x86/x86_64)のみを使って "バックトレース"(gdbのように)を得る方法

ここでは、シンボル名に変換せずに、スタックから返されたアドレスだけを読みたいと思っています。

ので、テストプログラムのために、gcc-4.5によって-O0モードでコンパイル:

int g() { 
    kill(getpid(),SIGALRM); 
    } 
    int f() { 
    int a; 
    int b; 
    a = g(); 
    b = a; 
    return a+b; 
    } 
    int e() { 
    int c; 
    c = f(); 
    } 
    main() { 
    return e(); 
    } 

私は私のプログラムを起動して、一番最初にプログラムをテストするためにptraceで接続します。次に、私はPTRACE_CONTを行い、信号を待つでしょう。テストプログラムが自己殺しをするとき。信号は私のプログラムに送られます。私はリターンアドレスを読みたいこの瞬間、彼らは次のようになります(kill機能は、現時点ではアクティブであるため):

0x00_some_address_in_g 
0x00_some_address_in_f 
0x00_some_address_in_e 
0x00_some_address_in_main 
0x00_some_address_in__libc_start_main 

がどのように私はptraceで現在停止して、テストプロセスのリターンアドレスを見つけることができますか?フレームの上にループがありますか?いつループを止めるべきですか?

PS:はい、これもideaのbacktrace(3) libc functionと非常によく似ていますが、私はこれを外部からptrace経由で行いたいと思います。

答えて

5

osgxによって投稿された例は、フ​​レームポインタを使用するコードでのみ機能します。 x86_64 GCCによって最適化されたコードは生成されません。 x86のカーネルvdsoは、少なくとも一部のプロセッサではフレームポインタを使用しません。 GCC 4.6(最適化あり)は、x86モードでもフレームポインタを使用しません。

上記のすべてが組み合わされて、「フレームポインタによるスタッククロール」が非常に信頼性が低くなります。

local(インプロセス)とglobal(アウトプロセスのptraceを介して)巻き戻し)の両方をサポートするlibunwindを使用できます。

またはlibunwindの非常に大きな部分を再実装する必要があります。

Exampleptracelibunwindを使用してバックトレースを取得しています。

+0

libunwindの外部解凍文書へのリンクを提供できますか? – osgx

+0

これは? http://www.nongnu.org/libunwind/man/libunwind-ptrace(3).html - libunwind-ptraceでバックトレースを取得する例がいくつかありますか? – osgx

+0

ドキュメントへのリンクを追加しました。いいえ:libunwind-ptraceはあなたが望むものではありません.ptrace機能を実装するlibunwindの一部です。その部分を使ってより高いレベルのlibunwindを再実装することができますが、そうする必要はありません。 –

0

役に立つかもしれないが、pstack(1)のソースが役に立つ:(オンラインgitからdebian)。残念ながら、これはx86の32ビットのみが

http://anonscm.debian.org/gitweb/?p=collab-maint/pstack.git;a=blob;f=pstack.c;h=61beb8d10fa490492ab351115f261614d00adb6d;hb=HEAD#l547

547 static int crawl(int pid) 
548 { 
549 unsigned long pc, fp, nextfp, nargs, i, arg; 
550 int error_occured = 0; 
551 
552 errno = 0; 
553 fp = -1; 
554 
555 pc = ptrace(PTRACE_PEEKUSER, pid, EIP * 4, 0); 
556 if (pc != -1 || !errno) 
557  fp = ptrace(PTRACE_PEEKUSER, pid, EBP * 4, 0); 
558 
559 if ((pc != -1 && fp != -1) || !errno) { 
560  print_pc(pc); 
561  for (; !errno && fp;) { 
562  nextfp = ptrace(PTRACE_PEEKDATA, pid, fp, 0); 
563  if (nextfp == (unsigned) -1 && errno) break; 
564 
565  nargs = (nextfp - fp - 8)/4; 
566  if (nargs > MAXARGS) nargs = MAXARGS; 
567  if (nargs > 0) { 
568   fputs(" (", stdout); 
569   for (i = 1; i <= nargs; i++) { 
570   arg = ptrace(PTRACE_PEEKDATA, pid, fp + 4 * (i + 1), 0); 
571   if (arg == (unsigned) -1 && errno) break; 
572   printf("%lx", arg); 
573   if (i < nargs) fputs(", ", stdout); 
574   } 
575   fputc(')', stdout); 
576   nargs = nextfp - fp - 8 - (4 * nargs); 
577   if (!errno && nargs > 0) printf(" + %lx\n", nargs); 
578   else fputc('\n', stdout); 
579  } else fputc('\n', stdout); 
580 
581  if (errno || !nextfp) break; 
582  pc = ptrace(PTRACE_PEEKDATA, pid, fp + 4, 0); 
583  if (pc == (unsigned) -1 && errno) break; 
584  fp = nextfp; 
585  print_pc(pc); 
586  } 
587  if (fp) error_occured = 1; 
588 } else error_occured = 1; 
589 
590 if (error_occured) perror("crawl"); 
591 else errno = 0; 
592 return errno; 
593 } 
594 

また、簡単なテストでは、それは非常に信頼性がないと言うが、時にはそれが何かを印刷することができます。

関連する問題