2011年10月19日
簡単なLinuxカーネルのプロファイル (readprofile)
Linuxカーネルのタイマー割り込みのところのソースを見ていたら、簡単なしくみのプロファイラが組み込まれているのを見つけました。これはタイマー割り込みのところでプログラムカウンタがどこを指していたかを累計していき、readprofileというツールでその結果を見ることができます。
このプロファイラはCONFIG_PROFILING=y でカーネルをビルドするだけで使うことができます。
Ubuntu 10.04 (x86_64)のカーネルはその条件でビルドされているのですぐに試すことができました。
プロファイラの準備
rootで
# echo 2 > /sys/kernel/profiling
または
$ sudo sh -c "echo 2 > /sys/kernel/profiling"
(この'2'の意味は後述。)
実行例
$ readprofile
1 __ticket_spin_lock 0.0312
1 __ticket_spin_unlock 0.0625
1 finish_task_switch 0.0045
1 find_pid_ns 0.0052
1 tick_nohz_restart_sched_tick 0.0027
1 kmem_cache_free 0.0039
1 kmem_cache_alloc 0.0030
1 do_filp_open 0.0003
1 pid_revalidate 0.0035
1 security_sock_rcv_skb 0.0312
1 security_inode_getattr 0.0208
1 rb_insert_color 0.0028
1 strnlen 0.0156
2 number 0.0025
2 format_decode 0.0020
1 copy_user_generic_string 0.0156
1 memcpy_c 0.0312
3542 acpi_idle_enter_bm 4.9747
1 scsi_request_fn 0.0007
1 _cond_resched 0.0156
9 *unknown*
3563 total 0.0006
データをリセットするには
$ sudo readprofile -r
あるコマンドの開始から終了までをプロファイルするには
$ sudo readprofile -r; some_command ; readprofile
動作原理
カーネルのテキスト領域 _stext から _etext までの範囲をカバーする個数のカウンタの配列をメモリ上に確保します。そしてタイマー割り込みのときに保存されたプログラムカウンタの指すアドレスに対応するカウンタをインクリメントします。カウンタの個数を節約するためにプログラムカウンタを指定されたビット数分だけ右シフトしてから処理しています。プロファイラのセットアップのときに/sys/kernel/profiling に書き込んだ数値がそれです。
_stextから_etextの範囲外のアドレスは"unknown"としてカウントされます。
詳しくは kernel/profile.cを参照してください。サンプルしたPCのカウントは以下の関数で行っています。(!SMPの場合)
void profile_hits(int type, void *__pc, unsigned int nr_hits)
{
unsigned long pc;
if (prof_on != type || !prof_buffer)
return;
pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
atomic_add(nr_hits, &prof_buffer[min(pc, prof_len - 1)]);
}
メモリ上のカウンタの配列は、そのままバイナリデータとして/proc/profileとしてユーザー空間から読み出すことができるようになっています。readprofileコマンドはこのデータとカーネルのシンボル情報をつきあわせて表示を行っています。
制限事項
簡単であるがゆえに制限があります。
- ローダブルモジュールは全て"unknown"でカウントされます。プロファイルできるのはカーネル内の静的にリンクされたテキスト領域だけです。
- インターバルタイマの周期では粗すぎて有意な結果が取得できません。通常のカーネルではタイマーの周期は100Hz、つまり10msecごとです。また、最近のカーネルはダイナミックティックなので、不要なタイマー割り込みは間引かれてしまうのでさらにサンプリング数が少なくなってしまいます。
実用性を考えるとOprofileかperfを使うほうがよいでしょう。