创建和调试 Linux 转储文件

了解如何处理转储文件将帮助您查找和修复应用程序中难以重现的错误。
78 位读者喜欢这篇文章。
How to find files in Linux

Lewis Cowles, CC BY-SA 4.0

崩溃转储、内存转储、核心转储、系统转储…… 都会产生相同的结果:一个文件,其中包含应用程序在特定时间(通常是应用程序崩溃时)的内存状态。

了解如何处理这些文件可以帮助您找到失败的根本原因。 即使您不是开发人员,在您的系统上创建的转储文件对于理解软件也可能非常有用(并且易于理解)。

这是一篇实践性文章,您可以克隆示例应用程序存储库来跟随示例:

git clone https://github.com/hANSIc99/core_dump_example.git

信号与转储的关系

信号是操作系统和用户应用程序之间的一种进程间通信。 Linux 使用 POSIX 标准中定义的信号。 在您的系统上,您可以在 /usr/include/bits/signum-generic.h 中找到定义的标准信号。 如果您想了解更多关于在应用程序中使用信号的信息,还有一个信息丰富的 man signal 页面。 简而言之,Linux 使用信号来触发进一步的活动,具体取决于它们是预期的还是意外的。

当您退出正在运行的应用程序时,该应用程序通常会收到 SIGTERM 信号。 因为这种类型的退出信号是预期的,所以此操作不会创建内存转储。

