前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >调试coredump步骤(coredump原理)

调试coredump步骤(coredump原理)

作者头像
全栈程序员站长
发布2022-07-28 17:36:33
2.7K0
发布2022-07-28 17:36:33
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

文章目录

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
代码语言:javascript
复制
acuity@ubuntu:~$ ulimit -c
1024

  可以使用“ulimit -c [size]”命令指定记录coredump文件的大小,即是开启coredump记录。需要注意的是,单位为block,1block=512bytes。

  • 开启coredump
代码语言:javascript
复制
acuity@ubuntu:~$ ulimit -c 1024

  万一程序比较糟糕,指定的coredump文件大小限制,导致文件记录不到或者缺失怎么办。此时,一劳永逸的办法就是不限制coredump文件大小;执行“ulimit -c unlimited”设定,设置时需要root权限。

  • 不限制coredump文件大小
代码语言:javascript
复制
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文件的存储位置和更详细的文件名称。默认位置和名称信息如下:

代码语言:javascript
复制
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”文件
代码语言:javascript
复制
echo ./core.%e.%p.%t > /proc/sys/kernel/core_pattern

  如需指定其他存储路径,可以修改路径部分。

  • “/home”目录生成“core-name-pit-time”文件
代码语言:javascript
复制
echo /home/core-%e-%p-%t > /proc/sys/kernel/core_pattern

注: 指定某些目录,可以生成coredump文件,但文件内容为空,可能是权限问题??

3 使用coredump

  编写一个“非法”程序,让系统记录coredump,结合gdb来分析过程;编译时需加入"-g",保留调试信息。

代码语言:javascript
复制
#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]\r\n", p);
	
	free(p);	
	free(p);	/* 重复释放*/
	
    return 0;
}

  编译执行该程序,由于访问野指针,程序异常退出,将产生一个coredump文件。

  • 查看coredump文件
代码语言:javascript
复制
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 调试命令
代码语言:javascript
复制
gdb exe-file core-file
  • 查看coredump信息
代码语言:javascript
复制
gdb后,键入“bt”
  • 执行结果

  通过分析,出现异常的地方是第17行,翻阅源码,17行执行了重复释放动态申请内存的操作。

4 参考文章

【1】详解coredump

【2】Linux上Core Dump文件的形成和分析

【3】由coreDump引发的一次探讨

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/128707.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1 前言
  • 2 coredump
    • 2.1 什么是coredump
      • 2.2 coredump意义
        • 2.3 coredump产生的场景
          • 2.2 开启coredump
            • 2.3 coredump存储位置与命名
            • 3 使用coredump
            • 4 参考文章
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档