文章目录

  • ​​一、单步执行和跟踪函数调用​​
  • ​​1.gdb基本命令1​​
  • ​​2.函数调试实例​​
  • ​​(1)原始代码如下​​
  • ​​(2)在编译时要加上 -g 选项,生成的可执行文件才能用 gdb 进行源码级调试​​
  • ​​(3)在(gdb)提示符下输入 help 可以查看命令的类别​​
  • ​​(4)也可以进一步查看某一类别中有哪些命令,例如查看 files 类别下有哪些命令可用​​
  • ​​(5)现在试试用 list 命令从第一行开始列出源代码​​
  • ​​(6)gdb 提供了一个很方便的功能:在提示符下直接敲回车表示重复上一条命令​​
  • ​​(7)要列一个函数的源代码:l 函数名(或者list 函数名)​​
  • ​​(8)退出 gdb 的环境​​
  • ​​(9)把源代码改名或移到别处再用 gdb 调试,这样就列不出源代码了​​
  • ​​(10)首先用 start 命令开始执行程序:gdb main​​
  • ​​(11)我们可以用 next 命令(简写为 n ) 控制这些语句一条一条地执行​​
  • ​​(12)现在用 start 命令重新来过,这次用 step 命令(简写为 s ) 钻进 add_range 函数中去跟踪执行​​
  • ​​(13)在函数中有几种查看状态的办法, backtrace 命令(简写为 bt ) 可以查看函数调用的栈帧​​
  • ​​(14)现在可以用 info 命令(简写为 i ) 查看 add_range 函数局部变量的值:i locals​​
  • ​​(15)如果想查看 main 函数当前局部变量的值也可以做到​​
  • ​​(16)用 s 或 n 往下走几步,然后用 print 命令(简写为 p )打印出变量 sum 的值​​
  • ​​(16)可以用 finish 命令让程序一直运行到从当前函数返回为止​​
  • ​​(17)下面用 s 命令进入第二次 add_range 调用,进入之后首先查看参数和局部变量​​
  • ​​(18)如果我们不想浪费这次调试机会,可以在 gdb 中马上把 sum 的初值改为0继续运行,看看这一处改了之后还有没有别的Bug:set var sum=0(print 命令也可以)​​
  • ​​二、断点​​
  • ​​1.gdb基本命令2​​
  • ​​2.函数调试实例如下​​
  • ​​(1)断点调试实例如下​​
  • ​​(2)编译运行,看看出现什么问题​​
  • ​​(3)开始GDB调试:gdb main​​
  • ​​(4) display sum:显示当前 sum 的值;undisplay sum编号:取消跟踪显示​​
  • ​​(5)break(或b) 行数:设置断点​​
  • ​​(6)现在用 continue 命令(简写为 c ) 连续运行而非单步运行,程序到达断点会自动停下来​​
  • ​​(7)一次调试可以设置多个断点,用 info 命令可以查看已经设置的断点​​
  • ​​(8)delete breakpoints 断点编号:每个断点都有一个编号,可以用编号指定删除某个断点​​
  • ​​(9)disable breakpoints 断点编号:有时候一个断点暂时不用可以禁用掉而不必删除​​
  • ​​(10)还可以设置断点在满足某个条件时才激活​​
  • ​​三、习题:反转字符串初学者常见的一个错误​​
  • ​​四、观察点​​
  • ​​1.gdb基本命令3​​
  • ​​2.断点调试实例如下​​
  • ​​(1)开始调试start​​
  • ​​(2)打印指定存储单元的内容:x/7b input​​
  • ​​(3)设一个条件断点从 i 等于4开始单步调试​​
  • ​​(4)用观察点(Watchpoint) 来跟踪input[4]后面的字节什么时候变的​​
  • ​​注意:断点是当程序执行到某一代码行时中断,而观察点是当程序访问某个存储单元时中断​​
  • ​​五、段错误​​
  • ​​1.段错误调试实例1​​
  • ​​(1)调试代码如下​​
  • ​​(2)查看段错误的配合指令:r和bt​​
  • ​​2.段错误调试实例2​​
  • ​​(1)调试代码如下​​
  • ​​(2)调试过程如下​​

一、单步执行和跟踪函数调用

(1)本章我们介绍一种很强大的调试工具 gdb ,可以完全操控程序的运行,使得程序就像
你手里的玩具一样,叫它走就走,叫它停就停,并且随时可以查看程序中所有的内部状态,
比如各变量的值、传给函数的参数、当前执行的代码行等。掌握了 gdb 的用法之后,调试手
段就更加丰富了。

