※ 左右のカーソルキーでもページ繰りができます
DTrace の使用 (= dtrace コマンドの実行)には、 root 権限が必要です。
dtrace
ユーザプロセスの関数呼び出しフローを採取しましょう
show_flow.c から生成した show_flow コマンドの関数フローを、 watch_flow_whole.d を使用して採取してみましょう。
show_flow.c
show_flow
watch_flow_whole.d
$ dtrace -s ./watch_flow_whole.d \ -F \ -c ./show_flow \ show_flow
dtrace コマンドの起動における各要素について説明します。
-s ./watch_flow_whole.d
-n script
-F
-c ./show_flow
-c './show_flow 1 2 3'
D スクリプト(watch_flow_whole.d ファイル) の各要素について説明します。
pid$target:$1::entry, pid$target:$1::return { }
pid
$target
-c
$1
entry
exit
{ }
基本的な D スクリプトは、 以下の構成を持っています。
probe-description [, .....] { action-statements }
上記を「節」(clause) と呼びます。
probe-description は、 以下の要素から構成されています。
probe-description
probeprov : probemod : probefunc : probename
probeprov
probemod
probefunc
probename
"-p" でのプロセスID指定が、 "-c" によるコマンド指定の代替として機能します。
-p
$ dtrace -s ./watch_flow_whole.d \ -F \ -p PID \ ※ $target を置換 show_flow
-p による対象指定を行う場合、 デバッガ等の制御下にある(例: ブレークポイント等での停止状態) プロセスを対象とすることはできません。
デバッガ等と併用する場合には、watch_flow_attach.d のような D スクリプトを使用します:
watch_flow_attach.d
pid$1:$2::entry, pid$1:$2::return { }
以下の要領で使用します。
$ dtrace -s ./watch_flow_attach.d \ -F \ PID \ ※ $1 を置換 show_flow ※ $2 を置換
採取された関数フローに以下の様な不整合が見られる場合があります。
一般には最適化の影響によるものです。
関数フロー以外の詳細情報を採取してみましょう。
show_args.c から生成されたコマンド show_args 中の、 multiply() 関数呼び出しにおける引数は、 以下の D スクリプト watch_args_val.d で採取することができます。
show_args.c
show_args
multiply()
watch_args_val.d
pid$target:show_args:multiply:entry { printf("%s(%d, %d)", probefunc, arg0, arg1); }
printf
arg0
args[n]
先の例と同じ要領で、引数文字列を表示してみましょう。
pid$target:show_args:showname:entry { printf("%s(%s)", probefunc, arg0); }
このままでは上手く機能しません。
D スクリプトはカーネル内部で動作するため、 ユーザープログラムのメモリ内容を参照するには以下の手順が必要です:
show_args.c から生成されたコマンド show_args 中の、 showname() 関数呼び出しにおける引数は、 以下の D スクリプト watch_args_str.d で採取することができます。
showname()
watch_args_str.d
pid$target:show_args:showname:entry { printf("%s(%s)", probefunc, copyinstr(arg0)); }
ユーザプロセスの文字列を扱う場合、 以下の機能を使用する必要があります。
copyin
stringof
string
copyinstr
stringof(copyin(addr))
printf の %s フォーマットは、 string 変換されたものしか受け付けません。
%s
文字列を扱う DTrace の組み込み変数は string を返却するものが殆どであり、 ついつい忘れてしまうので注意が必要です!
show_args.c から生成されたコマンド show_args 中の、 checksum() 関数呼び出しにおける引数は、 以下の D スクリプト watch_args_mem.d で採取することができます。
checksum()
watch_args_mem.d
pid$target:show_args:checksum:entry { this->iobuf = alloca(32); copyinto(arg0, 32, this->iobuf); tracemem(this->iobuf, 32); }
this->iobuf
iobuf
alloca(size)
copyinto(from, size, to)
from
size
to
tracemem(addr, size)
addr
※1: とりあえずは、C/C++ の局所変数相当と考えてください。
※2: 可変長メモリ領域の内容取得に関しては、応用編で説明します。
show_args.c から生成されたコマンド show_args 中の、 各関数呼び出しにおける戻り値は、 以下の D スクリプト watch_retval.d で採取することができます。
watch_retval.d
pid$target:show_args::return { printf("%s()=0x%p", probefunc, arg1); }
arg1
return
戻り値無し(void)や、構造体返しの関数の場合、 arg1 値は不定となるので注意が必要です。
pid プロバイダの return プローブ使用時における arg0 は関数の戻り先アドレスを保持します。
必ずしも「完全なフロー」の採取が必要なケースばかりではありません。
「どこから呼び出されたか?」という情報のみで十分なケースもあります。
show_flow.c から生成した show_flow コマンドにおける関数 f1() が、 どのような契機で呼び出されているのかを、 watch_ustack.d を使用して採取してみましょう。
f1()
watch_ustack.d
$ dtrace -s ./watch_ustack.d \ -c ./show_flow \ show_flow \ ※ $1 を置換 f1 ※ $2 を置換
D スクリプト watch_ustack.d では、 ustack() アクションを使用して、 特定の関数が呼び出された際の呼び出しスタックを表示します。
ustack()
pid$target:$1:$2:entry { ustack(); }