Pouvez-vous obtenir n'importe quel programme sous Linux pour imprimer une trace de pile s'il se sépare?


20

Si j'exécute un programme à partir du shell, et il segfaults:

$ buggy_program
Segmentation fault

Il me dira cependant qu'il existe un moyen d'obtenir des programmes pour imprimer une trace, peut-être en exécutant quelque chose comme ceci:

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

Je préfère également ne pas utiliser strace ou ltrace pour ce type d'informations, car ils s'impriment de toute façon ...

Réponses:


25

Il pourrait y avoir une meilleure façon, mais ce genre d'automatisation.

Mettez ce qui suit dans ~/backtrace:

backtrace
quit

Mettez ceci dans un script appelé seg_wrapper.shdans un répertoire de votre chemin:

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

La ulimitcommande fait en sorte que le noyau soit vidé. "$@"sont les arguments donnés au script, donc ce serait votre programme et ses arguments. $?détient le statut de sortie, 139 semble être le statut de sortie par défaut de ma machine en cas de panne.

Pour gdb, -qsignifie silencieux (pas de message d'introduction) et -xindique gdbd'exécuter des commandes dans le fichier qui lui est donné.

Usage

Donc, pour l'utiliser, vous devez simplement:

seg_wrapper.sh ./mycommand and its arguments 

Mise à jour

Vous pouvez également écrire un gestionnaire de signal qui fait cela, voir ce lien .


2
Votre lien vers la solution du gestionnaire de signaux est mort - c'est pourquoi les réponses ne doivent pas être
liées

1
vous voulez probablement dire "-x dit à gdb de s'exécuter" au lieu de "-x dit à gdb de quitter"
josch

19

Désolé de venir ici 2 ans plus tard ... tombé sur tout en cherchant autre chose. Ajouter ceci pour être complet.

1) Bien que je pense que la réponse acceptée est excellente, elle nécessite gdb. La méthode que je connais utilise libSegFault.so.

Si vous exécutez votre application avec

LD_PRELOAD = ... chemin vers ... / libSegFault.so myapp

Vous obtiendriez un rapport avec backtrace, des bibliothèques chargées, etc.

2) Un script wrapper catchsegvest également disponible qui tenterait d'utiliser addr2linepour traduire les adresses en nom de fichier + numéro de ligne.

Ce sont des solutions beaucoup plus légères que les fichiers core ou gdb (bon pour les systèmes embarqués par exemple)


En fait, LD_PRELOAD=libSegFault.soça va si c'est dans le chemin dl.
Fernando Silveira

1
@FernandoSilveira ok. Écrire la réponse de cette façon laisse entendre au lecteur qu'il doit vérifier ce qu'est ce chemin.
nhed le

6

Vous avez besoin de l'ami de tout le monde GDB

gdb <program> [core file]

Une fois que vous avez chargé votre corefile, la commande 'backtrace' (peut être abrégée en bt) vous donnera la pile d'appels actuelle. Si vous exécutez votre programme à partir de gdb, vous pouvez définir des points d'arrêt arbitraires et examiner le contenu de la mémoire, etc.


Existe-t-il un moyen de l'obtenir simplement pour imprimer la trace et quitter?
Neil

5

catchsegv

Il a été mentionné dans une autre réponse (mais en aucun cas concentré sur). C'est un outil pratique intégré au projet glibc. Il ne fournira une trace (et d'autres informations de débogage utiles) que si un programme fait effectivement une erreur de segmentation.

Une bonne rédaction existe ici .

Vous pouvez l'inclure dans vos propres scripts comme bon vous semble.



2

Voici une variante légèrement modifiée du script de Kyle Brandt. Il est amélioré des manières suivantes:

  • ne nécessite pas d'interaction manuelle si la trace de pile est longue
  • certains coredumps sont enregistrés avec le noyau du modèle de nom., respectez ce paramètre
  • ne nécessite pas de fichier de commande explicite volant pour gdb (il en créera un temporaire)
  • attendre les travaux d'arrière-plan

Scénario:

#!/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

1
Pour une chaîne de commandes aussi simples, j'utiliserais simplement à la -explace. gdb ... -ex 'set pagination off' -ex backtrace -ex quit
Josh Stone
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.