调试利器GDB

本文介绍了GDB作为调试利器的常规应用,包括自定义启动方式、条件断点和CoreDump分析。详细讲解了GDB的启动方式、软件和硬件断点的设置与管理,以及数据断点和内存查看的方法。此外,还通过实例展示了如何利用GDB进行函数调用栈的查看和深入的`info`命令使用,最后分享了一些调试技巧和编程实验。
摘要由CSDN通过智能技术生成

调试利器GDB(上)

1 GDB的常规应用

  1. 自定义程序的启动方式(指定影响程序运行的参数)

  2. 设置条件断点(在条件满足时暂停程序的执行)

  3. 回溯检查导致程序异常结束的原因(Core Dump)

  4. 动态改变程序执行流(定位问题的辅助方式)

2 GDB的启动方式

  • 直接启动
    • gdb
    • gdb test.out
    • gdb test.out core
  • 动态连接
    • gdb test.out pid

3 应用示例一

book@100ask:~/ypd_dir$ gdb  //启动
(gdb) file app				//载入目标程序
(gdb) set args arg1 arg2 	//设置命令行参数
(gdb) run					//执行目标程序


book@100ask:~/ypd_dir$ gdb  //启动
(gdb) file app				//载入目标程序

等价于

book@100ask:~/ypd_dir$ gdb app

4 应用示例二

book@100ask:~/ypd_dir$ gdb  	//启动
(gdb) attach pid				//链接到目标进程,链接成功后,目标进程将停止执行
(gdb) continue					//恢复执行


book@100ask:~/ypd_dir$ gdb  
(gdb) attach pid				

等价于

book@100ask:~/ypd_dir$ gdb app pid

5 编程实验

book@100ask:~/ypd_dir/gdb$ ulimit -a			//查看Linux配置参数
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15407
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15407
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited


book@100ask:~/ypd_dir/gdb$ ulimit -c unlimited		//设置程序崩溃时,输出core文件

//func.c

#include <stdio.h>

int* g_pointer;

void func()
{
    *g_pointer = (int)"Hello World.";

    return;
}


//test.c

#include <stdio.h>
#include <unistd.h>

extern int* g_pointer;
extern void func();

void test_1()
{
    printf("test_1():%p\n",test_1);
}

void test_2()
{
    printf("test_2():%p\n",test_2);
}

void test_3()
{
    printf("test_3():%p\n",test_3);
}

