GDB debugging method in Linux environment

1. Introduction to GDB

  GDB (GNU Debugger) is a debugging tool for GCC. Its powerful functions are described as follows:
  
  GDB mainly helps you complete the following four functions:

  • 1. Start your program, you can run the program as you want according to your custom requirements.
  • 2. Allows the debugged program to stop at the breakpoint you specify. (Breakpoints can be conditional expressions)
  • 3. When the program is stopped, you can check what happened in your program at that time.
  • 4. Dynamically change the execution environment of your program.

2. GDB basic debugging commands

2.1 Preparation

  Debug information is added to the executable via the gcc -goption .

$ gcc -g hello.c -o hello

  If building with a Makefile, -goptions .

CFLAGS := -Wall -O2 -g

  Note that after adding the optimization option to the GCC compiler, the actual execution order may be different from the source code order due to optimization, so using the debugger to trace the runtime sometimes executes to inexplicable places, causing confusion.

2.2 Start

gdb 程序名Start  with the command .

2.3 Set breakpoints

  Breakpoints can be set on function names and line numbers, etc. When the program reaches the breakpoint, it will automatically stop running. At this point, you can view the variable value at that moment, display the stack frame, reset breakpoints, or re-run. The breakpoint command breakcan be abbreviated as b, the command is break <断点>.
  
  Breakpoints can be set by the function name, the line number in the current file, or by specifying the file name and then the line number, or by specifying the offset from the pause position, or by using an address. Format:

 

Format illustrate
break <function name> Set a breakpoint on the specified function in the currently executing file
break <line number> Set a breakpoint on a specific line in the currently executing file
break <filename:linenumber> Set a breakpoint on a specified line of a specified file, the most common way to set a breakpoint
break <filename:functionname> Set a breakpoint on the specified function of the specified file
break <+/- offset> Set breakpoint at current command line +/- offset out
break <*address> Set a breakpoint at the specified address


  
  The set breakpoint can be info breakconfirmed .

2.4 Running

  Start the run with the run 参数command , where the parameters are the parameters of the executable program. If a breakpoint is set, it will execute to the position where the breakpoint is set and pause the operation. Can be abbreviated as r.

2.5 Display stack frame

  backtraceThe command can display the stack frame when a breakpoint or exception is encountered and execution is suspended, the command is abbreviated as bt. In addition, the aliases for backtrace are whereand info stack.

 

Format illustrate

bt

show all stack frames

bt <N>

Display only the first N stack frames

bt <-N>

Show only the last N stack frames

bt full

Show not only backtrace, but also local variables

bt full <N>


  
  After displaying the stack frame, you can see where the program stopped and the call path of the program.

2.6 Display variables

  printCommands can display variables, which can be abbreviated as p.
  
  Format: print 变量.

2.7 Display registers

  info registersRegisters can be displayed, abbreviated as info reg.
  
  Add $ before the register name to display the contents of the register, eg p $eax.
  p/格式 $寄存器The display format of the registers can be specified, eg p/c $eax. The available formats are as follows:

 

Format illustrate
x Display as hexadecimal number
d Display as a decimal number
u Display as unsigned decimal
O Display as an octal number
t Display as binary number
a address
c Display as characters (ASCII)
f floating point decimal
s display as string
i Displayed in machine language, only available in the x command that displays memory


  
  The program pointer can be written as $pc or as $eip to p $pcdisplay contents of the program pointer. The program pointer points to the address of the running point of the current program.
  
  xThe command can display the contents of the memory in the format: x/<格式> <地址>. For example, x/i $ps displays assembly instructions.
  
  Generally, when using the x command, the format is x/<NFU> <ADDR>. Here ADDR is the address you want to display, N is the number of repetitions, F is the previous display format, and the unit represented by U is as follows:

 

unit illustrate
b byte
h Halfword (2 bytes)
w word (4 bytes) (default)
g Double word (8 bytes)


  For example the command x\10i $pc prints 10 instructions starting at the address pointed to by pc.
  
  Disassembly command disassemble, abbreviated as disas. Format:

  • disassemble. Disassemble the current entire function.
  • disassemble program counter. Disassemble the entire function of the function where the program counter is located.
  • disassemble start address end address. Disassemble the part from the start address to the end address.

2.8 Single Step

  Single-stepping means executing line by line according to the source code. The command to execute a line in the source code is next, abbreviated as n. If you encounter a function call during execution and want to execute it inside the function, use the stepcommand , abbreviated as p.
  If you want to execute assembly instructions one by one, you can use the nexti and stepi commands, respectively.

