prex(1) カーネルトレース


1.prexカーネルトレース Solaris 8 prex、tnfxtract、およびtnfdumpコマンド

プロセスやスレッドの動き、ディスク入出力の動き等を詳細に調査したい場合、Solaris 8より提供されたprexコマンド【備考1】によって情報を採取し、分析できるようになりました。prexは、TNF(Trace Normal Form)に基づいたカーネルプローブを組み込んだ幾つかのカーネルルーチンのトレース情報を採取します。prexは、所定の条件に基づき、キー"keys"で定義されたセレクタを判別して、トレースデータをバッファに順次書き込みます。フィルタにプロセスIDを使用することも出来ます。トレース終了後、tnfxtractコマンドでそのバッファからトレース情報を抜き出し、tnfdumpコマンドで編集する流れになります。このprexバッファは、採取されるトレース情報量により、どれだけのサイズが最適かは現在のところ調査中です。判明次第お知らせします。当面、システムの実装メモリ、空きメモリ等から判断いただきますようお願いします。図1にカーネルトレース採取の手順を示します。

No 手順 操作概要
1 開始
prexコマンドの実行 # prex -k <CR>
(1) バッファの割り当て prex> buffer alloc <CR> 【備考2】
(2) トレースのイネーブル prex> enable $all <CR>  【備考3】【備考4】
(3) トレースのオン prex> trace $all <CR>   【備考3】【備考4】
(4) カーネルトレースのオン prex> ktrace on <CR>
(5) prexプロンプトの終了 prex> quit <CR>
2 情報採取 トレースを採取したい目的の操作を行います
3 停止
prexコマンドの実行 # prex -k <CR>
(1) カーネルトレースのオフ prex> ktrace off <CR>
(2) prexプロンプトの終了 prex> quit <CR>
4 情報の抜き出し
(1) tnfxtractコマンドの実行 # tnfxtract tracefile.trc <CR>
5 情報の編集
(1) tnfdumpコマンドの実行 # tnfdump tracefile.trc > tracefile.dump <CR> 【備考5】
6 バッファ解放
prexコマンドの実行 # prex -k <CR>
(1) バッファの解放 prex> buffer dealloc <CR> 【備考6】
(2) prexプロンプトの終了 prex> quit <CR>

図1.カーネルトレースの手順

【備考1】prexコマンドについて
デバッグ中のプログラムにプローブを埋め込み、所定の方法でコンパイルすることでprexコマンドを用いてデバッグすることも出来ます。ユーザプログラム(コマンド)のトレーシングは"prex [ -o tracefile ] command ... "と指定して実行します。詳細はprex(1)、tracing(3TNF)、およびTNF_PROBE(3TNF)のマニュアルページをご覧下さい。また、GNUのgccによるプロファイル機能を使用してデバッグや、計測、プロファイリングを行うことも出来ます。 詳細はGNU gccコンパイラのドキュメントと、prof(1)、gprof(1)のマニュアルページをご覧下さい。

【備考2】バッファ割り当てについて
バッファはカーネルのsegkと呼ばれるセグメントに確保されます。この領域はページングの対象外で、メモリにロックされます。prexを実行すると会話モードになります。会話モードに入って最初に実行するコマンドは、次のようになります。単に"buffer alloc"とすると、384キロバイトのバッファが確保されます。数値の後ろの"m"はメガバイト単位(1,048,576バイト)で確保することを示します。"k"を指定するとキロバイト単位(1,024バイト)で確保されます。バッファサイズの省略時値は393,216バイト(約400キロバイト)、最大長は134,217,728バイト(約134メガバイト)です。それよりも大きな値を指定しても134メガバイトに切り詰められます。

# prex -k <CR>
Type "help" for help ...
prex> buffer alloc 4m <CR>
Buffer of size 4194304 bytes allocated
prex>

図2.バッファの割り当て bufferファンクション

