<一>A really simple tracing debugger
在上一篇文章中讲到了ptrace,那么我们完全可以用ptrace来写一个非常简单的trace工具,用以trace程序的具体运行过程。
用它可以很清楚的回答,使用glibc编译后的hello world是从什么地方开始运行的。
(注:本文内容根据“A really simple tracing debugger"翻译而来,具体链接见参考资料一节)
itrace.c
#include <stdio.h> #include <sys/wait.h> #include <unistd.h> #include <sys/user.h> #include <sys/ptrace.h> int main(int argc, char **argv) { int pid = fork(); if(pid == 0) { if(ptrace(PTRACE_TRACEME) < 0) { perror("ptrace"); _exit(1); } execvp(argv[1], argv + 1); perror("exec"); _exit(1); } while(1) { int status; struct user_regs_struct regs; if(waitpid(pid, &status, 0) < 0) perror("waitpid"); if(!WIFSTOPPED(status)) break; if(ptrace(PTRACE_GETREGS, pid, 0, ®s) < 0) perror("ptrace/GETREGS"); printf("%lx %lx\n", regs.eip, regs.esp); if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0) perror("ptrace/SINGLESTEP"); } return 0; }
编译:
gcc -m32 itrace.c -o itrace
译者注: -m32表示编译成32位格式,如果是在64位机器上,不需要加此选项,同时将itrace.c源码中的eip和esp转换为rip,rsp.
hellow.c
#include <stdio.h> int main() { printf("Hello, world!\n"); return 0; }
编译:
gcc -static -o hellow hellow.c
译者注: itrace保持一致,itrace如果是按32位格式来编译的,此处也应该一样。
测试运行
./itrace ./hellow | addr2line -e ./hellow -f | grep -v "??\|:?" | uniq
说明如下:
addr2line 是将地址转换为相应的源码。运行的结果很长,所以就不打算没有贴出来了。
treeify
为了让运行结果在显示的时候能够更好的反映出调用关系,根据堆栈(%esp)中的信息采用python脚本将其层次化的打印出来。
import subprocess import sys def read(): for line in sys.stdin: try: regs = [int(x, 16) for x in line.split(" ")] yield {"eip": regs[0], "esp": regs[1]} # Ignore lines interspersed with other output! except (ValueError, IndexError): pass def addr2line(iterable): proc = subprocess.Popen(["addr2line", "-e", sys.argv[1], "-f"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) for regs in iterable: proc.stdin.write("%x\n" % regs["eip"]) a = proc.stdout.readline().rstrip("\n") b = proc.stdout.readline().rstrip("\n") regs["func"] = "%s %s" % (a, b) yield regs def entry_points(iterable): funcs = {} # We treat the first address we see for the function as its entry # point, and only report those entries from this point on. for regs in iterable: func = regs["func"].split(":")[0] if funcs.setdefault(func, regs["eip"]) == regs["eip"]: yield regs def add_nesting(iterable): stack = [2 ** 64] for regs in iterable: stack_pos = regs["esp"] if stack_pos < stack[-1]: stack.append(stack_pos) while stack_pos > stack[-1]: stack.pop() regs["indent"] = " " * len(stack) yield regs for x in add_nesting(entry_points(addr2line(read()))): print x["indent"], x["func"], "%x" % x["eip"]
运行
./itrace ./hellow|python2 ./treeify.py ./hellow
测试结果:
_start /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/start.S:61 8048d40 __libc_start_main ??:? 8048ea0 _dl_aux_init ??:? 806e590 _dl_discover_osversion ??:? 806f3b0 uname ??:? 80921c0 ?? ??:0 b77e0414 index ??:? 805b250 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 __init_cpu_features ??:? 806f570 strncasecmp_l ??:? 80b5ac0 strcmp ??:? 805b460 memset ??:? 805ba70 strcasecmp_l ??:? 805bc10 bcmp ??:? 805b6b0 strstr ??:? 806b080 memchr ??:? 808ce90 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 strrchr ??:? 808c660 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 wcslen ??:? 808eae0 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 __rawmemchr ??:? 805bcc0 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 memmove ??:? 805b9d0 __strnlen ??:? 808c620 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 strcpy ??:? 805b4d0 stpcpy ??:? 805bb30 __pthread_initialize_minimal ??:? 80494d0 __libc_setup_tls ??:? 8049240 sbrk ??:? 806cd70 __brk ??:? 80928e0 ?? ??:0 b77e0414 __brk ??:? 80928e0 ?? ??:0 b77e0414 memcpy ??:? 805bc50 __libc_init_first ??:? 806f4b0 __setfpucw ??:? 807ac00 __libc_init_secure ??:? 806f360 _dl_non_dynamic_init ??:? 806e7f0 _dl_get_origin ??:? 809a800 ?? ??:0 b77e0414 malloc ??:? 8058f60 malloc_hook_ini malloc.o:? 805a020 ptmalloc_init.part.7 malloc.o:? 8059c20 __linkin_atfork ??:? 806e0f0 malloc ??:? 8058f60 _int_malloc malloc.o:? 8057060 malloc_consolidate malloc.o:? 80560b0 malloc_init_state malloc.o:? 80552e0 __default_morecore ??:? 805b230 sbrk ??:? 806cd70 __brk ??:? 80928e0 ?? ??:0 b77e0414 __default_morecore ??:? 805b230 sbrk ??:? 806cd70 __brk ??:? 80928e0 ?? ??:0 b77e0414 mempcpy ??:? 805bb00 getenv ??:? 804e240 strlen ??:? 805b5f0 _dl_new_object ??:? 80972a0 strlen ??:? 805b5f0 __calloc ??:? 8059720 _int_malloc malloc.o:? 8057060 __memset_sse2_rep ??:? 805f690 memcpy ??:? 805bc50 _dl_setup_hash ??:? 8097150 strlen ??:? 805b5f0 malloc ??:? 8058f60 _int_malloc malloc.o:? 8057060 memcpy ??:? 805bc50 _dl_add_to_namespace_list ??:? 8097200 getenv ??:? 804e240 strlen ??:? 805b5f0 _dl_init_paths ??:? 80951f0 _dl_important_hwcaps ??:? 80991b0 malloc ??:? 8058f60 _int_malloc malloc.o:? 8057060 mempcpy ??:? 805bb00 malloc ??:? 8058f60 _int_malloc malloc.o:? 8057060 malloc ??:? 8058f60 _int_malloc malloc.o:? 8057060 getenv ??:? 804e240 strlen ??:? 805b5f0 getenv ??:? 804e240 strlen ??:? 805b5f0 getenv ??:? 804e240 strlen ??:? 805b5f0 getenv ??:? 804e240 strlen ??:? 805b5f0 getenv ??:? 804e240 strlen ??:? 805b5f0 __init_misc ??:? 806dd90 __strrchr_sse2_bsf ??:? 808d830 __ctype_init ??:? 807abb0 __cxa_atexit ??:? 804e5d0 __new_exitfn ??:? 804e440 __libc_csu_init ??:? 80494f0 _init /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:63 8048190 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 _init /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crtn.S:40 80481ae frame_dummy crtstuff.c:? 8048e20 __register_frame_info_bases ??:? 80bd5a0 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 register_tm_clones crtstuff.c:? 8048db0 init_cacheinfo cacheinfo.o:? 8048b10 handle_intel cacheinfo.o:? 806b380 intel_check_word cacheinfo.o:? 806b0b0 intel_check_word cacheinfo.o:? 806b0b0 intel_check_word cacheinfo.o:? 806b0b0 intel_check_word cacheinfo.o:? 806b0b0 handle_intel cacheinfo.o:? 806b380 intel_check_word cacheinfo.o:? 806b0b0 intel_check_word cacheinfo.o:? 806b0b0 _setjmp ??:? 804d970 main ??:? 8048e7c puts ??:? 804f350 strlen ??:? 805b5f0 _IO_new_file_xsputn ??:? 8052470 _IO_file_overflow ??:? 8052e60 _IO_doallocbuf ??:? 8053b10 _IO_file_doallocate ??:? 808a730 _IO_file_stat ??:? 8051f20 ___fxstat64 ??:? 806c300 ?? ??:0 b77e0414 __mmap ??:? 806ce60 ?? ??:0 b77e0414 _IO_setb ??:? 8053aa0 _IO_new_do_write ??:? 80527b0 _IO_default_xsputn ??:? 8053bc0 exit ??:? 804e420 __run_exit_handlers ??:? 804e320 __libc_csu_fini ??:? 8049590 fini sdlerror.o:? 8048b00 check_free.isra.0 sdlerror.o:? 80a6f30 __do_global_dtors_aux crtstuff.c:? 8048df0 deregister_tm_clones crtstuff.c:? 8048d80 __deregister_frame_info_bases ??:? 80bd7c0 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 _fini /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:82 80bec78 __x86.get_pc_thunk.bx /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crti.S:66 8048d70 _fini /root/glibc/src/glibc-2.18/csu/../sysdeps/i386/crtn.S:45 80bec87 _IO_cleanup ??:? 80543b0 _IO_flush_all_lockp ??:? 8054190 _IO_file_overflow ??:? 8052e60 _IO_new_do_write ??:? 80527b0 new_do_write fileops.o:? 8051100 _IO_file_write ??:? 8051f50 __write ??:? 806c420 ?? ??:0 b77e0414 _IO_file_setbuf ??:? 8051060 _IO_default_setbuf ??:? 8053d10 _IO_file_sync ??:? 8053060 _IO_setb ??:? 8053aa0 _Exit ??:? 806ba24 ?? ??:0 b77e0414
参考资料
<二>两个gdb集成测试环境
本节重点描述两个gdb集成测试环境。
有没有办法在一边调试的时候,一边显示对应的源码呢?有没有一种工具能够将gdb集成到ide中呢,本文就试图回答这些问题。
emacs gdb
在linux的世界里,emacs集成gdb来时行调试可是闻名已久,经过了不断的演进gud也变得越来越强大,越来越好使了。
那么如何进入gud呢,很简单在emacs中,输入M-x(也就是alt+x)后,输入gdb,然后回车。有一个简短的提示,无视直接回车即可。
在gdb所在窗口设置断点,运行程序。
程序将在设置的断点处停止运行,此时在菜单中选择gud->gdb-mi->display other windows,呈现如下图所示的效果:
gdb tui
与上述的emacs+gdb比较起来,这个gdb tui可能默默无名。我也是在无意之中发现这个东东的,还是比较好使的, 最主要的是这个功能是gdb本身内置的。无须第三方工具。
如何进入gdb tui模式
gdb -tui ./prog
或者在进入gdb之后输入如下快捷键
c-x a
想同时显示源码和反汇编代码的话,就c-x 2或者执行相应的指令layout
写在后面的话
花了15篇文章的内容,将自己日常软件调试时使用到的gdb知识作了一个小结,希望对大家有用。同时俺徽沪一郎也希望大家在转载的时候能够注明原文出处,谢谢。