2012-07-20 13 views
22

Intento encontrar la línea exacta de una llamada en el backtrace en el programa C++. En este momento estoy usando estas líneas (desde la página del manual de traza inversa) para obtener la traza:Números de línea incorrectos de addr2line

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

En bt_strings encuentro líneas de la forma

./prog() [0x402e42] 

Ahora tomo la dirección (el hexágono cadena) y alimentarlo a addr2line. Eso a veces resulta en números de línea aparentemente incorrectos. búsqueda en Internet me llevó a este post, en el que se demuestra que

readelf -wl ./prog 

indica que la línea realmente es, o más bien el número de líneas en el símbolo se ha movido a la línea actual.

Editar: Esto ocurre cuando compilo con -g -O0, es decir, explícitamente sin optimizaciones. El compilador es gcc 4.6.3 ¿Hay otra bandera del compilador que extraño?

Mi problema es el siguiente: Necesito automatizar esto. Necesito que mi programa cree una traza inversa (hecho), extraiga el archivo (hecho) y el número de línea (fallido).

Podría, por supuesto, llamar al readelf y analizar la salida, pero eso no es realmente adecuado, ya que la salida difiere de un símbolo a otro en función de lo que sucedió exactamente. A veces, la dirección de un símbolo se encuentra en una línea y la información acerca de la línea de desplazamiento en la línea siguiente ...

Para resumir:

¿Hay una manera elegante de obtener el número exacto de una línea de Llamada de función en el backtrace desde dentro del programa durante el tiempo de ejecución?

edit: código ejemplo:

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

compilar y ejecutar:

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

salida:

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 

pruebas, por ejemplo, 0x400ef0 con rendimientos addr2line

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

que es el archivo correcto, pero el número de línea incorrecta. En aplicaciones de la vida real, el número de línea puede diferir en muchas líneas, hacia adelante y hacia atrás.

edición: compilar con -S muestra que la parte pertinente es:

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

lo que se muestra por addr2line y por igual es la dirección de retorno, como se muestra en la línea después del call. Me gustaría obtener la línea de "entrada", es decir, ¡lo que se muestra antes!

+0

¿Se equivoca al compilar sin optimizaciones? – Flexo

+0

Sí, olvidé mencionar que ... editaré la publicación – steffen

+1

Pruebe 'gdb'ing el ejecutable y use' l * 0x

'. ¿Le da la dirección correcta, o también da lo mismo que 'addr2line'? – Shahbaz

Respuesta

3

¡Seguro que puedes!Sé de una implementación de ejemplo que usa libunwind. Ver esta entrada del blog: http://blog.bigpixel.ro/2010/09/stack-unwinding-stack-trace-with-gcc/

Todo se reduce a este pedazo de código (literalmente copiado del artículo):

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); 
    } 
} 
+0

¡Estoy emocionado de intentarlo tan pronto como llegue a la oficina! – steffen

+2

Lo probé y descubrí que en 'getFileAndLine' también usan una llamada' popen' a 'addr2line'. libunwind es interesante (+1 para eso) pero tiene una funcionalidad similar a backtrace. El resultado es los mismos números de línea desplazados. – steffen

+1

En su pregunta, dijo que está compilando '-g', ¿debería usar' -ggdb' en su lugar? – Bart

0

Ha intentado

__LINE__ 

Es un símbolo de preprocesador, pero puede compilarlo en.

+0

También he pensado en '__FILE__' y' __LINE__', pero quiero obtener la información de 'foo()' de una función a la que 'foo()' llama. Y no puedo cambiar cómo se llama 'foo()'. – steffen

Cuestiones relacionadas