(2)但要注意,即使调试手段丰富了,调试的基本思想仍然是“分析现象->假设错误原因->产生新的现象去验证假设”这样一个循环

1.gdb基本命令1

(第10章)LinuxC语言中GDB调试_函数调用

2.函数调试实例

(1)原始代码如下

(第10章)LinuxC语言中GDB调试_局部变量_02


(第10章)LinuxC语言中GDB调试_函数调用_03

(2)在编译时要加上 -g 选项,生成的可执行文件才能用 gdb 进行源码级调试

(第10章)LinuxC语言中GDB调试_函数调用_04

(3)在(gdb)提示符下输入 help 可以查看命令的类别

(第10章)LinuxC语言中GDB调试_基本命令_05

(4)也可以进一步查看某一类别中有哪些命令,例如查看 files 类别下有哪些命令可用

(第10章)LinuxC语言中GDB调试_函数调用_06

(5)现在试试用 list 命令从第一行开始列出源代码

(第10章)LinuxC语言中GDB调试_基本命令_07

(6)gdb 提供了一个很方便的功能:在提示符下直接敲回车表示重复上一条命令

(第10章)LinuxC语言中GDB调试_局部变量_08

(7)要列一个函数的源代码:l 函数名(或者list 函数名)

(第10章)LinuxC语言中GDB调试_基本命令_09


(第10章)LinuxC语言中GDB调试_基本命令_10

(8)退出 gdb 的环境

(第10章)LinuxC语言中GDB调试_基本命令_11

(9)把源代码改名或移到别处再用 gdb 调试,这样就列不出源代码了

(第10章)LinuxC语言中GDB调试_局部变量_12

(10)首先用 start 命令开始执行程序:gdb main

(第10章)LinuxC语言中GDB调试_局部变量_13


gdb 停在 main 函数中变量定义之后的第一条语句处等待我们发命令, gdb 列出的这条语句是即将执行的下一条语句。

(11)我们可以用 next 命令(简写为 n ) 控制这些语句一条一条地执行

(第10章)LinuxC语言中GDB调试_函数调用_14


用 n 命令依次执行两行赋值语句和一行打印语句,在执行打印语句时结果立刻打出来了,然

后停在 return 语句之前等待我们发命令。虽然我们完全控制了程序的执行,但仍然看不出哪

里错了,因为错误不在 main 函数中而在 add_range 函数中。

(12)现在用 start 命令重新来过,这次用 step 命令(简写为 s ) 钻进 add_range 函数中去跟踪执行

(第10章)LinuxC语言中GDB调试_函数调用_15


这次停在了 add_range 函数中变量定义之后的第一条语句处。

(13)在函数中有几种查看状态的办法, backtrace 命令(简写为 bt ) 可以查看函数调用的栈帧

(第10章)LinuxC语言中GDB调试_函数调用_16

  • 可见当前的 add_range 函数是被 main 函数调用的, main 传进来的参数是 low=1,high=10。
  • main 函数的栈帧编号为1, add_range 的栈帧编号为0。

(14)现在可以用 info 命令(简写为 i ) 查看 add_range 函数局部变量的值:i locals

(第10章)LinuxC语言中GDB调试_局部变量_17

(15)如果想查看 main 函数当前局部变量的值也可以做到

(第10章)LinuxC语言中GDB调试_函数调用_18


注意到 result 数组中有很多元素具有杂乱无章的值,我们知道未经初始化的局部变量具有不

确定的值。到目前为止一切正常。

(16)用 s 或 n 往下走几步,然后用 print 命令(简写为 p )打印出变量 sum 的值

(第10章)LinuxC语言中GDB调试_基本命令_19

(16)可以用 finish 命令让程序一直运行到从当前函数返回为止

(第10章)LinuxC语言中GDB调试_局部变量_20


第一个值55确实赋给了 result 数组的第0个元素。

(17)下面用 s 命令进入第二次 add_range 调用,进入之后首先查看参数和局部变量

(第10章)LinuxC语言中GDB调试_基本命令_21

(18)如果我们不想浪费这次调试机会,可以在 gdb 中马上把 sum 的初值改为0继续运行,看看这一处改了之后还有没有别的Bug:set var sum=0(print 命令也可以)

(第10章)LinuxC语言中GDB调试_函数调用_22

二、断点

1.gdb基本命令2

(第10章)LinuxC语言中GDB调试_函数调用_23

2.函数调试实例如下

(1)断点调试实例如下

(第10章)LinuxC语言中GDB调试_基本命令_24


