![我在哪里可以找到核心转储文件,以及如何查看和分析回溯(堆栈跟踪)?](https://linux22.com/image/938899/%E6%88%91%E5%9C%A8%E5%93%AA%E9%87%8C%E5%8F%AF%E4%BB%A5%E6%89%BE%E5%88%B0%E6%A0%B8%E5%BF%83%E8%BD%AC%E5%82%A8%E6%96%87%E4%BB%B6%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E6%9F%A5%E7%9C%8B%E5%92%8C%E5%88%86%E6%9E%90%E5%9B%9E%E6%BA%AF%EF%BC%88%E5%A0%86%E6%A0%88%E8%B7%9F%E8%B8%AA%EF%BC%89%EF%BC%9F.png)
当我在 Ubuntu 20.04 上运行 C 程序时,出现此运行时错误:
Segmentation fault (core dumped)
我确实需要找到并查看该core
文件,但我找不到它。它在哪里?我如何查看其中的回溯?
答案1
在 Ubuntu 20.04 中测试。
1. 启用核心文件
首先,运行ulimit -c
以查看系统上允许的核心文件的最大大小。对于我来说,在 Ubuntu 20.04 上,我的返回是0
,这意味着无法创建核心文件。
ulimit --help
显示的含义-c
:
-c the maximum size of core files created
因此,将允许的核心文件大小设置为unlimited
,如下所示。请注意,我认为这仅适用于到你运行这个命令的终端我也这么做不是认为它在重新启动后仍然是持久的,所以每次你想要创建核心文件时都必须运行它,并且在每个你希望它工作的终端中:
# set max core dump file size to unlimited
ulimit -c unlimited
# verify it is now set to "unlimited"
ulimit -c
就这样!现在,运行你的程序,如果它崩溃并进行“核心转储”,它会将核心作为core
文件转储到与您调用可执行文件时所在的目录相同。文件的名称很简单,就是“核心”。
更新:等一下,核心文件又在哪里?
我测试了这里的所有代码和示例,它们对我来说都是可行的。但是,如果你这样做不是按照上述方法在本地获取core
文件,显然你并不孤单。因此,请尝试以下操作:
- (从 Ubuntu 21.10 和 22.04 开始):查看内部
/var/lib/apport/coredump
,正如@guyr 在另一个答案中所解释的那样。 - 在这里寻找答案:
- 我在哪里可以找到 ubuntu 16.04LTS 中的核心转储?. 可能性(或要运行的命令)包括:
/var/crash/
cat /var/log/apport.log
- [很多投票!]堆栈溢出:核心已转储,但核心文件不在当前目录中?
- systemd 可能会在这里引起一些问题
- 我在哪里可以找到 ubuntu 16.04LTS 中的核心转储?. 可能性(或要运行的命令)包括:
- 如果你的核心转储文件不像我的一样工作,请留言说明你的 Linux 版本(例如:Ubuntu 22.04)、你的内核版本(跑步
cat /proc/version
),以及核心转储文件的位置。- 以下是我自己的答案:在 Linux Ubuntu 18.04 和 20.04 上,
ulimit -c unlimited
核心转储文件出现在我所在的目录中。我必须手动删除旧文件才能创建新文件。在我的 Ubuntu 18.04 机器上,cat /proc/version
显示Linux version 4.15.0-194-generic (buildd@lcy02-amd64-052) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04))
。
- 以下是我自己的答案:在 Linux Ubuntu 18.04 和 20.04 上,
2. 在 gdb 中查看 backtrace
您应该在编译 C 或 C++ 程序时启用调试符号,以便查看核心文件中的有用信息。如果没有调试符号,您只能看到调用的函数的地址,而看不到实际的名称或行号。
在 gcc 中,使用-ggdb -O0
来打开针对NU e uggerg
优化的 debu 符号。您也可以使用、等,但最好是。我们真的需要优化级别 0()吗?是的,是的。请在此处查看我的答案:gdb
G
d
b
-g -O0
-g3 -O0
-ggdb -O0
-O0
-O0
Stack Overflow:编译器的选项和-Og
选项之间有什么区别?
C 和 C++ 中的构建和运行命令示例:因此,使用 C 或 C++ 的完整构建和运行命令可能如下所示:
# C build and run command for "hello_world.c"
gcc -Wall -Wextra -Werror -ggdb -O0 -std=c11 -o hello_world hello_world.c \
&& ./hello_world
# C++ build and run command for "hello_world.c"
g++ -Wall -Wextra -Werror -ggdb -O0 -std=c++17 -o hello_world hello_world.c \
&& ./hello_world
像这样打开核心文件gdb
:
gdb path/to/my/executable path/to/core
假设您刚刚运行path/to/my/executable
,那么该core
文件将位于核心转储时所在的同一目录中,因此您只需运行以下命令:
gdb path/to/my/executable core
在 中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
重要的:当发生核心转储时,它不会自动core
用新文件覆盖当前目录中任何现有的文件,因此你必须手动删除当程序崩溃时,在生成新的核心文件之前先检查旧core
文件rm core
,以便始终有最新的核心文件可供分析。
3. 尝试一下
- 在终端中,运行
sleep 30
以启动一个休眠 30 秒的进程。 - 在运行时,按Ctrl+\强制进行核心转储。现在您将
core
在所在目录中看到一个文件。 - 由于我们没有包含调试符号的可执行文件,因此我们将在 gdb 中打开核心文件,而不是包含符号的可执行文件 + 核心文件。因此,运行
gdb -c core
以打开强制崩溃刚刚创建的核心文件。 - 你会看到这个。请注意,它知道
sleep 30
当核心转储发生时你调用了什么命令():Core was generated by `sleep 30'. Program terminated with signal SIGQUIT, Quit. #0 0x00007f93ed32d334 in ?? () (gdb)
- 运行
bt
或where
查看回溯。您将看到以下内容:(gdb) bt #0 0x00007f93ed32d334 in ?? () #1 0x000000000000000a in ?? () #2 0x00007f93ed2960a5 in ?? () #3 0x0000000000000000 in ?? () (gdb)
- 这些是调用堆栈上调用的函数的地址。如果您打开了调试符号,您会看到更多信息,包括函数名称和行号,如下所示(从我的 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
4. 忘记核心文件,直接在 gdb 中运行程序到崩溃点!
正如@Peter Cordes 在下面的评论中所述,您也可以直接在 gdb 内部运行该程序,让它在那里崩溃,这样您就无需在事后打开核心文件!他说:
这些 GDB 命令并非特定于核心文件,只要您在断点处停止,它们就会起作用。如果您遇到可重现的崩溃,在 GDB 下运行程序通常更容易/更好(例如
gdb ./a.out
),这样 GDB 就会将进程保存在内存中而不是核心文件中。主要优点是您可以在某处设置断点或观察点前崩溃的东西,然后单步查看发生了什么。或者使用 GDB 的记录功能,你可以单步执行向后并查看导致崩溃的原因,但这可能会很不稳定、很慢并且占用大量内存。
如上所述,您应该使用 编译了带有调试符号和优化级别 0 的程序-ggdb -O0
。请参阅上面 C 和 C++ 中的完整示例构建和运行命令。
现在在 gdb 中运行该程序:
# Open the executable in gdb
gdb path/to/my/executable
# Run it (if it's still crashing, you'll see it crash)
r
# View the backtrace (call stack)
bt
# Quit when done
q
如果你需要手动将回溯记录到日志文件以便稍后进行分析,你可以这样做(改编自在我的笔记中eRCaGuy_dotfiles回购这里):
set logging file gdb_log.txt
set logging on
set trace-commands on
show logging # prove logging is on
flush
set pretty print on
bt # view the backtrace
set logging off
show logging # prove logging is back off
完成了!现在您已将 gdb 回溯保存在文件“gdb_log.txt”中。
参考:
- [我需要的答案就在这个问题本身中]https://stackoverflow.com/questions/2065912/core-dumped-but-core-file-is-not-in-the-current-directory
- https://stackoverflow.com/questions/5115613/core-dump-file-analysis
- https://stackoverflow.com/questions/8305866/how-do-i-analyze-a-programs-core-dump-file-with-gdb-when-it-has-command-line-pa/30524347#30524347
- [非常有用的信息,包括强制核心转储的技巧!Ctrl]\https://unix.stackexchange.com/questions/277331/segmentation-fault-core-dumped-to-where-what-is-it-and-why/409776#409776
- [参考上面的答案]https://unix.stackexchange.com/questions/179998/where-to-search-for-the-core-file-generated-by-the-crash-of-a-linux-application/180004#180004
- [答案就在问题本身中]我在哪里可以找到 ubuntu 16.04LTS 中的核心转储?
- [我的答案]
-O0
Stack Overflow:编译器的选项和-Og
选项之间有什么区别?
其他阅读材料
- [我还需要学习和尝试]如何
LD_PRELOAD
使用gdb
:https://stackoverflow.com/questions/10448254/how-to-use-gdb-with-ld-preload
答案2
通过搜索找到。我正在运行 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
注意核心限制为 0,这意味着不会生成任何核心转储文件。因此,我运行了本文中显示的命令 ( ulimit -c unlimited
),这次apport.log
显示了以下内容:
writing core dump to core._my_prog.1000.e43b2f33-4708-438c-a7d7-05062f381382.5650.795448 (limit: -1)
我在当前目录或包含可执行文件的目录中找不到它,因此我在整个系统中进行了查找并在中找到它/var/lib/apport/coredump
。
答案3
核心文件的位置取决于/proc/sys/kernel/core_pattern
例如在 Ubuntu 22.04 上:
cat /proc/sys/kernel/core_pattern
给出:
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E
神奇的前导管道语法意味着 Linux 内核将/usr/share/apport/apport
使用一堆信息参数调用程序,然后apport
可执行文件将处理并将转储存储在某处。
apport
特别是作为 Ubuntu 超级自动化错误统计设置的一部分,我认为 Canonical 保存了所有同意提供崩溃信息的用户的数据库,以帮助确定开发的优先级https://wiki.ubuntu.com/Apport
如果您想要按照 Linux 的意图在当前目录中获取一个简单的原始核心文件,那么您必须将其设置为类似以下内容core
:
echo 'core' | sudo tee /proc/sys/kernel/core_pattern
如何在重启后持久更改它:如何永久编辑 core_pattern 文件?
如果你这样做然后:
ulimit -c unlimited
当前内核 5.15.0 的转储文件形式如下:
core.<pid>
例如:
core.494536
在当前工作目录下。
显然也可以通过以下方式关闭 apport:如何启用或禁用 Apport?
sudo systemctl disable apport.service
你可以用一个最小的 C 程序来测试这个:
段错误.c
#include <stdlib.h>
int main(void) {
*(int *)0 = 1;
return EXIT_SUCCESS;
}
编译并运行:
gcc -ggdb3 -O0 -pedantic-errors -std=c89 -Wall -Wextra -o segfault.out segfault.c
./segfault.out
如何获取 apport 核心转储?
如果您决定保留 Ubuntu 的超级自以为是的 apport 系统,让我们学习如何使用它。
也许最好的情况是非程序不会core.1234
时不时地在其目录中随机创建随机文件。
apport
将核心转储存储在下.crash
的文件下/var/crash/
。.crash
文件是包含核心转储和更多系统日志的包装器,以帮助 Ubuntu 开发人员进行调试。
如果你只是运行:
./segfault.out
你什么也没得到。“很棒”的默认行为!
但我在哪里可以找到 ubuntu 16.04LTS 中的核心转储?解释说我们可以在下方查找 apport 日志:
cat /var/log/apport.log
现在包含以下类型的条目:
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)
并且没有根据忽略生成任何文件。
https://stackoverflow.com/questions/14204961/how-to-change-apport-default-behaviour-for-non-packaged-application-crashes询问如何启用核心,因此我们尝试:
mkdir -p ~/.config/apport
printf '[main]
unpackaged=true
' >> ~/.config/apport/settings
现在它可以正常工作了,我们有一个.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
然后我们可以使用以下命令提取核心转储:
apport-unpack /var/crash/_home_ciro_segfault.out.1000.crash segfault
这将创建一个名为的目录,segfault/
其中包含从文件中分离出来的一堆文件.crash
,包括我们的核心转储segfault/CoreDump
。
然而由于一个错误,程序确实崩溃了,这很可怜:
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
报到地点:https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1889443但它在生成后崩溃了,CoreDump
因此我们可以成功运行:
gdb segfault.out segfault/CoreDump
还有一种可能更加自动化的方法:
apport-retrace /var/crash/_home_ciro_segfault.out.1000.crash
但对于非打包程序,它会失败:
ERROR: report file does not contain one of the required fields: Package
在 Ubuntu 22.04 上测试。
如何防止“该可执行文件已崩溃两次,忽略”?
如果您继续使用我们的最小测试./segfault.out
,您会注意到在某些时候.crash
文件会停止生成。
cat /var/log/apport.log
然后澄清说:
此可执行文件已崩溃 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)
如何阻止 apport 上传/请求上传非打包二进制文件的报告?
在 Ubuntu 22.04 下:
- 设置
- 隐私
- 诊断
- 向 Canonical 发送错误报告
有三种可能的选择:
- 绝不
- 自动,似乎更准确的意思:“永远无需询问”
- 手册,用简单的英语来说就是“发送前询问”,并弹出一个窗口
我不知道如何使用“自动”方法阻止上传。而“手动”方法会弹出很多弹窗,让你抓狂。因此,唯一合理的选项是“从不”或禁用 apport:
如何分析核心转储?
这与 Ubuntu 非常无关,因此只需执行以下操作:
- https://stackoverflow.com/questions/8305866/how-do-i-analyze-a-programs-core-dump-file-with-gdb-when-it-has-command-line-pa
- https://unix.stackexchange.com/questions/89933/how-to-view-core-files-for-debugging-purposes-in-linux
Mozillarr
逆向调试之终极“核心文件”
核心文件允许您检查中断时的堆栈。
但一般来说,您真正需要做的是回顾过去,进一步确定故障的根本原因。
令人惊奇的 Mozilla rr 允许您这样做,但代价是更大的跟踪文件和轻微的性能损失。
例如:https://stackoverflow.com/questions/1470434/how-does-reverse-debugging-work/53063242#53063242