【備考3】keysの確認
"keys"を設定することが出来ます。省略時は「全部」になっています。このため、プローブを$allでイネーブルすると全部のトレースになります。省略時の設定は次のようになっており、"keys"は/.*/の正規表現のため、全部がマッチすることになります。

prex> list sets <CR>
$all 'keys'=/.*/
prex>

図3.プローブの正規表現

【備考4】keysに指定できるもの
現在、明確に判明している"keys"は次の通りです。先の$allによって採取、抜き出し(tnfxtract)、編集(tnfdump)した出力の先頭に表示される行です。実際はもっと増えているか、または変わっている可能性もあります。目的のシステムで、$allによって試しに出力されることをお勧めします。

probe tnf_name: "syscall_end" tnf_string: "keys syscall thread;file ../../sparc/os/syscall.c;line 853;"
probe tnf_name: "syscall_start" tnf_string: "keys syscall thread;file ../../sparc/os/syscall.c;line 541;"
probe tnf_name: "thread_block" tnf_string: "keys synch;file ../../common/tnf/tnf_res.c;line 290;"
probe tnf_name: "thread_state" tnf_string: "keys thread;file ../../common/tnf/tnf_res.c;line 306;"
probe tnf_name: "thread_state" tnf_string: "keys thread;file ../../common/tnf/tnf_res.c;line 323;"
probe tnf_name: "thread_queue" tnf_string: "keys dispatcher;file ../../common/tnf/tnf_res.c;line 221;"
probe tnf_name: "thread_state" tnf_string: "keys thread;file ../../common/tnf/tnf_res.c;line 225;"
probe tnf_name: "biodone" tnf_string: "keys io blockio;file ../../common/os/bio.c;line 1201;"
probe tnf_name: "pageout" tnf_string: "keys vm pageio io;file ../../common/vm/vm_pvn.c;line 553;"
probe tnf_name: "thread_state" tnf_string: "keys thread;file ../../sun4u/os/trap.c;line 337;"
probe tnf_name: "address_fault" tnf_string: "keys vm pagefault;file ../../common/vm/vm_as.c;line 1239;"
probe tnf_name: "thread_state" tnf_string: "keys thread;file ../../sun4u/os/trap.c;line 1141;"
probe tnf_name: "page_unmap" tnf_string: "keys vm pagefault;file ../../common/vm/vm_page.c;line 2683;"
probe tnf_name: "thread_exit" tnf_string: "keys thread;file ../../common/tnf/tnf_res.c;line 165;"
probe tnf_name: "anon_zero" tnf_string: "keys vm pagefault;file ../../common/vm/vm_anon.c;line 1209;"
probe tnf_name: "anon_private" tnf_string: "keys vm pagefault;file ../../common/vm/vm_anon.c;line 1105;"
probe tnf_name: "strategy" tnf_string: "keys io blockio;file ../../common/os/driver.c;line 529;"
probe tnf_name: "thread_create" tnf_string: "keys thread;file ../../common/tnf/tnf_res.c;line 150;"
probe tnf_name: "major_fault" tnf_string: "keys vm pagefault;file ../../common/os/bio.c;line 1270;"
probe tnf_name: "pagein" tnf_string: "keys vm pageio io;file ../../common/os/bio.c;line 1297;"
---------------- ---------------- ----- ----- ---------- --- ------------------------- ------------------------
Elapsed (ms) Delta (ms) PID LWPID TID CPU Probe Name Data / Description . . .
---------------- ---------------- ----- ----- ---------- --- ------------------------- ------------------------
                                          :

図4.keysの具体例

grepコマンドに指定するように、例えば"io"を指定すると"biodone"、"pageout"、"strategy"、および"pagein"のプローブが対象になります。これらを$allの代わりに、"enable""trace"ファンクションに指定します。例えば"thread"(スレッド)をセットする場合は次のようになります。

prex> enable thread <CR>
prex> trace thread <CR>
prex>

図5.threadのセット例

