Wie kann backtrace()/backtrace_symbols() die Funktionsnamen drucken?

Lesezeit: 5 Minuten

Benutzeravatar von Lyke
Lyke

Das Linux-spezifische backtrace() und backtrace_symbols() ermöglicht es Ihnen, eine Aufrufverfolgung des Programms zu erstellen. Es gibt jedoch nur Funktionsadressen aus, nicht ihre Namen für mein Programm. Wie kann ich sie dazu bringen, auch die Funktionsnamen zu drucken? Ich habe versucht, das Programm mit zu kompilieren -g ebenso gut wie -ggdb. Der folgende Testfall gibt nur Folgendes aus:


    BACKTRACE ------------
    ./a.out() [0x8048616]
    ./a.out() [0x8048623]
    /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
    ./a.out() [0x8048421]
    ----------------------
    

Ich möchte, dass die ersten 2 Elemente auch die Funktionsnamen anzeigen, foo und main

Code:

#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>

static void full_write(int fd, const char *buf, size_t len)
{
        while (len > 0) {
                ssize_t ret = write(fd, buf, len);

                if ((ret == -1) && (errno != EINTR))
                        break;

                buf += (size_t) ret;
                len -= (size_t) ret;
        }
}

void print_backtrace(void)
{
        static const char start[] = "BACKTRACE ------------\n";
        static const char end[] = "----------------------\n";

        void *bt[1024];
        int bt_size;
        char **bt_syms;
        int i;

        bt_size = backtrace(bt, 1024);
        bt_syms = backtrace_symbols(bt, bt_size);
        full_write(STDERR_FILENO, start, strlen(start));
        for (i = 1; i < bt_size; i++) {
                size_t len = strlen(bt_syms[i]);
                full_write(STDERR_FILENO, bt_syms[i], len);
                full_write(STDERR_FILENO, "\n", 1);
        }
        full_write(STDERR_FILENO, end, strlen(end));
    free(bt_syms);
}
void foo()
{
    print_backtrace();
}

int main()
{
    foo();
    return 0;
}

  • mögliches Duplikat von So erhalten Sie einen detaillierteren Backtrace

    – Nemo

    4. August 2011 um 0:51 Uhr

  • btw, funktion backtrace_symbols_fd führt die gleiche Operation aus wie backtrace_symbols()aber die resultierenden Zeichenfolgen werden sofort in den Dateideskriptor geschrieben fd.

    – nhngia

    16. Juli 2018 um 10:01 Uhr

  • 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

  • Da das zweite Argument von backtrace() die maximale Anzahl von Adressen angibt, die in dem vom ersten angegebenen Puffer (man 3 backtrace) gespeichert werden können, ist hier ein Wert von 1024 unnötig großzügig. Ein Wert von 20 sollte ausreichen, und das Drucken einer Warnung für eine mögliche Kürzung, wenn bt_size == 20 ist, ist eine gute Praxis.

    – Robert

    27. Juli 2021 um 13:13 Uhr

Benutzeravatar von Matthew Slattery
Matthew Slattery

Die Symbole werden der dynamischen Symboltabelle entnommen; du brauchst die -rdynamic Option zu gccwodurch ein Flag an den Linker übergeben wird, der dies sicherstellt alle Symbole werden in der Tabelle platziert.

(Siehe die Link-Optionen Seite der GCC-Handbuchund / oder die Rückverfolgungen Seite der glibc-Handbuch.)

  • Dies funktioniert jedoch nicht für statische Symbole. libunwind das @Nemo erwähnt, funktioniert für statische Funktionen.

    – Nathan Kidd

    18. März 2014 um 0:22 Uhr

  • In einem CMake-Projekt wurde dies fälschlicherweise als festgelegt ADD_COMPILE_OPTIONS(-rdynamic). Stattdessen muss es sein ADD_LINK_OPTIONS(-rdynamic) oder gleichwertig, um das gewünschte Verhalten zu haben.

    – Stefan

    17. Juli 2020 um 19:54 Uhr

  • Noch besser für diejenigen, die cmake verwenden: SET (CMAKE_ENABLE_EXPORTS TRUE). Dadurch wird das Linker-Flag -rdynamic gesetzt.

    – Stefan

    17. Januar 2021 um 18:55 Uhr