int main(int argc, char *argv[])
{
    typedef void (TFunc)();
    TFunc* fa[] = {test_1, test_2, test_3};
    int i = 0;

    printf("main():begin...\n");

    for(int i = 0; i < argc; i++)
    {
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    for(int i = 0; i < 100; i++)
    {
        fa[i%3]();
        sleep(argc > 1);
    }

    printf("g_pointer = %p\n", g_pointer);

    func();

    printf("main():end...\n");

    return 0;
}


6 使用GDB进行断点调试

6.1 断点类型

  1. 软件断点:由非法指令异常实现(软件实现)

    ——适用于在内存中运行的程序

  2. 硬件断点:由硬件特性实现(数量有限)

    ——适用于直接在flash中运行的程序

  3. 数据断点:由硬件特性实现(数量有限)

    ——用于监视一段内存,如果该段内存被写,或被读,程序执行会立即停止

6.2 软件断点的相关操作

  • 通过函数名设置断点
    • break func_name [ if var = value]——永久断点
    • tbreak func_name [ if var = value]——一次性断点
  • 通过文件名+行号设置断点
    • break file_name:line_num [ if var = value]——永久断点
    • tbreak file_name:line_num [ if var = value]——一次性断点
操作命令
断点查看info breakpoints
断点删除delete 1 2 n
delete breakpoints
断点状态改变enable 1 2 n
enable breakpoints
disable 1 2 n
disable breakpoints

6.3 调试时的常用操作

操作命令
变量查看print name
变量设置set var name=value
执行下一行代码next
连续执行n行代码next n
执行进入函数step
强制当前函数返回return [value]
运行至当前函数返回finish
执行至目标行until line
跳转执行jump line

6.4 硬件断点及其应用

  • 代码位于只读存储器(flash)时,只能通过硬件断点调试
  • 硬件断点需要硬件支持,数量有限
  • GDB中通过hbreak命令支持硬件断点
  • hbreakbreak使用方式完全一致

7 编程实验

——使用GDB进行断点调试

8 小结

  • GDB是GNU项目中的调试器,能够跟踪或改变程序的执行
  • GDB能够根据Core Dump回溯检查导致程序异常结束的原因
  • GDB同时支持软件断点,硬件断点和数据断点
  • GDB是嵌入式开发中必须掌握的重要工具

调试利器GDB(下)

1 数据断点

  • GDB中支持数据断点的设置
  • watch命令用于监视变量是否被改变(本质为硬件断点)
  • watch命令的用法:watch var_name

2 GDB中内存查看

  • GDB中可以检查任意内存区域中的数据
  • 命令语法:x /Nuf expression
    • N:需要打印的单元数
    • u:每个单元的大小
    • f:数据打印的格式

例如:x /4bx 0x84a024

  • x命令中参数u对应的单位
格式打印方式
b单字节
h双字节
w四字节
g八字节
  • GDB中打印格式
格式打印方式
x十六进制
d有符号十进制
u无符号十进制
o八进制
t二进制
a地址
c字符
f浮点数

3 示例:判断系统大小端


(gdb) set var i = 1
(gdb) print i
$2 = 1
(gdb) print /a &i
$3 = 0x7fffffffe34c
(gdb) x /4bx 0x7fffffffe34c
0x7fffffffe34c: 0x01    0x00    0x00    0x00
(gdb) x /1bx 0x7fffffffe34c
0x7fffffffe34c: 0x01

4 编程实验

​ ——数据断点和内存查看

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int g_var = 0;

void* thread_func(void* args)
{
    sleep(5);
    
    g_var = 1;
}

int main()
{
    int i = 0;
    pthread_t tid = 0;
    
    pthread_create(&tid, NULL, thread_func, NULL);
    
    for(int i = 0; i < 10; i++)
    {
        printf("g_var = %d\n", g_var);
        sleep(1);
    }
}

$ gdb a.out
(gdb) start
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) watch g_var
Hardware watchpoint 2: g_var
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
2       hw watchpoint  keep y                      g_var
(gdb) continue
Continuing.
[New Thread 0x7ffff77c2700 (LWP 34213)]
g_var = 0
g_var = 0
g_var = 0
g_var = 0
g_var = 0
[Switching to Thread 0x7ffff77c2700 (LWP 34213)]

Thread 2 "a.out" hit Hardware watchpoint 2: g_var

Old value = 0
New value = 1
thread_func (args=0x0) at watch.c:12
12      }
(gdb) print g_var
$1 = 1
(gdb)
$2 = 1
(gdb) print /a &g_var
$3 = 0x555555755014 <g_var>
(gdb) x /4bx 0x555555755014
0x555555755014 <g_var>: 0x01    0x00    0x00    0x00
(gdb) continue
Continuing.
g_var = 1
[Thread 0x7ffff77c2700 (LWP 34213) exited]
g_var = 1
g_var = 1
g_var = 1
g_var = 1
[Inferior 1 (process 34208) exited normally]

5 函数调用栈的查看(backtraceframe

  • backtrace
    • 查看函数调用的顺序(函数调用栈的信息)
  • frame N
    • 切换到栈编号为N的上下文
  • info frame
    • 查看当前函数调用的栈帧信息

6 深入info命令

命令功能说明
info registers查看当前寄存器的值
info args查看当前函数参数的值
info locals查看当前局部变量的值
info frame查看当前栈帧的详细信息
info variables查看程序中的变量符号
info functions查看程序中的函数符号

7 栈帧信息

image-20221130000324158

8 编程实验

#include <stdio.h>

int sum(int n)
{
    int ret = 0;
    
    if(n > 0)
    {
        ret = n + sum(n);
    }
    
    return ret;
}

int main()
{
    int s = 0;
    
    s = sum(10);
    
    printf("sum = %d\n", s);
    
    return 0;
}

$ gdb a.out——调试a.out
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) start——开始调试
Temporary breakpoint 1 at 0x686: file sum.c, line 17.
Starting program: /home/book/ypd_dir/gdb/a.out

Temporary breakpoint 1, main () at sum.c:17
17          int s = 0;
(gdb) info breakpoints——查看下有没有断点信息
No breakpoints or watchpoints.
(gdb) break sum if n == 0——在函数sum中,如果n=0,设置一个断点
Breakpoint 2 at 0x555555554655: file sum.c, line 5.
(gdb) info breakpoints——查看断点信息
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000555555554655 in sum at sum.c:5
        stop only if n == 0
(gdb) continue——运行至断点处
Continuing.

