Como gerar um dump principal no Linux quando um processo recebe uma falha de segmentação?

Eu tenho um processo no Linux que está recebendo uma falha de segmentação. Como posso dizer para gerar um dump de núcleo quando ele falhar?

Isso depende do shell que você está usando. Se você estiver usando o bash, o comando ulimit controlará várias configurações relacionadas à execução do programa, por exemplo, se você deve fazer o dump core. Se você digitar

ulimit -c unlimited 

então isso dirá que seus programas podem despejar núcleos de qualquer tamanho. Você pode especificar um tamanho como 52M em vez de ilimitado, se quiser, mas na prática isso não deve ser necessário, pois o tamanho dos arquivos principais provavelmente nunca será um problema para você.

No tcsh, você digitaria

 limit coredumpsize unlimited 

Como explicado acima, a pergunta real que está sendo feita aqui é como habilitar os core dumps em um sistema onde eles não estão habilitados. Essa pergunta é respondida aqui.

Se você veio aqui esperando aprender como gerar um dump principal para um processo suspenso, a resposta é

 gcore  

se o gcore não estiver disponível no seu sistema, então

 kill -ABRT  

Não use kill -SEGV, pois isso frequentemente invoca um manipulador de sinais, dificultando o diagnóstico do processo travado.

O que eu fiz no final foi append o gdb ao processo antes dele travar, e então quando ele conseguiu o segfault eu executei o comando generate-core-file . Isso forçou a geração de um dump de núcleo.

Talvez você possa fazer dessa maneira, esse programa é uma demonstração de como interceptar uma falha de segmentação e shells para um depurador (esse é o código original usado no AIX ) e imprime o rastreamento de pilha até o ponto de uma falha de segmentação. Você precisará alterar a variável sprintf para usar o gdb no caso do Linux.

 #include  #include  #include  #include  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 */ } 

Você pode ter que adicionar adicionalmente um parâmetro para obter o gdb para despejar o núcleo, como mostrado aqui neste blog.

Para verificar onde os dumps principais são gerados, execute:

 sysctl kernel.core_pattern 

onde %e é o nome do processo e %t a hora do sistema. Você pode alterá-lo em /etc/sysctl.conf e recarregar por sysctl -p .

Se os arquivos principais não forem gerados (teste-o por: sleep 10 & e killall -SIGSEGV sleep ), verifique os limites por: ulimit -a .

Se o tamanho do arquivo principal for limitado, execute:

 ulimit -c unlimited 

para torná-lo ilimitado.

Em seguida, teste novamente, se o core dumping for bem sucedido, você verá “(core dumping)” após a indicação de falha de segmentação, conforme abaixo:

Falha de segmentação: 11 (core dumping)


Ubuntu

No Ubuntu, geralmente, os dumps são manipulados pelo apport em /var/crash/ , mas em formato diferente, no entanto, ele não é ativado por padrão em versões estáveis. Leia mais no wiki do Ubuntu .

Ele usa core_pattern para core_pattern diretamente o dump principal para o apport:

 $ cat /proc/sys/kernel/core_pattern |/usr/share/apport/apport %p %s %c 

Assim, mesmo os arquivos principais são desativados pelo ulimit , o apport continuará capturando o travamento ( Como habilitar ou desabilitar o Apport? ).


Mac OS

Para macOS, veja: Como gerar core dumps no Mac OS X?

Há mais coisas que podem influenciar a geração de um dump principal. Eu encontrei estes:

  • o diretório para o despejo deve ser gravável. Por padrão, este é o diretório atual do processo, mas isso pode ser alterado pela configuração /proc/sys/kernel/core_pattern .
  • em algumas condições, o valor do kernel em /proc/sys/fs/suid_dumpable pode impedir que o núcleo seja gerado.

Existem mais situações que podem impedir a geração descrita na página man – tente man core .

Para ativar o dump principal, faça o seguinte:

  1. Em /etc/profile comente a linha:

     # ulimit -S -c 0 > /dev/null 2>&1 
  2. Em /etc/security/limits.conf comente a linha:

     * soft core 0 
  3. execute o cmd limit coredumpsize unlimited e verifique com 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. para verificar se o corefile foi escrito, você pode matar o processo relacionado com cmd kill -s SEGV (não deve ser necessário, apenas no caso de nenhum arquivo principal ser gravado, isso pode ser usado como uma checagem):

     # kill -s SEGV  

Uma vez que o corefile tenha sido escrito, certifique-se de desativar as configurações do coredump novamente nos arquivos relacionados (1./2./3.)!

Para o Ubuntu 14.04

  1. Verifique o dump principal ativado:

     ulimit -a 
  2. Uma das linhas deve ser:

     core file size (blocks, -c) unlimited 
  3. Se não :

    gedit ~/.bashrc e adicione ulimit -c unlimited ao final do arquivo e salve, execute novamente o terminal.

  4. Construa seu aplicativo com informações de debugging:

    Em Makefile -O0 -g

  5. Execute o aplicativo que cria o dump principal (o arquivo de dump principal com o nome ‘core’ deve ser criado próximo ao arquivo application_name):

     ./application_name 
  6. Execute sob o gdb:

     gdb application_name core 

Por padrão, você receberá um arquivo principal. Verifique se o diretório atual do processo é gravável ou se nenhum arquivo principal será criado.

É melhor ativar programaticamente o despejo de setrlimit usando a chamada de sistema setrlimit .

exemplo:

 #include  bool enable_core_dump(){ struct rlimit corelim; corelim.rlim_cur = RLIM_INFINITY; corelim.rlim_max = RLIM_INFINITY; return (0 == setrlimit(RLIMIT_CORE, &corelim)); }