Неверные номера строк в addr2line
Я пытаюсь найти точную строку вызова в backtrace в программе на C++. Прямо сейчас я использую эти строки (со страницы руководства backtrace), чтобы получить трассировку:
void *bt_buffer[1000];
char **bt_strings;
int bt_nptrs = backtrace(bt_buffer, 1000);
bt_strings = backtrace_symbols(bt_buffer, bt_nptrs);
В bt_strings я нахожу строки вида
./prog() [0x402e42]
Теперь я беру адрес (шестнадцатеричную строку) и передаю его на addr2line. Это иногда приводит к явно неправильным номерам строк. Поиск в Интернете привел меня к этому сообщению, в котором показано, что
readelf -wl ./prog
указывает, где на самом деле находится линия, или, скорее, на сколько строк символ переместился в текущую строку.
редактировать: это происходит, когда я компилирую с -g -O0
т.е. явно без оптимизаций. Компилятор gcc 4.6.3
Есть другой флаг компилятора, который я пропускаю?
Моя проблема заключается в следующем: мне нужно автоматизировать это. Мне нужна моя программа, чтобы создать обратную трассировку (сделано), извлечь файл (выполнено) и номер строки (ошибка).
Я мог конечно позвонить readelf
и анализировать вывод, но это не совсем подходит, так как вывод отличается от символа к символу в зависимости от того, что именно произошло. Иногда адрес символа находится в одной строке, а информация о смещении строки в следующей строке...
Подводить итоги:
Есть ли элегантный способ получить точный номер строки вызова функции в обратном следе изнутри программы во время выполнения?
редактировать: пример кода:
#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;
}
скомпилируйте и запустите:
g++ test_unwind.cc -g -O0 -lunwind && ./a.out
выход:
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
например, тестирование 0x400ef0 с выходами addr2line
/path/to/code/test_unwind.cc:73
это правильный файл, но неверный номер строки. В реальных приложениях номер строки может отличаться на многие строки, вперед и назад.
редактировать: компилировать с -S
показывает, что соответствующей частью является:
.LCFI34:
.cfi_def_cfa_register 6
.loc 2 72 0
call _Z15dummy_function2v
.loc 2 73 0
popq %rbp
Что отображается addr2line
а также адрес возврата, как показано в строке после call
, Я хотел бы получить строку "вход", то есть то, что показано раньше!
2 ответа
Вы уверены, что можете сделать! Я знаю пример реализации, которая использует libunwind. Смотрите этот пост в блоге: http://blog.bigpixel.ro/stack-unwinding-stack-trace-with-gcc/
Все сводится к следующему коду (буквально скопированному из статьи):
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);
}
}
Это связано с тем, что на большинстве архитектур счетчик программ указывает на инструкцию после текущей выполняющейся инструкции. Вы все еще можете использовать addr2line, но добавьте -1 для x86 или -8 для aarch32, чтобы получить фактический адрес строки исходного кода. Обратный CPP использует ту же идею. https://github.com/bombela/backward-cpp/blob/master/backward.hpp (ищите ip_before_instruction). Я обнаружил, что единственный момент, когда нельзя вычитать, - это адрес, по которому произошел сбой. Это имеет смысл, потому что после вызова обработчика ошибок (например, для обслуживания ошибки страницы) он может возобновить выполнение инструкции, вызвавшей сбой.
Ты пытался
__LINE__
Это символ препроцессора, но вы можете скомпилировать его в.