Você pode fazer com que algum programa no Linux imprima um rastreamento de pilha, se ele se segmentar?

20

Se eu executar um programa a partir do shell, e ele segfaults:

$ buggy_program
Segmentation fault

No entanto, há uma maneira de conseguir que os programas imprimam um backtrace, talvez executando algo como isto:

$ print_backtrace_if_segfault buggy_program
Segfault in main.c:35
(rest of the backtrace)

Também prefiro não usar strace ou ltrace para esse tipo de informação, pois elas serão impressas de qualquer maneira ...

Neil
fonte

Respostas:

25

Pode haver uma maneira melhor, mas esse tipo de automação.

Coloque o seguinte em ~/backtrace:

backtrace
quit

Coloque isso em um script chamado seg_wrapper.shem um diretório em seu caminho:

#!/bin/bash
ulimit -c unlimited
"$@"
if [[ $? -eq 139 ]]; then
    gdb -q $1 core -x ~/backtrace
fi

O ulimitcomando faz com que o núcleo seja despejado. "$@"são os argumentos fornecidos ao script; portanto, seria o seu programa e seus argumentos. $?detém o status de saída, 139 parece ser o status de saída padrão da minha máquina para um segfault.

Para gdb, -qsignifica silencioso (sem mensagem de introdução) e -xdiz gdbpara executar comandos no arquivo fornecido.

Uso

Então, para usá-lo, você deve:

seg_wrapper.sh ./mycommand and its arguments 

Atualizar

Você também pode escrever um manipulador de sinal que faça isso, consulte este link .

Kyle Brandt
fonte
2
Seu link para a solução manipulador de sinal está morto - é por isso que as respostas não devem conectar-se a outros recursos ...
Josch
11
você provavelmente quer dizer "-x diz ao gdb para executar" em vez de "-x diz ao gdb para sair"
josch 26/09/14
19

Desculpe vir aqui 2 anos depois ... tropeçou enquanto procurava outra coisa. Adicionando isso para completar.

1) Embora eu ache a resposta aceitável ótima, ela requer gdb. O método que eu estou familiarizado usa libSegFault.so.

Se você executar seu aplicativo com

LD_PRELOAD = ... caminho para ... / libSegFault.so myapp

Você receberá um relatório com backtrace, bibliotecas carregadas, etc.

2) catchsegvTambém está disponível um script de wrapper que tentaria usar addr2linepara converter endereços em nome do arquivo + número da linha.

Essas são soluções muito mais leves que os arquivos principais ou o gdb (bom para sistemas embarcados, por exemplo)

nhed
fonte
Na verdade, tudo LD_PRELOAD=libSegFault.sobem se estiver no caminho dl.
Fernando Silveira
11
@FernandoSilveira ok. Escrever a resposta dessa maneira sugere ao leitor que eles devem verificar qual é esse caminho.
nhed
6

Você precisa do amigo de todos, GDB

gdb <program> [core file]

Depois de carregar seu corefile, o comando 'backtrace' (pode ser abreviado para bt) fornecerá a pilha de chamadas atual. Se você executar seu programa a partir do gdb, poderá definir pontos de interrupção arbitrários e examinar o conteúdo da memória, etc.

Tel Janin
fonte
Existe uma maneira de obtê-lo apenas para imprimir o backtrace e sair?
214 Neil
5

catchsegv

Foi mencionado em outra resposta (mas de forma alguma focada). É uma ferramenta útil que acompanha o projeto glibc. Ele fornecerá um backtrace (e outras informações úteis sobre depuração) apenas se um programa realmente fizer o segfault.

Existe uma boa redação aqui .

Você pode incluí-lo em seus próprios scripts como achar melhor.

wulfgarpro
fonte
3

O Ubuntu (como um projeto) usa o Apport para fazer isso. Você pode ver como eles fizeram isso.

https://wiki.ubuntu.com/Apport

sendmoreinfo
fonte
2
+1: O Apport menciona alguns mecanismos úteis com os quais eu não estava familiarizado, como/proc/sys/kernel/core_pattern
RobM 13/12/11
2

Aqui está uma variante ligeiramente modificada do script de Kyle Brandt. É aprimorado das seguintes maneiras:

  • não requer interação manual se o rastreamento da pilha for longo
  • alguns coredumps são salvos com o nome padrão core., respeite essa configuração
  • não requer um arquivo de comando explícito voando para o gdb (ele criará um temporário)
  • aguardar trabalhos em segundo plano

Roteiro:

#!/bin/bash
gdbcommandfile=$(tempfile)
usepid=$(cat /proc/sys/kernel/core_uses_pid)
printf "set pagination off\nbacktrace\nquit\n" > $gdbcommandfile
ulimit -c unlimited
"$@"&
pid=$!
wait $!
if [[ $? -eq 139 ]]; then
    if [[ $usepid == 1 ]]; then 
        gdb -q $1 core.$pid -x $gdbcommandfile
    else
        gdb -q $1 core -x $gdbcommandfile
    fi
fi
rm $gdbcommandfile
Até Schäfer
fonte
11
Para uma cadeia de comandos tão simples, eu usaria apenas -ex. gdb ... -ex 'set pagination off' -ex backtrace -ex quit
Josh Stone