Czy możesz uzyskać w Linuksie jakiś program, który wydrukuje ślad stosu, jeśli wystąpi awaria?

20

Jeśli uruchomię program z powłoki i segfault:

$ buggy_program
Segmentation fault

Powie mi jednak, że istnieje sposób, aby programy wydrukowały ślad, być może uruchamiając coś takiego:

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

Wolałbym też nie używać strace ani ltrace do tego rodzaju informacji, ponieważ będą one drukowane w obu przypadkach ...

Neil
źródło

Odpowiedzi:

25

Może być lepszy sposób, ale ten rodzaj automatyzuje go.

Wprowadź następujące dane ~/backtrace:

backtrace
quit

Umieść to w skrypcie wywoływanym seg_wrapper.shw katalogu na swojej ścieżce:

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

ulimitKomenda czyni go tak rdzeń jest sprzedawany po cenach dumpingowych. "$@"są argumentami podanymi w skrypcie, więc byłby to twój program i jego argumenty. $?posiada status wyjścia, 139 wydaje się być domyślnym statusem wyjścia dla mojego komputera dla błędu segfault.

Dla gdb, -qśrodki spokojny (nie wiadomość intro), i -xmówi gdbdo wykonywania poleceń w pliku podanym do niego.

Stosowanie

Aby z niego skorzystać, wystarczy:

seg_wrapper.sh ./mycommand and its arguments 

Aktualizacja

Możesz także napisać procedurę obsługi sygnału, która to robi, zobacz ten link .

Kyle Brandt
źródło
2
Twój link do rozwiązania obsługi sygnału jest martwy - dlatego odpowiedzi nie powinny prowadzić do innych zasobów ...
josch
1
prawdopodobnie masz na myśli „-x mówi gdb do wykonania” zamiast „-x mówi gdb do wyjścia”
josch
19

Przepraszam, że przyszedłem tutaj 2 lata później ... natknąłem się na coś innego. Dodanie tego dla kompletności.

1) Chociaż uważam, że zaakceptowana odpowiedź jest świetna, wymaga gdb. Znana mi metoda wykorzystuje libSegFault.so.

Jeśli uruchomisz aplikację

LD_PRELOAD = ... ścieżka do ... / libSegFault.so myapp

Dostaniesz raport ze śledzeniem, załadowanymi bibliotekami itp

2) Dostępny catchsegvjest również skrypt otoki , który próbowałby addr2lineprzetłumaczyć adresy na nazwę pliku + numer linii.

Są to znacznie lżejsze rozwiązania niż pliki podstawowe lub gdb (dobre na przykład dla systemów osadzonych)

nhed
źródło
Właściwie LD_PRELOAD=libSegFault.sojest w porządku, jeśli znajduje się na ścieżce dl.
Fernando Silveira
1
@FernandoSilveira ok. Pisanie odpowiedzi w ten sposób sugeruje czytelnikowi, że powinni sprawdzić, co to jest ścieżka.
nhed
6

Potrzebujesz GDB przyjaciela wszystkich

gdb <program> [core file]

Po załadowaniu pliku podstawowego polecenie „backtrace” (może być skrócone do bt) daje aktualny stos wywołań. Jeśli uruchamiasz program z poziomu gdb, możesz ustawić dowolne punkty przerwania i sprawdzić zawartość pamięci itp.

Tel Janin
źródło
Czy istnieje sposób, aby uzyskać to, aby wydrukować ślad i wyjść?
Neil
5

catchsegv

Zostało to wspomniane w innej odpowiedzi (ale w żaden sposób nie dotyczyło). Jest to przydatne narzędzie dołączone do projektu glibc. Zapewni ślad zwrotny (i inne przydatne informacje debugowania) tylko wtedy, gdy program rzeczywiście nie działa poprawnie.

Dobry opis istnieje tutaj .

Możesz uwzględnić go we własnych skryptach według własnego uznania.

wulfgarpro
źródło
3

Ubuntu (jako projekt) używa do tego Apport. Możesz zobaczyć, jak to zrobili.

https://wiki.ubuntu.com/Apport

sendmoreinfo
źródło
2
+1: Apport wspomina o kilku użytecznych mechanizmach, których nie /proc/sys/kernel/core_pattern
znałem
2

Oto nieco zmodyfikowany wariant skryptu Kyle'a Brandta. Ulepszono go w następujący sposób:

  • nie wymagają ręcznej interakcji, jeśli śledzenie stosu jest długie
  • niektóre zrzuty rdzeniowe są zapisywane z rdzeniem wzorca nazwy. Przestrzegaj tego ustawienia
  • nie wymagają jawnego pliku poleceń latającego dla gdb (utworzy on plik tymczasowy)
  • poczekaj na zadania w tle

Scenariusz:

#!/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
źródło
1
W przypadku łańcucha takich prostych poleceń użyłbym -exzamiast tego. gdb ... -ex 'set pagination off' -ex backtrace -ex quit
Josh Stone,