2.9 Continue to run

  Continue running the program with the continuecommand , abbreviated as c. The program will pause again after hitting the breakpoint. Use the continue <次数>command to specify the number of times to ignore breakpoints.

2.10 Watchpoints

  In large software or programs that use a lot of pointers, it is difficult to figure out where variables are changed. To find out where variables are changed, you can use the watchcommand (watchpoint). The format is as follows:

  • watch <expression>. Pause when the expression changes.
  • awatch <expression>. Pauses execution when an expression is accessed or changed.
  • rwatch <expression>. Pauses execution when the expression is accessed.

  Note that setting watchpoints may slow down operation.

2.11 Deleting breakpoints and watchpoints

  deleteThe command deletes breakpoints and watchpoints, abbreviated as d. The format is delete <断点编号>, it means to delete the breakpoint or watchpoint indicated by the number, and the number can be viewed with the command info b.
  clearcommand to delete a defined breakpoint. Available commands include:
    clear <function name>
    clear <line number>
    clear <file name: line number> The
    clear <file name: function name>
  
  disablecommand disables breakpoints. The command format is as follows:
    disable: Disable all breakpoints.
    disable <breakpoint number>: Disable the specified breakpoint.
    disable display <display number>: Disable the automatic display defined by the display command.
    disable mem <memory display>: Disables the memory area defined by the mem command.
  
  enablecommand to enable breakpoints. The command format is as follows:
    enable
    enable <breakpoint number>
    enable once <breakpoint number>: enable the specified breakpoint only once.
    enable delete <breakpoint number>
    enable display <display number>
    enable mem <memory display>

2.12 Other breakpoints

  Hardware breakpoints (hbreak), suitable for programs in unmodifiable memory areas such as ROM space, are not available on some architectures.
  
  Temporary breakpoints (tbreak) and temporary hardware breakpoints (thbreak), pause when running there, and the breakpoint will be deleted at this time, which is convenient to use when you only need to stop once.

2.13 Changing the value of a variable

  Format: set variable <变量=表达式>. For example, the command set variable options = 0 changes the value of the variable options to 0.

2.14 Generating a core dump file

  Use generate-core-fileto generate a core dump file of the process being debugged. View the run history when the dump file was generated through the core dump file and debug objects.
  
  gcorecommand to generate a core dump file directly from the command line. Used on the command line gcore pid, where pid is the process ID. This command does not need to stop the running program to obtain the core dump file, which is very useful when the cause of the problem needs to be analyzed separately on other machines, or when the problem occurs at the customer site.


3. Kernel dump

  The biggest benefit of a core dump is that it preserves the state at the time of the problem. As long as there is an executable and a core dump of the program at the time of the problem, you can know the current state of the process.

3.1 Enable core dump

  Most Linux distributions turn off the kernel dump function by default. You can use the ulimitcommand to check whether the current kernel dump function is valid.

$ ulimit -c
0

  The -c option indicates the size limit for the core dump file, and 0 indicates that the core dump is invalid. To enable core dump execute the command:

$ ulimit -c unlimited
or
$ ulimit -c 上限

  Unlimited means that there is no limit to the size of the kernel dump file. When a problem occurs, all the memory of the process can be dumped into the kernel dump file. Or set the upper limit of the core dump file, and specify the upper limit size in the parameter in Kb.
  
  When an exception occurs in the program, a core dump file will be generated in the current directory. For example, the program a.out generates the dump file core and starts GDB with:

$ gdb -c core ./a.out

  
  Use GDB's list command to view nearby source code. How to use the command

 

Format illustrate
list <linenum> 显示程序第 linenum 行周围的源代码
list <function> 显示函数名为 function 的函数的源代码
list 显示当前行后面的源代码
list - 显示当前行前面的源代码
set listsize <count> 设置一次显示源代码的行数
show listsize 查看当前 listsize 的设置
list <first>,<last> 显示从 first 行到 last 行之间的源代码
list ,<last> 显示从当前行到 last 行之间的源代码
list + 往后显示源代码

 

3.2 在专用目录中生成内核转储

  转储保存位置的完整路径可以通过 sysctl 变量 kernel.core_pattern 设置。在文件 /etc/sysctl.conf 中设置如下:

$ cat /etc/sysctl.conf
kernel.core_pattern = /var/core/%t-%e-%p-%c.core
kernel.core_uses_pid = 0
$ sysctl -p

  
  此外,还可以在 /proc/sys/kernel 下修改设置。
  /proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 pid 作为扩展 ,如果添加则文件内容为 1 ,否则为 0。
  proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名 ,可以这样修改 :