以下信号将导致创建转储文件(来源:GNU C 库

  • SIGFPE:错误的算术运算
  • SIGILL:非法指令
  • SIGSEGV:无效的存储访问
  • SIGBUS:总线错误
  • SIGABRT:程序检测到的并通过调用 abort 报告的错误
  • SIGIOT:在 Fedora 上标记为过时,此信号过去会在 PDP-11 上的 abort() 上触发,现在映射到 SIGABRT

创建转储文件

导航到 core_dump_example 目录,运行 make,然后使用 -c1 开关执行示例

./coredump -c1

应用程序应以状态 4 退出并显示错误

"Abgebrochen (Speicherabzug geschrieben)" 大致翻译为 "Segmentation fault (core dumped)"(段错误(核心已转储))。

是否创建核心转储取决于运行该进程的用户的资源限制。 您可以使用 ulimit 命令修改资源限制。

检查当前核心转储创建设置

ulimit -c

如果输出 unlimited,则表示它使用(推荐的)默认值。 否则,使用以下命令更正限制:

ulimit -c unlimited

要禁用创建核心转储,请输入

ulimit -c 0

该数字以千字节为单位指定资源。

什么是核心转储?

内核处理核心转储的方式在以下位置定义:

/proc/sys/kernel/core_pattern

我正在运行 Fedora 31,在我的系统上,该文件包含

/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h

这表明核心转储已转发到 systemd-coredump 实用程序。 core_pattern 的内容在不同的 Linux 发行版之间差异很大。 当使用 systemd-coredump 时,转储文件以压缩形式保存在 /var/lib/systemd/coredump 下。 您无需直接触摸这些文件; 相反,您可以使用 coredumpctl。 例如

coredumpctl list

显示保存在您系统上的所有可用转储文件。

使用 coredumpctl dump,您可以从保存的最后一个转储文件中检索信息

[stephan@localhost core_dump_example]$ ./coredump 
Application started…

(…….)

Message: Process 4598 (coredump) of user 1000 dumped core.

Stack trace of thread 4598:
#0 0x00007f4bbaf22625 __GI_raise (libc.so.6)
#1 0x00007f4bbaf0b8d9 __GI_abort (libc.so.6)
#2 0x00007f4bbaf664af __libc_message (libc.so.6)
#3 0x00007f4bbaf6da9c malloc_printerr (libc.so.6)
#4 0x00007f4bbaf6f49c _int_free (libc.so.6)
#5 0x000000000040120e n/a (/home/stephan/Dokumente/core_dump_example/coredump)
#6 0x00000000004013b1 n/a (/home/stephan/Dokumente/core_dump_example/coredump)
#7 0x00007f4bbaf0d1a3 __libc_start_main (libc.so.6)
#8 0x000000000040113e n/a (/home/stephan/Dokumente/core_dump_example/coredump)
Refusing to dump core to tty (use shell redirection or specify — output).

这表明该进程已被 SIGABRT 停止。 此视图中的堆栈跟踪不是很详细,因为它不包含函数名称。 但是,使用 coredumpctl debug,您可以简单地使用调试器(默认情况下为 GDB)打开转储文件。 键入 bt(backtrace 的缩写)以获得更详细的视图

Core was generated by `./coredump -c1'.
Program terminated with signal SIGABRT, Aborted.
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50  return ret;
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007fc37a9aa8d9 in __GI_abort () at abort.c:79
#2  0x00007fc37aa054af in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fc37ab14f4b "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007fc37aa0ca9c in malloc_printerr (str=str@entry=0x7fc37ab130e0 "free(): invalid pointer") at malloc.c:5339
#4  0x00007fc37aa0e49c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:4173
#5  0x000000000040120e in freeSomething(void*) ()
#6  0x0000000000401401 in main ()

与后续帧相比,内存地址:main()freeSomething() 相当低。 由于共享对象映射到虚拟地址空间的末尾区域,因此您可以假定 SIGABRT 是由共享库中的调用引起的。 共享对象的内存地址在调用之间不是恒定的,因此在调用之间看到不同的地址是完全正常的。

堆栈跟踪表明后续调用源自 malloc.c,这表明内存(取消)分配可能出现问题。

在源代码中,您可以看到(即使没有任何 C++ 知识)它试图释放一个指针,该指针不是由内存管理函数返回的。 这会导致未定义的行为并导致 SIGABRT

void freeSomething(void *ptr){
    free(ptr);
}
int nTmp = 5;
int *ptrNull = &nTmp;
freeSomething(ptrNull);

systemd coredump 实用程序可以在 /etc/systemd/coredump.conf 下配置。 转储文件清理的轮换可以在 /etc/systemd/system/systemd-tmpfiles-clean.timer 中配置。

您可以在其 手册页上找到有关 coredumpctl 的更多信息。

使用调试符号编译

打开 Makefile 并注释掉第 9 行的最后一部分。 它现在应该看起来像

CFLAGS =-Wall -Werror -std=c++11 -g

-g 开关使编译器能够创建调试信息。 启动应用程序,这次使用 -c2 开关

./coredump -c2

您将收到一个浮点异常。 在 GDB 中使用以下命令打开转储:

coredumpctl debug

这次,您会被直接指向源代码中导致错误的行

Reading symbols from /home/stephan/Dokumente/core_dump_example/coredump…
[New LWP 6218]
Core was generated by `./coredump -c2'.
Program terminated with signal SIGFPE, Arithmetic exception.
#0 0x0000000000401233 in zeroDivide () at main.cpp:29
29 nRes = 5 / nDivider;
(gdb)

键入 list 以更好地了解源代码

(gdb) list
24	int zeroDivide(){
25	    int nDivider = 5;
26	    int nRes = 0;
27	    while(nDivider > 0){
28	        nDivider--;
29	        nRes = 5 / nDivider;
30	    }
31	    return nRes;
32	}

使用命令 info locals 检索应用程序失败时本地变量的值

(gdb) info locals
nDivider = 0
nRes = 5

结合源代码,您可以看到您遇到了除以零的情况

nRes = 5 / 0

结论

了解如何处理转储文件将帮助您查找和修复应用程序中难以重现的随机错误。 如果它不是您的应用程序,将核心转储转发给开发人员将帮助她/他查找并修复问题。

接下来阅读什么
标签
User profile image.
Stephan 是一位技术爱好者,他欣赏开源,因为它能深入了解事物的工作原理。 Stephan 作为一名全职支持工程师,在工业自动化软件(主要是专有领域)工作。 如果可能,他会从事基于 Python 的开源项目、撰写文章或驾驶摩托车。

评论已关闭。

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© 2025 open-source.net.cn. All rights reserved.