Wie kann man in C einen Stack-Trace abrufen?

Lesezeit: 6 Minuten

Kevins Benutzeravatar
Kevin

Ich weiß, dass es dafür keine Standard-C-Funktion gibt. Ich habe mich gefragt, was die Techniken dazu unter Windows und * nix sind? (Windows XP ist derzeit mein wichtigstes Betriebssystem, um dies zu tun.)

  • Ich habe mehrere Methoden ausführlich getestet unter: stackoverflow.com/questions/3899870/print-call-stack-in-c-or-c/…

    – Ciro Santilli OurBigBook.com

    25. Januar 2019 um 12:20 Uhr

Benutzeravatar von sanxiyn
sanxiin

glibc bietet backtrace() Funktion.

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

  • glibc FTW … wieder. (Das ist ein weiterer Grund, warum ich die glibc als den absoluten Goldstandard betrachte, wenn es um die C-Programmierung geht (das und der dazugehörige Compiler).)

    – Trevor Boyd Smith

    9. September 2011 um 17:53 Uhr

  • Aber warte, es gibt noch mehr! Die backtrace()-Funktion stellt nur ein Array von void *-Zeigern bereit, die die Callstack-Funktionen darstellen. “Das ist nicht sehr nützlich. arg.” Keine Angst! glibc bietet eine Funktion, die alle void *-Adressen (die Callstack-Funktionsadressen) in für Menschen lesbare Zeichenfolgensymbole umwandelt. char ** backtrace_symbols (void *const *buffer, int size)

    – Trevor Boyd Smith

    9. September 2011 um 17:55 Uhr


  • Vorbehalt: funktioniert nur für C-Funktionen, denke ich. @Trevor: Es sucht nur Syms nach Adresse in der ELF-Tabelle.

    – Konrad Meyer

    16. Oktober 2011 um 0:20 Uhr

  • Es gibt auch void backtrace_symbols_fd(void *const *buffer, int size, int fd) die die Ausgabe zum Beispiel direkt an stdout/err senden kann.

    – Wo

    24. April 2012 um 13:47 Uhr

  • backtrace_symbols() saugt. Es erfordert den Export aller Symbole und unterstützt keine DWARF-Symbole (Debugging). libbacktrace ist in vielen (den meisten) Fällen eine viel bessere Option.

    – Erwan Legrand

    8. Februar 2018 um 11:18 Uhr


Toms Benutzeravatar
Tom

Es gibt backtrace()und backtrace_symbols():

Aus der Manpage:

#include <execinfo.h>
#include <stdio.h>
...
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i < frames; ++i) {
    printf("%s\n", strs[i]);
}
free(strs);
...

Eine Möglichkeit, dies auf bequemere/OOP-Weise zu verwenden, besteht darin, das Ergebnis von zu speichern backtrace_symbols() in einem Ausnahmeklassenkonstruktor. Wenn Sie also diese Art von Ausnahme auslösen, haben Sie den Stack-Trace. Stellen Sie dann einfach eine Funktion zum Ausdrucken bereit. Zum Beispiel:

class MyException : public std::exception {

    char ** strs;
    MyException( const std::string & message ) {
         int i, frames = backtrace(callstack, 128);
         strs = backtrace_symbols(callstack, frames);
    }

    void printStackTrace() {
        for (i = 0; i < frames; ++i) {
            printf("%s\n", strs[i]);
        }
        free(strs);
    }
};

try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

Ta da!

Hinweis: Das Aktivieren von Optimierungs-Flags kann den resultierenden Stack-Trace ungenau machen. Idealerweise würde man diese Fähigkeit mit aktivierten Debug-Flags und deaktivierten Optimierungs-Flags verwenden.

  • @shuckc nur zum Konvertieren der Adresse in eine Symbolzeichenfolge, was bei Bedarf extern mit anderen Tools erfolgen kann.

    – Woodrow Barlow

    7. Juli 2016 um 19:57 Uhr

Überprüfen Sie für Windows die StackWalk64() API (auch auf 32-Bit-Windows). Für UNIX sollten Sie die native Methode des Betriebssystems verwenden oder auf backtrace() von glibc zurückgreifen, falls verfügbar.

Beachten Sie jedoch, dass es selten eine gute Idee ist, einen Stacktrace in nativem Code zu verwenden – nicht, weil es nicht möglich ist, sondern weil Sie normalerweise versuchen, das Falsche zu erreichen.

Die meiste Zeit versuchen die Leute, einen Stacktrace in, sagen wir, außergewöhnlichen Umständen zu bekommen, wie wenn eine Ausnahme abgefangen wird, ein Assert fehlschlägt oder – am schlimmsten und falschsten von allen – wenn Sie eine fatale “Ausnahme” oder ein Signal wie a erhalten Segmentierungsverletzung.

In Anbetracht des letzten Problems erfordern die meisten APIs, dass Sie Speicher explizit zuweisen oder dies intern tun. Wenn Sie dies in dem fragilen Zustand tun, in dem sich Ihr Programm möglicherweise derzeit befindet, kann dies die Dinge tatsächlich noch schlimmer machen. Der Absturzbericht (oder Coredump) spiegelt beispielsweise nicht die tatsächliche Ursache des Problems wider, sondern Ihren fehlgeschlagenen Versuch, es zu beheben).

