8

-finstrument-functions オプションを使用して関数呼び出しをプロファイリングしようとしています。基本的に、私が行ったことは、コンパイルされたソースに次のように書き込むことです。

static int __stepper=0;
void __cyg_profile_func_enter(void *this_fn, void *call_site)
                              __attribute__((no_instrument_function));
void __cyg_profile_func_enter(void *this_fn, void *call_site) {
  int i=0;
  for( ; i<__stepper; i++ ) printf(" ");
  printf("E: %p %p\n", this_fn, call_site);
  __stepper ++;
} /* __cyg_profile_func_enter */

void __cyg_profile_func_exit(void *this_fn, void *call_site)
                             __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *this_fn, void *call_site) {
  int i=0;
  __stepper --;
  for( ; i<__stepper; i++ ) printf(" ");
  printf("L:  %p %p\n", this_fn, call_site);
} /* __cyg_profile_func_enter */

そして、次の結果を得ました。

 E: 0xb7597ea0 0xb75987a8
  E: 0xb7597de0 0xb7597ef5
  L:  0xb7597de0 0xb7597ef5
 L:  0xb7597ea0 0xb75987a8

すべての関数呼び出しアドレスはその領域 (0xb7.......) の周りにありますが、「readelf -s」を使用して関数のシンボルを読み取ろうとすると、次のようになります。

2157: 00101150   361 FUNC    LOCAL  DEFAULT   13 usb_audio_initfn
2158: 00100940   234 FUNC    LOCAL  DEFAULT   13 usb_audio_handle_reset
2159: 00100de0   867 FUNC    LOCAL  DEFAULT   13 usb_audio_handle_control

すべての関数のバイナリのアドレス領域は 0x00 あたりです……。そのため、関数ポインタから関数名を取得できません。関数ポインターがオフセットなどを取得する方法のように見えます。

誰にもアイデアはありますか?

4

2 に答える 2

6

質問から、ライブラリ関数をプロファイリングしているようです。

測定されている関数が何であるかを知るには、2 つのオプションがあります。

1ライブラリを使用するプログラムを で実行し、gdbで停止しmainます。この時点pidで、プログラムのPID=...を取得し、「cat /proc/$PID/maps」を実行します。そこには、次のようなものが表示されます。

➜  ~  ps
  PID TTY          TIME CMD
18533 pts/4    00:00:00 zsh
18664 pts/4    00:00:00 ps
➜  ~  PID=18533
➜  ~  cat /proc/$PID/maps
00400000-004a2000 r-xp 00000000 08:01 3670052                            /bin/zsh5
006a1000-006a2000 r--p 000a1000 08:01 3670052                            /bin/zsh5
006a2000-006a8000 rw-p 000a2000 08:01 3670052                            /bin/zsh5
006a8000-006bc000 rw-p 00000000 00:00 0 
...
7fa174cc9000-7fa174ccd000 r-xp 00000000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ccd000-7fa174ecc000 ---p 00004000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ecc000-7fa174ecd000 r--p 00003000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ecd000-7fa174ece000 rw-p 00004000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
...

これはライブラリ7fa174cc9000のベースアドレスです。/lib/x86_64-linux-gnu/libcap.so.2.22したがって、取得するすべてのアドレスはreadelf -s、その値だけオフセットされます。ベースアドレスがわかれば、ファイル内の元のオフセットが何であったかを計算し直すことができます。

7fa174206370つまり、ライブラリの値とベースア​​ドレスを取得した場合7fa1741cf000、オフセットは7fa174206370 - 7fa1741cf000 = 37370です。私の例でsigsuspendは、GLIBC からのものです。

94: 0000000000037370   132 FUNC    WEAK   DEFAULT   12 sigsuspend@@GLIBC_2.2.5

2gdbこれらのライブラリを使用するプログラムで実行します。ロードされたライブラリをメモリ内ですぐに見つけるか.text、ライブラリのセクションを指す必要があります。

> gdb
(gdb) attach YOUR_PID
(a lot of output about symbols)
(gdb) x/i 0x00007fa174206386
=> 0x7fa174206386 <sigsuspend+22>:  cmp    $0xfffffffffffff000,%rax

0x7fa174206386このようにして、それが内部にあることがわかりますsigsuspend

gdb単独でシンボルをロードしない場合(Reading symbols from ... Loading symbols for ...アタッチ後のような出力がない場合)、オプション1のようにライブラリのベース アドレスを検索し、それに.textセクションのオフセットを追加できます。

➜  ~  readelf -S /lib/x86_64-linux-gnu/libcap.so.2.22 | grep '.text.'
  [11] .text             PROGBITS         0000000000001620  00001620

