文章目录
调试利器GDB(上)
1 GDB的常规应用
-
自定义程序的启动方式(指定影响程序运行的参数)
-
设置条件断点(在条件满足时暂停程序的执行)
-
回溯检查导致程序异常结束的原因(Core Dump)
-
动态改变程序执行流(定位问题的辅助方式)
2 GDB的启动方式
- 直接启动
gdbgdb test.outgdb 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 断点类型
-
软件断点:由非法指令异常实现(软件实现)
——适用于在内存中运行的程序
-
硬件断点:由硬件特性实现(数量有限)
——适用于直接在flash中运行的程序
-
数据断点:由硬件特性实现(数量有限)
——用于监视一段内存,如果该段内存被写,或被读,程序执行会立即停止
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 ndelete breakpoints |
| 断点状态改变 | enable 1 2 nenable breakpointsdisable 1 2 ndisable 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命令支持硬件断点 hbreak与break使用方式完全一致
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 函数调用栈的查看(backtrace和frame)
backtrace- 查看函数调用的顺序(函数调用栈的信息)
frame N- 切换到栈编号为N的上下文
info frame- 查看当前函数调用的栈帧信息
6 深入info命令
| 命令 | 功能说明 |
|---|---|
info registers | 查看当前寄存器的值 |
info args | 查看当前函数参数的值 |
info locals | 查看当前局部变量的值 |
info frame | 查看当前栈帧的详细信息 |
info variables | 查看程序中的变量符号 |
info functions | 查看程序中的函数符号 |
7 栈帧信息

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 expressionundisplay |
| 查看程序中的符号 | whatisptype |
| GDB中的代码查看 | listset 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)
本文介绍了GDB作为调试利器的常规应用,包括自定义启动方式、条件断点和CoreDump分析。详细讲解了GDB的启动方式、软件和硬件断点的设置与管理,以及数据断点和内存查看的方法。此外,还通过实例展示了如何利用GDB进行函数调用栈的查看和深入的`info`命令使用,最后分享了一些调试技巧和编程实验。
191

被折叠的 条评论
为什么被折叠?



