一,GDB调试器简介
GDB是Linux下的常用调试器,主要用于调试源代码、调试运行中的进程和查看core dump文件。
Linux下的调试器主要有gdb、cgdb、ddd、eclipse。GDB调试器的运行速度快、能够进行源代码同步显示。
使用 -tui 选项开启gdb命令输入和源代码的分屏显示,tui即Terminal User Interface。
二,GDB常用调试命令
a)
调试可执行文件
以源代码为/home/zebra/目录下的test.c文件产生的可执行文件test为例(可执行文件使用gcc进行编译,并使用-g选项以产生调试信息),进行命令的说明(详细源代码参见第三部分:三,调试实例分析
)。
gdb调试源代码流程:
1,开启gdb程序,即运行如下命令: gdb -q (-q用以使得gdb不输出gdb程序的版本等信息)
2,指定调试的二进制文件:file test
3,list查看源代码
4,设定断点 breakpoint main
breakpoint sub
上述分别在main函数和sub函数处设定了断点。
断点可以设置在任意已知源代码文件的某一行,某一个函数,同时可以指定是设置在哪个/哪些线程上(见下边描述)。
5,运行可执行文件:
run
6,这样程序会运行到断点处停止下来,gdb会打印当前断点的信息。
7,使用s 或者 n进行单步调试。
s即step,n即next都是执行一条语句,然后停下来。
如果想执行一条汇编语句,则可以使用 si ,ni,即step instruction,next instruction。
8,bt命令查看当前的调用栈,bt即backtrace。
9,info frame查看函数帧信息。
10,frame n 进入某个函数帧(编号为n)
11,info 命令可以对当前的函数帧的寄存器、局部变量、函数的参数进行查看。
info register;info local;info args。
12,disassemble对当前函数对应的二进制进行反汇编。
13,x/nfu address 查看内存其中 address是内存开始的地址,从该地址向高地址增加,
x是examinate的缩写,n表示重复次数,f表示输出格式,u表示内存大小的单位(默认是字,即4个字节)。
一般我都用 x/nx address,即打印n个从address开始的内存,每个是4字节,以十六进制打印。
14,continue,执行至该函数退出
15,info threads,显示当前可调试的所有线程
16,thread <ID>,切换当前调试的线程为指定ID的线程break File:LineNumber thread <ID|ALL> if x==y。
17,thread apply <ID1 ID2...|ALL> command让一个/多个/所有线程执行GDB的命令command。
18,set scheduler-locking off|on|step,设定线程运行的锁定模式,
off表示各个线程同时进行,On表示仅当前线程可运行,step则是在执行step命令单步执行的时候,其他线程不运行。
19,q 退出调试。
20, rbreak . 在所有函数上加上断点。
rbreak REGEX
这个命令可以在所有满足正则表达式REGEX的函数上设置断点。
命令中使用的正则表达式和grep中使用的类似。
当调试C++程序的时候,rbreak用在给不属于任何class的重载函数设置断点很方便。
rbreak可以直接向一个程序的所有函数设置断点,比如:
(gdb) rbreak .
rbreak FILE:REGEX
设置断点到一个文件中的符合该正则表达式的函数上,例如:
(gdb) rbreak file.c:. 在file.c的所有函数上设置断点。
21, thread apply all backtrace 查看所有线程调用栈。
b)
调试正在运行的进程
gdb可用以调试正在运行的进程。只需要知道进程的进程号。
gdb调试进程流程:
1,gdb -p PID指定调试的进程ID号。
或者先进入gdb再指定: gdb
attach PID。
2,bt查看当前进程执行的调用栈
3,info threads查看当前可调试的线程。
其他和上述a)类似。
另:
Gdb package中包含 /usr/bin/gstack命令(依赖gdb)取得对应PID的栈信息。
Usage: gstack <process-id>
c)查看core dump文件
一些信号的默认行为是导致一个进程终止并且产生一个core dump文件。core dump文件包含该进程的终止时内存映像的信息。导致进程产生core dump的信号可以在signal(7)中查阅到。
Unix定义的产生core dump文件的信号列表如下:
名字 |
说明 |
缺省动作 |
SIGABRT |
异常终止(abort) |
终止w/core
|
SIGBUS |
硬件故障 |
终止w/core |
SIGEMT |
硬件故障 |
终止w/core |
SIGFPE |
算术异常 |
终止w/core |
SIGILL |
非法硬件指令 |
终止w/core |
SIGIOT |
硬件故障 |
终止w/core |
SIGQUIT |
终端退出符 |
终止w/core |
SIGSEGV |
无效存储访问 |
终止w/core |
SIGSYS |
无效系统调用 |
终止w/core |
SIGTRAP |
硬件故障 |
终止w/core |
SIGXCPU |
超过
CPU限制
|
终止w/core |
SIGXFSZ |
超过
文件
长
度限制
|
终止w/core |
软件容易产生的引发core dump的信号有SIGABRT,SIGFPE,SIGSEGV。
产生core dump后,我们使用gdb进行分析。流程如下:
1,gdb -c core文件名称 /home/zebra/test (二进制文件名)
2,bt查看调用栈以便获取程序发生core dump时执行的函数
3,其他与 a)类似
三,调试实例分析
下边举个调试的例子,该例子是一个由于非法访问内存,将会产生core文件的简单程序。
程序使用如下命令编译: gcc -g -o test test.c,这样便产生了程序test。
其源代码test.c位于/root/zebra下,源代码为(在gdb里使用list命令进行查看):
(gdb) list
1 /* it will result in SIGSEGV */
2 int sub(int a,int b,int c){
3
4 *(int *)a=16;
5 return 0;
6 }
7
8 int main()
9 {
10 int a=0;
(gdb)
11 int b=1;
12 int c=2;
13 sub(a,b,c);
14 return 0;
15 }
16
a)调试源代码
指令: 输出和意义:
1,gdb -q 进入调试器,-q表示不打印gdb的版本等信息
2,file test 加载调试的程序
3,b main 在main函数处设置断点
4,b sub 在sub函数处设置断点
5,r Breakpoint 1, main () at test.c:10
10 int a=0;
运行至第一个断点,即main函数处,此时第10行的int a=0尚未执行
6,bt (gdb) bt
#0 main () at test.c:10
此时我们查看一下函数调用栈,发现调用栈中只有一个函数,即main。
7,info args (gdb) info args
No arguments.
发现没有参数(main函数没有参数)
8,info locals (gdb) info locals
a = 32767
b = 0
c = 0
查看当前的局部变量信息,发现a、b、c的值都是随机的,因为赋值语句尚未执行。
9,info register
rbp 0x7fffffffe350 0x7fffffffe350
rsp 0x7fffffffe340 0x7fffffffe340
rip 0x400513 0x400513 <main+8>
cs 0xe033 57395
上述是一部分当前的寄存器信息。
10,info f 0
(gdb) info f 0
Stack frame at 0x7fffffffe360:
rip = 0x400513 in main (test.c:10); saved rip 0x7ffff7a9ebc6
source language c.
Arglist at 0x7fffffffe350, args:
Locals at 0x7fffffffe350, Previous frame's sp is 0x7fffffffe360
Saved registers:
rbp at 0x7fffffffe350, rip at 0x7fffffffe358
打印此时的该函数帧的信息。
11,s 11 int b=1;
单步执行到int b=1(该句尚未执行)
12,s
12 int c=2;
单步执行到int c=2(该句尚未执行)
13,s
(gdb) s
13 sub(a,b,c);
单步执行到sub(a,b,c)(该句尚未执行)
14,info locals a = 0
b = 1
c = 2
查看发现,a=0,b=1,c=2
15,bt (gdb) bt
#0 main () at test.c:13
还是只有main,因为行数到了sub(a,b,c)那一行,但是尚未跳转进入执行
16,s
Breakpoint 2, sub (a=0, b=1, c=2) at test.c:4
4 *(int *)a=16;
到达第二个断点,即进入sub(int a,int b.int c)函数,但是*(int *)a=16尚未执行
17,bt #0 sub (a=0, b=1, c=2) at test.c:4
#1 0x0000000000400536 in main () at test.c:13
当前已经有两个函数被调用
18,info args (gdb) info args
a = 0
b = 1
c = 2
发现有a=0,b=1,c=2 ,即函数获得了正确的参数。
19,info locals (gdb) info locals
No locals.
发现没有局部变量
20,info register
rbp 0x7fffffffe330 0x7fffffffe330
rsp 0x7fffffffe330 0x7fffffffe330
rip 0x4004f9 0x4004f9 <sub+13>
cs 0xe033 57395
查看当前的寄存器信息
21,s
Program received signal SIGSEGV, Segmentation fault.
0x00000000004004fe in sub (a=0, b=1, c=2) at test.c:4
4 *(int *)a=16;
此时显示程序接收到信号SIGSEGV,Segmentation fault。
22,disassemble
0x00000000004004ec <sub+0>: push %rbp
0x00000000004004ed <sub+1>: mov %rsp,%rbp
0x00000000004004f0 <sub+4>: mov %edi,-0x4(%rbp)
0x00000000004004f3 <sub+7>: mov %esi,-0x8(%rbp)
0x00000000004004f6 <sub+10>: mov %edx,-0xc(%rbp)
0x00000000004004f9 <sub+13>: mov -0x4(%rbp),%eax
0x00000000004004fc <sub+16>: cltq
0x00000000004004fe <sub+18>: movl $0x10,(%rax)
0x0000000000400504 <sub+24>: mov $0x0,%eax
0x0000000000400509 <sub+29>: leaveq
0x000000000040050a <sub+30>: retq
查看汇编代码,发现有对应赋值语句,
使用的是rax寄存器存储值a,即此时rax=0,然后寄存器间接寻址,
指向该寄存器值的内存处即0处,写入数值16,发生core dump。
为了便于整体分析,顺便反汇编下main函数:
disassemble main
0x000000000040050b <main+0>: push %rbp
0x000000000040050c <main+1>: mov %rsp,%rbp
0x000000000040050f <main+4>: sub $0x10,%rsp
0x0000000000400513 <main+8>: movl $0x0,-0xc(%rbp)
0x000000000040051a <main+15>: movl $0x1,-0x8(%rbp)
0x0000000000400521 <main+22>: movl $0x2,-0x4(%rbp)
0x0000000000400528 <main+29>: mov -0x4(%rbp),%edx
0x000000000040052b <main+32>: mov -0x8(%rbp),%esi
0x000000000040052e <main+35>: mov -0xc(%rbp),%edi
0x0000000000400531 <main+38>: callq 0x4004ec <sub>
0x0000000000400536 <main+43>: mov $0x0,%eax
0x000000000040053b <main+48>: leaveq
0x000000000040053c <main+49>: retq
顺便看下当前(运行到接受到SIGSEGV,程序尚未处理该信号)的栈区的信息:
(gdb) x/24x 0x7fffffffe320
0x7fffffffe320: 0xf7ffa720 0x00000002 0x00000001 0x00000000
0x7fffffffe330: 0xffffe350 0x00007fff 0x00400536 0x00000000
0x7fffffffe340: 0xffffe420 0x00000000 0x00000001 0x00000002
0x7fffffffe350: 0x00000000 0x00000000 0xf7a9ebc6 0x00007fff
0x7fffffffe360: 0x00000000 0x00000000 0xffffe428 0x00007fff
0x7fffffffe370: 0x00000001 0x00000001 0x0040050b 0x00000000
b)查看core dump文件
假设运行程序test,产生core dump文件名为core,且与当前目录(core文件生成需要进行配置、包括其生成目录、生成文件名等)。
使用gdb进行查看:
1,gdb -c core test
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004004fe in sub (a=0, b=1, c=2) at test.c:4
4 *(int *)a=16;
查看产生core dump的原因:signal 11,Segmentation fault。
2,bt/where 查看
#0 0x00000000004004fe in sub (a=0, b=1, c=2) at test.c:4
#1 0x0000000000400536 in main () at test.c:13
查看在哪个函数调用时,产生的core dump,函数位于源文件哪一行。
3,disassemble 由于本程序很简单,查看汇编代码以定位错误。
四,本文结束语
gdb是linux平台下强大的debug工具。本文仅仅是菜鸟级入门教程,但是我相信越是菜鸟级越是对人有用。因为万事开头难,入门后,便是一片广阔天地,任你飞翔。
最后希望这篇文章对你有用。
分享到:
相关推荐
本文将基于"Linux菜鸟过关+Linux程序指南+Linux系统命令及使用详解"的主题,深入探讨Linux的基础知识、常用命令以及程序开发。 首先,我们要了解Linux的基本概念。Linux是一种自由开放源码的操作系统,其内核由...
7. **编译与调试**:了解如何使用编译器(如GCC或Clang)进行编译,以及使用调试工具(如GDB)来定位和修复程序中的错误。 在【c++ѧϰ.lnk】这个文件中,虽然名字看起来像是快捷方式,但很可能指向了具体的学习...
- 利用调试工具,如GDB,对ROS节点进行单步调试。 - 查看和修改ROS参数,以及监控话题和服务的状态。 - 利用图表示意图理解ROS系统结构,方便节点间的关系调整。 - 整合Git版本控制,便于团队协作。 "roboware-...
5. **GDB调试器**:GDB(GNU Debugger)是开源的调试工具,可以通过STM32的JTAG或SWD接口进行远程调试。开发者可以利用GDB服务器(如OpenOCD)与GDB客户端结合,实现对STM32的程序调试。 6. **Bootloader和固件更新...
最后,掌握使用ARM架构的开发环境和工具链,例如GCC编译器、GDB调试器以及ARM交叉编译器。 3. ARM开发环境: 常用的开发环境有Keil uVision、GNU Arm Embedded Toolchain和Eclipse集成开发环境(IDE)。这些工具...
在完成程序编写、编译无误后,使用GDB进行调试或通过IDE的内置功能进行调试。同时,你还需要了解STM32的启动流程、中断服务程序以及如何使用RTOS(实时操作系统)如FreeRTOS,以便于实现更复杂的任务调度。 最后,...
它集成了GCC编译器、GDB调试器和其他实用工具,为开发者提供了一个友好且高效的开发环境。KILE4支持代码自动完成、语法高亮、错误检查等功能,有助于提高编程效率和代码质量。 在这个毕业设计中,学生可能需要完成...
学习日志分析、使用`strace`和`gdb`进行调试、理解核心转储(core dump)等技术,能够有效地定位和解决问题。 总之,这份"Linux菜鸟专用资料"将引导新手逐步掌握Linux系统,从基础操作到进阶技能,为成为熟练的...
3. **编辑器、编译器GCC与调试器GDB**:掌握这些工具的使用方法,可以大大提高开发效率。 4. **项目管理工具Make与Makefile**:学习如何使用Make工具自动化构建项目,理解Makefile的工作原理。 5. **Shell脚本编写**...
Linux还提供了丰富的开发工具和库,如GCC编译器、Makefile的编写、GDB调试器等,这些都是软件开发中的必备技能。 总之,"Linux入门教程PPT"是一个全面且实用的学习资源,不仅适合初学者了解Linux基础,也对有经验的...
9. **调试技巧**:使用JLink、ST-Link等调试器进行硬件调试,掌握GDB调试器的使用,理解断点、变量查看、步进执行等功能。 10. **软件设计模式**:在大型项目中可能会运用到模块化、面向对象等设计原则,提高代码的...
如果具备硬件调试条件,还可以使用GDB调试器进行在线调试。 总结来说,使用VSCode编写51单片机的C程序是一个高效且灵活的方法。通过合适的扩展和配置,我们可以获得一个功能齐全的开发环境,同时享受到VSCode带来的...
9. **错误处理和调试**:学会使用调试工具(如GDB),编写异常处理代码,确保程序的健壮性。 10. **UI设计**:理解Qt中的布局管理,创建美观易用的用户界面,考虑不同屏幕尺寸和设备的适配。 11. **测试与优化**:...
10. **调试技巧**:掌握gdb等调试工具的使用,能有效地定位和解决程序中的问题。 《Unix环境高级编程》这本书深入浅出地讲解了这些主题,并辅以大量示例代码,是提升Unix编程技能的宝贵资源。通过学习和实践,你...
- **调试与性能分析**:介绍如何使用工具如perf、gdb等对内核进行调试和性能分析。 综上所述,《Linux内核设计与实现》第三版提供了全面而深入的内容,适合希望深入了解Linux内核的读者。无论你是想要从事操作系统...
它支持GDB调试器,提供断点设置、单步执行、变量查看、内存检查等功能,帮助开发者定位和修复程序中的错误。 6. **编译器集成**: CodeBlocks可以与多种编译器无缝集成,如GCC、MinGW等。用户无需手动配置编译环境...
学会查看日志文件(如/var/log下的各种日志),使用gdb调试程序,以及理解strace、lsof等工具,有助于诊断和解决问题。 对于有志于从事服务器管理的人员,熟悉Linux下的数据库(如MySQL、PostgreSQL)、Web服务器...
嵌入式系统编译、工程管理和调试主要包括GCC编译工具、Make工具管理软件工程、GDB调试和优化程序等几个方面。 嵌入式操作系统引导 嵌入式操作系统引导是指在嵌入式系统中引导操作系统的过程。它主要包括嵌入式LINUX...
同时,调试工具如GDB可以帮助定位和修复代码中的错误。 8. **用户手册** 虽然项目中未提及,但一个完善的学生成绩管理系统应该配有用户手册,指导用户如何安装、启动、操作以及解决常见问题。 9. **安全性与权限...