gdb可以用来调试进程发生错误时产生的coredump文件,那么coredump文件是怎么产生,以及生成在哪里呢?为什么很多时候我们找不到coredump文件呢?这主要有两个原因:
首先使用ulimit -a查看内核转储功能是否被关闭,如果core_file_size被设置为0则表示该功能被关闭。这时可以用ulimit -c命令设置core_file_size为非0值来开启的该功能。
内核转储文件生成路径和文件名取决于内核参数kernel.core_pattern和kernel.core_uses_pid。我们也可以通过这两个内核参数来查询和设置内核转储的路径和文件名。具体的设置方式有两种,如下图所示:
内核参数core_pattern,指定的实际上是内核转储文件的全路径名(包括路径和文件名)。例如/var/core/core表示在/var/core/路径下生成名为core的内核转储文件。如果只设置了文件名,例如上图中core_pattern=core的情况,则表示在可执行文件所在目录生成文件名为core的内核转储文件。
内核参数core_uses_pid表示是否在生成的内核转储文件后添加进程号后缀,1表示添加,0表示不添加。实际上,我们也可以将core_uses_pid设置为0,而直接在core_pattern里通过格式符来设置,具体的格式符较多,建议自行查阅。
需要说明的是,这种方式只是临时生效,当计算机重启之后则会恢复/etc/sysctl.conf中的设置或系统默认设置。因此如果希望每次重启计算机都能够生效,则可以通过修改/etc/sysctl.conf文件实现。例如在文件中增加如下两行设置:
kernel.core_pattern=/var/core/core.%p
kernel.core_uses_pid=0
在基础篇里曾列出了gdb的常用内部命令,实际使用时候还是有很多技巧的。这里针对部分常用内部命令做进一步的说明:
gdb可执行文件
gdb –args可执行文件 arguments
gdb可执行文件 coredump文件
gdb –c coredump文件可执行文件
gdb可执行文件进程pid
上面列出了多种启动方式,大致还是分为3类,调试可执行文件,coredump文件和正在运行的进程。
断点的设置:
break/b 函数名/行号/文件名:行号/文件名:函数名/+偏移量/-偏移量/*地址
条件断点的设置:
break 断点 if条件
condition 断点编号条件
调试可执行文件时才有可能需要指定参数,参数即可以在启动gdb时指定,也可以在开始调试时指定:
run/r 参数
backtrace/bt [ [full] N/-N]
说明:指定full时除了显示栈帧,还会显示局部变量。N和-N分别表示开头和最后N个栈帧。
显示寄存器:inforeg
显示内存:x/格式地址
说明:显示寄存器时,可以看到一个eip寄存器,那就是程序指针。x命令可用格式为i,表示以汇编语言显示。那么我要显示从程序指针指向地址开始的10条指令就可以用一下命令:
x/10i $eip
p/格式变量
说明:这里的重点在格式符,x,d,u,o,t,a,c,f,s分别表示十六进制,十进制,无符号十进制,八进制,二进制,地址,字符,浮点小数,字符串。
set variable 变量=值
next/n N:单步执行N步,不进入函数调用
step/s N:单步执行N步,进入函数调用
continue/c N:继续执行,N次遇到断点不停止,相当于连续执行N次continue
finish/fin:运行到当前函数的结束
clear 函数名/行号/文件名:行号/文件名:函数名
delete [breakpoints]断点编号
disable [breakpoints] [断点编号]
enable [breakpoints] [once/delete] [断点编号]
说明:disable和enable命令,不指定断点编号是会对所有断点起作用,once表示只启用一次,delete表示启用一次后删除该断点
commands 断点编号
命令
……
end
说明:这个功能非常适合在设置的断点处打印变量的值或者查看栈帧。
watch 表达式:表达式发生变化时,暂停运行
rwatch 表达式:表达式被访问时,暂停运行
awatch 表达式:表达式发生变化或者被访问时,暂停运行
在gdb调试过程中,可以使用generate-core-file子命令生成内核转储文件。我们也可以直接使用gcore命令对正在运行的进程直接生成内核转储文件:
gcore PID
除了在启动gdb时直接指定进程号,我们还可以在启动gdb之后,使用内部命令attach进行进程绑定。这非常适用于已经出现问题无法正常结束的进程。调试结束是可以使用detach命令释放进程。