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 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.)
Antworten:
glibc bietet die Funktion backtrace ().
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
char ** backtrace_symbols (void *const *buffer, int size)
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
welche, die die Ausgabe zum Beispiel direkt an stdout / err senden können.
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.
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 Möglichkeit, dies auf eine bequemere / OOP-Weise zu verwenden, besteht darin, das Ergebnis von backtrace_symbols () in einem Ausnahmeklassenkonstruktor zu speichern. Wenn Sie also diese Art von Ausnahme auslösen, haben Sie die Stapelverfolgung. 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 < frames; ++i) {
printf("%s\n", strs[i]);
}
free(strs);
}
};
...
try {
throw MyException("Oops!");
} catch ( MyException e ) {
e.printStackTrace();
}
Ta da!
Hinweis: Durch Aktivieren von Optimierungsflags kann die resultierende Stapelverfolgung ungenau werden. Idealerweise würde man diese Funktion mit aktivierten Debug-Flags und deaktivierten Optimierungs-Flags verwenden.
Überprüfen Sie für Windows die StackWalk64 () -API (auch unter 32-Bit-Windows). Unter UNIX sollten Sie die native Methode des Betriebssystems verwenden oder auf glibcs backtrace () zurückgreifen, falls verfügbar.
Beachten Sie jedoch, dass das Erstellen 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 meisten Leute versuchen, einen Stacktrace beispielsweise in einem außergewöhnlichen Umstand zu erhalten, z. B. wenn eine Ausnahme abgefangen wird, eine Behauptung fehlschlägt oder - am schlimmsten und am falschesten von allen - wenn Sie eine fatale "Ausnahme" oder ein Signal wie ein erhalten Segmentierungsverletzung.
In Anbetracht des letzten Problems müssen Sie bei den meisten APIs explizit Speicher zuweisen oder dies intern tun. Wenn Sie dies in dem fragilen Zustand tun, in dem sich Ihr Programm derzeit befindet, kann dies die Situation akut 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 nehme an, Sie versuchen, diese schwerwiegende 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 verlassen (während der Entwicklung) und den Prozess in der Produktion entleeren lassen (oder Mini-Dump unter Windows). Zusammen mit einer ordnungsgemäßen Symbolverwaltung sollten Sie keine Probleme haben, die verursachende Anweisung post mortem herauszufinden.
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 nicht aus einer gemeinsam genutzten Bibliothek anrufen.
Sie können den addr2line
Befehl unter Linux verwenden, um die Quellfunktion / Zeilennummer des entsprechenden PCs abzurufen.
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 visuellen C ++ - Debuggers oder GDB) und einen verwendbaren Stack-Trace abrufen.
Für Windows CaptureStackBackTrace()
ist dies auch eine Option, für die der Benutzer weniger Vorbereitungscode benötigt als für den Benutzer StackWalk64()
. (Auch für ein ähnliches Szenario, das ich hatte, CaptureStackBackTrace()
funktionierte es besser (zuverlässiger) als StackWalk64()
.)
Solaris hat den Befehl pstack , der auch in Linux kopiert wurde.
In den letzten Jahren habe ich Ian Lance Taylors Libbacktrace verwendet. Es ist viel sauberer als die Funktionen in der GNU C-Bibliothek, bei denen alle Symbole exportiert werden müssen. Es bietet mehr Nutzen für die Erzeugung von Backtraces als libunwind. Und last but not least wird es von ASLR nicht besiegt, ebenso wenig wie Ansätze, die externe Tools erfordern, wie z addr2line
.
Libbacktrace war ursprünglich Teil der GCC-Distribution, wird jetzt jedoch 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 von libbacktrace nicht unterstützt wird.
Sie können dies tun, indem Sie den Stapel rückwärts laufen lassen. In der Realität ist es jedoch häufig einfacher, zu Beginn jeder Funktion einen Bezeichner zu einem Aufrufstapel hinzuzufügen und am Ende zu platzieren, und dann einfach den Inhalt zu drucken. Es ist ein bisschen wie eine PITA, aber es funktioniert gut und spart Ihnen am Ende Zeit.