解释说明

(a)程序的作用:

(第10章)LinuxC语言中GDB调试_基本命令_25


例如输入是 “2345” ,则循环累加的过程是(((0*10+2)*10+3)*10+4)*10+5=2345。

(b)注意字符型的 ‘2’ 要减去 ‘0’ 的ASCII码才能转换成整数值2。

(2)编译运行,看看出现什么问题

(第10章)LinuxC语言中GDB调试_基本命令_26


(第10章)LinuxC语言中GDB调试_局部变量_27

(3)开始GDB调试:gdb main

(第10章)LinuxC语言中GDB调试_函数调用_28

(4) display sum:显示当前 sum 的值;undisplay sum编号:取消跟踪显示

(第10章)LinuxC语言中GDB调试_函数调用_29

(5)break(或b) 行数:设置断点

可以用 break 命令(简写为 b ) 在第9行设一个断点(Breakpoint);

break 命令的参数也可以是函数名,表示在某个函数开头设断点;

(第10章)LinuxC语言中GDB调试_局部变量_30

(6)现在用 continue 命令(简写为 c ) 连续运行而非单步运行,程序到达断点会自动停下来

这样就可以停在下一次循环的开头:

(第10章)LinuxC语言中GDB调试_局部变量_31


(第10章)LinuxC语言中GDB调试_函数调用_32

(7)一次调试可以设置多个断点,用 info 命令可以查看已经设置的断点

(第10章)LinuxC语言中GDB调试_基本命令_33

(8)delete breakpoints 断点编号:每个断点都有一个编号,可以用编号指定删除某个断点

(第10章)LinuxC语言中GDB调试_局部变量_34

(9)disable breakpoints 断点编号:有时候一个断点暂时不用可以禁用掉而不必删除

(第10章)LinuxC语言中GDB调试_局部变量_35

(10)还可以设置断点在满足某个条件时才激活

(第10章)LinuxC语言中GDB调试_基本命令_36


(第10章)LinuxC语言中GDB调试_函数调用_37

三、习题:反转字符串初学者常见的一个错误

(第10章)LinuxC语言中GDB调试_函数调用_38


解决办法:

(1)用gdb调试后,发现的结果是:

(第10章)LinuxC语言中GDB调试_局部变量_39


为啥呢?

因为用空字符串 “” 初始化一个同样长的字符数组 reverse_str ,相当于所有元素用 ‘\0’ 初始化。

而printf函数打印字符串的时候,看到NULL就会停止输出(就是0)。

所以,正确的做法是:

(第10章)LinuxC语言中GDB调试_局部变量_40

四、观察点

1.gdb基本命令3

(第10章)LinuxC语言中GDB调试_函数调用_41

2.断点调试实例如下

(第10章)LinuxC语言中GDB调试_基本命令_42


如果输入的字符串超长了会怎么样?

我们知道数组访问越界是不会检查的,所以 scanf 会写出界。现象是这样的:

(第10章)LinuxC语言中GDB调试_基本命令_43

(1)开始调试start

(第10章)LinuxC语言中GDB调试_函数调用_44

(2)打印指定存储单元的内容:x/7b input

(第10章)LinuxC语言中GDB调试_基本命令_45

(3)设一个条件断点从 i 等于4开始单步调试

(第10章)LinuxC语言中GDB调试_基本命令_46


(第10章)LinuxC语言中GDB调试_基本命令_47


(第10章)LinuxC语言中GDB调试_局部变量_48

(4)用观察点(Watchpoint) 来跟踪input[4]后面的字节什么时候变的

注意:断点是当程序执行到某一代码行时中断,而观察点是当程序访问某个存储单元时中断

(第10章)LinuxC语言中GDB调试_基本命令_49


(第10章)LinuxC语言中GDB调试_函数调用_50


(第10章)LinuxC语言中GDB调试_函数调用_51


说明:

(第10章)LinuxC语言中GDB调试_基本命令_52

五、段错误

1.段错误调试实例1

(1)调试代码如下

(第10章)LinuxC语言中GDB调试_基本命令_53

(2)查看段错误的配合指令:r和bt

(第10章)LinuxC语言中GDB调试_函数调用_54

2.段错误调试实例2

(1)调试代码如下

(第10章)LinuxC语言中GDB调试_基本命令_55


(第10章)LinuxC语言中GDB调试_函数调用_56

(2)调试过程如下

(第10章)LinuxC语言中GDB调试_局部变量_57


解释:

(第10章)LinuxC语言中GDB调试_函数调用_58