webentwicklung-frage-antwort-db.com.de

Wie kann man einen Stack-Trace in C abrufen?

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

78
Kevin

Wir haben dies für unsere Projekte verwendet:

https://www.codeproject.com/kb/threads/stackwalker.aspx

Der Code ist meiner Meinung nach ein bisschen chaotisch, aber es funktioniert gut. Nur für Windows.

22
Mark
78
sanxiyn

Es gibt backtrace () und backtrace_symbols ():

Von 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 bequemere/OOP-Methode besteht darin, das Ergebnis von backtrace_symbols () in einem Ausnahmeklassenkonstruktor zu speichern. Wenn Sie also diese Art von Ausnahme auslösen, haben Sie den Stack-Trace. Stellen Sie dann einfach eine Funktion zum Ausdrucken bereit. Beispielsweise:


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 

...



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

Ta da!

Hinweis: Wenn Sie Optimierungsflags aktivieren, wird der resultierende Stack-Trace möglicherweise ungenau. Idealerweise würde man diese Funktion mit aktivierten Debug-Flags und deaktivierten Optimierungs-Flags nutzen.

28
Tom

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

Beachten Sie jedoch, dass die Verwendung eines Stacktrace in nativem Code selten eine gute Idee ist - nicht, weil dies nicht möglich ist, sondern weil Sie normalerweise versuchen, das Falsche zu erreichen.

Die meiste Zeit versuchen die Leute, einen Stacktrace zu erstellen, zum Beispiel unter außergewöhnlichen Umständen, wie wenn eine Ausnahme abgefangen wird, eine Zusicherung fehlschlägt oder - am schlimmsten und falschesten von allen - wenn Sie eine fatale "Ausnahme" oder ein Signal wie a erhalten Segmentierungsverletzung.

In Anbetracht des letzten Problems müssen Sie für die meisten APIs explizit oder möglicherweise intern Speicher zuweisen. Wenn Sie dies in dem fragilen Zustand tun, in dem sich Ihr Programm gerade befindet, kann dies die Situation möglicherweise noch verschlimmern. Beispielsweise gibt der Absturzbericht (oder Coredump) nicht die tatsächliche Ursache des Problems wieder, sondern Ihren fehlgeschlagenen Versuch, das Problem zu beheben.

Ich gehe davon aus, dass Sie versuchen, dieses Problem mit der Behandlung schwerwiegender Fehler zu lösen, da die meisten Leute dies zu versuchen scheinen, wenn es darum geht, ein Stacktrace zu erhalten. Wenn ja, würde ich mich auf den Debugger verlassen (während der Entwicklung) und den Prozess in der Produktion entleeren (oder auf Windows mini-Dump). Zusammen mit einer ordnungsgemäßen Symbolverwaltung sollten Sie keine Probleme haben, die verursachende Anweisung post mortem zu ermitteln.

20
Christian.K

Unter Windows ist CaptureStackBackTrace() ebenfalls eine Option, die auf Benutzerseite weniger Vorbereitungscode erfordert als StackWalk64(). (In einem ähnlichen Szenario funktionierte CaptureStackBackTrace() besser (zuverlässiger) als StackWalk64().)

4
Quasidart

Sie sollten die Abwicklungsbibliothek verwenden.

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, wenn Sie keinen Anruf aus einer gemeinsam genutzten Bibliothek tätigen.

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

4
user262802

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

Das Nächste, was Sie tun können, ist, den Code ohne Optimierungen auszuführen. Auf diese Weise können Sie eine Verbindung zum Prozess herstellen (mithilfe des Visual C++ - Debuggers oder der GDB) und einen verwendbaren Stack-Trace abrufen.

4

Darf ich Sie auf meinen Artikel verweisen. Es sind nur ein paar Codezeilen.

Post-Mortem-Debugging

Obwohl ich derzeit Probleme mit der x64-Implementierung dieses habe.

2
RED SOFT ADAIR

Solaris verfügt über den Befehl pstack , der ebenfalls in Linux kopiert wurde.

2
wnoise

In den letzten Jahren habe ich Ian Lance Taylors Libbacktrace verwendet. Es ist viel übersichtlicher als die Funktionen in der GNU= C-Bibliothek, die den Export aller Symbole erfordern. Es bietet mehr Nutzen für die Erzeugung von Backtraces als libunwind. Und nicht zuletzt ist es nicht besiegt von ASLR ebenso wie Ansätze, die externe Tools wie addr2line.

Libbacktrace war ursprünglich Teil der GCC-Distribution, wird aber jetzt vom Autor als eigenständige Bibliothek unter einer BSD-Lizenz bereitgestellt:

https://github.com/ianlancetaylor/libbacktrace

Zum Zeitpunkt des Schreibens würde ich nichts anderes verwenden, es sei denn, ich muss Backtraces auf einer Plattform generieren, die nicht von libbacktrace unterstützt wird.

0
Erwan Legrand