Frage

Ich weiß, dass es keine Standard -C -Funktion gibt, dies zu tun. Ich habe mich gefragt, was die Techniken auf Windows und *Nix sind. (Windows XP ist mein wichtigstes Betriebssystem, um dies derzeit zu tun.)

War es hilfreich?

Lösung

Wir haben dies für unsere Projekte verwendet:

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

Der Code ist ein bisschen chaotisch imho, aber er funktioniert gut. Nur Windows.

Andere Tipps

Es gibt Backtrace () und Backtrace_Symbols ():

Aus der Mannseite:

     #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 in einer bequemeren/oop -Art und Weise zu verwenden, besteht darin, das Ergebnis von Backtrace_Symbols () in einem Ausnahmeklassenkonstruktor zu speichern. Wenn Sie also diese Art von Ausnahme auswerfen, haben Sie die Stapelspur. Geben Sie dann einfach eine Funktion zum Ausdrucken an. 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 

...


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

Ta da!

Hinweis: Das Aktivieren von Optimierungsflags kann die resultierende Stapelspur ungenau machen. Im Idealfall würde man diese Fähigkeit mit Debug -Flags und Optimierungsflags ausschalten.

Für Windows überprüfen Sie die StackWalk64 () -API (auch auf 32 -Bit -Windows). Für UNIX sollten Sie die native Art und Weise des Betriebssystems verwenden, um dies zu tun, oder zum Fallback bei GLIBCs Backtrace (), wenn Sie verfügbar sind.

Beachten Sie jedoch, dass es selten eine gute Idee ist, einen Stacktrace im nativen Code zu nehmen - nicht, weil es nicht möglich ist, sondern weil Sie üblicherweise versuchen, das Falsche zu erreichen.

Die meiste Zeit versuchen die Leute, beispielsweise einen außergewöhnlichen Umstand in einen Stacktrace zu bekommen, wie wenn eine Ausnahme erfasst wird, ein Behauptung fehlschlägt oder - am schlimmsten und fälschlichste am meisten -, wenn Sie eine tödliche "Ausnahme" oder ein Signal wie ein Segmentierungsverletzung.

In Anbetracht des letzten Problems müssen Sie die meisten APIs erfordern, dass Sie ausdrücklich Speicher zuweisen oder es intern tun können. Dies in dem fragilen Zustand, in dem sich Ihr Programm derzeit befindet, kann die Dinge auch akutal noch schlimmer machen. Beispielsweise spiegelt der Crash -Bericht (oder Coredump) nicht die tatsächliche Ursache des Problems wider, sondern Ihr fehlgeschlagener Versuch, damit zu handhaben).

Ich gehe davon aus, dass Sie versuchen, diese tödliche Sache mit der Handhabung zu erreichen, da die meisten Menschen das zu versuchen scheinen, wenn es darum geht, eine Stacktrace zu bekommen. In diesem Fall würde ich mich auf den Debugger (während der Entwicklung) verlassen und den Prozess Coredump in Produktion (oder Mini-Dump unter Windows) lassen. Zusammen mit dem richtigen Symbolmanagement sollten Sie keine Probleme haben, die Anweisung nach dem Mortem zu ermitteln.

Es gibt keine unabhängige Plattform, um dies zu tun.

Das nächste, was Sie tun können, ist, den Code ohne Optimierungen auszuführen. Auf diese Weise können Sie sich an den Prozess befinden (mit dem visuellen C ++ - Debugger oder GDB) und eine verwendbare Stapelverfolgung erhalten.

Für Windows, CaptureStackBackTrace() ist auch eine Option, die am Ende des Benutzers weniger Vorbereitungscode erfordert als StackWalk64() tut. (Auch für ein ähnliches Szenario, das ich hatte, CaptureStackBackTrace() arbeitete am Ende besser (zuverlässig) als besser als StackWalk64().)

Sie sollten die verwenden Bibliothek abwickeln.

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 nicht in einer gemeinsamen Bibliothek anrufen.

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

Solaris hat das pstack Befehl, das auch in Linux kopiert wurde.

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

Post -Mortem -Debugging

Obwohl ich derzeit Probleme mit dem habe X64 Implementierung davon.

In den letzten Jahren habe ich Ian Lance Taylors Libbacktrace verwendet. Es ist viel sauberer als die Funktionen in der GNU C -Bibliothek, für die alle Symbole exportiert werden müssen. Es bietet mehr Nützlichkeit für die Erzeugung von Backtraces als Libunwind. Und zu guter Letzt wird es nicht von ASLR besiegt, wie Ansätze, die externe Tools erfordern, wie z. addr2line.

LibbackTrace war ursprünglich Teil der GCC -Verteilung, wird aber jetzt vom Autor als eigenständige Bibliothek unter einer BSD -Lizenz zur Verfügung gestellt:

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.

Sie können es tun, indem Sie den Stapel rückwärts gehen. In Wirklichkeit ist es jedoch häufig einfacher, zu Beginn jeder Funktion einen Kenner zu einem Anrufstapel hinzuzufügen und am Ende zu knallen und dann einfach den Inhalt zu drucken. Es ist ein bisschen wie ein Pita, aber es funktioniert gut und wird am Ende Zeit ersparen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top