1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Al compilar y ejecutar repetidamente un programa en C con entr, hello terminaba de forma anormal, pero no aparecía el mensaje Segmentation fault después de la salida de compilación
  • Ese mensaje no lo imprime el programa que se estrelló, sino el shell padre, que lo muestra al recolectar el proceso hijo y confirmar que terminó por SIGSEGV
  • Incluso si se especifica bash -c, si en la práctica solo queda un comando por ejecutar, Bash puede no crear un proceso nuevo y reemplazarse a sí mismo con execve, por lo que desaparece el shell padre que debería imprimir el mensaje
  • En un script aparte, Bash iniciado por el shebang ejecuta ./hello como hijo y espera su finalización, así que al detectar SIGSEGV imprime Segmentation fault (core dumped)
  • Si se ejecuta ./hello en un subshell (./hello) o se agrega ; true al final para dejar trabajo posterior, se puede impedir la optimización de exec y volver a ver el mensaje de error

La ruta de ejecución en la que desaparece el mensaje

  • Con el siguiente comando de entr, se repite la compilación y ejecución cada vez que cambia hello.c

    ls hello.c | entr -s "gcc -o hello hello.c && ./hello"
    
  • Aunque hello terminaba con un error de segmentación, no quedaba ninguna salida: ni el mensaje Segmentation fault ni un estado de salida distinto de cero visible

  • Incluso al envolver el comando con bash -c, el resultado era el mismo

    ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'"
    
  • En cambio, al mover el mismo comando a run.sh y ejecutarlo, sí aparecía el error

    #!/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
    
  • Quién imprime Segmentation fault

    • El programa que se estrelló ya terminó; el que imprime el mensaje es el shell padre al recolectar el proceso hijo y comprobar que murió por SIGSEGV
    • Por eso, si no queda un shell padre que pueda imprimir el mensaje, no aparece nada
    • En bash -c "some_command", si en la práctica Bash no tiene más trabajo que ejecutar ese comando, puede no hacer fork y en su lugar reemplazarse a sí mismo con execve
    • En la mayoría de los casos, esta optimización no se nota desde afuera
    • En el enfoque con script, entr ejecuta ./run.sh como hijo, y el shebang inicia un Bash nuevo
    • Ese Bash nuevo ejecuta gcc y luego hace fork para ./hello y espera su finalización, por lo que puede detectar que el hijo terminó por SIGSEGV y dejar el mensaje de error

Dos formas de conservar el shell padre

  • Si el comando que falla se ejecuta dentro de un subshell, el mensaje vuelve a aparecer
    ls hello.c | entr -s "gcc -o hello hello.c && (./hello)"
    hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )
    
    • Los paréntesis fuerzan que ./hello se ejecute dentro de un subshell creado con fork
    • Como Bash mantiene su papel de padre, puede recolectar la terminación por SIGSEGV del subshell e imprimir el mensaje
  • Dejar trabajo pendiente después del comando que falla también puede impedir la optimización con exec
    ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'"
    bash: line 1: 109516 Segmentation fault (core dumped) ./hello
    
    • Debido a ; true, Bash todavía tiene algo que hacer después de que termine ./hello, así que no se reemplaza a sí mismo por ese programa
    • Así se puede volver a ver el mensaje de error de segmentación sin necesidad de un script wrapper aparte

1 comentarios

 
GN⁺ 4 시간 전
Comentarios en Lobste.rs
  • Las distribuciones modernas de Linux suelen tener los core dumps desactivados por defecto, lo que muchas veces resulta molesto
    Se pueden volver a activar con ulimit -c unlimited, pero al parecer el archivo de core ni siquiera se genera en el mismo directorio que el ejecutable, y parece que systemd lo mueve a algún otro lado
    Estaría bien que, como en OpenBSD, los core dumps estuvieran activados por defecto y se dejaran en el mismo directorio que el ejecutable, pero ahora, cuando ocurre un crash, normalmente hay que volver a ejecutar el binario dentro de gdb

    • En systemd se pueden listar y recuperar los core dumps recientes con coredumpctl
    • Dejar el core dump en la ubicación del ejecutable parece una decisión bastante extraña
      Nadie esperaría encontrar un core dump en lugares como /usr/bin, y además esas ubicaciones suelen estar en sistemas de archivos pequeños o de solo lectura
      Como ubicación predeterminada, el directorio de trabajo no sería mucho mejor?
    • La razón por la que los archivos core están desactivados por defecto parece ser que los expertos en seguridad creen que los usuarios no pueden operar Linux de forma segura y que un archivo core abandonado podría ser explotado
      Incluso da la impresión de que, aunque el usuario active los archivos core, se asume que no sabe lo que hace, así que los esconden en un lugar difícil de encontrar incluso para root
  • Si escribes programas en C o D y quieres saber la causa de terminación, puedes revisar el valor de retorno de la llamada al sistema wait, y así saber si el programa terminó por una señal
    Si el sistema operativo redirige el core dump a otro lugar, también tiene la ventaja de permitir depurar de inmediato cuando hace falta o enviar un reporte de error a la distribución
    Cuando el kernel de Linux genera un core dump, lee un archivo especial llamado /proc/sys/kernel/core_pattern para decidir dónde guardarlo
    En lugar de escribirlo a un archivo, también puede enviarlo por pipe a un programa, lo que permite mostrar un cuadro de diálogo GUI, iniciar un depurador al instante o enviar por correo un reporte al desarrollador
    systemd configura el core_pattern del kernel con un valor que él mismo administra, pero la misma función del kernel puede usarse aunque no se utilice systemd
    También se cuenta que, durante el desarrollo de Windows XP, enviar core dumps a Microsoft permitió corregir muchos bugs cuya existencia ni siquiera se conocía, y si instalas herramientas de desarrollo en Windows, el depurador bajo demanda resulta bastante útil al desarrollar tus propios programas
    Esta función se introdujo en Linux en 2003, y se puede consultar más detalle en man 5 core

  • Los core dumps son excelentes para depurar, pero si aparecen junto a archivos que no parecen relacionados pueden confundir a los usuarios comunes, y pueden ser terribles como sistema de reportes de diagnóstico
    La primera vez que vi cuántos datos entraban en un reporte de crash enviado desde Windows, me quedé horrorizado
    Si se parte de un sistema de reporte de crashes que proteja la privacidad, entonces hay que aprender a inferir la causa usando solo la pila y la información de registros para poder diagnosticar los crashes enviados

  • No tenía idea de que Bash a veces hace exec directamente sobre los comandos dentro de un script, y me parece una función sorprendente
    La sobrecarga del proceso de Bash en sí tampoco es tan grande, y como en los casos necesarios uno suele usar exec por costumbre, da la impresión de ser una función algo mal diseñada
    Aun así, sumando la cantidad total de ejecuciones, da la sensación de que podría producir una diferencia mayor de lo esperado