2017-11-10 15 views
1

perfツールを使用して自分のプログラムのコールチェーンを取得したいと考えています。しかし、結果は常に不完全で、最後に直接usleepを呼び出す関数が欠けています。私はsched:sched_switchusleepトレースイベントを記録しようとしましたが、結果は常に同じです。glibc内のperfレコードサンプルのアプリケーションコールチェーンが不完全です

$ g++ -g -rdynamic test_sleep.cpp -o test_sleep 
$ ./test_sleep & 
$ ps -ef |grep test_sleep |grep -v grep 
root  15292 14879 0 16:31 pts/1 00:00:02 ./test_sleep 

$ perf record -e probe_libc:usleep -e sched:sched_switch -gp 15292 
$ perf script 

test_sleep 15292 [019] 159309.652668:  probe_libc:usleep: (7ff615142030) 

        ee030 usleep (/usr/lib64/libc-2.17.so) 
        1039 funcs1 (/home/test_sleep) 
        1070 fun (/home/test_sleep) 
        107b main (/home/test_sleep) 
        21b15 __libc_start_main (/usr/lib64/libc-2.17.so) 

test_sleep 15292 [019] 159309.663728:  sched:sched_switch: test_sleep:15292 [120] S ==> swapper/19:0 [120] 

      7fff8168dbb0 __schedule ([kernel.kallsyms]) 
      7fff8168e069 schedule ([kernel.kallsyms]) 
      7fff8168cf36 do_nanosleep ([kernel.kallsyms]) 
      7fff810b786b hrtimer_nanosleep ([kernel.kallsyms]) 
      7fff810b79ae sys_nanosleep ([kernel.kallsyms]) 
      7fff81699089 system_call_fastpath ([kernel.kallsyms]) 
        bd410 __GI___libc_nanosleep (/usr/lib64/libc-2.17.so) 
        1039 funcs1 (/home/test_sleep) 
        1070 fun (/home/test_sleep) 
        107b main (/home/test_sleep) 
        21b15 __libc_start_main (/usr/lib64/libc-2.17.so) 

以下は私のプログラムのためのコードです:私たちは、funcs2がcallchainに欠けていることがわかります

void funcs2() 
{ 
    for(i = 0; i< 100; i++){}; 
    while(1) 
     usleep(10000); 
} 
void funcs1() 
{ 
    for(i = 0; i< 100; i++){}; 
    funcs2(); 
} 
void fun() 
{ 
    for(i = 0; i< 100; i++){}; 
    funcs1(); 
} 
main() 
{ 
    fun(); 
    return 0; 
} 

下記のコマンドラインがあります。この関数はg++コンパイラによって最適化されていますか?デフォルトperfことで

./test_sleep(_Z6funcs2v+0x32) [0x400e4f] 
./test_sleep(_Z6funcs1v+0x35) [0x401039] 
./test_sleep(_Z3funv+0x35) [0x401070] 
./test_sleep(main+0x9) [0x40107b] 
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7ff615075b15] 
./test_sleep() [0x400d59] 
+0

優れた説明とコードの例で、StackOverflowの最初の優れた質問は何ですか(私は間違った 'int'sを今のところ許しています;-))。私は自由を取ってタイトルを少し訂正して見つけやすくしました。 – Zulan

+0

ありがとう、Zulan。これはStackOverflowの最初の質問です。次回は詳細に注意します。 – Wind

答えて

0

は、ユーザ空間内callchainを生成するためのフレーム・ポインタを使用しています。しかし、私は-Oオプションを使用していないと私は私のプログラムでbacktrace_symbolsを使用して、完全なコールスタックをキャッチし、以下backtrace_symbolによって記録されたコールスタックです。残念ながら、フレームポインタは最適化のため信頼できません。 glibcが特定の最適化()でコンパイルされ、スタックの巻き戻しが禁止され、簡単に行われるため、問題が発生します。

perfは何を参照してください。カーネル内callchainが

 7fff8168dbb0 __schedule ([kernel.kallsyms]) 
     7fff8168e069 schedule ([kernel.kallsyms]) 
     7fff8168cf36 do_nanosleep ([kernel.kallsyms]) 
     7fff810b786b hrtimer_nanosleep ([kernel.kallsyms]) 
     7fff810b79ae sys_nanosleep ([kernel.kallsyms]) 
     7fff81699089 system_call_fastpath ([kernel.kallsyms]) 

異なる機構を用いて行われる初期のユーザ空間のエントリは、現在のユーザ空間の命令ポインタを使用するため、PERFこの右

   bd410 __GI___libc_nanosleep (/usr/lib64/libc-2.17.so) 

すぐPERFを決定するためにユーザ空間フレームポインタを使用して取得します次の関数。 libcは、最適化のためにフレームポインタを更新していないためです。フレームポインタが(__GI___libc_nanosleepではなく)になります。 funcs2の返信先アドレスはfuncs2を指しています。つまり、それはスタック上で次のように見えます。

   1039 funcs1 (/home/test_sleep) 
       1070 fun (/home/test_sleep) 
       107b main (/home/test_sleep) 
       21b15 __libc_start_main (/usr/lib64/libc-2.17.so) 

これは、プログラム内の最適化とは関係ありません。 libcは少なくともあなたのフレームポインタをそのまま残しておいて、一番上の機能を除いてスタックを見続けることは幸運です。 つまり、perfには、コールチェーンを生成するための2つのオプションがあります。 --call-graph=dwarfは、スタックの一部を実際に格納し、後処理で巻き戻します。私のシステムでは、これはfuncs2を示していますが、それ以上のものはユーザスペースにありません。 --call-graph=lbrも、Intel CPUのハードウェアサポートを使用しています。残念ながら私はそれをテストすることはできません。

この現象は、libc内のperfサンプルに影響します。異なるイベント、システムコール、またはハードウェアサンプルだけです。参考までに、たとえばdebian bug reportでlibcのフレームポインタバージョンについて議論してください。もちろん、フレームポインタを使用してlibcを独自に構築することもできます。

+0

こんにちはZulan。どのようなシステムを使用していますか? CentosまたはUbuntu?私はglibcがデフォルトで最適化されている(-fomit-frame-pointer)ことを覚えています。あなたの結果にfuncs2が含まれているとおっしゃいましたが、どうしましたか? – Wind