IT技术博客大学习 共学习 共进步

GDB的两个技巧

NOSQL Notes 2011-06-21 13:25:14 累计浏览 3,686 次

    分享两个GDB的小技巧:

    1, GDB失效时手工得到stack;

    2, GDB执行用户命令脚本;

    调试内存型服务程序的有时会遇到core dump或死锁问题,且gdb或者pstack都无法显示调用栈(call stack)。这是因为线程的调用栈被破坏了,而调用栈存放了函数的返回地址,gdb解析函数返回地址(根据地址查找符号表)失败,gdb也没有进行容错处理,只要有一处地址解析失败就无法展开调用栈。然而幸运的是,调用栈往往只是部分被破坏,RSP堆栈寄存器中保存的值往往也是正确的,可以通过手工的方法恢复。具体做法如下:

(gdb) set logging on
Copying output to gdb.txt.
(gdb) x /2000a $rsp
0x426cb890: 0x0 0x4
0x426cb8a0: 0x426cb8c0 0x100
0x426cb8b0: 0x3e8 0x552f59 <_ZN5tbnet16EPollSocketEvent9getEventsEiPNS_7IOEventEi+41>
0x426cb8c0: 0x1823c8a000000011 0x0
0x426cb8d0: 0x0 0x0
0x426cb8e0: 0x0 0x0
...

    如上图,类似”0x552f59 <_ZN5tbnet16EPollSocketEvent9getEventsEiPNS_7IOEventEi+41>”这样的代码符号看起来是有效的。通过所有看似有效的程序代码符号基本能够得出core dump时的调用栈。

    当然,有可能出现core dump线程的调用栈被完全破坏的情况,通过上述方法恢复的信息仍然是无效的。由于每个线程堆栈地址空间的大小为10M,因此,线程之间互相破坏调用堆栈的可能性几乎是不存在的,此时,可以通过其它线程的调用栈分析其行为,往往也能找到线索。如果所有线程的调用栈都“看似被破坏”,那么,往往有两种可能:

    a, 可执行程序和core文件对不上,被摆乌龙了,如发现core dump问题的时候可执行程序已经更新到最新版本,老版本没有保存;

    b, 磁盘满了或者ulimit设置太小,导致core dump文件信息不全;

    如果core文件对不上或者信息不全的问题,还可以通过dmesg命令找到程序core dump时的指令寄存器RIP的值,再通过addr2line获取程序最后执行的代码行。如:

[rizhao.ych@OceanBase036040 updateserver]$ dmesg | grep updateserver
updateserver[8099]: segfault at 0000000000000000 rip 0000000000500fbf rsp 000000004c296e30 error 4

[rizhao.ych@OceanBase036040 updateserver]$ addr2line -e updateserver 0000000000500fbf
/home/rizhao/dev/oceanbase/src/common/ob_base_server.cpp:222

    另外一个用得比较多的功能是GDB执行用户命令脚本。我们组无施同学有一个例子:Oceanbase系统有一个ObGetParam的类,是一个数组,里面的每个元素是一个ObCellInfo,ObGetParam中可能包含成百上千个ObCellInfo,现在需要在GDB调试的时候输出数组中所有的ObCellInfo对象信息。脚本如下:

define dumpGetParam
set $cell_list = ($arg0)
set $cell_num = ($arg1)
set $cell_idx = (0)
while ($cell_idx < $cell_num)
  printf "cell_idx:%d,table_id:%llu,column_id:%llu\\n", $cell_idx,
    $cell_list[$cell_idx].table_id_, $cell_list[$cell_idx].column_id
  set $cell_idx = $cell_idx + 1
end
end

    上面的代码定义了一个命令叫dumpGetParam,其第一个参数$arg0是cell数组的地址,第二个参数$arg1是数组大小,代码的功能就是打印所有cell的信息。

     把上面的代码写入一个文本文件dump_get_param.txt,在gdb中执行source dump_get_param.txt,然后就可以使用dumpGetParam命令了。

建议继续学习

  1. 调试工具之GDB (累计阅读 14,705)
  2. gdb的基本工作原理是什么? (累计阅读 11,524)
  3. 使用gdb调试运行时的程序小技巧 (累计阅读 7,125)
  4. GDB中应该知道的几个调试方法 (累计阅读 6,486)
  5. 使用GDB调试多进程程序 (累计阅读 6,243)
  6. GDB常用指令说明 (累计阅读 3,987)
  7. GDB 进行程序调试笔记 (累计阅读 3,604)
  8. 更简单的重现PHP Core的调用栈 (累计阅读 3,046)
  9. 用GDB排查Python程序故障 (累计阅读 2,087)