Wrong line numbers from addr2line

人盡茶涼 提交于 2021-02-04 10:40:28

问题


I try to find the exact line of a call in the backtrace in C++ program. Right now I am using these lines (from the man page of backtrace) to get the trace:

  void *bt_buffer[1000];
  char **bt_strings;
  int bt_nptrs = backtrace(bt_buffer, 1000);
  bt_strings = backtrace_symbols(bt_buffer, bt_nptrs);

In bt_strings I find lines of the form

./prog() [0x402e42]

Now I take the address (the hex string) and feed it to addr2line. That sometimes results in apparently wrong line numbers. Internet search led me to this post, in which it is shown that

readelf -wl ./prog

indicates where the line really is, or rather how many lines the symbol has moved to the current line.

edit: This happens when I compile with -g -O0, i.e. explicetly without optimisations. The compiler is gcc 4.6.3 Is there another compiler flag that I miss?

My problem is the following: I need to automate this. I need my program to create a backtrace (done), extract the file (done) and the line number (fail).

I could of course call readelf and parse the output, but that's not really suitable, as the output differs from symbol to symbol depending on what exactly happened. Sometimes The address of a symbol is in one line and the information about the line offset in the next line...

To sum up:

Is there an elegant way to get the exact line number of a function call in the backtrace from within the program during runtime?

edit: example code:

#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <execinfo.h>
#include <iostream>
#include <stdlib.h>

void show_backtrace()
{
  // get current address
  void* p = __builtin_return_address(0);
  std::cout << std::hex << p << std::endl;

  // get callee addresses
  p = __builtin_return_address(1);
  std::cout << std::hex << p << std::endl;

  p = __builtin_return_address(2);
  std::cout << std::hex << p << std::endl;
}

void show_backtrace2()
{
  void *array[10];
  size_t size;
  char **strings;
  int i;

  size = backtrace (array, 10);
  strings = backtrace_symbols ((void *const *)array, size);

  for (i = 0; i < size; i++)
    {
      std::cout << strings[i] << std::endl;
    }

  free (strings);
}

void show_backtrace3 (void)
{
  char name[256];
  unw_cursor_t cursor; unw_context_t uc;
  unw_word_t ip, sp, offp;

  unw_getcontext (&uc);
  unw_init_local (&cursor, &uc);

  while (unw_step(&cursor) > 0)
    {
      char file[256];
      int line = 0;

      name[0] = '\0';
      unw_get_proc_name (&cursor, name, 256, &offp);
      unw_get_reg (&cursor, UNW_REG_IP, &ip);
      unw_get_reg (&cursor, UNW_REG_SP, &sp);

      std::cout << std:: hex << name << " ip = " << (long) ip 
                << " , sp = " << (long) sp << std::endl;
    }
}

void dummy_function2()
{
  show_backtrace();
  show_backtrace2();
  show_backtrace3();
}

void dummy_function1()
{ 
  dummy_function2 (); 
} // line 73

int main(int argc, char **argv)
{
  dummy_function1 ();
  return 0;
}

compile and run:

g++ test_unwind.cc -g -O0 -lunwind  && ./a.out

output:

0x400edb
0x400ef0
0x400f06
./a.out() [0x400cfb]
./a.out() [0x400ee0]
./a.out() [0x400ef0]
./a.out() [0x400f06]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f2f044ae76d]
./a.out() [0x400b79]
_Z15dummy_function2v ip = 400ee5 , sp = 7fffdb564580
_Z15dummy_function1v ip = 400ef0 , sp = 7fffdb564590
main ip = 400f06 , sp = 7fffdb5645a0
__libc_start_main ip = 7f2f044ae76d , sp = 7fffdb5645c0
_start ip = 400b79 , sp = 7fffdb564680

testing e.g. 0x400ef0 with addr2line yields

/path/to/code/test_unwind.cc:73

which is the correct file, but wrong line number. In real life applications the line number can differ by many lines, forward and backward.

edit: compiling with -S shows that the relevant part is:

.LCFI34:
  .cfi_def_cfa_register 6
  .loc 2 72 0
  call  _Z15dummy_function2v
  .loc 2 73 0
  popq  %rbp

What is displayed by addr2line and alike is the return address, as shown in the line after the call. I would like to get the "entry" line, i.e. what is shown before!


回答1:


You sure can do! I know of an example implementation which uses libunwind. See this blog post: http://blog.bigpixel.ro/stack-unwinding-stack-trace-with-gcc/

It comes down to this piece of code (literally copied from the article):

void show_backtrace (void)
{
    char name[256];
    unw_cursor_t cursor; unw_context_t uc;
    unw_word_t ip, sp, offp;

    unw_getcontext(&uc);
    unw_init_local(&cursor, &uc);

    while (unw_step(&cursor) > 0)
    {
        char file[256];
        int line = 0;

        name[0] = '\0';
        unw_get_proc_name(&cursor, name, 256, &offp);
        unw_get_reg(&cursor, UNW_REG_IP, &ip);
        unw_get_reg(&cursor, UNW_REG_SP, &sp);

        //printf ("%s ip = %lx, sp = %lx\n", name, (long) ip, (long) sp);
        getFileAndLine((long)ip, file, 256, &line);
        printf("%s in file %s line %d\n", name, file, line);
    }
}



回答2:


Have you tried

__LINE__

It is a preprocessor symbol, but you can compile it in.




回答3:


This is because on most architectures, the program counter points to an instruction after the current executing instruction. You can still use addr2line, but add -1 for x86 or -8 for aarch32 to get the actual address of the source code line. Backward CPP uses the same idea. https://github.com/bombela/backward-cpp/blob/master/backward.hpp (search for ip_before_instruction). I found the only time to not subtract would be if the address is where a crash happened. This makes sense because after calling the fault handler (e.g. to service a page fault), it could resume executing the faulting instruction.



来源:https://stackoverflow.com/questions/11579509/wrong-line-numbers-from-addr2line

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!