$ echo "/corefile/core-%e-%p-%t" > core_pattern

  
  kernel.core_pattern 中可以设置的格式符如下:

 

格式符 说明
%% % 字符本身
%p 被转储进程的进程 ID(PID)
%u 被转储进程的真实用户 ID(real UID)
%g 被转储进程的真实组 ID(real GID)
%s 引发转储的信号编号
%t 转储时刻(从 1970/1/1 0:00 开始的秒数)
%h 主机名(同 uname(2) 返回的 nodename)
%e 可执行文件名
%c 转储文件的大小上限(内核版本 2.6.24 后可用)

 

3.3 使用用户模式辅助程序自动压缩内核转储文件

  修改 /etc/sysctl.conf中 的 kernel.core_pattern 变量来设置。

 $ cat /etc/sysctl.conf
kernel.core_pattern= | usr/local/sbin/core_helper %t %e %p %c
kernel.core_uses_pid= 0
 $ sysctl -p

  core_helper 的内容:

$cat usr/local/sbin/core_helper
#!/bin/sh

execgzip ->/var/core/$1-$2-$3-$4.core.gz

  这样,发生内核转储时就会在 /var/core 下生成压缩的内核转储文件。

3.4 启用整个系统的内核转储功能

  /etc/profile 文件中可以设置开启所有用户的内核转储功能,默认情况下禁止内核转储:

ulimit -S -c 0 > /dev/null 2>&1

  将其修改为

ulimit -S -c unlimited > /dev/null 2>&1

  
  接下来要让通过 init 脚本启动的守护进程的内核转储功能有效。在 /etc/sysconfig/init 文件中添加一行命令。

DAEMON_COREFILE_LIMIT='unlimited'

  
  最后在 /etc/sysctl.conf 中加入以下设置。

fs.suid_dumpable=1

  这个设置使得被 SUID 的程序也能内核转储。重新启动启动,就可以启用整个系统的内核转储。
  
  在我使用的Ubuntu、Debian和移植的嵌入式Linux系统中,没有找到 /etc/sysconfig 文件夹。修改了文件 /etc/security/limits.conf,按照文件的内容提示修改,使 unlimited 永久生效。

3.4 利用内核转储掩码排除共享内存

  多进程程序如果使用庞大的共享内存,内核转储时所有进程的共享内存全部转储,会对磁盘造成巨大的压力,转储过程也会加重系统的负载。由于共享内存的内容是相同的,只需要在某个进程中转储共享内存。
  
  通过 /proc/<PID>/coredump_filter 进行设置。coredump_filter 使用比特掩码表示内存类型。如下所示:

 

比特掩码 内存类型
比特 0 匿名专用内存
比特 1 匿名共享内存
比特 2 file-backed 专用内从
比特 3 file-backed 共享内存
比特 4 ELF 文件映射(内核版本 2.6.24 后可用)


  要跳过所有的共享内存区段,应将值改位 1。


4、GDB调试技巧

4.1 attach 到进程

  要调试已经启动的进程,或是调试陷入死循环而无法返回控制台的进程时,可以使用 attach 命令。格式:attach <pid>,执行这一命令可以 attach 到进程 ID 为 pid 的进程上。
  attach 之后就能使用普通的 gdb 命令。
  
  gdb 和进程分离时使用 detach 命令,调试的进程就从 gdb 的控制下释放出来。进程被 detach 后继续运行。
  
  进程信息可以用 info proc 命令显示。
  守护者进程在启动好子进程后,会自动关闭主进程,如果没有设定监控模式的话,gdb 会提示断开与进程的链接。所以必须设定监控对象,设置命令为 set follow-fork-mode child/parent

4.2 条件断点

  break <断点> if <条件>,这条命令将测试给定的条件,如果为帧则暂停运行。
  
  如果断点已经存在,condition <断点编号> <条件> 命令给断点添加触发条件,condition <断点编号> 命令删除指定编号断点的触发条件。

4.3 反复执行

  ignore <断点编号> <次数>:在编号指定的断点、监视点或捕获点忽略指定的次数。
  continue <次数>: 达到指定次数前,执行到断电时不暂停。
  s/stepi/n/nexti <次数>:执行指定次数的相应命令。
  finish:执行完当前函数后暂停。
  until:执行完当前函数等代码块后暂停,如果是循环,则在执行完循环后暂停,常用于跳出循环。
  until <地址>:执行到指定地址停止。

