【问题标题】:How to generate a core dump in Linux on a segmentation fault?如何在 Linux 中在分段错误时生成核心转储?
【发布时间】:2010-09-06 06:53:49
【问题描述】:

我在 Linux 中有一个进程出现分段错误。我如何告诉它在失败时生成核心转储?

【问题讨论】:

标签: linux bash unix coredump tcsh


【解决方案1】:

这取决于您使用的外壳。如果您使用 bash,则 ulimit 命令控制与程序执行相关的几个设置,例如是否应该转储内核。如果你输入

ulimit -c unlimited

然后这将告诉 bash 它的程序可以转储任何大小的核心。如果需要,您可以指定大小,例如 52M 而不是无限制,但实际上这不是必需的,因为核心文件的大小可能永远不会成为您的问题。

在 tcsh 中,你可以输入

limit coredumpsize unlimited

【讨论】:

  • @lzprgmr:澄清一下:默认情况下不生成核心转储的原因是未设置限制和/或设置为 0,从而防止核心转储。通过设置无限制的限制,我们保证始终可以生成核心转储。
  • 这个link 更深入,并提供了更多选项来启用在 linux 中生成核心转储。唯一的缺点是某些命令/设置无法解释。
  • 在 bash 4.1.2(1)-release 限制如 52M 无法指定,导致无效数字错误信息。手册页告诉“值以 1024 字节为增量”。
  • 好吧,我有一个“小”OpenGL 项目,它曾经做了一些奇怪的事情,并导致 X 服务器崩溃。当我重新登录时,我看到了一个可爱的 17 GB 核心文件(在 25 GB 分区上)。限制核心文件的大小绝对是个好主意:)
  • @PolarisUser:如果你想确保你的分区不会被吃掉,我建议设置一个像 1 gig 这样的限制。这应该足够大,可以处理任何合理的核心转储,同时不会威胁到用完所有剩余的硬盘空间。
【解决方案2】:

如上所述,这里提出的真正问题是如何在未启用核心转储的系统上启用核心转储。这个问题在这里得到解答。

如果您来这里是希望了解如何为挂起的进程生成核心转储,答案是

gcore <pid>

如果 gcore 在您的系统上不可用,那么

kill -ABRT <pid>

不要使用 kill -SEGV,因为这通常会调用信号处理程序,从而更难诊断卡住的进程

【讨论】:

  • 我认为-ABRT-SEGV 更有可能调用信号处理程序,因为中止比段错误更可能是可恢复的。 (如果您处理段错误,通常它会在您的处理程序退出时再次触发。)生成核心转储的更好的信号选择是-QUIT
【解决方案3】:

要检查生成核心转储的位置,请运行:

sysctl kernel.core_pattern

或:

cat /proc/sys/kernel/core_pattern

其中%e 是进程名称,%t 是系统时间。您可以在/etc/sysctl.conf 中更改它并通过sysctl -p 重新加载。

如果没有生成核心文件(测试:sleep 10 &amp;killall -SIGSEGV sleep),检查限制:ulimit -a

如果您的核心文件大小有限,请运行:

ulimit -c unlimited

使其不受限制。

然后再次测试,如果core dumped成功,在segmentation fault指示后会看到“(core dumped)”如下:

分段错误:11(核心转储)

另请参阅:core dumped - but core file is not in current directory?


Ubuntu

在 Ubuntu 中,核心转储由 Apport 处理,可以位于 /var/crash/。但是,它在稳定版本中默认禁用。

更多详情请查看:Where do I find the core dump in Ubuntu?

macOS

对于 macOS,请参阅:How to generate core dumps in Mac OS X?

【讨论】:

  • 对于 Ubuntu,要快速恢复正常行为(在当前目录中转储核心文件),只需使用“sudo service apport stop”停止 apport 服务。另请注意,如果您在 docker 中运行,则该设置在主机系统上控制,而不是在容器内。
【解决方案4】:

我最后所做的是在进程崩溃之前将 gdb 附加到进程,然后当它出现段错误时,我执行了generate-core-file 命令。强制生成核心转储。

