在哪里可以找到核心转储文件,以及如何查看和分析回溯(堆栈跟踪)?

Gab*_*les 6 debugging segmentation-fault

当我在 Ubuntu 20.04 上运行我的 C 程序时,我得到这个运行时错误:

Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)

我真的需要查找和查看该core文件,但我在任何地方都找不到它。它在哪里,我如何查看其中的回溯?

小智 10

通过搜索找到的。我运行的是 Ubuntu Mate 21.10。对于那些运行较晚型号 Ubuntu 的用户,apport将生成/var/lib/apport/coredump.

如果找不到核心转储文件,cat /var/log/apport.log. 当我这样做时,我看到:

executable does not belong to a package, ignoring
called for pid 5545, signal 11, core limit 0, dump mode 1
Run Code Online (Sandbox Code Playgroud)

请注意核心限制为 0,这意味着不会生成核心转储文件。因此,我运行了这篇文章 ( ) 中显示的命令ulimit -c unlimited,这次apport.log显示了以下内容:

writing core dump to core._my_prog.1000.e43b2f33-4708-438c-a7d7-05062f381382.5650.795448 (limit: -1)
Run Code Online (Sandbox Code Playgroud)

我在当前目录或包含可执行文件的目录中找不到它,所以我在整个系统上进行了查找并在/var/lib/apport/coredump.


Gab*_*les 8

在 Ubuntu 20.04 中测试。

1.启用核心文件

首先,运行ulimit -c以查看系统上核心文件的最大允许大小。在 Ubuntu 20.04 上,我的返回0,这意味着无法创建核心文件。

ulimit --help显示的含义-c

-c  the maximum size of core files created
Run Code Online (Sandbox Code Playgroud)

因此,将允许的核心文件大小设置为unlimited,如下所示。请注意,我认为这仅适用于您在其中运行它的一个终端,并且我认为它不会在重新启动后持续存在,因此每次您想要创建核心文件时都必须运行它,并且在您希望它的每个终端中运行工作于:

# set max core dump file size to unlimited
ulimit -c unlimited
# verify it is now set to "unlimited"
ulimit -c
Run Code Online (Sandbox Code Playgroud)

就是这样!现在,运行您的程序,它会将核心作为core文件转储到您调用可执行文件时所在的同一目录中。该文件的名称只是“核心”。

2.在gdb中查看backtrace

您应该已经构建了带有调试符号的 C 或 C++ 程序,以便在核心文件中看到有用的信息。如果没有调试符号,您只能看到被调用函数的地址,而不能看到实际名称或行号。

在GCC,使用-ggdb -O0以打开得不g用于优化符号gdb GNU dë bugger。您也可以使用-g -O0,-g3 -O0等,但-ggdb -O0最好使用。-O0为此,我们真的需要优化级别 0 ( ) 吗?是的,是的,我们这样做。在此处查看我的答案:堆栈溢出:编译器-O0选项和-Og选项之间有什么区别?

gdb像这样打开核心文件:

gdb path/to/my/executable path/to/core
Run Code Online (Sandbox Code Playgroud)

假设您刚刚运行path/to/my/executable,那么该core文件将位于您刚刚转储核心时所在的同一目录中,因此您可以运行以下命令:

gdb path/to/my/executable core
Run Code Online (Sandbox Code Playgroud)

在 中gdb,使用以下命令查看回溯(崩溃时的函数调用堆栈):

bt
# or (exact same command)
where

# OR (for even more details, such as seeing all arguments to the functions--
# thanks to Peter Cordes in the comments below)
bt full

# For gdb help and details, see:
help bt
# or
help where
Run Code Online (Sandbox Code Playgroud)

重要提示:发生核心转储时,它不会自动用新core文件覆盖当前目录中的任何预先存在的文件,因此您必须在程序崩溃时在生成新核心文件之前手动删除core文件rm core,以便始终拥有要分析的最新核心文件。