4.4 断点命令

  commands 命令可以定义在断点终端后自动执行的命令。格式如下:

(gdb) commands <断点编号>
<命令>
...
end

4.5 值的历史

  通过 print 命令显示过的值会记录在内部的值历史中。这些值通过 $ 进行引用,使用 show value 命令可以显示历史中的最后 10 个值。

 

变量 说明
$ 值历史的最后一个值
$n 值历史的第 n 个值
$$ 值历史的倒数第 2 个值
$$n 值历史的倒数第 n 个值
$_ x 命令显示过的最后的地址
$__ x 命令显示过的最后的地址的值
$_eexitcode 调试过程中的程序的返回代码
$bpnum 最后设置的断点编号


  
  还可以随意定义变量,变量以 $ 开头,由英文字母和数字组成。例如:

(gdb) set $i=0
(gdb) p $i
$i = 0

4.6 命令历史

  show history 将命令历史保存到文件中,默认命令历史文件位于 ./.gdb_history。
  

  set history expansion
  show history expansion

  可以使用 csh 风格的 ! 字符。
  

  set history filename <文件名>
  show history filename

  将命令历史保存到文件中。可以通过环境变量 GDBHISTFILE 改变默认文件名。
  

  set history save
  show history save

  启用命令历史保存到文件和恢复的功能。
  

  set history size <数字>
  show history size

  可设置保存到命令历史中的命令数量。默认值为 256。

4.7 初始化文件(.gdbinit)

  Linux 环境下的初始化文件为 .gdbinit。如果存在 .gdbinit 文件,GDB 会在启动之前将其作为命令文件运行。初始化文件和命令文件的运行顺序如下。

  • 1、$HOME/.gdbinit。
  • 2、运行命令行选项。
  • 3、./.gdbinit。
  • 4、通过 -x 选项给出的命令文件。

  初始化文件和命令文件的语法相同。利用 define 命令可以自定义命令,document 命令给自定义命令添加说明,GDB 运行时使用 help <命令名> 可以查看定义的命令。示例如下:

define li
    x/10i $pc
end
document li
    list machine instruction
end

  
  除了初始化文件,还可以把设置写在文件中,在运行 GDB 时读取这些文件。命令为 source <文件名>,例如:

(gdb) source gdbcalc
(gdb) p $log10(10000.0)
$1 = 4

  其中 gdbcalc 文件内容如下:

#!/usr/bin/gdb -x
file /usr/bin/gdb
start
set $e = 2.7182818284590452354
set $pi = 3.14159265358979323846
set $fabs = (double (*)(double)) fabs
set $sqrt = (double (*)(double)) sqrt
set $cbrt = (double (*)(double)) cbrt
set $exp = (double (*)(double)) exp
set $exp2 = (double (*)(double)) exp2
set $exp10 = (double (*)(double)) exp10
set $log = (double (*)(double)) log
set $log2 = (double (*)(double)) log2
set $log10 = (double (*)(double)) log10
set $pow = (double (*)(double, double)) pow
set $sin = (double (*)(double)) sin
set $cos = (double (*)(double)) cos
set $tan = (double (*)(double)) tan
set $asin = (double (*)(double)) asin
set $acos = (double (*)(double)) acos
set $atan = (double (*)(double)) atan
set $atan2 = (double (*)(double, double)) atan
set $sinh = (double (*)(double)) sinh
set $cosh = (double (*)(double)) cosh
set $tanh = (double (*)(double)) tanh
set $asinh = (double (*)(double)) asinh
set $acosh = (double (*)(double)) acosh
set $atanh = (double (*)(double)) atanh

5、总结

  GDB 常用命令及缩略形式如下表:

 

命令 简写形式 说明
backtrace bt、where 显示 backtrace
break   设置断点
continue c、cont 继续运行
delete d 删除断点
finish   运行到函数结束
info breakpoints   显示断点信息
next n 执行下一行
print p 显示表达式
run r 运行程序
step s 一次执行一行,包括函数内部
x   显示内存内容
until u 执行到指定行
directory dir 插入目录
disable dis 禁用断点
down do 在当前调用的栈帧中选择要显示的栈帧
edit e 编辑文件或函数
frame f 选择要显示的栈帧
forward-search fo 向前搜索
generate-core-file gcore 生成内核转储
help h 显示帮助一览
info i 显示信息
list l 显示函数或行
nexti ni 执行下一行(以汇编代码为单位)
print-object po 显示目标信息
sharedlibrary share 加载共享库的符号
setpi si 执行下一行

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324085607&siteId=291194637