¿A dónde se fue mi error de segmentación?
(rmpr.xyz)- Al compilar y ejecutar repetidamente un programa en C con
entr,helloterminaba de forma anormal, pero no aparecía el mensajeSegmentation faultdespué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 conexecve, por lo que desaparece el shell padre que debería imprimir el mensaje - En un script aparte, Bash iniciado por el shebang ejecuta
./hellocomo hijo y espera su finalización, así que al detectarSIGSEGVimprimeSegmentation fault (core dumped) - Si se ejecuta
./helloen un subshell(./hello)o se agrega; trueal final para dejar trabajo posterior, se puede impedir la optimización deexecy 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 cambiahello.cls hello.c | entr -s "gcc -o hello hello.c && ./hello" -
Aunque
helloterminaba con un error de segmentación, no quedaba ninguna salida: ni el mensajeSegmentation faultni un estado de salida distinto de cero visible -
Incluso al envolver el comando con
bash -c, el resultado era el mismols hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'" -
En cambio, al mover el mismo comando a
run.shy ejecutarlo, sí aparecía el error#!/bin/bash gcc -o hello hello.c && ./hellols 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 hacerforky en su lugar reemplazarse a sí mismo conexecve - En la mayoría de los casos, esta optimización no se nota desde afuera
- En el enfoque con script,
entrejecuta./run.shcomo hijo, y el shebang inicia un Bash nuevo - Ese Bash nuevo ejecuta
gccy luego haceforkpara./helloy espera su finalización, por lo que puede detectar que el hijo terminó porSIGSEGVy dejar el mensaje de error
- 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
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
./hellose ejecute dentro de un subshell creado confork - Como Bash mantiene su papel de padre, puede recolectar la terminación por
SIGSEGVdel subshell e imprimir el mensaje
- Los paréntesis fuerzan que
- Dejar trabajo pendiente después del comando que falla también puede impedir la optimización con
execls 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
- Debido a
1 comentarios
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 ladoEstarí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
coredumpctlNadie 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 lecturaComo ubicación predeterminada, el directorio de trabajo no sería mucho mejor?
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ñalSi 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_patternpara decidir dónde guardarloEn 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_patterndel kernel con un valor que él mismo administra, pero la misma función del kernel puede usarse aunque no se utilice systemdTambié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 coreLos 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
execdirectamente sobre los comandos dentro de un script, y me parece una función sorprendenteLa sobrecarga del proceso de Bash en sí tampoco es tan grande, y como en los casos necesarios uno suele usar
execpor costumbre, da la impresión de ser una función algo mal diseñadaAun así, sumando la cantidad total de ejecuciones, da la sensación de que podría producir una diferencia mayor de lo esperado