Können Sie in Linux ein beliebiges Programm zum Drucken eines Stack-Trace veranlassen, wenn der Fehler auftritt?

20

Wenn ich ein Programm von der Shell aus starte und es segfaults:

$ buggy_program
Segmentation fault

Es wird mir jedoch mitgeteilt, dass es eine Möglichkeit gibt, Programme zum Drucken eines Backtraces zu veranlassen, indem sie möglicherweise Folgendes ausführen:

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

Ich würde auch lieber nicht strace oder ltrace für diese Art von Informationen verwenden, da sie so oder so gedruckt werden ...

Neil
quelle

Antworten:

25

Es könnte einen besseren Weg geben, aber diese Art automatisiert ihn.

Geben Sie Folgendes ein ~/backtrace:

backtrace
quit

Fügen Sie dies in ein Skript ein, das seg_wrapper.shin einem Verzeichnis in Ihrem Pfad aufgerufen wird:

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

Der ulimitBefehl bewirkt, dass der Kern gelöscht wird. "$@"sind die Argumente, die dem Skript gegeben werden, also wäre es Ihr Programm und seine Argumente. $?hält den Exit-Status, 139 scheint der Standard-Exit-Status für meinen Computer für einen Segfault zu sein.

Für gdb, -qMittel ruhig (kein Intro - Nachricht), und -xerzählt gdbBefehle in der Datei , um es gegeben auszuführen.

Verwendung

Um es zu benutzen, würden Sie einfach:

seg_wrapper.sh ./mycommand and its arguments 

Aktualisieren

Sie können auch einen Signal-Handler schreiben, der dies ausführt, siehe diesen Link .

Kyle Brandt
quelle
2
Ihr Link zur Signal-Handler-Lösung ist nicht mehr
verfügbar
1
du meinst wahrscheinlich "-x weist gdb an auszuführen" anstatt "-x weist gdb an zu
beenden
19

Tut mir leid, dass ich 2 Jahre später hierher komme ... bin auf der Suche nach etwas anderem gestoßen. Der Vollständigkeit halber hinzufügen.

1) Obwohl ich die akzeptierte Antwort für großartig halte, erfordert sie gdb. Die mir vertraute Methode verwendet libSegFault.so.

Wenn Sie Ihre App mit ausführen

LD_PRELOAD = ... Pfad zu ... / libSegFault.so myapp

Sie erhalten einen Bericht mit Backtrace, geladenen Bibliotheken usw

2) Es catchsegvist auch ein Wrapper-Skript verfügbar, mit dem versucht wird, addr2lineAdressen in Dateiname + Zeilennummer zu übersetzen.

Dies sind viel leichtere Lösungen als Core-Dateien oder GDB (gut für eingebettete Systeme zum Beispiel)

nhed
quelle
Eigentlich LD_PRELOAD=libSegFault.soist es in Ordnung, wenn es im dl-Pfad liegt.
Fernando Silveira
1
@FernandoSilveira ok. Das Schreiben der Antwort auf diese Weise weist den Leser darauf hin, dass er den Pfad überprüfen sollte.
8.
6

Sie brauchen jedermanns Freund GDB

gdb <program> [core file]

Sobald Sie Ihr Corefile geladen haben, erhalten Sie mit dem Befehl 'backtrace' (abgekürzt als bt) den aktuellen Aufrufstapel. Wenn Sie Ihr Programm in gdb ausführen, können Sie beliebige Haltepunkte setzen und den Speicherinhalt usw. untersuchen.

Tel Janin
quelle
Gibt es eine Möglichkeit, den Backtrace auszudrucken und zu beenden?
Neil
5

catchsegv

Es wurde in einer anderen Antwort erwähnt (aber in keiner Weise darauf konzentriert). Es ist ein praktisches Tool, das mit dem glibc-Projekt gebündelt ist. Es wird nur dann eine Rückverfolgung (und andere nützliche Debug-Informationen) bereitstellen, wenn ein Programm tatsächlich einen Fehler aufweist.

Eine gute aufzuschreiben existiert hier .

Sie können es nach Belieben in Ihre eigenen Skripte einbinden.

wulfgarpro
quelle
3

Ubuntu (als Projekt) verwendet dazu Apport. Sie können sehen, wie sie es gemacht haben.

https://wiki.ubuntu.com/Apport

sendmoreinfo
quelle
2
+1: Apport erwähnt einige nützliche Mechanismen, mit denen ich nicht vertraut war, wie/proc/sys/kernel/core_pattern
RobM
2

Hier ist eine leicht modifizierte Variante des Skripts von Kyle Brandt. Es wird auf folgende Weise verbessert:

  • Keine manuelle Interaktion erforderlich, wenn die Stapelverfolgung lang ist
  • Einige Coredumps werden mit dem Namen Pattern Core gespeichert. Beachten Sie diese Einstellung
  • Benötige keine explizite Kommandodatei für gdb (es wird eine temporäre erstellt)
  • Warten auf Hintergrundjobs

Skript:

#!/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
Till Schäfer
quelle
1
Für eine Kette so einfacher Befehle würde ich -exstattdessen einfach verwenden . gdb ... -ex 'set pagination off' -ex backtrace -ex quit
Josh Stone