今天解决了一个长期会碰到的问题,就是用 GDB 如何来调试动态链接库。我这个问题的难点是我的需要调试代码是在动态链接库里面,但是启动的不是普通的可以调试的二进制文件,换句话说这不是我所能控制的代码所编译出来的,甚至可能是由脚本程序来控制启动的。这个问题时不时地困扰着我,总结一下尝试过几种调试方式:
1 使用 print 来打印 log,有时候有用,不好的地方是有时候定位出问题的代码位置还是稍显麻烦。很常用的会定义一对宏,进入函数和退出某个函数的时候都相应调用。
#define APP_LOG(X) \
fprintf(stderr, "log: %s %d %s %s\n", \
__FILE__, __LINE__, __FUNCTION__, X); \
#define LOG_ENTER \
APP_LOG("enter")
#define LOG_LEAVE \
APP_LOG("leave")
2 对于 crash 掉的 bug,打印出来调用栈是非常有用的。使用libc 提供的 Backtraces 函数来获取调用栈。这是在不能提供 GDB 环境下拿到调用栈的不错方法。不过经过我的实验这对于动态链接库有一定的问题。
3 最后就是今天试用的比较通用办法。
我们不管是如何调用到动态链接库文件的,但是肯定会调用进来。所以需要想办法让代码在库代码处停下来,然后把找机会把 GDB 弄进去。于是乎有这么一个变态的办法,在动态链接库入口处来这么一段,就是执行到这里停住,等待 GDB attach 这个进程,然后在 GDB 里设置一个断点,touch 创建当前文件夹 debug 文件就跳出死循环,接下来就是一切在 GDB 控制下了。
void wait_attach() {
fprintf(stderr, "Waiting attach pid: %d\n", getpid());
while(1) {
if((access("./debug", F_OK)) != -1) {
break;
}
else
;
}
}
这是一个 stupid and work 的方法,不过我总觉得还有更好的办法来在这种情况下调试。
在查找资料的过程中有点意外收获,顺便推荐 GDB 一个选项,gdb -tui,以 texture gui 方式启动 GDB,这是非常方便的文字界面。如果不用这个选项也可以在运行 GDB 以后按下快捷键盘 C-x C-a(怎么这么像 Emacs 快捷键) 来进行 gui 和非 gui 的切换。CLI 爱好者可以试用一下,DDD 什么的可以放下了,嘿嘿。
另外一些有用的 GDB 命令:
rbreak: break on function matching regular expression
where: Line number currently being executed
tbreak: Break once, and then remove the breakpoint
watch: Suspend the process when a certain condition is met
finish: Continue till end of function
info locals: View all local variables
backtrace full: Complete backtrace with local variables
up, down, frame: Move through frames
set print pretty on: Prints out prettily formatted C source code
set print array on: Pretty array printing
enable and disable: Enable/disable breakpoints
set logging on: Log debugging session to show to others for support