【備考5】編集ファイルサイズについて
$allの場合、とんでもない編集ファイルサイズになりますので、くれぐれもご注意下さい(ORACLEのdbstartとdbshutだけで、約18メガバイト、約16万行でした!!)。

【備考6】バッファの解放
バッファを開放すると、"buffer deallocated"のメッセージが表示されます。segk(カーネルセグメント)からトレースバッファが解放されます。バッファを開放しないでトレースを採取した場合、情報がミックスされます。トレース情報がバッファの最後までいくと先頭から上書きされます。正確な情報を採取したい場合は逐次、割り当て→解放することをお勧めします。

# prex -k <CR>
Type "help" for help ...
prex> buffer dealloc <CR>
buffer deallocated
prex> quit <CR>
#

図6.バッファの解放

2.syscallを選択してトレースする

keysにsyscallを選択してトレースする例を示します。この結果、システムコールの最初と終わり(図4の1行目と2行目のプローブ)の2種類のトレースが採取されます。

# prex -k <CR>
Type "help" for help ...
prex> buffer alloc 4m <CR>
Buffer of size 4194304 bytes allocated
prex> enable syscall <CR>
prex> trace syscall <CR>
prex> ktrace on <CR>

         : →quitしてから、prexコマンドを再起動しても良いです。

prex> ktrace off <CR>
prex> quit <CR>
# tnfxtract syscall.trc <CR>
# tnfdump syscall.trc > syscall.dump <CR>
# prex -k <CR>
Type "help" for help ...
prex> buffer dealloc <CR>
buffer deallocated
prex> quit <CR>
#

図7.syscallを選択してトレースする例

 

3.プロセスフィルタリング

常駐しているプロセスに関連するトレースを採取したい場合、pfilterファンクションで目的のプロセスを指定することが出来ます。keysを"thread"(スレッド)としてプロセスフィルタリングを行う例を図11に示します。

# ps -efl | grep ckpt | grep -v grep <CR>
 8 S  oracle  592  1  1  41  20  ?  53304  ?  15:48:48  ?  0:13 ora_ckpt_ora900
# prex -k <CR>
Type "help" for help ...
prex> buffer alloc <CR>
Buffer of size 393216 bytes allocated
prex> pfilter add 592 <CR>
prex> pfilter on <CR>
prex> pfilter <CR>
Process filtering is on
Process filter set is {592}
prex> tarce thread <CR>
prex> enable thread <CR>
prex> ktrace on <CR>

      :      →quitしてから、prexコマンドを再起動しても良いです。

prex> ktrace off <CR>
prex> pfilter off <CR>
prex> quit <CR>
# tnfxtract /var/tmp/ckpt_thread.trc <CR>
# tnfdump /var/tmp/ckpt_thread.trc > /var/tmp/ckpt_thread.dump <CR>
# prex -k <CR>
Type "help" for help ...
prex> buffer dealloc <CR>
buffer deallocated
prex> quit <CR>
#

図8.プロセスフィルタリングの例

【参考】prex(1)は他に多くの機能を持っています。詳細は関連するマニュアルページtracing(1)、TNF_PROBE(3TNF)、tnfdump(1)、tnfxtrace(1)をご覧下さい。

 

4.トレース情報の見方

前述の$allによって編集した出力の先頭は、トレース可能なプローブの一覧です。各行は、カーネルルーチン名と、ソースコードの行番号が表示されています。それぞれ、プローブによって採取される情報が異なります。トレース情報は、tnfdumpで出力される編集出力の"Data / Description"項目に表示されます。図4の範囲でどのような情報がトレースされるのかを図9に示します。なお、ソースコード行番号、および"Data / Description"はカーネルレベルやパッチレベルで変更される可能性があります。

