一、场景

在调试或定位后端线上问题时,会碰到因缺少某些日志信息,导致定位困难的情况。如果重加日志并重启程序,重新复现问题,一是需要花费加代码、编译、发布、重启的时间,二是重启破坏了问题复现时的条件,对于非必现的问题,这种重启程序的方式不可取。如果设置断点调试,输出信息,又会阻塞程序,影响线上用户的使用。

本文将介绍一种不用加日志重启,同时又不用让程序阻塞太久的调试方式,来输出调试者想要的定位信息。

二、GDG调试技巧命令列表(commands)

这种方式就是gdb断点加命令列表(commands),具体步骤为:

1、设置想要输出信息的相关代码的断点,如果是处理线上问题,最好是设置条件断点,尽量减少中断的产生及非必要的信息输出;

2、设置这个断点的命令列表(commands),在命令列表中加要输出信息的命令及continue命令,加continue命令的目的是让gdb输出信息后继续运行,而不是在断点处阻塞,从而避免影响线上用户。

断点命令列表(commands)的用法如下:

commands [range…]

… command-list …

end

三、示例

有如下示例代码:

#include <iostream>
#include <algorithm>
#include <ctime>
#include <cstdlib>int main(int argc, char* argv[])
{std::srand(unsigned(std::time(0)));int nums[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };for (int i = 0; i < 10; ++i){std::random_shuffle(nums, nums+(sizeof(nums)/sizeof(nums[0])));std::cout << nums[0];}return 0;
}

代码保存文件为main.cpp,欲当for循环中i等于5时,在gdb输出数组nums的内容,同时不阻塞程序,步骤如下:

1、首先将示例代码编译为可执行程序

[admin@LOCAL-96-152 gdbdebug]# g++ main.cpp  -o gdbdebug

2、通过gdb启动程序gdbdebug,当然,如果要用gdb调试已在运行中的程序,可以使用gdb的attach命令

[admin@LOCAL-96-152 gdbdebug]# gdb ./gdbdebug

3、在gdb中,设置条件断点,条件为for循环中i等于5

(gdb) b main.cpp:14 if 5==i
Breakpoint 1 at 0x40085a: file main.cpp, line 14.

4、加此断点的命令列表(commands)

(gdb) commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>p nums
>continue 
>end

commands命令以end结束,p nums命令在断点处输出数组num值,continue命令使输出后程序继续运行,而不是在断点处阻塞。

5、至此,设置完成,输入r命令运行程序,如果是用gdb attach到运行中的程序,则输入continue命令继续程序的运行。示例结果输出如下:

(gdb) r
Starting program: /home/gdbdebug/gdbdebug Breakpoint 1, main (argc=1, argv=0x7fffffffe628) at main.cpp:14
14			std::cout << nums[0];
$1 = {0, 3, 5, 6, 1, 8, 2, 7, 9, 4}