কিভাবে ব্যাকট্রেস () / backtrace_symbols () ফাংশন নাম মুদ্রণ করতে?


90

লিনাক্স নির্দিষ্ট backtrace()এবং backtrace_symbols()আপনাকে প্রোগ্রামটির একটি কল ট্রেস তৈরি করতে দেয় to তবে এটি কেবলমাত্র প্রোগ্রামের ঠিকানাগুলি মুদ্রণ করে, আমার প্রোগ্রামের জন্য তাদের নাম নয়। আমি কীভাবে তাদের ফাংশনটির নাম মুদ্রণ করতে পারি? আমি -gপাশাপাশি প্রোগ্রামটি সংকলন করার চেষ্টা করেছি -ggdb। নীচের পরীক্ষার কেসটি কেবল এটি মুদ্রণ করে:

    পটভূমি ------------
    ./a.out () [0x8048616]
    ./a.out () [0x8048623]
    /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
    ./a.out () [0x8048421]
    ----------------------
    

আমি প্রথম দুটি আইটেমগুলিও ফাংশনের নামগুলি প্রদর্শন করতে চাই fooএবংmain

কোড:

#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;
}

possible duplicate of How to get more detailed backtrace
Nemo

btw, function backtrace_symbols_fd performs the same operation as backtrace_symbols(), but the resulting strings are immediately written to the file descriptor fd.
nhnghia

উত্তর:


64

The symbols are taken from the dynamic symbol table; you need the -rdynamic option to gcc, which makes it pass a flag to the linker which ensures that all symbols are placed in the table.

(See the Link Options page of the GCC manual, and / or the Backtraces page of the glibc manual.)


10
This doesn't work for static symbols, though. libunwind that @Nemo mentions, works for static functions.
Nathan Kidd

In a CMake project, this had mistakenly been set as ADD_COMPILE_OPTIONS(-rdynamic). Instead, it needs to be ADD_LINK_OPTIONS(-rdynamic) or equivalent to have the desired behaviour.
Stéphane

30

Use the addr2line command to map executable addresses to source code filename+line number. Give the -f option to get function names as well.

Alternatively, try libunwind.


3
addr2 line is nice because it includes file name and line number in the output, but it fails as soon as relocations are performed by the loader.
Erwan Legrand

... and with ASLR relocations are more common than ever.
Erwan Legrand

@ErwanLegrand: Before ASLR, I thought the relocations were predictable and addr2line worked reliably even for addresses in shared objects (?) But yeah, on modern platforms you would need to know the actual load address of the relocatable object even to do this operation in principle.
Nemo

I can't tell for sure how well it worked before ASLR. Nowadays, it will only for for code inside "regular" executables and fail for code inside DSOs and PIEs (Position Independent Executables). Fortunately, libunwind appears to work for all of these.
Erwan Legrand

I had forgotten about this discussion. Libbacktrace, which I did not know of at the time, is a better option. (See my new answer.)
Erwan Legrand

11

The excellent Libbacktrace by Ian Lance Taylor solves this issue. It handles stack unwinding and supports both ordinary ELF symbols and DWARF debugging symbols.

Libbacktrace does not require exporting all symbols, which would be ugly, and ASLR does not break it.

Libbacktrace was originally part of the GCC distribution. Now, a standalone version can be found on Github:

https://github.com/ianlancetaylor/libbacktrace


2

the answer on the top has a bug if ret == -1 and errno is EINTER you should try again, but not count ret as copied (not going to make an account just for this, if you don't like it tough)

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;
        }
}

0

Boost backtrace

Very convenient because it prints both:

  • unmangled C++ function names
  • line numbers

automatically for you.

Usage summary:

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

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

I have provided a minimal runnable example for it and many other methods at: print call stack in C or C++


The question is tagged [c], not [c++].
Yakov Galka
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.