How to get a "backtrace" (like gdb) using only ptrace (linux, x86/x86_64)
I want to get a backtrace
-like output as gdb does. But I want to do this via ptrace()
directly. My platform is Linux, x86; and, later x86_64.
Now I want only to read return addresses from the stack, without conversion into symbol names.
So, for test program, compiled in -O0
mode by gcc-4.5
:
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();
}
I will start a my program and connect with ptrace
to test program at very beginn开发者_StackOverflowing. Then, I will do PTRACE_CONT and will wait for signal. When test program will do a self-kill; the signal will be delivered to my program. At this moment I want to read return addresses, they will be like (because kill
function is active at the moment):
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
How can I find return addresses of currently stopped test process with ptrace
? There will be a loop over frames? When should I stop such loop?
PS: yes, this is also very like backtrace(3)
libc function in idea, but I want to do this externally via ptrace.
The example posted by osgx will work only with code that uses frame pointers. x86_64
code produced by GCC with optimizations doesn't. The kernel vdso
code on x86
doesn't use frame pointers on at least some processors. GCC 4.6 (with optimization) doesn't use frame pointers in x86
mode either.
All of the above combine to make the "stack crawl via frame pointers" exceedingly unreliable.
You can use libunwind
(which supports both local (in-process) and global (out-of-process via ptrace) unwinding).
Or you'll have to re-implement very large portion of libunwind
.
Example of getting backtrace via ptrace
using libunwind
.
May be, source of pstack(1)
utility will help me: (online git from debian). Unfortunately this is x86 32-bit only
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
Also, quick test says that is it not very reliable, but sometimes it can print something.
精彩评论