【讨论】:

  • 你是如何将 gdb 附加到进程的?
  • 要回答 Ritwik G,要将进程附加到 gdb,只需启动 gdb 并输入 'attach ' 其中 是您要附加的进程的 pid 号。跨度>
  • (简称ge
  • 如果他们有新问题,他们应该ask a new question 而不是在评论中提问。
  • 奇怪的是我已经将ulimit -c 设置为unlimited,但是仍然没有创建核心文件,gdb 会话中的generate-core-file 文件确实创建了核心文件,谢谢。
【解决方案5】:

也许你可以这样做,这个程序演示了如何捕获分段错误并将 shell 输出到调试器(这是在AIX 下使用的原始代码)并打印堆栈跟踪到该点的分段错误。对于 Linux,您需要将 sprintf 变量更改为使用 gdb

#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 转储核心,如本博客 here 中所示。

【讨论】:

    【解决方案6】:

    还有更多因素可能会影响核心转储的生成。我遇到了这些:

    • 转储的目录必须是可写的。默认情况下,这是进程的当前目录,但可以通过设置/proc/sys/kernel/core_pattern 来更改。
    • 在某些情况下,/proc/sys/fs/suid_dumpable 中的内核值可能会阻止内核生成。

    还有更多情况可能会阻止手册页中描述的生成 - 尝试man core

    【讨论】:

      【解决方案7】:

      适用于 Ubuntu 14.04

      1. 检查核心转储是否启用:

        ulimit -a
        
      2. 其中一行应该是:

        core file size          (blocks, -c) unlimited
        
      3. 如果不是:

        gedit ~/.bashrc 并将ulimit -c unlimited 添加到文件末尾并保存,重新运行终端。

      4. 使用调试信息构建您的应用程序:

        在 Makefile -O0 -g

      5. 运行创建核心转储的应用程序(应在 application_name 文件附近创建名为“core”的核心转储文件):

        ./application_name
        
      6. 在gdb下运行:

        gdb application_name core
        

      【讨论】:

      • 在第 3 步中,如何“重新运行”终端?你的意思是重启?
      • @Naveen 不,只需关闭终端并打开新终端,似乎您也可以将ulimit -c unlimited 放在终端中作为临时解决方案,因为只有编辑~/.bashrc 需要重新启动终端才能使更改生效。
      【解决方案8】:

      为了激活核心转储,请执行以下操作:

      1. /etc/profile 中评论该行:

        # ulimit -S -c 0 > /dev/null 2>&1
        
      2. /etc/security/limits.conf 中注释掉该行:

        *               soft    core            0
        
      3. 执行 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
        #
        
      4. 要检查核心文件是否被写入,您可以使用 cmd kill -s SEGV &lt;PID&gt; 终止相关进程(应该不需要,以防万一没有写入核心文件,这可以用作检查):

        # kill -s SEGV <PID>
        

      写入核心文件后,请确保在相关文件 (1./2./3.) 中再次停用核心转储设置!

      【讨论】:

        【解决方案9】:

        Ubuntu 19.04

        所有其他答案本身并没有帮助我。但是下面的总结完成了这项工作

        使用以下内容创建~/.config/apport/settings

        [main]
        unpackaged=true
        

        (这告诉 apport 还要为自定义应用编写核心转储)

        检查:ulimit -c。如果它输出 0,用

        修复它
        ulimit -c unlimited
        

        以防万一重启应用:

        sudo systemctl restart apport
        

        崩溃文件现在写入/var/crash/。但是您不能将它们与 gdb 一起使用。要将它们与 gdb 一起使用,请使用

        apport-unpack <location_of_report> <target_directory>
        

        更多信息:

        • 一些答案​​建议更改core_pattern。请注意,该文件可能会在重新启动时被 apport 服务覆盖。
        • 仅仅停止 apport 并没有完成这项工作
        • ulimit -c 值可能会在您尝试其他网络答案时自动更改。请务必在设置核心转储创建过程中定期检查。

        参考资料:

        【讨论】:

          【解决方案10】:

          默认情况下,您将获得一个核心文件。检查进程当前目录是否可写,否则不会创建core文件。

          【讨论】:

          • “进程的当前目录”是指进程运行时的 $cwd 吗? ~/abc> /usr/bin/cat def 如果 cat 崩溃,当前目录是有问题的 ~/abc 还是 /usr/bin?
          • ~/abc.嗯,cmets 必须是 15 个字符长!
          • 这将是 SEGV 时的当前目录。此外,使用与实际用户/组不同的有效用户和/或组运行的进程将不会写入核心文件。
          【解决方案11】:

          最好使用系统调用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));
          }
          

          【讨论】:

          • 为什么这样更好?
          • 崩溃后生成核心文件,无需在命令行环境ulimit -c unlimited,然后重新运行应用程序。
          • 我不希望每次崩溃时都进行核心转储,只有当用户联系我作为开发人员查看它时。如果它崩溃 100 次,我不需要查看 100 个核心转储。
          • 在这种情况下,最好使用ulimit -c unlimited。您也可以使用宏定义进行编译,如果在发布时未定义该宏,应用程序将不包含enable_core_dump 符号,您将获得核心转储替换为调试版本。
          • 即使它是由宏限定的,如果我想生成核心转储,仍然需要我重新编译,而不是在重新运行之前简单地在 shell 中执行命令。
          【解决方案12】:

          值得一提的是,如果您设置了systemd,那么情况会有些不同。该设置通常会通过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
          

          【讨论】:

            【解决方案13】:

            这通常就足够了:

            ulimit -c unlimited
            

            请注意,这将不会在 ssh 部分之间持续存在!添加持久性:

            echo '* soft core unlimited' >> /etc/security/limits.conf
            

            现在,如果您使用的是 Ubuntu,“apport”可能正在运行。检查方法如下:

            sudo systemctl status apport.service
            

            如果是,您可能会在以下位置之一找到核心转储:

            /var/lib/apport/coredump 
            /var/crash
            

            如果您想更改核心转储的位置

            确保您拥有创建文件的权限,并且目录存在在您要将核心转储发送到的目录中!

            这是一个例子。请注意,这不会在重新启动后持续存在

            sysctl -w kernel.core_pattern=/coredumps/core-%e-%s-%u-%g-%p-%t
            mkdir /coredumps
            

            确保崩溃的进程有权对此进行写入。最简单的方法是这样的示例:

            chmod 777 /coredumps
            

            测试核心转储是否有效

            > crash.c
            gcc -Wl,--defsym=main=0 crash.c
            ./a.out
            ==output== Segmentation fault (core dumped)
            

            如果上面没有说“核心转储”,则说明某些东西不工作。

            【讨论】:

              猜你喜欢
              • 2014-06-17
              • 1970-01-01
              • 1970-01-01
              • 2013-06-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-07-14
              • 2015-06-25
              相关资源
              最近更新 更多