3. 试一试

  1. 在终端中,运行sleep 30以启动休眠 30 秒的进程。
  2. 在它运行时,按Ctrl+\强制进行核心转储。您现在将core在您所在的目录中看到一个文件。
  3. 由于我们没有带有调试符号的可执行文件,我们将只在 gdb 中打开核心文件而不是带有符号的可执行文件+核心文件。所以,运行gdb -c core打开刚刚被强制崩溃创建的核心文件。
  4. 你会看到这个。请注意,sleep 30当发生核心转储时,它知道您调用了什么命令 ( ):
    Core was generated by `sleep 30'.
    Program terminated with signal SIGQUIT, Quit.
    #0  0x00007f93ed32d334 in ?? ()
    (gdb) 
    
    Run Code Online (Sandbox Code Playgroud)
  5. 运行btwhere查看回溯。你会看到这个:
    (gdb) bt
    #0  0x00007f93ed32d334 in ?? ()
    #1  0x000000000000000a in ?? ()
    #2  0x00007f93ed2960a5 in ?? ()
    #3  0x0000000000000000 in ?? ()
    (gdb)
    
    Run Code Online (Sandbox Code Playgroud)
  6. 这些是在调用堆栈上调用的函数的地址。如果您打开了调试符号,您会看到更多信息,包括函数名称和行号,如下所示(从我的 C 程序中提取):
    #10 0x00007fc1152b8ebf in __printf (format=<optimized out>) at printf.c:33
    #11 0x0000562bca17b3eb in fast_malloc (num_bytes=1024) at src/fast_malloc.c:225
    #12 0x0000562bca17bb66 in malloc (num_bytes=1024) at src/fast_malloc.c:496
    
    Run Code Online (Sandbox Code Playgroud)

4.忘记核心文件,直接在gdb中运行程序到crash点即可!

正如@Peter Cordes 在下面的评论中所述,您也可以直接在 gdb 中运行程序,让它在那里崩溃,因此您无需事后打开核心文件!他说

这些 GDB 命令并非特定于核心文件,只要您在断点处停止,它们就会起作用。如果您遇到可重现的崩溃,在 GDB 下运行程序通常更容易/更好(如gdb ./a.out),因此 GDB 会将进程保存在内存中而不是核心文件中。主要优点是您可以崩溃之前的某处设置断点或观察点,并单步查看发生了什么。或者使用 GDB 的记录工具,您可能能够倒退并查看导致崩溃的原因,但这可能是不稳定的、缓慢的和内存密集型的。

如上所述,您应该使用调试符号和优化级别 0 编译程序,使用-ggdb -O0.

现在在 gdb 中运行程序:

Core was generated by `sleep 30'.
Program terminated with signal SIGQUIT, Quit.
#0  0x00007f93ed32d334 in ?? ()
(gdb) 
Run Code Online (Sandbox Code Playgroud)

如果您需要手动将回溯记录到日志文件以供稍后分析,您可以这样做(改编自我的eRCaGuy_dotfiles repo here 中的注释):

(gdb) bt
#0  0x00007f93ed32d334 in ?? ()
#1  0x000000000000000a in ?? ()
#2  0x00007f93ed2960a5 in ?? ()
#3  0x0000000000000000 in ?? ()
(gdb)
Run Code Online (Sandbox Code Playgroud)

完毕!您现在已将 gdb 回溯保存在文件“gdb_log.txt”中。

参考:

  1. [我需要的答案就在这个问题本身] /sf/ask/144613871/
  2. /sf/ask/358092941/
  3. /sf/ask/581410651/#30524347
  4. [非常有用的信息,包括。强制核心转储的Ctrl+\技巧!] https://unix.stackexchange.com/questions/277331/segmentation-fault-core-dumped-to-where-what-is-it-and-why/409776#409776
  5. [参考上述答案] https://unix.stackexchange.com/questions/179998/where-to-search-for-the-core-file-generated-by-the-crash-of-a-linux-application /180004#180004
  6. [答案就在问题本身中]我在哪里可以找到 ubuntu 16.04LTS 中的核心转储?
  7. [我的回答] Stack Overflow:编译器的-O0选项和-Og选项有什么区别?

补充阅读

  1. [我还需要研究和尝试这种]如何使用LD_PRELOADgdb/sf/ask/731377811/

  • 如果您有调试符号,“bt full”就很好:显示参数和其他内容。或者对于多线程程序甚至是“线程应用所有 bt full”。(尽管这比您通常想要一次性查看的内容要多,所以它对于发送错误报告比您自己使用更有用。) (2认同)
  • 这些 GDB 命令并非特定于核心文件,只要您在断点处停止,它们就会起作用。如果您有可重现的崩溃,在 GDB 下运行您的程序通常更容易/更好(如`gdb ./a.out`),因此 GDB 会将进程保存在内存中而不是核心文件中。主要优点是您可以在崩溃之前* 某处设置断点或观察点,并单步查看发生了什么。或者使用 GDB 的记录工具,您可能能够*后退* 并查看导致崩溃的原因,但这可能会不稳定、缓慢且占用大量内存。 (2认同)

