DTrace
Table of Contents
DTrace
DTraceはSunが開発したプロダクト。Sunと言えばもう一つ遺産を残してくれて いた。ZFSね。Zは最後って意味を含んでいるから、色々なfsが出ては消えてき たリナにも、もちろん登場してる。 OpenZFS 2.2.5がリリース、Linux 6.9をサポート まあ、ZFSは金持ちの為のFSだから庶民には関係無い。
そんな訳で、 BSD界隈四方山話 記事一覧 Dtraceの記事が連載されたので追試してみる。 リナ方面でも、独自の実装が有るみたいだ。 eBPFに3日で入門した話が、入口っぽいけど、色々準備が必要そうなので手出 しはしない。
まずはサンプルで実行。
# dtrace -n 'proc:::exec-success { trace(curpsinfo->pr_psargs); }' dtrace: description 'proc:::exec-success ' matched 1 probe CPU ID FUNCTION:NAME 0 60677 none:exec-success /bin/sh /usr/bin/man dtrace 0 60677 none:exec-success /sbin/sysctl -n hw.machine_arch 0 60677 none:exec-success /sbin/sysctl -n hw.machine 0 60677 none:exec-success /usr/bin/locale 0 60677 none:exec-success /usr/bin/gzcat /usr/share/man/man1/dtrace.1.gz 0 60677 none:exec-success head -1 0 60677 none:exec-success /usr/bin/gzcat /usr/share/man/man1/dtrace.1.gz 0 60677 none:exec-success mandoc -Tlint -Wunsupp 0 60677 none:exec-success /usr/bin/gzcat /usr/share/man/man1/dtrace.1.gz 0 60677 none:exec-success mandoc 0 60677 none:exec-success less 0 60677 none:exec-success /bin/sh -c /usr/libexec/atrun 0 60677 none:exec-success /usr/libexec/atrun 0 60677 none:exec-success /bin/sh -c /usr/libexec/atrun 0 60677 none:exec-success /usr/libexec/atrun 0 60677 none:exec-success ps awx ^C
execされたコマンドのヒストリー。manが色々なコマンドの元に実行されてる な。暫くすると、cronの片割れであるatrunも自動的に起動されてる。カーネ ルの活動状況が、exec系を通して眺められるんだな。こりゃ便利だわい。
# dtrace -n 'io:::start { @bytes = quantize(args[0]->bio_bcount); }' dtrace: description 'io:::start ' matched 1 probe ^C value ------------- Distribution ------------- count -1 | 0 0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ 126 1 | 0 : 256 | 0 512 | 2 1024 | 0 2048 | 0 4096 |@@ 11 8192 | 1 16384 | 0 32768 |@@@@@@@@@@ 49 65536 | 0
そしてこちらは、ファイルをどんな単位で読み出ししたかの統計情報。value 0 は、多分パイプ経由かなにかだろう。
他にも面白そうなサンプルが用意されてた。
sakae@fb:/usr/share/dtrace $ ls blocking* hotopen* siftr* tcpstate* disklatency* nfsattrstats* tcpconn* tcptrack* disklatencycmd* nfsclienttime* tcpdebug* udptrack*
HandBook Chapter 27. DTrace 更にハンドブックにも解説が出てた。
with awk
どんなセンサーもとえプローブが埋めこまれているかリストしてみます。
# dtrace -l | head ID PROVIDER MODULE FUNCTION NAME 1 dtrace BEGIN 2 dtrace END 3 dtrace ERROR 4 fbt kernel camstatusentrycomp entry 5 fbt kernel camstatusentrycomp return 6 fbt kernel cam_compat_handle_0x19 entry 7 fbt kernel cam_compat_handle_0x19 return 8 fbt kernel cam_compat_handle_0x17 entry 9 fbt kernel cam_compat_handle_0x17 return dtrace: failed to write to <stdout>: Broken pipe # dtrace -l | wc 63368 315990 4812317
提供者、モジュール、関数と名前で、特定できる様になってるとな。
#!/bin/sh awk '{cnt[$2]++ } END {for (i in cnt) { print i, cnt[i] } } '
ならば、awkの連想配列を利用して、集約してみましょう。$2で集約って事は、 プロバイダーを列挙するんだな。
# dtrace -l | ./cnt.awk | sort -nr -k2 fbt 60663 syscall 1152 dtmalloc 818 mac_framework 242 vfs 183 nfscl 172 lockstat 26 :
その結果、fbtってのが断トツで、銀メダルはsyscallってなった。
# dtrace -l | ./cnt.awk | sort -nr -k2 | head kernel 60479 freebsd 1152 vmci 255 vop 158 nfs4 84 opensolaris 71 hidbus 58 nfs3 46 usbhid 44 nfs2 34
今度は、$3でモジュール毎に集約してみた。
dwatch
dtraceを使い易いように間をshellで仲介したコマンドがFreeBSD12から登場し てる。
# dwatch -h Illegal option -h Usage: dwatch [-1defFmnPqRvVwxy] [-B num] [-E code] [-g group] [-j jail] [-k name] [-K num] [-N count] [-o file] [-O cmd] [-p pid] [-r regex] [-t test] [-T time] [-u user] [-X profile] [-z regex] probe[,...] [args ...] dwatch -l [-fmnPqy] [-r regex] [probe ...] dwatch -Q [-1qy] [-r regex]
大事なのは、-l と -Q だ。いわゆるどんなセンサーと言うかプローブの種類 の表示をするのが -lだ。-Qはそれを結合して便利に使用できるようにした、D 言語の案内(manではprofileと説明してる)。
# dwatch -l | wc 62645 62645 2275696 # dwatch -l | head PROVIDER:MODULE:FUNCTION:NAME callout_execute:kernel:none:callout-end callout_execute:kernel:none:callout-start dtmalloc::$PIR:free dtmalloc::$PIR:malloc dtmalloc::80211crypto:free dtmalloc::80211crypto:malloc dtmalloc::80211dfs:free dtmalloc::80211dfs:malloc dtmalloc::80211mesh_gt:free
# dwatch -Q | wc 44 86 2177 # dwatch -Q | head PROFILES: chmod errno fchmodat io io-done io-start ip ip-receive ip-send kill lchmod nanosleep open openat proc proc-create proc-exec proc-exec-failure
こちらの実行は、dwatch -X proc のようにする。
-d
-d Debug. Send dtrace(1) script to stdout instead of executing.
面白いオプションが有るので試してみる。
# dwatch -d exec >dcode # less dcode #!/usr/sbin/dtrace -s #pragma D option quiet #pragma D option dynvarsize=16m #pragma D option switchrate=10hz int console; dtrace:::BEGIN { console = 0 } /* probe ID 1 */ /*********************************************************/ proc:kernel:none:exec /* probe ID 2 */ { /* * Examine process, parent process, and grandparent process details */ /******************* CURPROC *******************/ this->proc = curthread->td_proc; this->uid0 = this->proc ? this->proc->p_ucred->cr_uid : -1; this->gid0 = this->proc ? this->proc->p_ucred->cr_rgid : -1; this->pid0 = this->proc ? this->proc->p_pid : -1; : proc:kernel:none:exec /* probe ID 3 */ { /***********************************************/ printf("%s%Y%s ", console ? "\033[32m" : "", walltimestamp, console ? "\033[39m" : ""); /****************** EVENT_TAG ******************/ printf("%d.%d %s[%d]: ", this->uid0, this->gid0, execname, this->pid0); /**************** EVENT_DETAILS ****************/ printf("%s", this->args0); /***********************************************/ printf("\n"); }
こんな風に翻訳されて、それがdtraceに渡るんだな。ならば、これを実行。
# chmod 755 dcode # ./dcode 2024 Aug 18 08:12:25 1001.1001 sh[14102]: -sh 2024 Aug 18 08:12:25 1001.1001 sh[14103]: /bin/sh /usr/bin/man ls 2024 Aug 18 08:12:25 1001.1001 sh[14104]: /bin/sh /usr/bin/man ls 2024 Aug 18 08:12:25 1001.1001 sh[14105]: /bin/sh /usr/bin/man ls :
ふむ、しっかり日時が付いていて、ログの形式になってる。
# cat LOG | awk '{print $6, $7, $8}' | uniq | less
日時を除くなら、awkに喰わせればいいか。それとも、ソースを変更しちゃう のも手だな。
::open: /* probe ID 3 */ /execname == "date"/ { printf("%d.%d %s[%d]: ", this->uid0, this->gid0, execname, this->pid0); printf("%s", this->args0); printf("\n"); }
こんな風に、プローブするコマンドを特定な者に制限できる。
# ./dcode 1001.1001 date[14252]: date -r 12345 1001.1001 date[14252]: date -r 12345 1001.1001 date[14252]: date -r 12345 :
openなプローブって、どんなの? そんな時は、下記のようにする。
# dwatch -l open PROVIDER:MODULE:FUNCTION:NAME nfscl:nfs4:open:done nfscl:nfs4:open:start syscall:freebsd:open:entry syscall:freebsd:open:return
/usr/libexec/dwatch
profile用のスクリプトは、まとめて置いてある。どんなのがあるか、いちど 参照してみる。例として、多用しそうな、procを確認。
sakae@fb:/usr/libexec/dwatch $ ls proc* proc proc-exec-success proc-signal-discard proc-create proc-exit proc-signal-send proc-exec proc-signal proc-status proc-exec-failure proc-signal-clear
こんな系列も有るけど、真実はひとつならぬ、カメレオン・モードを実現する ためみたいです。
# $Title: dwatch(8) module for dtrace_proc(4) activity $ case "$PROFILE" in proc) : ${PROBE:=$( echo \ proc:::create, \ proc:::exec, \ proc:::exec-failure, \ proc:::exec-success, \ proc:::exit, \ proc:::signal-clear, \ proc:::signal-discard, \ proc:::signal-send )} ;; proc-signal) : inline string probealias[string name] = name == "create" ? "FORK" : name == "exec" ? "EXEC" : name == "exec-failure" ? "FAIL" : name == "exec-success" ? "INIT" : name == "exit" ? "EXIT" : : proc:::create /* probe ID $(( $ID + 1 )) */ {${TRACE:+ printf("<$(( $ID + 1 ))>"); } $( pproc -P _create "(struct proc *)args[0]" ) /* details = "pid <pid of args[0]> -- <proc args of args[0]>" */ this->details = strjoin( strjoin("pid ", lltostr(this->pid_create)), strjoin(" -- ", this->args_create)); } :
シェルスクリプトですねぇ。注目は冒頭付近のコメントにさりげなく案内され
てる、 dtrace_proc(4)
です。
# dwatch -X proc -o LOG INFO Sourcing proc profile [found in /usr/libexec/dwatch] INFO Watching 'proc:::create, proc:::exec, proc:::exec-failure, proc:::exec-success, proc:::exit, proc:::signal-clear, proc:::signal-discard, proc:::signal-send' ... INFO Setting output: LOG ^C
manの動作を追跡しようと思っていたらcronの動作もキャプチャーしてくれて いたので、そちらに焦点をあててみる。近頃は、あちこちに監視カメラやらド ラレコがついていて、油断も隙もない世界になっちまった。1984年が現実です。
# cat LOG | cut -b22- 2.5 cron[14493]: EXEC /bin/sh 2.5 sh[14493]: INIT /bin/sh -c /usr/libexec/save-entropy 2.5 sh[14493]: EXEC /usr/libexec/save-entropy 2.5 sh[14493]: INIT /bin/sh /usr/libexec/save-entropy 2.5 sh[14493]: FORK pid 14496 -- /bin/sh /usr/libexec/save-entropy : 2.5 sh[14493]: FORK pid 14498 -- /bin/sh /usr/libexec/save-entropy 2.5 sh[14498]: EXEC /bin/dd 2.5 dd[14498]: INIT dd if=/dev/random of=saved-entropy.8 bs=4096 count=1 2.5 dd[14498]: EXIT child exited 2.5 dd[14498]: SEND SIGCHLD[20] pid 14493 -- /bin/sh /usr/libexec/save-entropy 2.5 dd[14498]: DISCARD SIGCHLD[20] pid 14493 -- /bin/sh /usr/libexec/save-entropy 2.5 sh[14493]: FORK pid 14499 -- /bin/sh /usr/libexec/save-entropy 2.5 sh[14499]: EXEC /bin/chflags 2.5 chflags[14499]: INIT chflags nodump saved-entropy.8 2.5 chflags[14499]: EXIT child exited 2.5 chflags[14499]: SEND SIGCHLD[20] pid 14493 -- /bin/sh /usr/libexec/save-entropy 2.5 chflags[14499]: DISCARD SIGCHLD[20] pid 14493 -- /bin/sh /usr/libexec/save-entropy 2.5 sh[14493]: FORK pid 14500 -- /bin/sh /usr/libexec/save-entropy 2.5 sh[14500]: EXEC /usr/bin/fsync 2.5 fsync[14500]: INIT fsync saved-entropy.8 . 2.5 fsync[14500]: EXIT child exited 2.5 fsync[14500]: SEND SIGCHLD[20] pid 14493 -- /bin/sh /usr/libexec/save-entropy 2.5 fsync[14500]: DISCARD SIGCHLD[20] pid 14493 -- /bin/sh /usr/libexec/save-entropy
ログの冒頭の日時は、cutしました。冒頭の 2.5 はpid.gidです。翻訳すると operator.operatorになります。
カメレオン・モードって表現したのは、こんな事です。
# dwatch -X proc-exec-success -o LOG INFO Sourcing proc-exec-success profile [found in /usr/libexec/dwatch] INFO Watching 'proc:::exec-success' ... INFO Setting output: LOG ^C
特定なプローブだけをログする、すなわちS/N比を向上させます。なお、同一 名のログだと追記になるんで注意。
1001.1001 sh[14595]: INIT /bin/sh /usr/bin/man ls 1001.1001 sysctl[14596]: INIT /sbin/sysctl -n hw.machine_arch 1001.1001 sysctl[14597]: INIT /sbin/sysctl -n hw.machine 1001.1001 locale[14598]: INIT /usr/bin/locale 1001.1001 gzcat[14600]: INIT /usr/bin/gzcat /usr/share/man/man1/ls.1.gz 1001.1001 head[14601]: INIT head -1 1001.1001 gzcat[14602]: INIT /usr/bin/gzcat /usr/share/man/man1/ls.1.gz 1001.1001 mandoc[14603]: INIT mandoc -Tlint -Wunsupp 1001.1001 gzcat[14604]: INIT /usr/bin/gzcat /usr/share/man/man1/ls.1.gz 1001.1001 mandoc[14605]: INIT mandoc 1001.1001 less[14606]: INIT less
man dtrace_xxx
dtrace_proc(4)
なんてのを見付けてしまったものだから、他にも有るだろ
うと推測。やっぱり有った。分類上は、デバイス若しくは、デバドラ扱い。ま
あ、センサーと見做せるから、当然か。
sakae@fb:/usr/share/man/man4 $ ls dtrace* dtrace_audit.4.gz dtrace_proc.4.gz dtrace_udp.4.gz dtrace_io.4.gz dtrace_sched.4.gz dtrace_udplite.4.gz dtrace_ip.4.gz dtrace_sctp.4.gz dtrace_lockstat.4.gz dtrace_tcp.4.gz
src
sakae@fb:/usr/src/cddl/usr.sbin/dwatch $ ls Makefile dwatch* dwatch.1 examples/ libexec/
dwatchのソースは、こんな所にあった。cddlってのは、ライセンス名。GNUラ イセンスとは犬猿の仲なんで、リナは指を咥えて▼みんちょうたい
save-entropy
ここで会ったが百年目、どんな奴だか見極めておく。源流は、 /usr/libexec/save-entropy
難しい名前が付いているけど、乱数を貯蔵しておく仕組みだ。
# ls -l /var/db/entropy/ total 64 -rw------- 1 operator operator 4096 Aug 19 08:33 saved-entropy.1 -rw------- 1 operator operator 4096 Aug 19 08:44 saved-entropy.2 -rw------- 1 operator operator 4096 Aug 20 06:11 saved-entropy.3 -rw------- 1 operator operator 4096 Aug 20 06:22 saved-entropy.4 -rw------- 1 operator operator 4096 Aug 19 07:55 saved-entropy.5 -rw------- 1 operator operator 4096 Aug 19 08:00 saved-entropy.6 -rw------- 1 operator operator 4096 Aug 19 08:11 saved-entropy.7 -rw------- 1 operator operator 4096 Aug 19 08:22 saved-entropy.8
4Kブロックの乱数データが8個用意されてて、それを11分間隔で更新してんだ な。で、それを誰が利用するねん? おあつらえの例がmanに掲載されてた。
-P Enable probe matching the specified provider name.
ランダムを使う人は誰って質問をしてみる。多分sshなんてのは、良いお得意 様だと思うので、別端末で ssh localhostしてみた。
# dwatch -P random INFO Watching 'random:::' ... dtrace: buffer size lowered to 6m dtrace: dynamic variable size lowered to 8m 2024 Aug 20 06:27:37 1001.1001 ssh[18918]: ssh localhost 2024 Aug 20 06:27:46 0.0 sshd[18923]: sshd: sakae [priv] 2024 Aug 20 06:27:49 1001.1001 sh[18924]: -sh ^C # dwatch -l random PROVIDER:MODULE:FUNCTION:NAME fbt:kernel:random:entry
ビンゴでしたねぇ。で、そのプローブの正体は? それより先に、 save-entropyの動作だなあ。肝は、
# Save entropy to the selected slot. chmod 600 "${save_file}" 2>/dev/null || : dd if=/dev/random of="${save_file}" bs=${entropy_save_sz} count=1 2>/dev/null chflags nodump "${save_file}" 2>/dev/null || : fsync "${save_file}" "."
ソースは、//dev/randomとな。デバイスだからmanのセクション4だな。
To provide prompt access to the random device at boot time, FreeBSD automatically persists some entropy data in /boot/entropy for the loader to provide to the kernel. Additional entropy is regularly saved in /var/db/entropy. This saved entropy is sufficient to unblock the random device on devices with writeable media. To see the current settings of the software random device, use the command line: sysctl kern.random
カーネルが用意した乱数装置を確認。
sakae@fb:~ $ sysctl kern.random kern.random.fortuna.concurrent_read: 1 kern.random.fortuna.minpoolsize: 64 kern.random.rdrand.rdrand_independent_seed: 0 kern.random.use_chacha20_cipher: 1 kern.random.block_seeded_status: 0 kern.random.random_sources: 'Intel Secure Key RNG' kern.random.harvest.mask_symbolic: PURE_RDRAND,[UMA],[FS_ATIME],SWI,INTERRUPT,NET_NG,[NET_ETHER],NET_TUN,MOUSE,KEYBOARD,ATTACH,CACHED kern.random.harvest.mask_bin: 000000001000000111011111 kern.random.harvest.mask: 33247 kern.random.initial_seeding.disable_bypass_warnings: 0 kern.random.initial_seeding.arc4random_bypassed_before_seeding: 0 kern.random.initial_seeding.read_random_bypassed_before_seeding: 0 kern.random.initial_seeding.bypass_before_seeding: 1
kern.random.harvest.mask_symbolic
の括弧付き以外が元データになるん
か。割り込みとかネットとかマウスとかの動きは誰にも想像がつかないから、
良い種になるとな。
昔のコンピュータだと放射能のカウンターが生成するデータを利用してたらし い。そんな設備は無いという人は、ツェナー・ダイオードから出てくる、電子 なだれの雑音を、トランジスタで整形してディジタル値に変換して、それを使っ てたらしい。昔、その回路を眺めた事があるぞ。
README
病院とかでの待ち時間に読もおとして借りた、特大文字版の小説。だって、眼 鏡してマスクじゃ鬱陶しいですから。裸眼で読めるって条件です。どうせなら、 活字が明朝体より黒々のゴシック体がよかったんだけど。
浅田次郎の小説である。6部屋のアパートの住人それぞれの話が綴られている。 これだけで6話になる。7話目が全体を〆ていて、とっても良かった。