3

update: this question cannot fix my need, addr2line source is not only 400 lines source, it's relative with other binutils source, I wanna a light solution to do the "get backtrace line number"

I use following that can get backtrace line number:

addr2line -e /home/roroco/Dropbox/c/ro-c/cmake-build-debug/ex/test_backtrace_with_line_number  0x400d0b

but if I wanna get backtrace all line numbers, I must invoke addr2line cli line by line, it's slow, is there way to get backtrace line number without cli but use pure c++? or other alternative lib can get line number

I know if I see addr2line, I can do this, if has more convenient c++ lib, please tell me

here is my code to get line number with addr2line, I hope pure c++ solution to instead addr2line cli

#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <zconf.h>
#include "regex"

std::string getexepath() {
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
}

std::string sh(std::string cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
            result += buffer.data();
        }
    }
    return result;
}


void print_backtrace(void) {
    void *bt[1024];
    int bt_size;
    char **bt_syms;
    int i;

    bt_size = backtrace(bt, 1024);
    bt_syms = backtrace_symbols(bt, bt_size);
    std::regex re("\\[(.+)\\]");
    auto exec_path = getexepath();
    for (i = 1; i < bt_size; i++) {
        std::string sym = bt_syms[i];
        std::smatch ms;
        if (std::regex_search(sym, ms, re)) {
            std::string addr = ms[1];
            std::string cmd = "addr2line -e " + exec_path + " -f -C " + addr;
            auto r = sh(cmd);
            std::regex re2("\\n$");
            auto r2 = std::regex_replace(r, re2, "");
            std::cout << r2 << std::endl;
        }
    }
    free(bt_syms);
}

void test_m() {
    print_backtrace();
}

int main() {
    test_m();
    return 0;
}
chikadance
  • 3,591
  • 4
  • 41
  • 73
  • 2
    Possible duplicate of [Is there a library call to addr2line?](https://stackoverflow.com/questions/11556321/is-there-a-library-call-to-addr2line) – Lanting Aug 17 '18 at 09:15
  • IMO, using gdb would be faster and easier – Clonk Aug 17 '18 at 09:15
  • Out of curiosity, I googled a bit and found [`addr2line.c` on github](https://kernel.googlesource.com/pub/scm/linux/kernel/git/hjl/binutils/+/hjl/secondary/binutils/addr2line.c). It's part of GNU Binutils and under GPL (V3) if I got it right. – Scheff's Cat Aug 17 '18 at 09:19
  • Note that addr2line allows you to specify an arbitrary list of line numbers to be resolved in one invocation - not just a single one. – 500 - Internal Server Error Aug 17 '18 at 10:58
  • I got following output after using above code. ??:0?? ??:0?? ??:0?? ??:0?? ??:0main ??:??? ??:0_start ??:? – Pabitra Dash Jan 31 '19 at 04:59
  • Have you looked at [Boost::Stacktrace](https://github.com/boostorg/stacktrace)? I think it might be using addr2line code, and might thus be relevant to what you're trying to achieve. – einpoklum May 11 '20 at 11:16

1 Answers1

2

change addr2line source is so hard and I give up this way, I receive @500 - Internal Server Error suggestion, addr2line cli can receive multi addrs, so I change my code like following, only run addr2line once

#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <zconf.h>
#include "regex"
#include "vector"

std::string getexepath() {
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
}

std::string sh(std::string cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
            result += buffer.data();
        }
    }
    return result;
}


void print_backtrace(void) {
    void *bt[1024];
    int bt_size;
    char **bt_syms;
    int i;

    bt_size = backtrace(bt, 1024);
    bt_syms = backtrace_symbols(bt, bt_size);
    std::regex re("\\[(.+)\\]");
    auto exec_path = getexepath();
    std::string addrs = "";
    for (i = 1; i < bt_size; i++) {
        std::string sym = bt_syms[i];
        std::smatch ms;
        if (std::regex_search(sym, ms, re)) {
            std::string m = ms[1];
            addrs += " " + m;
        }
    }
    auto r = sh("addr2line -e " + exec_path + " -f -C " + addrs);
    std::cout << r << std::endl;
    free(bt_syms);
}

void test_m() {
    print_backtrace();
}

int main() {
    test_m();
    return 0;
}
chikadance
  • 3,591
  • 4
  • 41
  • 73