如何在 Linux 中针对分段错误生成核心转储?
题
我的 Linux 中有一个进程出现分段错误。我怎样才能告诉它在失败时生成核心转储?
解决方案
这取决于您使用的 shell。如果您使用的是 bash,则 ulimit 命令控制与程序执行相关的多个设置,例如是否应该转储核心。如果您输入
ulimit -c unlimited
那么这将告诉 bash 它的程序可以转储任何大小的核心。如果需要,您可以指定一个大小,例如 52M,而不是无限制,但实际上这不是必需的,因为核心文件的大小可能永远不会成为您的问题。
在 tcsh 中,您可以输入
limit coredumpsize unlimited
其他提示
如上所述,这里提出的真正问题是如何在未启用核心转储的系统上启用核心转储。这个问题在这里得到了解答。
如果您来这里希望了解如何为挂起的进程生成核心转储,答案是
gcore <pid>
如果 gcore 在您的系统上不可用
kill -ABRT <pid>
不要使用kill -SEGV,因为这通常会调用信号处理程序,从而使诊断卡住的进程变得更加困难
我最后做的是在崩溃之前将 gdb 附加到进程,然后当它出现段错误时我执行了 generate-core-file
命令。这强制生成了核心转储。
要检查核心转储的生成位置,请运行:
sysctl kernel.core_pattern
或者:
cat /proc/sys/kernel/core_pattern
在哪里 %e
是进程名称并且 %t
系统时间。你可以改变它 /etc/sysctl.conf
并重新加载 sysctl -p
.
如果没有生成核心文件(通过以下方式测试: sleep 10 &
和 killall -SIGSEGV sleep
),通过以下方式检查限制: ulimit -a
.
如果您的核心文件大小有限,请运行:
ulimit -c unlimited
使其不受限制。
然后再次测试,如果核心转储成功,您将在分段错误指示后看到“(core dumped)”,如下所示:
分段故障:11(核心转储)
也可以看看: 核心转储 - 但核心文件不在当前目录中?
乌班图
在 Ubuntu 中,核心转储由 同意 并且可以位于 /var/crash/
. 。但是,在稳定版本中默认情况下禁用它。
欲了解更多详情,请查看: 在 Ubuntu 中哪里可以找到核心转储?.
苹果系统
对于 macOS,请参阅: 如何在 Mac OS X 中生成核心转储?
也许你可以这样做,这个程序演示了如何捕获分段错误并外壳到调试器(这是在下使用的原始代码 AIX
)并打印堆栈跟踪直到出现分段错误。您将需要更改 sprintf
要使用的变量 gdb
以Linux为例。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>
static void signal_handler(int);
static void dumpstack(void);
static void cleanup(void);
void init_signals(void);
void panic(const char *, ...);
struct sigaction sigact;
char *progname;
int main(int argc, char **argv) {
char *s;
progname = *(argv);
atexit(cleanup);
init_signals();
printf("About to seg fault by assigning zero to *s\n");
*s = 0;
sigemptyset(&sigact.sa_mask);
return 0;
}
void init_signals(void) {
sigact.sa_handler = signal_handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGSEGV);
sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGBUS);
sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGQUIT);
sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGHUP);
sigaction(SIGHUP, &sigact, (struct sigaction *)NULL);
sigaddset(&sigact.sa_mask, SIGKILL);
sigaction(SIGKILL, &sigact, (struct sigaction *)NULL);
}
static void signal_handler(int sig) {
if (sig == SIGHUP) panic("FATAL: Program hanged up\n");
if (sig == SIGSEGV || sig == SIGBUS){
dumpstack();
panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown"));
}
if (sig == SIGQUIT) panic("QUIT signal ended program\n");
if (sig == SIGKILL) panic("KILL signal ended program\n");
if (sig == SIGINT) ;
}
void panic(const char *fmt, ...) {
char buf[50];
va_list argptr;
va_start(argptr, fmt);
vsprintf(buf, fmt, argptr);
va_end(argptr);
fprintf(stderr, buf);
exit(-1);
}
static void dumpstack(void) {
/* Got this routine from http://www.whitefang.com/unix/faq_toc.html
** Section 6.5. Modified to redirect to file to prevent clutter
*/
/* This needs to be changed... */
char dbx[160];
sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname);
/* Change the dbx to gdb */
system(dbx);
return;
}
void cleanup(void) {
sigemptyset(&sigact.sa_mask);
/* Do any cleaning up chores here */
}
您可能还需要额外添加一个参数来让 gdb 转储核心,如本博客中所示 这里.
还有更多因素可能会影响核心转储的生成。我遇到过这些:
- 转储目录必须可写。默认情况下,这是进程的当前目录,但可以通过设置更改
/proc/sys/kernel/core_pattern
. - 在某些情况下,核值
/proc/sys/fs/suid_dumpable
可能会阻止核心的生成。
还有更多可能阻止生成的情况,如手册页中所述 - 尝试 man core
.
要激活核心转储,请执行以下操作:
在
/etc/profile
评论该行:# ulimit -S -c 0 > /dev/null 2>&1
在
/etc/security/limits.conf
注释掉这一行:* soft core 0
执行cmd
limit coredumpsize unlimited
并用cmd检查一下limit
:# limit coredumpsize unlimited # limit cputime unlimited filesize unlimited datasize unlimited stacksize 10240 kbytes coredumpsize unlimited memoryuse unlimited vmemoryuse unlimited descriptors 1024 memorylocked 32 kbytes maxproc 528383 #
要检查 corefile 是否已写入,您可以使用 cmd 终止相关进程
kill -s SEGV <PID>
(应该不需要,以防万一没有写入核心文件,这可以用作检查):# kill -s SEGV <PID>
写入核心文件后,请确保在相关文件 (1./2./3.) 中再次停用核心转储设置!
对于 Ubuntu 14.04
检查核心转储是否启用:
ulimit -a
其中一行应该是:
core file size (blocks, -c) unlimited
如果不 :
gedit ~/.bashrc
并添加ulimit -c unlimited
到文件末尾并保存,重新运行终端。使用调试信息构建您的应用程序:
在生成文件中
-O0 -g
运行创建核心转储的应用程序(应在 application_name 文件附近创建名为“core”的核心转储文件):
./application_name
在gdb下运行:
gdb application_name core
默认情况下,您将获得一个核心文件。检查进程当前目录是否可写,否则不会创建core文件。
最好使用系统调用以编程方式打开核心转储 setrlimit
.
例子:
#include <sys/resource.h>
bool enable_core_dump(){
struct rlimit corelim;
corelim.rlim_cur = RLIM_INFINITY;
corelim.rlim_max = RLIM_INFINITY;
return (0 == setrlimit(RLIMIT_CORE, &corelim));
}
等待。它会自动执行。无需制作
值得一提的是,如果您有 系统 设置好之后,事情就有点不同了。设置通常会通过管道传输核心文件 core_pattern
sysctl 值,通过 systemd-coredump(8)
. 。核心文件大小 rlimit 通常已配置为“无限制”。
然后可以使用以下命令检索核心转储 coredumpctl(1)
.
核心转储的存储等配置为 coredump.conf(5)
. 。coredumpctl 手册页中有如何获取核心文件的示例,但简而言之,它看起来像这样:
找到核心文件:
[vps@phoenix]~$ coredumpctl list test_me | tail -1
Sun 2019-01-20 11:17:33 CET 16163 1224 1224 11 present /home/vps/test_me
获取核心文件:
[vps@phoenix]~$ coredumpctl -o test_me.core dump 16163