gdb学习篇

操作教程(要敲一遍):https://www.ibm.com/developerworks/cn/linux/sdk/gdb/index.html

连载系列:https://developer.aliyun.com/article/681075

目录

一、Gdb介绍

1. 背景及用处

2. gdb启动

二、常用命令

1. breakpoint/watchpoint

2. 程序执行控制

3. 打印显示

4. info显示信息

5. 线程

6. 分割窗口

7. 其他

三、调试coredump问题

1. coredump介绍

2. gdb调试core文件


一、Gdb介绍

1. 背景及用处

GDB是一个由GNU开源组织UNIX/LINUX操作系统下的基于命令行的、功能强大的程序调试工具。

GDB主要帮助完成下面4个方面的功能:

1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。(比如给变量赋值)

2、可以让被调试的程序在你所指定的位置的断点处停住。

3、当程序被停住时,可以检测此时你的程序中所发生的事。

4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其它BUG。

根据程序执行时的出错现象假设错误原因,然后在代码中找到适当的位置,构造错误现象触发的环境,进入gdb环境添加断点,执行程序并分析,通过程序执行过程以及变量的值确认错误原因。

2. gdb启动

开始调试之前,必须用程序中的调试信息编译要调试的程序。这样,gdb 才能够调试所使用的变量、代码行和函数。如果要进行编译,请在 gcc(或 g++)下使用额外的 '-g' 选项来编译程序

g++ -g hello.cpp -o hello

在 shell 中,可以使用 'gdb' 命令并指定程序名作为参数来运行 gdb,例如 'gdb eg';或者在 gdb 中,可以使用 file 命令来装入要调试的程序,例如 'file eg'。

1.调试可执行文件:

gdb

program也就是你的执行文件,一般在当前目录下。

2.调试core文件(core是程序非法执行后core dump后产生的文件):

$gdb

$gdb program core.11127

3. 调试服务程序:

$gdb

$gdb hello 11127

如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。

GDB启动时,可以加上一些GDB的启动开关,详细的开关可以用gdb -help查看。我在下面只例举一些比较常用的参数:   

    -symbols 
    -s 
    从指定文件中读取符号表。

    -se file
    从指定文件中读取符号表信息,并把他用在可执行文件中。

    -core 
    -c 
    调试时core dump的core文件。

    -directory 
    -d 
    加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。

二、常用命令

gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来

1. breakpoint/watchpoint