7fa174cc9000 + 000000000000162016 進数で を与えると、上記のように7FA174CCA620接続して実行しますgdb

(gdb) add-symbol-file /lib/x86_64-linux-gnu/libcap.so.2.22 7FA174CCA620

次に、シンボルを単独でロードしない場合でも、(x/i ADDRESSオプション1のように)シンボルを見つけることができるはずです。gdb

ご不明な点がございましたら、ご質問ください。説明を試みます。

なぜこれがそうなのかについての説明

観測された動作は、 Position-Independent Codeとしてコンパイルされているライブラリによるものです。これにより、動的ライブラリを簡単にサポートできます。PIC は基本的に、ライブラリの ELF にセクション.plt.gotセクションがあり、任意のベース アドレスにロードできることを意味します。PLT はプロシージャ リンク テーブルであり、他のモジュールにある関数の呼び出しのトラップが含まれています。最初にプログラム インタープリターに移動して、呼び出された関数を再配置できるようにし、最初の呼び出しの後に関数にジャンプします。これは、プログラム インタープリターが、呼び出す関数のアドレスを含む GOT (グローバル オフセット テーブル) を更新するために機能します。最初にGOTが初期化され、最初の関数呼び出しで、現在呼び出されている関数の解決を実行するプログラムインタープリターの関数にジャンプが実行されます。

x86-64 では、PLT エントリは通常次のようになります。

0000000000001430 <free@plt>:
    1430:       ff 25 e2 2b 20 00       jmpq   *0x202be2(%rip)        # 204018 <_fini+0x201264>
    1436:       68 00 00 00 00          pushq  $0x0
    143b:       e9 e0 ff ff ff          jmpq   1420 <_init+0x28>

1 つ目jmpqは、GOT の location に保存されているアドレスへのジャンプです%rip + 0x202be2

  [20] .got              PROGBITS         0000000000203fd0  00003fd0
       0000000000000030  0000000000000008  WA       0     0     8

%rip + 0x202be2になり0x204012、ライブラリのベースアドレスに追加されて、ライブラリが実際にロードされる場所に関連する絶対アドレスが生成されます。つまり、 にロードされた場合0x7f66dfc03000、対応する GOT エントリの結果のアドレスは になります0x7F66DFE07012。その場所に格納されているアドレスは、(この例では)free関数のアドレスです。実際のfreeinを指すようにプログラムインタープリターによって維持されlibcます。

詳細については、こちらを参照してください

于 2013-11-04T23:19:46.490 に答える
3

必要なのは、このdladdr関数です。問題の関数が定義されているモジュール (メイン プログラムまたは共有ライブラリ) をデバッグ モードでビルドした場合、dladdr関数を呼び出すことで、そのアドレスとベース アドレスに基づいて関数名を取得できます。モジュール (共有ライブラリなど) がロードされる場所:

#define _GNU_SOURCE
#include <dlfcn.h>

void find_func(void* pfnFuncAddr)
{
    Dl_info info;
    memset(&info,0,sizeof(info));
    if(dladdr(pfnFuncAddr,&info) && info.dli_fname)
    {
            /*here: 'info.dli_fname' contains the function name */
            /*      'info.dli_fbase' contains Address at which shared library is loaded */
    }
    else
    {
           /* if we got here it means that the module was not built with debug
              information or some other funny thing happened (e.g. we called function)
              written purely in assembly) */ 
    }
}

リンク時に-ldlを追加する必要があります。

次の点に注意してください。

  • アドレスは実際の関数アドレスであるため、find_funcプロファイルされたプロセスから関数を呼び出す必要があります (読み取り:__cyg_profile_func_enterまたは__cyg_profile_func_exit関数のどこか) 。pfnFuncAddrthis_fncall_site__cyg_*

  • 取得する関数名は壊れている可能性があります(クラスの c++ 関数/メソッドの場合)。c++filtというコマンド ライン ツールを使用して名前をデマングルできます。プロファイラー コードからデマングルしたい場合は、bfdライブラリーと や のような関数を調べる必要がありbfd_read_minisymbols bfd_demangleます。本当にプロファイリングしたい場合は、コードを後で (プロファイリング後に) すべての関数名をデマングルすることをお勧めします。

  • 観察したアドレス値の違いは、問題の関数の実際のアドレスと、関数を含むモジュールがロードされたベース アドレス (読み取り: info.dli_fbase) の違いとまったく同じです。

それが役立つことを願っています。

于 2013-11-04T04:00:29.920 に答える