- Ao compilar e executar repetidamente um programa em C com
entr, ohelloterminou de forma anormal, mas a mensagemSegmentation faultnão apareceu após a saída da compilação - Essa mensagem não é emitida pelo programa que travou, mas pelo shell pai, que coleta o processo filho e confirma que ele terminou por
SIGSEGV - Mesmo ao explicitar
bash -c, se na prática restar apenas um comando, o Bash pode substituir a si mesmo comexecve, sem criar um novo processo, fazendo desaparecer o shell pai que imprimiria a mensagem - Em um script separado, o Bash iniciado pelo shebang executa
./hellocomo filho e espera por ele; por isso, ao confirmar oSIGSEGV, imprimeSegmentation fault (core dumped) - Executar
./helloem um subshell(./hello)ou adicionar; truedepois, deixando uma tarefa subsequente, impede a otimização comexece permite ver a mensagem de erro
Caminho de execução em que a mensagem desapareceu
-
Com o seguinte comando
entr, a compilação e a execução eram repetidas sempre quehello.cmudavals hello.c | entr -s "gcc -o hello hello.c && ./hello" -
O
helloterminou com erro de segmentação, mas não deixou nenhuma saída, sem a mensagemSegmentation faultnem um status de saída não zero visível -
Mesmo envolvendo o comando com
bash -c, o resultado foi o mesmols hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'" -
Por outro lado, ao mover o mesmo comando para
run.she executá-lo, o erro é exibido#!/bin/bash gcc -o hello hello.c && ./hellols hello.c | entr -s ./run.sh ./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello -
Quem imprime
Segmentation fault- O programa que travou já terminou, e o shell pai coleta o processo filho; depois de confirmar que ele morreu por
SIGSEGV, imprime a mensagem - Portanto, se não houver mais um shell pai para imprimir a mensagem, nada aparece
- No
bash -c "some_command", se na prática a única coisa a executar for aquele comando, o Bash pode não fazer fork de um novo processo e substituir a si mesmo comexecve - Essa otimização não fica visível na maioria das execuções
- No modo com script, o
entrexecuta./run.shcomo filho, e o shebang inicia um novo Bash - O novo Bash executa o
gcce depois faz fork de./helloe espera por ele; assim, ao confirmar o término do filho porSIGSEGV, consegue deixar a mensagem de erro
- O programa que travou já terminou, e o shell pai coleta o processo filho; depois de confirmar que ele morreu por
Duas maneiras de manter o shell pai
- Ao executar o comando que trava em um subshell, a mensagem volta a aparecer
ls hello.c | entr -s "gcc -o hello hello.c && (./hello)" hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )- Os parênteses forçam
./helloa ser executado em um subshell criado por fork - Como o Bash mantém o papel de pai, ele pode coletar o término do subshell por
SIGSEGVe imprimir a mensagem
- Os parênteses forçam
- Deixar uma tarefa para ser executada depois do comando que trava também impede a otimização com
execls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'" bash: line 1: 109516 Segmentation fault (core dumped) ./hello- Por causa de
; true, o Bash ainda tem algo a fazer depois que./hellotermina, então não substitui a si mesmo por esse programa - Mesmo sem um script wrapper separado, é possível ver a mensagem de erro de segmentação
- Por causa de
1 comentários
Comentários no Lobste.rs
Distribuições Linux modernas costumam deixar core dumps desativados por padrão, o que muitas vezes é inconveniente
É possível reativá-los com
ulimit -c unlimited, mas parece que o arquivo core nem é criado no mesmo diretório do executável, e o systemd parece movê-lo para algum lugarSeria bom se os core dumps viessem ativados por padrão e fossem colocados no mesmo diretório do executável, como no OpenBSD, mas hoje, quando ocorre uma falha, geralmente é preciso executar de novo o binário dentro do gdb
coredumpctlAlém de ninguém pensar em procurar um core dump em lugares como
/usr/bin, esses locais muitas vezes ficam em sistemas de arquivos pequenos ou somente leituraComo local padrão, o diretório de trabalho não seria muito melhor?
Mesmo quando o usuário ativa os arquivos core, a sensação é de que eles assumem que ele não sabe o que está fazendo e os escondem em um lugar difícil até para o root encontrar
Ao escrever programas em C, D etc. e querer saber a causa do encerramento, basta verificar o valor de retorno da chamada de sistema
wait, que permite saber se o programa terminou por causa de um sinalQuando o sistema operacional encaminha o core dump para outro lugar, isso também tem a vantagem de permitir depurar imediatamente quando necessário ou enviar um relatório de erro para a distribuição
Ao criar um core dump, o kernel Linux lê um arquivo especial chamado
/proc/sys/kernel/core_patternpara decidir onde salvá-loEm vez de gravar em um arquivo, ele também pode ser enviado por pipe para um programa, permitindo ações como exibir uma caixa de diálogo GUI, iniciar um depurador imediatamente ou enviar um relatório por e-mail aos desenvolvedores
O systemd define o
core_patterndo kernel para um valor gerenciado por ele, mas é possível usar o mesmo recurso do kernel mesmo sem systemdHá também relatos de que, durante o desenvolvimento do Windows XP, core dumps foram enviados à Microsoft e permitiram corrigir vários bugs cuja existência talvez nem fosse conhecida; e, ao instalar ferramentas de desenvolvimento no Windows, o depurador sob demanda é bastante útil ao desenvolver seus próprios programas
Esse recurso foi introduzido no Linux em 2003, e mais detalhes podem ser vistos em
man 5 coreCore dumps são excelentes para depuração, mas, quando aparecem ao lado de arquivos aparentemente sem relação, confundem usuários comuns e podem ser péssimos como sistema de relatório de diagnóstico
Fiquei chocado na primeira vez que vi quanta informação há em um relatório de falha enviado pelo Windows
Pressupondo um sistema de relatório de falhas que proteja a privacidade, é preciso aprender a inferir a causa apenas com informações de stack e registradores para diagnosticar as falhas enviadas
Eu não fazia ideia de que o Bash às vezes faz
execdiretamente de comandos dentro de scripts, e isso é um recurso surpreendenteComo o próprio processo do Bash nem tem tanta sobrecarga e, quando necessário, costumo usar
execpor hábito, parece um recurso um tanto mal projetadoAinda assim, somando o número total de execuções, dá para imaginar que ele possa fazer uma diferença maior do que eu esperava