大家好,又见面了,我是你们的朋友全栈君。
文章目录
- 1 前言
- 2 coredump
- 2.1 什么是coredump
- 2.2 coredump意义
- 2.3 coredump产生的场景
- 2.2 开启coredump
- 2.3 coredump存储位置与命名
- 3 使用coredump
- 4 参考文章
1 前言
在上一篇文章中描述了如何使用Valgrind工具检查内存相关问题,包括内存泄露、空指针使用、野指针使用、重复释放等问题。对于大多数情况下,Valgrind的作用性体现更多在于“内存泄露”检查,因为空指针、野指针的访问,会引发程序段错误(segment fault )而终止,此时可以借助linux系统的coredump文件结合gdb工具可以快速定位到问题发生位置。此外,程序崩溃引发系统记录coredump文件的原因是众多的,野指针、空指针访问只是其中一种,如堆栈溢出、内存越界等等都会引起coredump,利用好coredump文件,可以帮助我们解决实际项目中的异常问题。
2 coredump
2.1 什么是coredump
coredump指的是应用程序因为各种原因导致异常终止时,操作系统将应用程序的异常发生时的状态信息记录为一个coredump的文件。一个coredump文件主要包含了应用程序的内存信息、寄存器状态、堆栈地址、函数调用上下文,开发人员通过分析这些信息,确定程序异常发生时的调用位置,如果是堆栈溢出,还需分析多层函数的调用信息。
通俗来说,coredump是操作系统记录应用程序非正常终止的信息,留给我们排查问题的依据。
2.2 coredump意义
coredump对于分析程序异常的作用是不言而喻的。以以前我们学习ARM 32位MCU为例(STM32),由于初学过程,代码质量参差不齐,经常引起硬件错误中断(Hard Fault)。面对这种情况,我们是束手无策的,一方面是程序发生错误后没有记录到有参考意义的信息(当然,可以通过仿真器实时获取堆栈信息,但对于实际产品不不现实);另一方面是问题复现概率比较低,复现条件不确定。linux系统是一个“考虑周全”的操作系统,应用程序发生异常,会记录一些关键的信息,已便于我们分析。coredump的意义就在于此。
- 根据记录信息分析程序异常的原因
- 根据记录信息反推出现问题的条件,复现问题来验证
2.3 coredump产生的场景
应用程序发生异常时,会产生coredump文件记录,这些异常几乎都与内存相关,总结起来包括几点。
【1】内存访问越界
- 数组下标越界
- 超出动态(malloc/new)内存申请范围
- 字符串没有结束符,一些函数依赖于字符串结束符,如 strcpy、strcmp、sprintf
【2】访问非法指针
- 空指针(未申请内存)
- 野指针(已释放内存)
- 重复释放指针(内存)
- 指针强制转换,指针强制转换需特别谨慎,可能因为对齐、起始地址等问题引起内存访问错误
【3】堆栈溢出,分配大量局部变量、多重函数调用、较深的函数递归等可能导致堆栈溢出
【4】多线程访问
- 调用不可重入函数
- 共享数据未互斥访问
2.2 开启coredump
系统默认不开启coredump记录功能,执行"ulimit -c"
查看是否开启,返回0表示未开启coredump记录功能。
- 查看是否记录coredump
acuity@ubuntu:~$ ulimit -c
1024
可以使用“ulimit -c [size]”
命令指定记录coredump文件的大小,即是开启coredump记录。需要注意的是,单位为block
,1block=512bytes。
- 开启coredump
acuity@ubuntu:~$ ulimit -c 1024
万一程序比较糟糕,指定的coredump文件大小限制,导致文件记录不到或者缺失怎么办。此时,一劳永逸的办法就是不限制coredump文件大小;执行“ulimit -c unlimited”
设定,设置时需要root权限。
- 不限制coredump文件大小
root@ubuntu:/home/acuity# ulimit -c unlimited
root@ubuntu:/home/acuity# ulimit -c
unlimited
以上方式都是在终端临时设置开启coredump记录功能,系统重启后失效,很显然这不是理想的方法。理想的方法是修改配置文件,使得系统一直开启coredump记录功能,至少在项目开发测试阶段是需要开启的。原则上,软件发布后也应该记录,出现问题后能够有追溯和分析问题的依据。
- 通过配置文件使能
在"/etc/profile"
文件增加" ulimit -c unlimited "
。
注: ulimit 命令是一个设置资源限制的命令,除了coredump外,还可以设定其他资源限制
- -a:查看当前资源限制信息
- -c <core最大值>:设定core文件的最大值,单位为块(block)
- -d <数据节段大小>:进程数据段最大值,单位为KB
- -f <文件大小>:进程可创建最大文件值,单位为块(block)
- -H:设置资源的硬性限制,设置后不可更改
- -l <内存大小>: 可加锁内存大小,单位 为KB
- -m <内存大小>:指定可使用内存的上限,单位为KB
- -n <文件数目>:进程最大可打开的文件数(文件描述符数目)
- -p <缓冲区大小>:管道缓冲区的大小,单位为KB
- -s <堆栈大小>:线程最大堆栈大小,单位为KB
- -S:设置资源的弹性限制,不可超过硬性资源限制
- -t <cpu时间>:cpu最大占用时间,单位为秒
- -u <进程数目>:用户可创建的最大进程数
- -v <虚拟内存大小>:进程最大可用虚拟内存,单位为KB
**除此之外,还有可以通过在代码中设定开启coredump。**然而一般不推荐该方式, 因为如果代码中没有增加开启功能,而应用程序又发生了异常,系统将无法记录coredump。建议在系统配置文件设置开启。
访问接口:
代码语言:javascript复制#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim); /* 获取coredump 文件限制大 小 */
int setrlimit(int resource, const struct rlimit *rlim);/* 设置coredump 文件限制 大小 */
例子:
代码语言:javascript复制#include <sys/resource.h>
int main(int argc, char * argv [ ])
{
struct rlimit rlmt;
rlmt.rlim_cur = (rlim_t)1024;
rlmt.rlim_max = (rlim_t)1024;
if (-1 == setrlimit(RLIMIT_CORE, &rlmt))
{
perror("setrlimit error");
return -1;
}
}
2.3 coredump存储位置与命名
coredump文件默认存储于应用程序执行目录下,文件名称为“core”。使用默认文件名称显然不是一个好的方式,如果有多个应用程序异常终止,将覆盖core文件;或者同一个应用程序,在异常终止后被守护进程重新启动运行,再次异常时导致core文件被覆盖。
- 文件名称带进程id(PID)
修改"/proc/sys/kernel/core_uses_pid"
文件,可以将进程的id作为作为扩展名,文件内容为1表示使用扩展名,默认为0;使用进程id扩展名时,生成的core文件格式为"core.xxx"
,xxx为进程id。
- 更详细的名称以及存储位置
修改"/proc/sys/kernel/core_pattern"
文件可以设置coredump文件的存储位置和更详细的文件名称。默认位置和名称信息如下:
root@ubuntu:/home/acuity# cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport %p %s %c %d %P %E
扩展字符含义:
代码语言:javascript复制%p - 扩展进程id(pid)
%P - 与%p作用相同
%u - 扩展用户id(uid)
%g - 扩展组id(gid)
%s - 扩展产生信号
%t - 扩展当前时间,从1970-01-0100:00:00开始的秒数
%h - 扩展主机名
%e - 扩展应用程序文件名称
%E - 扩展应用程序文件名称,包括文件绝对路径
coredump存储目录不变(存储于当前应用程序目录下),文件扩展名称增加应用程序文件名称、进程id、当前时间,这是实际场景常用的基本用法,能否适用绝对部分场合。可以用vi直接打开文件编辑,也可以使用echo
修改文件内容,前提都是必须以root权限修改。
- 在应用程序当前目录生成
“core.name.pit.time”
文件
echo ./core.%e.%p.%t > /proc/sys/kernel/core_pattern
如需指定其他存储路径,可以修改路径部分。
- 在
“/home”
目录生成“core-name-pit-time”
文件
echo /home/core-%e-%p-%t > /proc/sys/kernel/core_pattern
注: 指定某些目录,可以生成coredump文件,但文件内容为空,可能是权限问题??
3 使用coredump
编写一个“非法”程序,让系统记录coredump,结合gdb来分析过程;编译时需加入"-g"
,保留调试信息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char * argv [ ])
{
int *p = NULL;
p = malloc(4);
if (p == NULL)
{
perror("malloc failed");
}
printf("address [0x%p]rn", p);
free(p);
free(p); /* 重复释放*/
return 0;
}
编译执行该程序,由于访问野指针,程序异常退出,将产生一个coredump文件。
- 查看coredump文件
root@ubuntu:/usr# file core.coredump.2046.1591860958
core.coredump.2046.1591860958: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './coredump'
注: 有时候coredump只生成一个空文件,可以通“file”命令查看
- 启动gdb 调试命令
gdb exe-file core-file
- 查看coredump信息
gdb后,键入“bt”
- 执行结果
通过分析,出现异常的地方是第17行,翻阅源码,17行执行了重复释放动态申请内存的操作。
4 参考文章
【1】详解coredump
【2】Linux上Core Dump文件的形成和分析
【3】由coreDump引发的一次探讨
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/128707.html原文链接:https://javaforall.cn