Ich nehme an, Sie versuchen, diese fatale Fehlerbehandlung zu erreichen, da die meisten Leute dies zu versuchen scheinen, wenn es darum geht, einen Stacktrace zu erhalten. Wenn ja, würde ich mich auf den Debugger (während der Entwicklung) verlassen und den Prozess Coredump in der Produktion (oder Minidump unter Windows) lassen. Zusammen mit der richtigen Symbolverwaltung sollten Sie keine Probleme haben, die verursachende Anweisung nach dem Tod herauszufinden.

  • Sie haben Recht damit, dass es zerbrechlich ist, die Speicherzuweisung in einem Signal- oder Ausnahmehandler zu versuchen. Ein möglicher Ausweg besteht darin, beim Programmstart eine feste Menge an “Notfall” -Speicherplatz zuzuweisen oder einen statischen Puffer zu verwenden.

    – j_random_hacker

    19. Februar 2009 um 12:30 Uhr

  • Ein anderer Ausweg besteht darin, einen Coredump-Dienst zu erstellen, der unabhängig ausgeführt wird

    – Kobor42

    13. Juli 2012 um 7:59 Uhr

Für Windows, CaptureStackBackTrace() ist auch eine Option, die weniger Vorbereitungscode auf der Benutzerseite erfordert als StackWalk64() tut. (Außerdem hatte ich für ein ähnliches Szenario CaptureStackBackTrace() funktionierte am Ende besser (zuverlässiger) als StackWalk64().)

Benutzeravatar von user262802
Benutzer262802

Sie sollten die verwenden Bibliothek abspulen.

unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
unsigned long a[100];
int ctr = 0;

while (unw_step(&cursor) > 0) {
  unw_get_reg(&cursor, UNW_REG_IP, &ip);
  unw_get_reg(&cursor, UNW_REG_SP, &sp);
  if (ctr >= 10) break;
  a[ctr++] = ip;
}

Ihr Ansatz würde auch gut funktionieren, es sei denn, Sie rufen von einer gemeinsam genutzten Bibliothek aus an.

Du kannst den … benutzen addr2line Befehl unter Linux, um die Quellfunktion / Zeilennummer des entsprechenden PCs zu erhalten.

  • “Quellfunktion/Zeilennummer”? Was ist, wenn die Verknüpfung für reduzierte Codegröße optimiert ist? Ich werde jedoch sagen, dass dies wie ein nützliches Projekt aussieht. Schade, dass es keine Möglichkeit gibt, die Register zu bekommen. Ich werde das auf jeden Fall prüfen. Wussten Sie, dass es absolut prozessorunabhängig ist? Funktioniert einfach auf allem, was einen C-Compiler hat?

    – Mawg sagt, Monica wieder einzusetzen

    31. Januar 2010 um 7:19 Uhr

  • Ok dieser Kommentar hat sich gelohnt, allein schon wegen der Erwähnung des hilfreichen addr2line-Befehls!

    – Oger Psalm33

    23. September 2010 um 14:43 Uhr

  • addr2line versagt für verschiebbaren Code auf Systemen mit ASLR (dh das meiste, was die Leute in den letzten zehn Jahren benutzt haben).

    – Erwan Legrand

    8. Februar 2018 um 11:13 Uhr

Benutzeravatar von Nils Pipenbrinck
Nils Pipenbrink

Es gibt keine plattformunabhängige Möglichkeit, dies zu tun.

Am ehesten können Sie den Code ohne Optimierungen ausführen. Auf diese Weise können Sie an den Prozess anhängen (mithilfe des visuellen C++-Debuggers oder GDB) und erhalten einen brauchbaren Stack-Trace.

  • “Quellfunktion/Zeilennummer”? Was ist, wenn die Verknüpfung für reduzierte Codegröße optimiert ist? Ich werde jedoch sagen, dass dies wie ein nützliches Projekt aussieht. Schade, dass es keine Möglichkeit gibt, die Register zu bekommen. Ich werde das auf jeden Fall prüfen. Wussten Sie, dass es absolut prozessorunabhängig ist? Funktioniert einfach auf allem, was einen C-Compiler hat?

    – Mawg sagt, Monica wieder einzusetzen

    31. Januar 2010 um 7:19 Uhr

  • Ok dieser Kommentar hat sich gelohnt, allein schon wegen der Erwähnung des hilfreichen addr2line-Befehls!

    – Oger Psalm33

    23. September 2010 um 14:43 Uhr

  • addr2line versagt für verschiebbaren Code auf Systemen mit ASLR (dh das meiste, was die Leute in den letzten zehn Jahren benutzt haben).

    – Erwan Legrand

    8. Februar 2018 um 11:13 Uhr

Benutzeravatar von Peter Mortensen
Peter Mortensen

Solaris hat die Stapel Befehl, der auch in Linux kopiert wurde.

  • Nützlich, aber nicht wirklich C (es ist ein externes Dienstprogramm).

    – vergänglich

    31. Januar 2010 um 6:48 Uhr

  • auch aus der Beschreibung (Abschnitt: Einschränkungen) “: pstack funktioniert derzeit nur unter Linux, nur auf einem x86-Computer, auf dem 32-Bit-ELF-Binärdateien ausgeführt werden (64-Bit wird nicht unterstützt)”

    – Ciro Costa

    31. Oktober 2015 um 19:32 Uhr

1419610cookie-checkWie kann man in C einen Stack-Trace abrufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy