温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Linux系统中GDB是什么

发布时间:2022-01-30 11:00:04 来源:亿速云 阅读:419 作者:小新 栏目:开发技术

小编给大家分享一下Linux系统中GDB是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

GDB是 Linux 平台下最常用的一款程序调试器。Linux GDB调试是C/C++程序员必须掌握的技能。

Linux系统中GDB是什么

1、GDB简介:

GDB(GNU symbolic debugger)是 GNU Project 调试器,它使你可以查看另一个程序在“执行”期间正在执行的操作–或该程序崩溃时正在执行的操作。

GDB 可以做四种主要的事情(以及支持这些事情的其他事情)来帮助你捕获行为中的错误:

  • 启动你的程序,并指定可能影响其行为的所有内容。

  • 使程序在指定条件下停止。

  • 检查程序停止时发生的情况。

  • 更改程序中的内容,以便你可以尝试纠正一个错误的影响,然后继续学习另一个错误。

这些程序可能与GDB(本机)在同一台计算机上执行,在另一台计算机(远程)上或在模拟器上执行。GDB可以在大多数流行的UNIX和Microsoft Windows变体以及Mac OS X上运行。

2、GDB基本调试命令

2.1 准备

  通过 gcc 的 -g 选项,将调试信息加到可执行文件中。

 $ gcc -g hello.c -o hello1

  如果使用 Makefile 构建,一般要在 CFLAGS 中指定 -g 选项。

 CFLAGS := -Wall -O2 -g1

  注意,给 GCC 编译器加上优化选项后,实际的执行顺序可能由于优化而与源代码顺序不同,因此利用调试器跟踪运行时,有时会执行到莫名奇妙的地方,从而造成混乱。

2.2 启动

  使用命令 gdb 程序名启动。

2.3 设置断点

  可以在函数名和行号等上设置断点。程序到达断点就会自动暂停运行。此时可以查看该时刻的变量值,显示栈帧、重新设置断点或重新运行等。断点命令 break 可以简写为 b,命令为 break 。      断点可以通过函数名、当前文件内的行号来设置,也可以先指定文件名再指定行号,还可以指定与暂停位置的偏移量,或者用地址来设置。格式:

格式说明
break对当前正在执行的文件中的指定函数设置断点
break对当前正在执行的文件中的特定行设置断点
break 文件名:行号对指定文件的指定行设置断点,最常用的设置断点方式
break 文件名:函数名对指定文件的指定函数设置断点
break当前指令行+/-偏移量出设置断点
break指定地址处设置断点

     设置好的断点可以通过 info break 确认。

2.4 运行

  使用 run 参数 命令开始运行,其中参数为可执行程序的参数。如果设置了断点,会执行到设置了断点的位置后暂停运行。可以简写为 r。

2.5 显示栈帧

  backtrace 命令可以在遇到断点或异常而暂停执行时显示栈帧,该命令简写为 bt。此外,backtrace 的别名还有 where 和 info stack。

格式说明
bt显示所有栈帧
bt 只显示开头 N 个栈帧
bt只显示最后 N 个栈帧
bt full不仅显示 backtrace,还要显示局部变量
bt full 

     显示栈帧之后,就可以看出程序在何处停止,以及程序的调用路径。

2.6 显示变量

  print 命令可以显示变量,可以简写为 p。      格式:print 变量。

2.7 显示寄存器

  info registers 可以显示寄存器,简写为 info reg。      在寄存器名之前添加 $,显示寄存器的内容,例如 p $eax。   p/格式 $寄存器 可以指定寄存器的显示格式,例如 p/c $eax。可使用的格式如下:

格式说明
x显示为十六进制数
d显示为十进制数
u显示为无符号十进制数
o显示为八进制数
t显示为二进制数
a地址
c显示为字符(ASCII)
f浮点小数
s显示为字符串
i显示为机器语言,仅在显示内存的 x 命令中可用

显示详细信息

     程序指针可以写为 ,也可以写为eip,使用 p $pc 显示程序指针内容。程序指针指向当前程序的运行点的地址。      x 命令可以显示内存的内容,格式:x/ 。例如 x/i $ps,显示汇编指令。      一般使用 x 命令时,格式为 x/ 。此处 ADDR 为希望显示的地址,N 为重复次数,F 为前面的显示格式,U 代表的单位如下:

