2020/03/08 GDB调试笔记

目录

调试和带参调试

Core Dump调试 

调试正在运行的程序

 

断点

变量查看

查看源码

执行调试

补充1:产生core的原因分析

 

补充2:ulimit命令


 


调试和带参调试

   进入调试  gdb 【可执行文件】 or

   在编译时带 -g:gcc -g -o test test.c

   接着运行: 没有参数 run

   有参数    run【参数】 or    set args 【参数】 然后 run

   退出调试:按下Ctrl-D退出

Core Dump调试 

  • 启用Core Dump

在Linux中,如果程序由于异常或bug突然崩溃,会产生一个叫做core的文件。core文件包含程序运行时的内存,寄存器状态,堆栈指针,内存管理信息以及各种函数调用堆栈信息,它存储了程序工作在当前状态的各种信息,通过gdb工具分析这个文件,分析前后变量,检查程序异常退出对应的堆栈调用等信息,找出问题所在。

首先检查系统有没有禁止core文件的产生

ulimit -c  如果为0表示core dump处于关闭状态

ulimit -c unlimited 开启core dump,不限制core文件大小

ulimit -c 10 #设置最大大小,单位为块,一块默认为512字节

但这种方法只是当前有效,重启后就不再生效。下面介绍永久配置core的方法:

方法1: /etc/profile:找到ulimit -S -c 0 > /dev/null 2>&1,把0改为 unlimited ,保存退出;  然后 执行source /etc/profile 设置生效

若只针对某一用户配置,则修改此用户的~/.bashrc或者~/.bash_profile文件:limit -c unlimited

方法2: /etc/security/limits.conf 按如下信息配置

<domain> <type> <item> <value>
   *     soft    core unlimited
  • 调试core文件

gdb 程序文件名 core  进入core调试

调试正在运行的程序

查找进程ID : ps -ef | grep 进程名   or  pidof 进程名

在GDB中通过进程ID调试: gdb 进入   然后  (gdb) attach 进程ID

若没有调试信息,在attach前加一句:file hello

如有报错,将/etc/sysctl.d/10-ptrace.conf中

kernel.yama.ptrace_scope = 1  改为  0 

直接调试相关id进程:gdb hello 20829  or   gdb hello --pid 20829

 

断点

查看已设置断点:info breakpoints

设置断点b num 或  b program:num  b funcname

条件断点:break test.c:23 if b==0(如果b=0,就在23行停止)

                  condition 1 b==0(如果b=0,断点1)condition bnum(取消断点条件)

                  rbreak printNum*(对函数printNum设置断点,调用该函数时都要停下来)

                  rbreak file:regex (对所有函数设置断点)rbreak .   or   rbreak test.c:.    or    rbreak test.c:^print

临时断点:tbreak test.c:l0 

                  ignore 1 30(跳过前面30次)

设置观察点:watch a  rwatch当变量值被读时断住,awatch被读或者被改写时断住

禁用断点:

禁用所有断点disable  禁用标号为bnum的断点 disable bnum   

启用所有断点enable  启用标号为bnum的断点 enable bnum

启动标号为bnum的断点,并且在此之后删除该断点enable delete bnum

清除断点:

删除当前行所有断点:clear 删除函数处断点clear function

删除文件filename中函数function处的断点 :clear filename:function

删除行号为lineNum处的断点 :clear lineNum

删除文件filename中行号为lineNum处的断点 :clear f:lename:lineNum

删除所有breakpoints,watchpoints和catchpoints :delete

删除断点号为bnum的断点 :delete bnum

变量查看

普通查看: p a                        查看变量a的值,p=print   

                   p *d                      打印指针内容,但只能打印一个数值;如果不解引用,打印地址

                   p *d@10              打印指针内容,使用@并指定长度

指定查看: p 'testGdb.h'::a    查看指定函数or文件中的变量

$可表示上一个变量:p *linkNode  显示节点指向的内容    p *$.next 显示下一个节点的内容

                                  set $index=0 设置类unix环境变量的下标   p b[$index++]累加下标访问并显示

格式控制:x 十六进制    d十进制    u 十六进制无符号整型   a 十六进制    o 八进制    t 二进制    c 字符格式    f 浮点格式

                  p/x c  按十六进制打印

查看内存地址的值:x/[n][f][u] addr 

n表示要显示的内存单元数,默认值为1;f 表示要打印的格式;u 要打印的单元长度;addr 内存地址

单元类型:b 字节        h 半字,即双字节     w 字,即四字节     g 八字节

