1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Ao compilar e executar repetidamente um programa em C com entr, o hello terminou de forma anormal, mas a mensagem Segmentation fault nã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 com execve, sem criar um novo processo, fazendo desaparecer o shell pai que imprimiria a mensagem
  • Em um script separado, o Bash iniciado pelo shebang executa ./hello como filho e espera por ele; por isso, ao confirmar o SIGSEGV, imprime Segmentation fault (core dumped)
  • Executar ./hello em um subshell (./hello) ou adicionar ; true depois, deixando uma tarefa subsequente, impede a otimização com exec e 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 que hello.c mudava

    ls hello.c | entr -s "gcc -o hello hello.c && ./hello"
    
  • O hello terminou com erro de segmentação, mas não deixou nenhuma saída, sem a mensagem Segmentation fault nem um status de saída não zero visível

  • Mesmo envolvendo o comando com bash -c, o resultado foi o mesmo

    ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'"
    
  • Por outro lado, ao mover o mesmo comando para run.sh e executá-lo, o erro é exibido

    #!/bin/bash
    gcc -o hello hello.c && ./hello
    
    ls 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 com execve
    • Essa otimização não fica visível na maioria das execuções
    • No modo com script, o entr executa ./run.sh como filho, e o shebang inicia um novo Bash
    • O novo Bash executa o gcc e depois faz fork de ./hello e espera por ele; assim, ao confirmar o término do filho por SIGSEGV, consegue deixar a mensagem de erro

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 ./hello a 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 SIGSEGV e imprimir a mensagem
  • Deixar uma tarefa para ser executada depois do comando que trava também impede a otimização com exec
    ls 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 ./hello termina, 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

1 comentários

 
GN⁺ 4 시간 전
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 lugar
    Seria 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

    • No systemd, é possível listar e recuperar core dumps recentes com coredumpctl
    • Colocar o core dump no local onde está o executável parece uma escolha bem estranha
      Alé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 leitura
      Como local padrão, o diretório de trabalho não seria muito melhor?
    • O motivo de os arquivos core virem desativados por padrão parece ser que especialistas de segurança acreditam que usuários não conseguem operar o Linux com segurança e que arquivos core abandonados podem ser explorados
      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 sinal
    Quando 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_pattern para decidir onde salvá-lo
    Em 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_pattern do kernel para um valor gerenciado por ele, mas é possível usar o mesmo recurso do kernel mesmo sem systemd
    Há 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 core

  • Core 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 exec diretamente de comandos dentro de scripts, e isso é um recurso surpreendente
    Como o próprio processo do Bash nem tem tanta sobrecarga e, quando necessário, costumo usar exec por hábito, parece um recurso um tanto mal projetado
    Ainda assim, somando o número total de execuções, dá para imaginar que ele possa fazer uma diferença maior do que eu esperava