单位说明
b字节
h半字(2 字节)
w字(4 字节)(默认值)
g双字(8 字节)

  例如命令 x\10i $pc 显示从 pc 所指地址开始的 10 条指令。      反汇编命令 disassemble,简写为 disas。格式:

  • disassemble。反汇编当前整个函数。

  • disassemble 程序计数器。反汇编程序计数器所在函数的整个函数。

  • disassemble 开始地址 结束地址。反汇编从开始地址到结束地址之间的部分。

2.8 单步执行

  单步执行的意思时根据源代码一行一行地执行。执行源代码中一行的命令为 next ,简写为 n。执行时如果遇到函数调用,想执行到函数内部,使用 step 命令,简写为 p。   如果要逐条执行汇编指令,可以分别使用 nexti 和 stepi 命令。

2.9 继续运行

  使用 continue 命令继续运行程序,简写为 c。程序会在遇到断点后再次暂停运行。使用 continue  命令指定忽略断点的次数。

2.10 监视点

  大型软件或大量使用指针的程序中,很难弄清变量在什么地方被改变,要想找到变量在何处被改变,可以使用 watch 命令(监视点,watchpoint)。格式如下:

  • watch 。表达式发生变化时暂停运行。

  • awatch 。表达式被访问、改变时暂停运行。

  • rwatch 。表达式被访问时暂停运行。

  需要注意,设置监视点可能会降低运行速度。

2.11 删除断点和监视点

  delete 命令删除断点和监视点,简写为 d。格式为 delete ,表示删除编号指示的断点或监视点,编号可以用命令 info b 查看。   clear 命令删除已定义的断点。可用命令包括:     clear      clear      clear 文件名:行号     clear 文件名:函数名      disable 命令禁用断点。命令格式如下:     disable:禁用所有断点。     disable :禁用指定断点。     disable display :禁用 display 命令定义的自动显示。     disable mem :禁用 mem 命令定义的内存区域。      enable 命令用于启用断点。命令格式如下:     enable     enable      enable once :使指定的断点只启用一次。     enable delete      enable display      enable mem

2.12 其他断点

  硬件断点(hbreak),适用于 ROM 空间等无法修改的内存区域中的程序,在有些架构中无法使用。      临时断点(tbreak)和临时硬件断点(thbreak),在运行到该处时暂停,此时断点会被删除,在只需要停止一次时用起来方便。

2.13 改变变量的值

  格式:set variable 。例如命令 set variable options = 0,将变量 options 的值改成了 0。

2.14 生成内核转储文件

  使用 generate-core-file 可将调试中的进程生成内核转储文件。通过内核转储文件和调试对象,查看生成转储文件时的运行历史。      gcore 命令可以从命令行直接生成内核转储文件。在命令行使用 gcore pid,其中 pid 为进程号。该命令无须停止正在运行的程序以获得内核转储文件,当需要在其他机器上单独分析问题原因,或是分析客户现场发生的问题时十分有用。


3、内核转储

  内核转储(core dump)的最大好处是,它能保存问题发生时的状态。只要有问题发生时程序的可执行文件和内核转储,就可以知道进程当前的状态。

3.1 启用内核转储

  大多数 Linux 发行版默认关闭了内核转储功能,使用 ulimit 命令可以查看当前的内核转储功能是否有效。

 $ ulimit -c
 012

  -c 选项表示内核转储文件的大小限制,0 表示内核转储无效。开启内核转储执行命令:

 $ ulimit -c unlimited
 or
 $ ulimit -c 上限123

  unlimited 意思是不限制内核转储文件的大小,发生问题时进程的内存就可以全部转储到内核转储文件中。或者设置内核转储文件的上限,在参数中指定上限大小,单位为 Kb。      程序发生异常时会在当前目录下生成内核转储文件。例如程序 a.out 生成转储文件 core,使用以下方式启动 GDB:

 $ gdb -c core ./a.out1

     使用 GDB 的 list 命令可以查看附近的源代码。命令使用方法

格式说明
list 显示程序第 linenum 行周围的源代码
list 显示函数名为 function 的函数的源代码
list显示当前行后面的源代码
list –显示当前行前面的源代码
set listsize 设置一次显示源代码的行数
show listsize查看当前 listsize 的设置
list ,显示从 first 行到 last 行之间的源代码
list ,显示从当前行到 last 行之间的源代码
list +往后显示源代码
3.2 在专用目录中生成内核转储

  转储保存位置的完整路径可以通过 sysctl 变量 kernel.core_pattern 设置。在文件 /etc/sysctl.conf 中设置如下:

 $ cat /etc/sysctl.conf
 kernel.core_pattern = /var/core/%t-%e-%p-%c.core
 kernel.core_uses_pid = 0
 $ sysctl -p1234

     此外,还可以在 /proc/sys/kernel 下修改设置。   /proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 pid 作为扩展 ,如果添加则文件内容为 1 ,否则为 0。   proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名 ,可以这样修改 :

 $ echo "/corefile/core-%e-%p-%t" > core_pattern1

     kernel.core_pattern 中可以设置的格式符如下:

