ftraceの使い方
ftraceの使い方については、以下の記事でも紹介しましたが、
過去には紹介しなかった使い方も分かったので、あらためてまとめます。
過去に紹介したときは、
「関数単位でカーネルの実行状況のトレースログを出してくれる機能」
という紹介でしたが、実は他にも機能があります。
なお以降の説明は、Linux 3.14.51で確認したものです。
また、debugfsのマウントポイントを
参考: debugfsのマウント方法
(マウントポイント /sys/kernel/debug の場合)
sudo mount -t debugfs none /sys/kernel/debug
カーネルコンフィギュレーションの変更
ftraceを使用する際もカーネルコンフィギュレーションの変更が必要です。
- Kernel hacking
- [*] Tracers <- 有効化
- [*] Kernel Function Tracer <- 有効化
- [*] Kernel Function Graph Tracer <- 有効化
- [*] Kernel Function Tracer <- 有効化
- [*] Tracers <- 有効化
上記の通り、有効化してカーネルをビルドしてください。
ftraceの有効化
カーネルコンフィギュレーションを変更して作成したカーネルでブートしたあと、
まずは、ftraceが有効になっているかを以下のように確認します。
$ sudo cat /proc/sys/kernel/ftrace_enabled
1
ftrace_enabledから1が読み出せる場合は有効になっています。
有効になっていない場合は、ftrace_enabledに1を書いて有効化してください。
# echo 1 > /proc/sys/kernel/ftrace_enabled あるいは、 $ echo 1 | sudo tee /proc/sys/kernel/ftrace_enabled
※ ftrace_enabledは所有者(root)にしか書き込み権限が無いので、sudo+リダイレクトでは書き込めません
なお、sysctlコマンドを使用して、以下のように設定することもできます。
$ sudo sysctl kernel.ftrace_enabled=1
trace-cmdを使用した関数単位トレース
ここではまず、trace-cmdコマンドを使用した関数単位でのトレース方法を紹介します。
trace-cmdのソースコードをダウンロード(Gitリポジトリのclone)し、
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git
ビルド・インストールします。
$ cd trace-cmd/
$ make
$ sudo make install
なお、sudo make install完了後、
Note: to install the gui, type "make install_gui"
to install man pages, type "make install_doc"
「GUIが必要なら、"make install_gui"
man pageが必要なら"make install_doc"」
と表示が出ます。必要に応じてインストールしてください。
make install_guiは試していませんが、
make install_docには、"asciidoc"パッケージが必要です。
$ sudo apt-get install asciidoc
でインストールしておいてください。
trace-cmdコマンドを使用方法について、
例えばlsコマンド実行時のカーネルの動きをトレースしたい場合は、
以下のコマンドを実行します。
sudo trace-cmd record -p function ls
トレース結果は"trace.dat"というファイルに保存されます。
(なお、保存先は-oオプションで指定できます。)
そして、保存したログを閲覧するには以下のコマンドを実行します。
$ trace-cmd report | lv
ただし、↑のコマンドは、トレース結果がカレントディレクトリに"trace.dat"という名前で
保存されていることが条件です。
別名で保存している場合は、-iオプションで入力ファイルを指定できます。
trace-cmd reportでは、以下のような結果が得られます。(先頭部分のみ抜粋)
cpus=4 ls-13012 [003] 9874.688303: function: mutex_unlock ls-13012 [003] 9874.688304: function: __mutex_unlock_slowpath ls-13012 [003] 9874.688304: function: _raw_spin_lock ls-13012 [003] 9874.688304: function: wake_up_process ls-13012 [003] 9874.688304: function: try_to_wake_up ls-13012 [003] 9874.688304: function: _raw_spin_lock_irqsave ls-13012 [003] 9874.688304: function: task_waking_fair ls-13012 [003] 9874.688304: function: select_task_rq_fair ls-13012 [003] 9874.688305: function: source_load ls-13012 [003] 9874.688305: function: target_load ls-13012 [003] 9874.688305: function: effective_load.isra.50 ls-13012 [003] 9874.688305: function: effective_load.isra.50 ls-13012 [003] 9874.688305: function: select_idle_sibling ls-13012 [003] 9874.688305: function: idle_cpu ls-13012 [003] 9874.688305: function: _raw_spin_lock ls-13012 [003] 9874.688306: function: ttwu_do_activate.constprop.93 ls-13012 [003] 9874.688306: function: activate_task ls-13012 [003] 9874.688306: function: enqueue_task ls-13012 [003] 9874.688306: function: update_rq_clock.part.83 ls-13012 [003] 9874.688306: function: enqueue_task_fair ls-13012 [003] 9874.688306: function: update_curr
"ls"コマンドを実行中のカーネルの動きを関数単位でトレースしていることがわかります。
trace-cmdには他にも機能があります。
詳しくは、man trace-cmdを参照してください。
debugfsを使用した方法
debugfsからftraceの機能を利用できます。
にftraceの機能を利用するためのファイルが配置されています。
debugfs経由でのftraceでは、単なる関数単位のトレースではなく、
「スケジューラ」・「システムコール」・「割り込み」などのイベントをトレースできます。
使用中のカーネルで利用可能なトレースイベントは、
の中を除くと確認できます。
$ sudo ls <debugfs>/tracing/events/ block hda kmem nmi sched udp cfg80211 hda_intel mac80211 oom scsi vmscan compaction header_event mce pagemap signal vsyscall drm header_page migrate power skb workqueue enable i915 module printk sock writeback exceptions iommu napi random sunrpc ext4 irq net ras swiotlb filemap irq_vectors nfs raw_syscalls task ftrace jbd2 nfs4 rcu timer
ここでは、試しに、スケジューラのイベントをトレースしてみます。
そのためには、
"enable"ファイルに"1"を書き込みます。
$ echo 1 | sudo tee <debugfs>/tracing/events/sched/enable
そして、
以下のようにスケジューラのイベントのトレース結果を確認できます。
# tracer: nop # # entries-in-buffer/entries-written: 6663/6663 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | tee-13632 [003] d... 12936.308699: sched_wakeup: comm=kworker/3:1 pid=13299 prio=120 success=1 target_cpu=003 tee-13632 [003] .... 12936.308767: sched_process_exit: comm=tee pid=13632 prio=120 tee-13632 [003] d... 12936.308772: sched_stat_runtime: comm=tee pid=13632 runtime=105223 [ns] vruntime=261513731595 [ns] tee-13632 [003] d... 12936.308773: sched_stat_runtime: comm=tee pid=13632 runtime=1259 [ns] vruntime=1394219007352 [ns] tee-13632 [003] d... 12936.308778: sched_wake_idle_without_ipi: cpu=0 tee-13632 [003] d... 12936.308779: sched_wakeup: comm=sudo pid=13631 prio=120 success=1 target_cpu=000 tee-13632 [003] d... 12936.308779: sched_stat_runtime: comm=tee pid=13632 runtime=6344 [ns] vruntime=1394219013696 [ns] tee-13632 [003] d... 12936.308780: sched_switch: prev_comm=tee prev_pid=13632 prev_prio=120 prev_state=x ==> next_comm=kworker/3:1 next_pid=13299 next_prio=120 kworker/3:1-13299 [003] d... 12936.308784: sched_wake_idle_without_ipi: cpu=2 kworker/3:1-13299 [003] d... 12936.308784: sched_wakeup: comm=screen pid=1076 prio=120 success=1 target_cpu=002 kworker/3:1-13299 [003] d... 12936.308785: sched_stat_runtime: comm=kworker/3:1 pid=13299 runtime=5466 [ns] vruntime=1394210026004 [ns] <idle>-0 [002] d... 12936.308786: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=screen next_pid=1076 next_prio=120 kworker/3:1-13299 [003] d... 12936.308789: sched_switch: prev_comm=kworker/3:1 prev_pid=13299 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 <idle>-0 [000] d... 12936.308797: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=sudo next_pid=13631 next_prio=120 screen-1076 [002] dN.. 12936.308811: sched_wakeup: comm=kworker/2:0 pid=17397 prio=120 success=1 target_cpu=002 screen-1076 [002] dN.. 12936.308812: sched_stat_runtime: comm=screen pid=1076 runtime=26836 [ns] vruntime=3991760921 [ns] screen-1076 [002] d... 12936.308812: sched_switch: prev_comm=screen prev_pid=1076 prev_prio=120 prev_state=R ==> next_comm=kworker/2:0 next_pid=17397 next_prio=120 kworker/2:0-17397 [002] d... 12936.308815: sched_migrate_task: comm=xterm pid=1044 prio=120 orig_cpu=0 dest_cpu=3 sudo-13631 [000] .... 12936.308815: sched_process_wait: comm=sudo pid=13632 prio=120 kworker/2:0-17397 [002] d... 12936.308816: sched_wake_idle_without_ipi: cpu=3
ただし、この結果だとスケジューラに関していろいろなイベントが起きている様子はわかりますが、
情報が多すぎます。
実は、スケジューラのイベントの中で特定のイベントのみトレースを有効化することができます。
# ls <debugfs>/tracing/events/sched/ enable sched_process_exit sched_stat_wait filter sched_process_fork sched_stick_numa sched_kthread_stop sched_process_free sched_swap_numa sched_kthread_stop_ret sched_process_wait sched_switch sched_migrate_task sched_stat_blocked sched_wait_task sched_move_numa sched_stat_iowait sched_wakeup sched_pi_setprio sched_stat_runtime sched_wakeup_new sched_process_exec sched_stat_slee
"enable"、"filter"はファイルで、その他はディレクトリです。
ここに並ぶディレクトリは、スケジューラに関してトレースできるイベントで、
先ほど、schedディレクトリ直下のenableに1を書くことで、すべて有効化していました。
今度は、Forkイベントのみトレースしてみます。
まずは、先ほどの有効化を元に戻します(無効化)。
$ echo 0 | sudo tee <debugfs>/tracing/events/sched/enable
そして、sched/sched_process_forkディレクトリのenableに1を書き、Forkイベントのみトレースを有効化します。
$ echo 1 | sudo tee <debugfs>/tracing/events/sched/sched_process_fork/enable
このまま再度traceファイルを読みだすと、スケジューラのイベントすべて有効だった時の結果が残っているので、
一旦クリアします。(空文字を書き込みます。)
$ echo | sudo tee /debug/tracing/trace
再度、
# tracer: nop # # entries-in-buffer/entries-written: 5/5 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | emacs-1100 [002] .... 13732.423490: sched_process_fork: comm=emacs pid=1100 child_comm=emacs child_pid=13799 x-window-manage-855 [002] .... 13733.807122: sched_process_fork: comm=x-window-manage pid=855 child_comm=x-window-manage child_pid=13800 sh-13800 [002] .... 13733.808288: sched_process_fork: comm=sh pid=13800 child_comm=sh child_pid=13801 zsh-22818 [003] .... 13739.756022: sched_process_fork: comm=zsh pid=22818 child_comm=zsh child_pid=13802 sudo-13802 [002] .... 13739.763045: sched_process_fork: comm=sudo pid=13802 child_comm=sudo child_pid=13803
なお、トレース結果を確認するファイルとして、traceの他に、trace_pipeがあります。
両者の違いは以下のとおりです。
- trace
- 読みだすと、トレースログの先頭から末尾まで吐き出し、EOFを返す
- 再度読みだすと、また先頭から吐き出す
- trace_pipe
- 読み出すと、前回最後に吐き出した次の行から末尾まで吐き出し、EOFは返さずに新たに得られた結果を随時吐き出す
その他、詳しくは以下のドキュメントを参照してください。