Verwenden Sie die addr2line-Befehl um ausführbare Adressen auf den Quellcode Dateiname+Zeilennummer abzubilden. Gib die -f Option, um auch Funktionsnamen zu erhalten.

Alternativ versuchen Libunwind.

  • Die Zeile addr2 ist nett, weil sie den Dateinamen und die Zeilennummer in der Ausgabe enthält, aber sie schlägt fehl, sobald der Loader Verschiebungen durchführt.

    – Erwan Legrand

    5. Juni 2014 um 14:14 Uhr

  • … und bei ASLR sind Umzüge häufiger denn je.

    – Erwan Legrand

    5. Juni 2014 um 14:18 Uhr

  • @ErwanLegrand: Vor ASLR dachte ich, die Umzüge seien vorhersehbar und addr2line funktionierte zuverlässig sogar für Adressen in gemeinsam genutzten Objekten (?). Aber ja, auf modernen Plattformen müssten Sie die tatsächliche Ladeadresse des verschiebbaren Objekts kennen, selbst um diese Operation im Prinzip durchzuführen.

    – Nemo

    5. Juni 2014 um 14:24 Uhr

  • Ich kann nicht sicher sagen, wie gut es vor ASLR funktioniert hat. Heutzutage wird es nur für Code in “normalen” ausführbaren Dateien verwendet und schlägt für Code in DSOs und PIEs (Position Independent Executables) fehl. Glücklicherweise scheint libunwind für all diese zu funktionieren.

    – Erwan Legrand

    5. Juni 2014 um 14:34 Uhr

  • Ich hatte diese Diskussion vergessen. Libbacktrace, das ich damals noch nicht kannte, ist eine bessere Option. (Siehe meine neue Antwort.)

    – Erwan Legrand

    27. Mai 2015 um 10:15 Uhr

Benutzeravatar von Erwan Legrand
Erwan Legrand

Das hervorragende Libbacktrace von Ian Lance Taylor löst dieses Problem. Es handhabt das Stack-Unwinding und unterstützt sowohl gewöhnliche ELF-Symbole als auch DWARF-Debugging-Symbole.

Libbacktrace erfordert nicht den Export aller Symbole, was hässlich wäre, und ASLR bricht es nicht.

Libbacktrace war ursprünglich Teil der GCC-Distribution. Jetzt ist eine Standalone-Version auf Github zu finden:

https://github.com/ianlancetaylor/libbacktrace

Die Antwort oben hat einen Fehler, wenn ret == -1 und errno EINTER ist, sollten Sie es erneut versuchen, aber ret nicht als kopiert zählen (werden nur dafür keinen Account erstellen, wenn Sie es nicht hart mögen)

static void full_write(int fd, const char *buf, size_t len)
{
        while (len > 0) {
                ssize_t ret = write(fd, buf, len);

                if ((ret == -1) {
                        if (errno != EINTR))
                                break;
                        //else
                        continue;
                }
                buf += (size_t) ret;
                len -= (size_t) ret;
        }
}

Backtrace verstärken

Sehr praktisch, weil es beides druckt:

  • unverfälschte C++-Funktionsnamen
  • Linien Nummern

automatisch für Sie.

Zusammenfassung der Verwendung:

#define BOOST_STACKTRACE_USE_ADDR2LINE
#include <boost/stacktrace.hpp>

std::cout << boost::stacktrace::stacktrace() << std::endl;

Ich habe ein minimales lauffähiges Beispiel dafür und viele andere Methoden bereitgestellt unter: print call stack in C or C++

  • Die Frage ist markiert [c]nicht [c++].

    – Jakow Galka

    5. Dezember 2019 um 0:55 Uhr

  • Die Frage ist markiert [c]nicht [c++].

    – Jakow Galka

    5. Dezember 2019 um 0:55 Uhr

1421110cookie-checkWie kann backtrace()/backtrace_symbols() die Funktionsnamen drucken?

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

Privacy policy