Cir*_*郝海东 8

核心文件的位置取决于/proc/sys/kernel/core_pattern

例如在 Ubuntu 22.04 上:

cat /proc/sys/kernel/core_pattern
Run Code Online (Sandbox Code Playgroud)

给出:

|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E
Run Code Online (Sandbox Code Playgroud)

神奇的前导管道语法意味着 Linux 内核将/usr/share/apport/apport使用一堆信息参数调用程序,然后apport可执行文件将处理转储并将其存储在某处。

apport特别是 Ubuntu 超自动化错误统计设置的一部分,我认为 Canonical 保留了同意提供这些数据以帮助确定开发优先级的用户的所有崩溃的数据库https://wiki.ubuntu.com/Appport

如果您想按照 Linux 的预期在当前目录中获取一个简单的原始核心文件,则必须将其设置为类似以下内容core

echo 'core' | sudo tee /proc/sys/kernel/core_pattern
Run Code Online (Sandbox Code Playgroud)

如何在重新启动后持久更改它:如何永久编辑 core_pattern 文件?

如果你这样做然后:

ulimit -c unlimited
Run Code Online (Sandbox Code Playgroud)

当前内核 5.15.0 转储文件的格式为:

core.<pid>
Run Code Online (Sandbox Code Playgroud)

例如:

core.494536
Run Code Online (Sandbox Code Playgroud)

在当前工作目录下。

显然也可以通过以下方式关闭批准:如何启用或禁用批准?

sudo systemctl disable apport.service
Run Code Online (Sandbox Code Playgroud)

您可以使用最小的 C 程序来测试它:

段错误.c

#include <stdlib.h>