自动显示: display e   程序停止时自动显示e

                   into display查看哪些变量设置了自动显示 

                   delete display num 清除编号为num的自动显示     disable display num  去使能编号为num的自动显示

查看寄存器:info registers

查看源码

list 简写为l    l num  显示num附近的源码  

                     l  funcname  显示函数funcname附近的源码

                     list first,last   显示first到last之间的源码

                     l test.c:1   l test.c:1,test.c:3   显示指定路径中的源码

设置源码一次列出行数:   set listsize 20     show listsize

指定源码目录 : dir ./temp

查看原来的源码路径 :readelf main -p .debug_str

改变源码目录: set substitute-path from to

查看设置的源码路径 :show substitute-path

执行调试

单步执行:next 简写为n    n   num  单步执行num次

单步进入:step简写为s    单步跟踪到函数内部,但前提是该函数有调试信息并且有源码信息

                  如果没有函数源码会报错 No such file or directory  可以用finish结束这个步骤

                 show step-mode  查看step-mode的状态

                 set step-mode on 打开step-mode,即当遇到没有调试信息的函数,不跳过这个函数

                 set step-mode off 关闭step-mode,默认为off,没有调试信息就跳过这个函数   

                 si  单步执行一个机器指令

继续执行到下一个断点:-continue 简写为c或fp

继续执行到指定位置:-until 简写为 u

跳过执行: skip        skip function xxx    skip file xxx                 info skip

                                 skip delete [num]    skip enable [num]       skip disable [num]


补充1:产生core的原因分析

(1)内存访问越界:
   a) 由于使用错误的下标,导致数组访问越界。
   b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符。
   c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。

 (2)多线程程序使用了线程不安全的函数:
应该使用下面这些可重入的函数,它们很容易被用错:
asctime_r(3c) 、gethostbyname_r(3n) 、getservbyname_r(3n)、ctermid_r(3s) 、gethostent_r(3n) 、getservbyport_r(3n)、 ctime_r(3c) 、getlogin_r(3c)、getservent_r(3n) 、fgetgrent_r(3c) 、getnetbyaddr_r(3n) 、getspent_r、(3c)fgetpwent_r、(3c) getnetbyname_r(3n)、 getspnam_r(3c)、 fgetspent_r(3c)、getnetent_r(3n) 、gmtime_r(3c)、 gamma_r(3m) 、getnetgrent_r(3n) 、lgamma_r(3m) 、getauclassent_r(3)、getprotobyname_r(3n) 、localtime_r(3c) 、getauclassnam_r(3) 、etprotobynumber_r(3n)、nis_sperror_r(3n) 、getauevent_r(3) 、getprotoent_r(3n) 、rand_r(3c) 、getauevnam_r(3)、getpwent_r(3c) 、readdir_r(3c) 、getauevnum_r(3) 、getpwnam_r(3c) 、strtok_r(3c)、 getgrent_r(3c)、getpwuid_r(3c) 、tmpnam_r(3s) 、getgrgid_r(3c) 、getrpcbyname_r(3n)、 ttyname_r(3c)、getgrnam_r(3c) 、getrpcbynumber_r(3n) 、gethostbyaddr_r(3n) 、getrpcent_r(3n)

(3)多线程读写的数据未加锁保护:

    对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成coredump。

 (4)非法指针:

    a) 使用空指针;
    b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。

 (5)堆栈溢出:
    不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。 

参考:https://blog.csdn.net/tenfyguo/article/details/8159176

 


补充2:ulimit命令

ulimit命令用来限制系统用户对shell资源的访问。限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。

ulimit -a # 显示当前所有limit信息 

-a:显示目前资源限制的设定

-c <core文件上限>:设定core文件的最大值,单位为区块

-d <数据节区大小>:程序数据节区的最大值,单位为KB

-f <文件大小>:shell所能建立的最大文件,单位为区块

-H:设定资源的硬性限制,也就是管理员所设下的限制

-m <内存大小>:指定可使用内存的上限,单位为KB

-n <文件数目>:指定同一时间最多可开启的文件数

-p <缓冲区大小>:指定管道缓冲区的大小,单位512字节

-s <堆叠大小>:指定堆叠的上限,单位为KB

-S:设定资源的弹性限制

-t <CPU时间>:指定CPU使用时间的上限,单位为秒

-u <程序数目>:用户最多可开启的程序数目

-v <虚拟内存大小>:指定可使用的虚拟内存上限,单位为KB


猜你喜欢

转载自blog.csdn.net/Xinyue_Lu/article/details/104729793
今日推荐