格式符说明
%%% 字符本身
%p被转储进程的进程 ID(PID)
%u被转储进程的真实用户 ID(real UID)
%g被转储进程的真实组 ID(real GID)
%s引发转储的信号编号
%t转储时刻(从 1970/1/1 0:00 开始的秒数)
%h主机名(同 uname(2) 返回的 nodename)
%e可执行文件名
%c转储文件的大小上限(内核版本 2.6.24 后可用)
3.3 使用用户模式辅助程序自动压缩内核转储文件

  修改 /etc/sysctl.conf中 的 kernel.core_pattern 变量来设置。

  $ cat /etc/sysctl.conf
 kernel.core_pattern= | usr/local/sbin/core_helper %t %e %p %c
 kernel.core_uses_pid= 0
  $ sysctl -p1234

  core_helper 的内容:

 $cat usr/local/sbin/core_helper
 #!/bin/sh
 
 execgzip ->/var/core/$1-$2-$3-$4.core.gz1234

  这样,发生内核转储时就会在 /var/core 下生成压缩的内核转储文件。

3.4 启用整个系统的内核转储功能

  /etc/profile 文件中可以设置开启所有用户的内核转储功能,默认情况下禁止内核转储:

 ulimit -S -c 0 > /dev/null 2>&11

  将其修改为

 ulimit -S -c unlimited > /dev/null 2>&11

     接下来要让通过 init 脚本启动的守护进程的内核转储功能有效。在 /etc/sysconfig/init 文件中添加一行命令。

 DAEMON_COREFILE_LIMIT='unlimited'1

     最后在 /etc/sysctl.conf 中加入以下设置。

 fs.suid_dumpable=11

  这个设置使得被 SUID 的程序也能内核转储。重新启动启动,就可以启用整个系统的内核转储。      在我使用的Ubuntu、Debian和移植的嵌入式Linux系统中,没有找到 /etc/sysconfig 文件夹。修改了文件 /etc/security/limits.conf,按照文件的内容提示修改,使 unlimited 永久生效。

3.4 利用内核转储掩码排除共享内存

  多进程程序如果使用庞大的共享内存,内核转储时所有进程的共享内存全部转储,会对磁盘造成巨大的压力,转储过程也会加重系统的负载。由于共享内存的内容是相同的,只需要在某个进程中转储共享内存。      通过 /proc//coredump_filter 进行设置。coredump_filter 使用比特掩码表示内存类型。如下所示:

比特掩码内存类型
比特 0匿名专用内存
比特 1匿名共享内存
比特 2file-backed 专用内从
比特 3file-backed 共享内存
比特 4ELF 文件映射(内核版本 2.6.24 后可用)

  要跳过所有的共享内存区段,应将值改位 1。


4、GDB调试技巧

4.1 attach 到进程

  要调试已经启动的进程,或是调试陷入死循环而无法返回控制台的进程时,可以使用 attach 命令。格式:attach,执行这一命令可以 attach 到进程 ID 为 pid 的进程上。   attach 之后就能使用普通的 gdb 命令。      gdb 和进程分离时使用 detach 命令,调试的进程就从 gdb 的控制下释放出来。进程被 detach 后继续运行。      进程信息可以用 info proc 命令显示。   守护者进程在启动好子进程后,会自动关闭主进程,如果没有设定监控模式的话,gdb 会提示断开与进程的链接。所以必须设定监控对象,设置命令为 set follow-fork-mode child/parent。

4.2 条件断点

  break  if ,这条命令将测试给定的条件,如果为帧则暂停运行。      如果断点已经存在,condition   命令给断点添加触发条件,condition  命令删除指定编号断点的触发条件。

4.3 反复执行

  ignore  :在编号指定的断点、监视点或捕获点忽略指定的次数。   continue : 达到指定次数前,执行到断电时不暂停。   s/stepi/n/nexti :执行指定次数的相应命令。   finish:执行完当前函数后暂停。   until:执行完当前函数等代码块后暂停,如果是循环,则在执行完循环后暂停,常用于跳出循环。   until :执行到指定地址停止。