int main(void) {
    *(int *)0 = 1;
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

gcc -ggdb3 -O0 -pedantic-errors -std=c89 -Wall -Wextra -o segfault.out segfault.c
./segfault.out
Run Code Online (Sandbox Code Playgroud)

如何获取 apport 核心转储?

解释于:/sf/ask/144613871/#47481884

如果您决定保留 Ubuntu 庞大的固执己见的审批系统,让我们学习如何使用它。

也许非程序不会core.1234时不时地在其目录中随机创建随机文件是最好的。

apport将核心转储存储.crash/var/crash/. .crash文件是包含核心转储和更多系统日志的包装器,以帮助 Ubuntu 开发人员进行调试。

如果你只是运行:

./segfault.out
Run Code Online (Sandbox Code Playgroud)

你什么也得不到。“伟大的”默认行为!

但是我在哪里可以找到 ubuntu 16.04LTS 中的核心转储?解释说我们可以在以下位置查找批准日志:

cat /var/log/apport.log
Run Code Online (Sandbox Code Playgroud)

现在包含类型的条目:

ERROR: apport (pid 503174) Sat Nov 26 21:51:47 2022: called for pid 503173, signal 11, core limit 18446744073709551615, dump mode 1
ERROR: apport (pid 503174) Sat Nov 26 21:51:47 2022: ignoring implausibly big core limit, treating as unlimited
ERROR: apport (pid 503174) Sat Nov 26 21:51:47 2022: executable: /home/ciro/segfault.out (command line "./segfault.out")
ERROR: apport (pid 503174) Sat Nov 26 21:51:47 2022: executable does not belong to a package, ignoring
ERROR: apport (pid 503174) Sat Nov 26 21:51:47 2022: writing core dump to core._home_ciro_segfault_out.1000.57a7653e-d57e-4871-ad0d-f7b8d9f5b3b9.503173.5035472 (limit: -1)
Run Code Online (Sandbox Code Playgroud)

并且根据忽略不会生成任何文件。

/sf/ask/994347301/询问如何启用核心,因此我们尝试:

mkdir -p ~/.config/apport
printf '[main]
unpackaged=true
' >> ~/.config/apport/settings
Run Code Online (Sandbox Code Playgroud)

现在它可以工作了,我们.crash在下面有一个文件/var/crash

-rw-r--r-- 1 ciro     whoopsie    0 Nov 26 22:09 _home_ciro_segfault.out.1000.upload
-rw-r----- 1 ciro     whoopsie 137K Nov 26 22:09 _home_ciro_segfault.out.1000.crash
-rw------- 1 whoopsie whoopsie   37 Nov 26 22:09 _home_ciro_segfault.out.1000.uploaded
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用以下命令提取核心转储:

apport-unpack /var/crash/_home_ciro_segfault.out.1000.crash segfault
Run Code Online (Sandbox Code Playgroud)

它创建一个名为的目录segfault/,其中包含一堆从.crash文件中分离出来的文件,包括我们的 core dump segfault/CoreDump

程序确实崩溃了,但是由于一个bug,这是可怜的:

Traceback (most recent call last):
  File "/usr/bin/apport-unpack", line 77, in <module>
    pr.extract_keys(f, bin_keys, dir)
  File "/usr/lib/python3/dist-packages/problem_report.py", line 269, in extract_keys
    raise ValueError('%s has no binary content' %
ValueError: ['separator'] has no binary content
Run Code Online (Sandbox Code Playgroud)

报告地址: https: //bugs.launchpad.net/ubuntu/+source/apport/+bug/1889443但在生成我们的代码后它崩溃了CoreDump,所以我们可以成功运行:

gdb segfault.out segfault/CoreDump
Run Code Online (Sandbox Code Playgroud)

还有一种可能更自动化的方法:

apport-retrace /var/crash/_home_ciro_segfault.out.1000.crash
Run Code Online (Sandbox Code Playgroud)

但对于非打包程序则失败:

ERROR: report file does not contain one of the required fields: Package
Run Code Online (Sandbox Code Playgroud)

在 Ubuntu 22.04 上测试。

如何防止“此可执行文件已崩溃2次,忽略”?

如果您继续使用我们的最小测试./segfault.out,您会注意到在某些时候.crash文件停止生成。

cat /var/log/apport.log
Run Code Online (Sandbox Code Playgroud)

然后澄清:

这个可执行文件已经崩溃了 2 次,忽略

天哪,这个系统不是为开发人员考虑的!

从源代码来看,apt source apport乍一看似乎不可配置:

    crash_counter = 0

    # Create crash report file descriptor for writing the report into
    # report_dir
    try:
        report = '%s/%s.%i.crash' % (apport.fileutils.report_dir, info['ExecutablePath'].replace('/', '_'), pidstat.st_uid)
        if os.path.exists(report):
            if apport.fileutils.seen_report(report):
                # do not flood the logs and the user with repeated crashes
                # and make sure the file isn't a FIFO or symlink
                fd = os.open(report, os.O_NOFOLLOW | os.O_RDONLY | os.O_NONBLOCK)
                st = os.fstat(fd)
                if stat.S_ISREG(st.st_mode):
                    with os.fdopen(fd, 'rb') as f:
                        crash_counter = apport.fileutils.get_recent_crashes(f)
                    crash_counter += 1
                    if crash_counter > 1:
                        write_user_coredump(
                            pid, process_start, core_ulimit, coredump_fd
                        )
                        error_log('this executable already crashed %i times, ignoring' % crash_counter)
                        sys.exit(0)
                # remove the old file, so that we can create the new one with
                # os.O_CREAT|os.O_EXCL
                os.unlink(report)
Run Code Online (Sandbox Code Playgroud)

如何防止 apport 上传/请求上传非打包二进制文件的报告?

在 Ubuntu 22.04 下:

  • 设置
  • 隐私
  • 诊断
  • 向 Canonical 发送错误报告

有三种可能的选择:

  • 绝不
  • 自动,这似乎意味着更准确的意思:“总是不问”
  • 手册,用简单的英语来说,意思是“发送前询问”,并带有弹出窗口

我不知道如何阻止使用“自动”方法上传。而“手动”的弹出窗口会让你发疯。因此,唯一合理的选项是“从不”或禁用 apport:

如何分析核心转储?

这是非常独立于 Ubuntu 的,所以只需:

Mozillarr逆向调试是终极“核心文件”

核心文件允许您检查中断时的堆栈。

但总的来说,您真正需要做的是及时返回以进一步确定根本故障原因。

令人惊奇的 Mozilla rr 允许您做到这一点,但代价是更大的跟踪文件和轻微的性能影响。

示例: https: //stackoverflow.com/questions/1470434/how-does-reverse-debugging-work/53063242#53063242