23

C++ プログラムのバックトレースで呼び出しの正確な行を見つけようとしています。現在、これらの行を使用して (backtrace の man ページから) トレースを取得しています。

  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]

次に、アドレス (16 進文字列) を取得し、addr2line に入力します。その結果、明らかに間違った行番号が表示されることがあります。インターネット検索でこの投稿にたどり着きました。

readelf -wl ./prog

が実際にどこにあるか、またはシンボルが現在の行に移動した行数を示します。

編集:-g -O0これは、最適化なしで明示的にコンパイルすると発生します。コンパイラはgcc 4.6.3私が見逃している別のコンパイラフラグはありますか?

私の問題は次のとおりです。これを自動化する必要があります。バックトレースを作成し(完了)、ファイルを抽出し(完了)、行番号を抽出する(失敗)プログラムが必要です。

もちろん、出力を呼び出しreadelfて解析することもできますが、正確に何が起こったかによって出力がシンボルごとに異なるため、これはあまり適していません。シンボルのアドレスが 1 行に表示され、次の行に行オフセットに関する情報が表示されることがあります。

総括する:

実行時にプログラム内からバックトレースで関数呼び出しの正確な行番号を取得するエレガントな方法はありますか?

編集:コード例:

#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。「エントリ」行、つまり前に表示されているものを取得したいと思います!

4

3 に答える 3

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);
    }
}
于 2012-07-24T07:25:50.033 に答える
-1

やってみました

__LINE__

これはプリプロセッサ シンボルですが、コンパイルできます。

于 2012-07-26T01:44:07.707 に答える