ルーチン名 TNF名 keys 行番号 関数名・概要 Data/Description 関連ヘッダーファイル
bio.c
【備考1】
biodone io
blockio
1201 biodone
ブロック型入出力の完了
device: b_edev
block: b_lblkno
buf: buf構造体アドレス
buf.h(buf構造体)
major_fault vm
pagefault
1270 pageio_setup
メジャーフォルト
vnode: p_vnode
offset: p_offset
page.h(page構造体)
pagein vm
pageio
io
1297 pageio_setup
ページイン
vnode: p_vnode
offset: p_offset
size: len
page.h(page構造体)
driver.c
【備考1】
strategy io
blockio
529 bdev_strategy
ディスクストラテジー
device: b_edev
block: b_lblkno
size: b_bcount
buf: buf構造体アドレス
flags: b_flags
buf.h(buf構造体)
tnf_res.c
【備考2】
thread_create thread 150 tnf_thread_create
スレッドクリエイト
tid: kthread_t
pid: p_pid
start_pc: t_startpc
thread.h(thread構造体)
ttoprocによるプロセスID
thread_exit thread 165 tnf_thread_exit
スレッドエグジット
ops = t_tnf_tpdp thread.h(thread構造体)
tnf_writer.h(TNFデータ構造体)
thread_queue dispatcher 221 tnf_thread_queue
スレッドキュー
tid: kthread_t
cpuid: cpu_id
priority: pri_t
queue_length: cpu_disp.disp_nrunnable
thread.h(thread構造体)
cpuvar.h(cpu構造体)
types.h(short型)
disp.h(disp構造体)
thread_state thread 225 tnf_thread_queue
スレッドキュー
tid: kthread_t
state: LMS_WAIT_CPU (8)
thread.h(thread構造体)
msacct.h(LWPマイクロステート)
thread_block synch 290 tnf_thread_switch
スレッドブロック
reason: t_wchan
stack: pcstack(pcs)
thread.h(thread構造体)
pcstackによるスタックポインタ
thread_state thread 306 tnf_thread_switch
スレッドスイッチ
state: SLPSTATE SLPSTATEによるステート
thread_state thread 323 tnf_thread_switch
スレッドスイッチ
state: RUNSTATE RUNSTATEによるステート
vm_anon.c
【備考3】
anon_private vm
pagefault
1105 anon_private
COWセグメント
address: addr caddr_t(ページアドレス)
anon_zero vm
pagefault
1209 anon_zero
ゼロフィルセグメント
address: addr caddr_t(ページアドレス)
vm_as.c
【備考3】
address_fault vm
pagefault
1239 as_fault
ページフォルト
address: addr
fault_type: type
access: rw
caddr_t(ページアドレス)
seg_enum.h(タイプと型)
vm_page.c
【備考3】
page_unmap vm
pagefault
2683 page_hashout
ページの削除
vnode: vp
offset: p_offset
vnode.h(vnode構造体)
page.h(page構造体)
vm_pvn.c
【備考3】
pageout vm
pageio
io
553 pvn_write_done
ページ書き込み
vnode: vp
pages_pageout: pgpgout
pages_freed: dfree
pages_reclaimed: pgrec
vnode.h(vnode構造体)
pageout、free、およびreclaimedのカウント
syscall.c
【備考4】
syscall_start syscall
thread
541 pre_syscall
システムコール開始
sysnum: t_sysnum thread.h(system call number)
syscall_end syscall
thread
853 post_syscall
システムコール終了
rval1: rval1
rval2: rval2
errno: errno
rval1、rval2は64ビットの場合の戻り値。
errno.h
trap.c
【備考5】
thread_state thread 337 trap
プロセッサトラップ
state: mstate msacct.h
thread_state thread 1141 trap
ユーザトラップ
state: LMS_USER (0) msacct.h

【備考】ソースコードのパス(ディレクトリ)名
1.uts/common/os
2.uts/common/tnf
3.uts/common/vm
4.uts/sparc/os
5.uts/sun4u/os

図9.トレース情報の見方

 

Copyright (C) 2004 by The Art of Computer Technologies, Corp.  All rights reserved.