break(b:添加断点,可对函数添加断点外,也可对当前函数中的行号添加断点。

  • break ,在指定的函数入口处设置断点
  • break ,在指定的行号处设置断点
  • break *
    ,在指定地址对应的代码处(指令处)设置断点, 在了解并查看汇编位置后再下断点。
  • break + / break -,在当前行的下offset行/上offset行,设置断点
  • break if :设置条件
  • condition :代替在断点设置条件

delete :删除断点编号所对应的断点,编号通过info breakpoints查看。,不指定就是删除所有断点。

enable :使能之前加过的断点,可以指定num,不指定就是对所有断点都使能。默认断点加入后就是使能状态

disable :去使能之前加过的断点

info breakpoint:查看断点信息

clear 行号n:清除第n行的断点,不加行号清空所有断点信息

 

watch:监视某块内存,当内存被改变时触发.一般用来观察某个表达式(变量也是一种表达式)的值是否有变化。相当于,在每次该表达式值变化的地方,都设置一个断点。我们需要使用continue命令前进。用法类似break。

whatis :查询变量或函数

2. 程序执行控制

start:开始执行程序,停在main函数第一行语句前等待命令

run(r:执行程序

continue(c):继续执行,直到下一个断点或者程序结束

  • continue :忽略ignore-count个断点

 

next(n:下一步(逐过程),如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)

  • next ,执行后面的count条语句(不进入函数),然后再停住

step(s:下一步(逐语句)。如果该语句为函数调用,则进入函数执行其中第一条语句

  • step ,执行后面的count条语句,然后再停住

 

finish(fin:执行到退出当前函数,并打印函数返回时的堆栈地址和返回值及参数值等信息。

until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。

until 行号: 运行至某行,不仅仅用来跳出循环。

quit(q)或者ctrl+d:退出gdb调试环境

call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)

3. 打印显示

printf(p变量名:打印变量的值

  • print / ,将指定表达式的值以f格式输出
  • f的格式包括:

x 按十六进制格式显示变量。

d 按十进制格式显示变量。

u 按十六进制格式显示无符号整型。

o 按八进制格式显示变量。

t 按二进制格式显示变量。

a 按十六进制格式显示变量。

c 按字符格式显示变量。

f 按浮点数格式显示变量。

set var变量名=:修改某个变量的值(p 变量名=  也可以修改)

set args 参数:指定运行时的参数

show args:显示运行时的参数

display(disp:跟踪查看某个变量,每次停下来都显示它的值

 

list(l:显示源代码,,接着上次的位置往下列,每次列十行,可以自己接上一个list再回车继续向后列出list -,向前显示源程序.

  • list 函数名:可以用来显示某个函数在文件夹中的行号
  • list 行号:这个命令我在其他一些资料里是列出从第几行开始的源代码,但事实上我在使用时是显示该行号作为中间值的十行。

4. info显示信息

info<> 显示各类信息,详情查看“help info”,例如info breakpoint 查看所有断点

  • info threads:查看当前进程的线程都在忙啥
  • info symbol 0xffff888812345678:用来看某个地址的栈信息,比如断点打出的堆栈信息
  • info registers:查看当前各寄存器信息
  • info locals:显示当前堆栈页的所有变量
  • 查询运行信息

5. 线程

thread 线程id:进入某个线程

b XXXX thread xxx :为某个线程设置断点函数(确认线程id:对进程加断点,当某个线程调度到后使用info threads,线程id前有*的就是当前线程)

detach:可以直接取消当前挂在的pid

attch pid:不用退出直接再挂接其它进程,最好先detach上一个进程

thread apply all bt:打开所有线程的堆栈信息

  • thread apply threadID bt:查看指定线程堆栈信息

6. 分割窗口

layout:用于分割窗口,可以一边查看代码,一边测试:

layout src:显示源代码窗口

layout asm:显示反汇编窗口

layout regs:显示源代码/反汇编和CPU寄存器窗口

layout split:显示源代码和反汇编窗口

Ctrl + L:刷新窗口

7. 其他

examine(x)/nfu 用于打印某个地址所指向的内存中的内容。n f u也可以省略。

  • n:表示要显示的内存单元个数。取值为:1 2 3...
  • f:表示显示方式, 可取如下值:x d u o t c f i(指令地址格式)
  • u:表示一个地址单元的长度,与n一起表示显示的地址长度。取值如下:b 表示单字节,h 表示双字节,w 表示四字节,g 表示八字节

 

shell [commandline]:在不退出调试环境的情况下使用 shell 命令

make :可以在gdb中执行make命令来重新build自己的程序。这个命令等价于“shell make ”。

 

backtrace(bt:查看当前堆栈信息,info stack一样。

up/down :改变堆栈显示的深度

disassemble:查看当前函数的反汇编代码。

help :来查看命令的帮助

 

三、调试coredump问题

1. coredump介绍

在 gdb 下运行程序可以使俘获错误变得更容易,但在调试器外运行的程序通常会中止而只留下一个 core 文件。gdb 可以装入 core 文件,并让您检查程序中止之前的状态。

Linux下core开关设置:https://www.cnblogs.com/lee-qi/p/12422235.html

Coredump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里。

该文件也是二进制文件,可以使用gdb、elfdump、objdump或者windows下的windebug、solaris下的mdb进行打开分析里面的具体内容。

2. gdb调试core文件

1. gdb打开core文件的命令:

gdb程序名(包含路径) core*(core文件名和路径)

gdb打开core文件时,显示没有调试信息,是因为之前编译的时候没有带上-g选项,没有调试信息是正常的,实际上它也不影响调试core文件。因为调试core文件时,符号信息都来自符号表,用不到调试信息。

 

2. 查看coredump时的堆栈:

bt或者where命令

在带上调试信息的情况下,我们实际上是可以看到core的地方和代码行的匹配位置。没有调试信息的情况下,打开coredump堆栈,并不会直接显示core的代码行。

 

3. 没有调试信息的情况下找core的代码行

frame addr(帧数):跳到堆栈的第几帧,f 1跳转到core堆栈的第1帧。

disassemble:打开该帧函数的反汇编代码。

shell echo free@plt |c++filt 去掉函数的名词修饰。我们可以推测到这里是在调用free函数。如此,我们就能知道我们coredump的位置,从而进一步能推断出coredump的原因。

4. info frame查看堆栈寄存器信息。

具体可参照(后面有几个例子):https://blog.csdn.net/u014403008/article/details/54174109

 

除了调试 core 文件或程序之外,gdb 还可以连接到已经运行的进程(它的程序已经过编译,并加入了调试信息),并中断该进程。

启动 gdb 并指定进程标识,在我举的这个例子中是 'gdb eg2 1283'。gdb 会查找一个叫作 "1283" 的 core 文件。如果没有找到,那么只要进程 1283 正在运行(在本例中可能在 sleep() 中),gdb 就会连接并中断该进程。

此时,可以发出所有常用 gdb 命令。可以使用 'backtrace' 来查看当前位置与 main() 的相对关系,以及 main() 的帧号是什么,然后切换到 main() 所在的帧,查看已经在 "for" 循环中运行了多少次:

(gdb) backtrace
#0  0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
#1  0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:78
#2  0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
(gdb) frame 2
#2  0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
7           sleep(1);
(gdb) print i
$1 = 50

还可以首先使用 'file eg2' 装入文件,然后发出 'attach 1283' 命令连接到进程标识 1283 下的 eg2。如果已经完成了对程序的修改,可以 'detach' 命令继续执行程序,或者 'kill' 命令杀死进程

你可能感兴趣的:(工具)