Breakpoint 2, sum (n=0) at sum.c:5
5           int ret = 0;
(gdb) backtrace——查看函数调用的顺序
#0  sum (n=0) at sum.c:5
#1  0x000055555555466f in sum (n=1) at sum.c:9
#2  0x000055555555466f in sum (n=2) at sum.c:9
#3  0x000055555555466f in sum (n=3) at sum.c:9
#4  0x000055555555466f in sum (n=4) at sum.c:9
#5  0x000055555555466f in sum (n=5) at sum.c:9
#6  0x000055555555466f in sum (n=6) at sum.c:9
#7  0x000055555555466f in sum (n=7) at sum.c:9
#8  0x000055555555466f in sum (n=8) at sum.c:9
#9  0x000055555555466f in sum (n=9) at sum.c:9
#10 0x000055555555466f in sum (n=10) at sum.c:9
#11 0x0000555555554697 in main () at sum.c:19
(gdb) next
7           if(n > 0)
(gdb) next
12          return ret;
(gdb) info args——查看当前函数参数的值
n = 0
(gdb) frame 7——切换到栈编号为7的上下文
#7  0x000055555555466f in sum (n=7) at sum.c:9
9               ret = n + sum(n-1);
(gdb) info args
n = 7
(gdb) info locals——查看当前局部变量的值
ret = 0
(gdb) frame 0——切换到栈编号为0的上下文
#0  sum (n=0) at sum.c:12
12          return ret;
(gdb) info locals
ret = 0
(gdb) frame 0
#0  sum (n=0) at sum.c:12
12          return ret;
(gdb) info registers——查看当前寄存器的值
rax            0x0      0
rbx            0x0      0
rcx            0x5555555546c0   93824992233152
rdx            0x7fffffffe468   140737488348264
rsi            0x7fffffffe458   140737488348248
rdi            0x0      0
rbp            0x7fffffffe170   0x7fffffffe170
rsp            0x7fffffffe150   0x7fffffffe150
r8             0x7ffff7dced80   140737351839104
r9             0x7ffff7dced80   140737351839104
r10            0x0      0
r11            0x1      1
r12            0x555555554540   93824992232768
r13            0x7fffffffe450   140737488348240
r14            0x0      0
r15            0x0      0
rip            0x555555554679   0x555555554679 <sum+47>
eflags         0x246    [ PF ZF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
(gdb) info frame——查看当前栈帧的详细信息
Stack level 0, frame at 0x7fffffffe180:
 rip = 0x555555554679 in sum (sum.c:12); saved rip = 0x55555555466f
 called by frame at 0x7fffffffe1b0
 source language c.
 Arglist at 0x7fffffffe170, args: n=0
 Locals at 0x7fffffffe170, Previous frame's sp is 0x7fffffffe180
 Saved registers:
  rbp at 0x7fffffffe170, rip at 0x7fffffffe178
(gdb) x /1wx 0x7fffffffe170
0x7fffffffe170: 0xffffe1a0
(gdb) next
13      }
(gdb) next
12          return ret;
(gdb) info args
n = 1
(gdb) info registers
rax            0x1      1
rbx            0x0      0
rcx            0x5555555546c0   93824992233152
rdx            0x0      0
rsi            0x7fffffffe458   140737488348248
rdi            0x0      0
rbp            0x7fffffffe1a0   0x7fffffffe1a0
rsp            0x7fffffffe180   0x7fffffffe180
r8             0x7ffff7dced80   140737351839104
r9             0x7ffff7dced80   140737351839104
r10            0x0      0
r11            0x1      1
r12            0x555555554540   93824992232768
r13            0x7fffffffe450   140737488348240
r14            0x0      0
r15            0x0      0
rip            0x555555554679   0x555555554679 <sum+47>
eflags         0x202    [ IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
(gdb)

注意:回顾《C进阶:函数调用过程活动记录变化》

9 一些调试中的小技巧

操作命令
断点处自动打印display /f expression
undisplay
查看程序中的符号whatis
ptype
GDB中的代码查看list
set listsize N
GDB中的shell操作shell

9.1 技巧示例

​ ——断点处自动打印

(gdb) display /d i  —— 此处为断点1处,未运行至断点2处,故无法显示i
No symbol "i" in current context.
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
(gdb) display /d i  ——断点处自动打印i
1: /d i = 0
(gdb) display /d i * i ——断点处自动打印i*i
2: /d i * i = 0
(gdb) display /a &i
3: /a &i = 0x7fffffffe32c
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 1
2: /d i * i = 1
3: /a &i = 0x7fffffffe32c

9.2 技巧示例

​ ——符号查看

(gdb) whatis g_var
type = int
(gdb) ptype g_var
type = int
(gdb) whatis struct ST
type = struct ST
(gdb) ptype struct ST
type = struct ST {
    int i;
    int j;
}

#include <stdio.h>

int g_var = 1;

struct ST
{
    int i;
    int j;
};

int func()
{
    struct ST st[5] = {0};
    int i = 0;
    
    for(i = 0; i < 5; i++)
    {
        st[i].i = i;
        st[i].j = i * i;
    }
    
    for(i = 0; i < 5; i++)
    {
        printf("st[%d].i = %d\n", i, st[i].i);
        printf("st[%d].j = %d\n", i, st[i].j);        
    }
}

int main()
{
    static c_var = 2;
    
    func();
    
    return 0;
}


(gdb) file  a.out
Reading symbols from a.out...done.
(gdb) start
Temporary breakpoint 1 at 0x78d: file trick.c, line 33.
Starting program: /home/book/ypd_dir/gdb/a.out

Temporary breakpoint 1, main () at trick.c:33
33          func();
(gdb) break trick.c:18
Breakpoint 2 at 0x5555555546f9: file trick.c, line 18.
(gdb) list trick.c:18
13          struct ST st[5] = {0};
14          int i = 0;
15
16          for(i = 0; i < 5; i++)
17          {
18              st[i].i = i;
19              st[i].j = i * i;
20          }
21
22          for(i = 0; i < 5; i++)
(gdb) set listsize 20
(gdb) show listsize
Number of source lines gdb will list by default is 20.
(gdb) list trick.c:18
8           int j;
9       };
10
11      int func()
12      {
13          struct ST st[5] = {0};
14          int i = 0;
15
16          for(i = 0; i < 5; i++)
17          {
18              st[i].i = i;
19              st[i].j = i * i;
20          }
21
22          for(i = 0; i < 5; i++)
23          {
24              printf("st[%d].i = %d\n", i, st[i].i);
25              printf("st[%d].j = %d\n", i, st[i].j);
26          }
27      }
(gdb) display /d i  —— 此处为断点1处,未运行至断点2处,故无法显示i
No symbol "i" in current context.
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
(gdb) display /d i  ——断点处自动打印i
1: /d i = 0
(gdb) display /d i * i ——断点处自动打印i*i
2: /d i * i = 0
(gdb) display /a &i
3: /a &i = 0x7fffffffe32c
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 1
2: /d i * i = 1
3: /a &i = 0x7fffffffe32c
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 2
2: /d i * i = 4
3: /a &i = 0x7fffffffe32c
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 3
2: /d i * i = 9
3: /a &i = 0x7fffffffe32c
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 4
2: /d i * i = 16
3: /a &i = 0x7fffffffe32c
(gdb)
Continuing.
st[0].i = 0
st[0].j = 0
st[1].i = 1
st[1].j = 1
st[2].i = 2
st[2].j = 4
st[3].i = 3
st[3].j = 9
st[4].i = 4
st[4].j = 16
[Inferior 1 (process 39800) exited normally]
(gdb) run
Starting program: /home/book/ypd_dir/gdb/a.out

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 0
2: /d i * i = 0
3: /a &i = 0x7fffffffe32c
(gdb) undisplay 3  ——取消断点处自动打印&i
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) n
Program not restarted.
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 1
2: /d i * i = 1
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 2
2: /d i * i = 4
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 3
2: /d i * i = 9
(gdb) continue
Continuing.

Breakpoint 2, func () at trick.c:18
18              st[i].i = i;
1: /d i = 4
2: /d i * i = 16
(gdb) continue
Continuing.
st[0].i = 0
st[0].j = 0
st[1].i = 1
st[1].j = 1
st[2].i = 2
st[2].j = 4
st[3].i = 3
st[3].j = 9
st[4].i = 4
st[4].j = 16
[Inferior 1 (process 39806) exited normally]
(gdb) whatis g_var
type = int
(gdb) ptype g_var
type = int
(gdb) whatis c_var
No symbol "c_var" in current context.
(gdb) whatis i
No symbol "i" in current context.
(gdb) whatis struct ST
type = struct ST
(gdb) ptype struct ST
type = struct ST {
    int i;
    int j;
}

10 小结

  • GDB支持数据断点的设置(一种类型的硬件断点)
  • watch用于监视变量是否被改变,x用于查看内存中的数据
  • GDB支持函数调用栈的查看(backtrace,info frame)
  • GDB支持运行时对程序中的符号进行查看(whatis,ptype)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值