4.4 断点命令

  commands 命令可以定义在断点终端后自动执行的命令。格式如下:

 (gdb) commands 
 
 ...
 end1234
4.5 值的历史

  通过 print 命令显示过的值会记录在内部的值历史中。这些值通过 $ 进行引用,使用 show value 命令可以显示历史中的最后 10 个值。

变量说明
$值历史的最后一个值
$n值历史的第 n 个值
$$值历史的倒数第 2 个值
$$n值历史的倒数第 n 个值
$_x 命令显示过的最后的地址
$__x 命令显示过的最后的地址的值
$_eexitcode调试过程中的程序的返回代码
$bpnum最后设置的断点编号

     还可以随意定义变量,变量以 $ 开头,由英文字母和数字组成。例如:

 (gdb) set $i=0
 (gdb) p $i
 $i = 0123
4.6 命令历史

  show history 将命令历史保存到文件中,默认命令历史文件位于 ./.gdb_history。

  set history expansion   show history expansion

  可以使用 csh 风格的 ! 字符。

  set history filename    show history filename

  将命令历史保存到文件中。可以通过环境变量 GDBHISTFILE 改变默认文件名。

  set history save   show history save

  启用命令历史保存到文件和恢复的功能。

  set history size    show history size

  可设置保存到命令历史中的命令数量。默认值为 256。

4.7 初始化文件(.gdbinit)

  Linux 环境下的初始化文件为 .gdbinit。如果存在 .gdbinit 文件,GDB 会在启动之前将其作为命令文件运行。初始化文件和命令文件的运行顺序如下。

  • 1、$HOME/.gdbinit。

  • 2、运行命令行选项。

  • 3、./.gdbinit。

  • 4、通过 -x 选项给出的命令文件。

  初始化文件和命令文件的语法相同。利用 define 命令可以自定义命令,document 命令给自定义命令添加说明,GDB 运行时使用 help  可以查看定义的命令。示例如下:

 define li
    x/10i $pc
 end
 document li
    list machine instruction
 end123456

     除了初始化文件,还可以把设置写在文件中,在运行 GDB 时读取这些文件。命令为 source ,例如:

 (gdb) source gdbcalc
 (gdb) p $log10(10000.0)
 $1 = 4123

  其中 gdbcalc 文件内容如下:

 #!/usr/bin/gdb -x
 file /usr/bin/gdb
 start
 set $e = 2.7182818284590452354
 set $pi = 3.14159265358979323846
 set $fabs = (double (*)(double)) fabs
 set $sqrt = (double (*)(double)) sqrt
 set $cbrt = (double (*)(double)) cbrt
 set $exp = (double (*)(double)) exp
 set $exp2 = (double (*)(double)) exp2
 set $exp10 = (double (*)(double)) exp10
 set $log = (double (*)(double)) log
 set $log2 = (double (*)(double)) log2
 set $log10 = (double (*)(double)) log10
 set $pow = (double (*)(double, double)) pow
 set $sin = (double (*)(double)) sin
 set $cos = (double (*)(double)) cos
 set $tan = (double (*)(double)) tan
 set $asin = (double (*)(double)) asin
 set $acos = (double (*)(double)) acos
 set $atan = (double (*)(double)) atan
 set $atan2 = (double (*)(double, double)) atan
 set $sinh = (double (*)(double)) sinh
 set $cosh = (double (*)(double)) cosh
 set $tanh = (double (*)(double)) tanh
 set $asinh = (double (*)(double)) asinh
 set $acosh = (double (*)(double)) acosh
 set $atanh = (double (*)(double)) atanh22345678910111213141516171819202122232425262728

5、总结

  GDB 常用命令及缩略形式如下表:

命令简写形式说明
backtracebt、where显示 backtrace
break设置断点
continuec、cont继续运行
deleted删除断点
finish运行到函数结束
info breakpoints显示断点信息
nextn执行下一行
printp显示表达式
runr运行程序
steps一次执行一行,包括函数内部
x显示内存内容
untilu执行到指定行
directorydir插入目录
disabledis禁用断点
downdo在当前调用的栈帧中选择要显示的栈帧
edite编辑文件或函数
framef选择要显示的栈帧
forward-searchfo向前搜索
generate-core-filegcore生成内核转储
helph显示帮助一览
infoi显示信息
listl显示函数或行
nextini执行下一行(以汇编代码为单位)
print-objectpo显示目标信息
sharedlibraryshare加载共享库的符号
setpisi执行下一行

显示详细信息

以上是“Linux系统中GDB是什么”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI