DTrace (4)
Table of Contents
ArchLinux sound
リナ方面ではサウンドが、どうなってるか調査。 サウンドシステム
言う事を聞かない場合は、魔法が有るみたいだ。
Linuxのサウンドまわりのデバイスが動かないときはsof-firmwareを入れておけ
[ 9.597456] snd_ens1371 0000:02:02.0: enabling device (0000 -> 0001)
qemuと同類のVMWareでは、こんなデバイスを検出した。まあFreeBSD時と一緒 なんだけどね。でも、慣れないせいかdmesgがやたら不親切に映るぞ。そして、 ここから先、指が動かなくなってしまった。
speaker for morse
やっぱり、ソースが盛り沢山の方が良いな。前回、避けていたモールスに挑戦。 で、/dev/speakerが無いぞと叱られた。 そんなのサウンドの説明には無かったぞと、ブツブツ。
コンソールだけでも音が出るようにした、仮想デバイスだった。サウンドカー ド不要な貧乏人仕様。まあ、初期のゲーム機だな。
使うには、モジュールをロードしてから、一般人にも開放してあげる。
sakae@fb:/boot/kernel $ doas kldload speaker sakae@fb:/boot/kernel $ doas chmod 666 /dev/speaker
440Hz, 15WPM(1WPM=5文字/分)の条件で、パリは燃えているか? って、打電してみた。 モールス界でのハロワは、パリって単語の打電になります。それにつられて、 ちょいと古い戦争映画のタイトルを思い出しました。
sakae@fb:~ $ morse -f 440 -w 15 -p Is Paris Burning?
-pを付けているので、音はスピーカーから出てくる。早朝とかで音を出すと、 安眠妨害と怒られる場合、
sakae@fb:/boot/kernel $ morse -s ab .- -... sakae@fb:/boot/kernel $ morse ab di dah ; アレー (亜鈴) dah di di dit ; ボータオシ (棒たおし)
奴らは、トツーの音をこんな風に認識してるんだな。日本では、コメントに記したよう に覚えろと、戦前の通信兵を指導してたそうです。今なら、モールス信号も ミュージックですよ。モールス・ミュージック A to Z
sakae@fb:/tmp $ echo hello CW world | morse -w 15 -p -e hello CW world
ちゃんと練習に利用するなら、こういうのが良いだろう。echoじゃなくて、適 当なファイルをcatで流し込めば長大なテキストの受信練習になる。-eは、打 鍵前に、文字を表示するオプションだ。
何方の作かと思ったら、クレジットが掲載されてた。
/* * Taught to send *real* morse by Lyndon Nerenberg (VE6BBM) * <lyndon@orthanc.ca> */
遊ぶだけじゃ能が無いので周辺を探ったら、スピーカー・テストもできるように、コマンドが用意されてました。
/usr/sbin/spkrtest
┌─────────────────────────────Speaker test────────────────────────────────┐ │ Please select the melodies you wish to play (space for select) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ [ ] reveille Reveille │ │ │ │ [ ] contact Contact theme from Close Encounters │ │ │ │ [ ] dance Lord of the Dance (aka Simple Gifts) │ │ │ │ [ ] loony Loony Toons theme │ │ │ │ [ ] sinister Standard villain's entrance music │ │ │ │ [ ] rightstuff A trope from 'The Right Stuff' score by Bill Conti │ │ │ │ [ ] toccata Opening bars of Bach's Toccata and Fugue in D Minor │ │ │ │ [ ] startrek Opening bars of the theme from Star Trek Classic │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │
中身はどうなってる? shell scriptだった。
toccata) title="opening bars of Bach's Toccata and Fugue in D Minor" music="msl16oldcd4mll8pcb-agf+4.g4p4<msl16dcd4mll8pa.a+f+4p16g4" ;; startrek) title="opening bars of the theme from Star Trek Classic" music="l2b.f+.p16a.c+.p l4mn<b.>e8a2mspg+e8c+f+8b2" ;; echo "Title: ${title}" echo ${music} > ${speaker}
バッハのトッカータとフーガ Dマイナーとか、スタートレークのテーマとかが、 謎の言語で記述されてた。
サウンド一般の説明は、こちらにもありました。 Sound & Audio
/dev/speaker
Eric S. Raymond さんが関わってる。IBM Advanced BASIC 2.0のplayコマンド を模倣するようになっている。
基本は、どんな周波数でどれぐらい継続するかってデータの連なりでハードを 制御してる。音の休止は、周波数を0Hzとして表現。10ms分解能で音をon/off してるんだな。
typedef struct { int frequency; /* in hertz */ int duration; /* in 1/100ths of a second */ } tone_t;
dev/speaker/spkr.c内に、平均律のテーブルを保持していて、C,C#,D,D# .. みたいな12音階、7オクターブをカバーしている。実際の音の制御は、こん なハードを叩いて実現してる。
sys/x86/isa/clock.c
int timer_spkr_release(void) { if (timer2_state != ACQUIRED) return (-1); timer2_state = RELEASED; outb(TIMER_MODE, TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT); ppi_spkr_off(); /* disable counter2 output to speaker */ return (0); } void timer_spkr_setfreq(int freq) { freq = i8254_freq / freq; mtx_lock_spin(&clock_lock); outb(TIMER_CNTR2, freq & 0xff); outb(TIMER_CNTR2, freq >> 8); mtx_unlock_spin(&clock_lock); }
speaker conf
起動時から使用するには、下記設定をする。 /boot/loader.conf
speaker_load="YES"
/etc/devfs.conf
# Allow members of group operator to cat things to the speaker own speaker root:operator perm speaker 0660
コメントになってるのを外す。
attimer0: <AT timer> at port 0x40 on isa0 Timecounter "i8254" frequency 1193182 Hz quality 0 Event timer "i8254" frequency 1193182 Hz quality 100 attimer0: non-PNP ISA device will be removed from GENERIC in FreeBSD 15. WARNING: Device "spkr" is Giant locked and may be deleted before FreeBSD 15.0.
悲しい事に、スピーカーの元は、将来削除されてしまうらしい。まあ、古い仕 様ですから、しょうがないのか。FreeBSD 13.4は先週Releaseされたんで、適 当なタイミングでUpdateして、永久保存かな。
なお、/sbin/devd を利用して、動的にパーミションを制御する事ができるみ たいだけど、確実性に欠けるそう。もし利用するなら、
cat /var/run/devd.pipe | grep spkr
等として、確認が必要らしい。
filter of breakpoint ?
前回のioctlで、特定のfdの時だけBPしたい。
(gdb) b kern_ioctl if fd == 3 No symbol "fd" in current context.
こんな風にしたけど、無視される。
Breakpoint 3, kern_ioctl (td=0xccfe780, fd=16, com=2150396949, data=0xcb5cb68 "\002k") at /usr/src/sys/kern/sys_generic.c:727 727 AUDIT_ARG_FD(fd); (gdb) condition 3 fd == 0x3 No symbol "fd" in current context. (gdb) condition 3 td == 0xccfe780 (gdb) info breakpoints Num Type Disp Enb Address What 3 breakpoint keep y 0x0108ef8f in kern_ioctl at /usr/src/sys/kern/sys_generic.c:727 stop only if td == 0xccfe780 breakpoint already hit 1 time
他の引数だと上手く設定できる事がある。何で?
Breakpoint 1, kern_ioctl (td=0xccffb40, fd=16, com=2150396949, data=0xcb61b68 "\002k") at /usr/src/sys/kern/sys_generic.c:727 727 AUDIT_ARG_FD(fd); (gdb) p fd $1 = 16 (gdb) info locals tmp = 36634624 fdp = <optimized out> locked = <optimized out> fp = <optimized out> error = <optimized out> (gdb) condition 1 $arg2 == 3 (gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x0108ef8f in kern_ioctl at /usr/src/sys/kern/sys_generic.c:72 7 stop only if $arg2 == 3 breakpoint already hit 1 time (gdb) c Continuing. Error in testing condition for breakpoint 1: Invalid type combination in equality test. Breakpoint 1, kern_ioctl (td=0xccff000, fd=15, com=1074033783, data=0xcb52b68 "") at /usr/src/sys/kern/sys_generic.c:727 727 AUDIT_ARG_FD(fd); (gdb) condition 1 $fd == 3 (gdb) c Continuing. Error in testing condition for breakpoint 1: Invalid type combination in equality test.
引数を確認。ローカルには認定されていない。ならば、引数番号とかグローバ ル?扱いでどうか。やはり駄目。
(gdb) l 722 { 723 struct file *fp; 724 struct filedesc *fdp; 725 int error, tmp, locked; 726 727 AUDIT_ARG_FD(fd); 728 AUDIT_ARG_CMD(com); 729 730 fdp = td->td_proc->p_fd; 731 (gdb) delete breakpoints Delete all breakpoints? (y or n) y (gdb) b /usr/src/sys/kern/sys_generic.c:730 if fd == 3 Breakpoint 2 at 0x108efa6: file /usr/src/sys/kern/sys_generic.c, line 730. (gdb) c Continuing. Breakpoint 2, kern_ioctl (td=0xccff000, fd=3, com=3221508102, data=0xcb52b68 "\001") at /usr/src/sys/kern/sys_generic.c:730 730 fdp = td->td_proc->p_fd;
ならば、監査が終了した辺りでBPを設定。今度はうまくいった。やれやれ! こんなのBADノウハウだよな。
ioctl log
いかんいかん、つい道草してしまった。前回trussなんてのを思い出してしまっ たので、dtraceしてみる。
# truss -o LOG dtrace -n random::: dtrace: description 'random:::' matched 1 probe CPU ID FUNCTION:NAME 0 61682 event_processor:debug ^C
# grep ioctl LOG ioctl(7,DTRACEIOC_CONF,0x207d4008) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfe3d4) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfe390) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfe0b8) = 0 (0x0) ioctl(7,DTRACEIOC_PROVIDER,0xffbfdbb0) ERR#3 'No such process' ioctl(0,TIOCGETA,0xffbfe0b8) = 0 (0x0) ioctl(7,DTRACEIOC_PROVIDER,0xffbfdbb0) ERR#3 'No such process' ioctl(0,TIOCGETA,0xffbfe4f8) = 0 (0x0) ioctl(7,DTRACEIOC_PROVIDER,0xffbfdff0) ERR#3 'No such process' ioctl(0,TIOCGETA,0xffbfe4f8) = 0 (0x0) ioctl(7,DTRACEIOC_PROVIDER,0xffbfdff0) ERR#3 'No such process' ioctl(0,TIOCGETA,0xffbfe4f8) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfe828) = 0 (0x0) ioctl(7,DTRACEIOC_PROVIDER,0xffbfe394) = 0 (0x0) ioctl(7,DTRACEIOC_PROBEMATCH,0xffbfe764) = 0 (0x0) ioctl(7,DTRACEIOC_PROBEMATCH,0xffbfe764) ERR#3 'No such process' ioctl(7,DTRACEIOC_PROBEARG,0xffbfc1e0) = 0 (0x0) ioctl(7,DTRACEIOC_PROBEARG,0xffbfc2ec) = 0 (0x0) ioctl(7,DTRACEIOC_PROBEARG,0xffbfc3f8) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfbd98) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfbd60) = 0 (0x0) ioctl(0,TIOCGETA,0xffbfe820) = 0 (0x0) ioctl(7,DTRACEIOC_PROVIDER,0xffbfe384) = 0 (0x0) ioctl(7,DTRACEIOC_PROBEMATCH,0xffbfe754) = 0 (0x0) ioctl(7,DTRACEIOC_PROBEMATCH,0xffbfe754) ERR#3 'No such process' ioctl(7,DTRACEIOC_PROBEARG,0xffbfc1e0) = 0 (0x0) ioctl(7,DTRACEIOC_ENABLE,0xffbfec68) = 0 (0x0) ioctl(7,DTRACEIOC_ENABLE,0xffbfeba8) = 0 (0x0) ioctl(7,DTRACEIOC_ENABLE,0xffbfebdc) = 0 (0x0) ioctl(7,DTRACEIOC_GO,0x207d4604) = 0 (0x0) ioctl(7,DTRACEIOC_STATUS,0x207d4784) = 0 (0x0) ioctl(7,DTRACEIOC_BUFSNAP,0xffbfebd4) = 0 (0x0) ioctl(7,DTRACEIOC_BUFSNAP,0xffbfebd4) ERR#2 'No such file or directory' ioctl(7,DTRACEIOC_EPROBE,0xffbfe310) = 0 (0x0) ioctl(7,DTRACEIOC_PROBES,0x22f1aa80) = 0 (0x0) ioctl(1,TIOCGETA,0xffbfddfc) = 0 (0x0) ioctl(7,DTRACEIOC_STATUS,0x207d47dc) = 0 (0x0) ioctl(7,DTRACEIOC_BUFSNAP,0xffbfebd4) = 0 (0x0) ioctl(7,DTRACEIOC_BUFSNAP,0xffbfebd4) ERR#2 'No such file or directory' ioctl(7,DTRACEIOC_STATUS,0x207d4784) = 0 (0x0) ioctl(7,DTRACEIOC_BUFSNAP,0xffbfebd4) = 0 (0x0) :
対象デバイスは0,1が有るな。圧倒的なのは、7(dtrace/dtrace)か。1秒間隔で ポーリングしてるっぽい。
dtrace log
dtraceの資料を見ていたら、こんな援助が有る事を知った。一度dtraceをrun すると、有効になる。
root@qemu:/tmp # sysctl debug.dtrace.verbose_ioctl=1
早速の実行。
Sep 23 08:32:28 qemu kernel: dtrace_ioctl(361): DTRACEIOC_CONF Sep 23 08:32:29 qemu kernel: dtrace_ioctl(720): DTRACEIOC_PROVIDER Sep 23 08:32:30 qemu syslogd: last message repeated 4 times Sep 23 08:32:30 qemu kernel: dtrace_ioctl(658): DTRACEIOC_PROBEMATCH Sep 23 08:32:30 qemu syslogd: last message repeated 1 times Sep 23 08:32:30 qemu kernel: dtrace_ioctl(587): DTRACEIOC_PROBEARG Sep 23 08:32:30 qemu syslogd: last message repeated 2 times Sep 23 08:32:30 qemu kernel: dtrace_ioctl(720): DTRACEIOC_PROVIDER Sep 23 08:32:30 qemu kernel: dtrace_ioctl(658): DTRACEIOC_PROBEMATCH Sep 23 08:32:30 qemu syslogd: last message repeated 1 times Sep 23 08:32:30 qemu kernel: dtrace_ioctl(587): DTRACEIOC_PROBEARG Sep 23 08:32:30 qemu kernel: dtrace_ioctl(402): DTRACEIOC_ENABLE Sep 23 08:32:30 qemu syslogd: last message repeated 2 times Sep 23 08:32:30 qemu kernel: dtrace_ioctl(576): DTRACEIOC_GO Sep 23 08:32:31 qemu kernel: dtrace_ioctl(379): DTRACEIOC_DOFGET Sep 23 08:32:31 qemu syslogd: last message repeated 1 times Sep 23 08:32:31 qemu kernel: dtrace_ioctl(770): DTRACEIOC_STATUS Sep 23 08:32:31 qemu kernel: dtrace_ioctl(230): DTRACEIOC_BUFSNAP curcpu 0 cpu 0 Sep 23 08:32:31 qemu kernel: dtrace_ioctl(329): copyout the buffer snapshot Sep 23 08:32:31 qemu kernel: dtrace_ioctl(348): copyout buffer desc: size 0 drops 0 errors 0 Sep 23 08:32:31 qemu kernel: dtrace_ioctl(230): DTRACEIOC_BUFSNAP curcpu 0 cpu 1 Sep 23 08:32:32 qemu kernel: dtrace_ioctl(770): DTRACEIOC_STATUS :
check dtrace
sakae@fb:/sys $ grep dtrace_ioctl -l -r . ./cddl/dev/dtrace/dtrace_ioctl.c ./cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
取り敢えず、どこら辺で定義されてるか確認。後は、sysctlしながらの調査だ な。長くなりそうなので、別の機会にやってみる。
ask dtrace
そして、もう一人の助っ人を蒐集。専属のアドバイザーだな。
Q: dtraceのカーネル側の動作を教えてください
A: DTrace(Dynamic Tracing)は、動的にカーネルやユーザ空間のプログラムの挙動を監視・解析するツールです。カーネル側での動作は、プローブと呼ばれる観察ポイントを設置し、特定のイベントが発生した際にそれを捕捉して情報を収集する仕組みになっています。以下は、DTraceのカーネル側の基本的な動作の概要です。
### 1. プローブの登録 DTraceは、カーネル内部に「プロバイダ」と呼ばれるモジュールを用意しており、これが観察対象のプログラムやカーネル内部のイベントに対してプローブを登録します。プロバイダは、カーネルの関数やシステムコール、割り込み処理などの様々なポイントにフックを設ける役割を果たします。代表的なプロバイダには、以下があります。
- fbt(Function Boundary Tracing): 関数の入口と出口にプローブを挿入。
- syscall: システムコールにプローブを挿入。
- io: I/O操作に関連するイベントにプローブを挿入。
### 2. プローブの有効化 ユーザがDTraceスクリプトを実行して特定のイベントや関数をトレースする要求を出すと、対応するプローブがカーネル内で有効化されます。この時点で、プローブはまだ実際の観察を行わず、対応するイベントが発生した際にのみ情報を取得するように動作します。
### 3. プローブの発火 対象となるイベント(例えば、システムコールの呼び出しやカーネル関数のエントリ/エグジット)が発生すると、該当のプローブが「発火」します。これにより、指定されたアクション(情報収集、データの加工など)が実行されます。DTraceのカーネルコードは、非常に効率的に動作するように設計されており、必要最小限のオーバーヘッドでプローブを処理します。
### 4. データの収集とバッファリング プローブが発火すると、DTraceは収集したデータをカーネル内のバッファに一時的に格納します。このデータには、例えば以下のような情報が含まれます。
- 関数の引数や戻り値
- 実行スレッドのID
- 実行時のスタックトレース
- タイミング情報
バッファリングされたデータは、DTraceのユーザ空間の部分(`dtrace`コマンドなど)に送られ、最終的にユーザに出力されます。
### 5. プローブの無効化 ユーザがトレースを停止したり、DTraceスクリプトが終了すると、プローブは無効化されます。無効化されたプローブはカーネル上での動作にほとんど影響を与えないため、システムのパフォーマンスに悪影響を与えることはありません。
### まとめ DTraceのカーネル側の動作は、プローブを設定して、イベントが発生したときにそれを検出し、効率的にデータを収集する流れに基づいています。システム全体の動作や性能への影響を最小限に抑えながら、詳細な情報を得ることができるように設計されています。
100 rust
ArchLinuxでやる事がなかったんで、思い出したように、rustする。
シェルスクリプトを学ぶ人のための「新しいUNIX哲学」 〜 ソフトウェアツールという考え方
100 Exercises To Learn Rust 🦀 完走記事 🏃】
一応、OpenBSDに引き継ぎできるかも確認。これがOKならFreeBSDも大丈夫でしょ う。最近は、WSL/Debianの出番は全くなくて、FreeBSDは常に稼動のサーバー 状態になったますから。
ob$ make /home/sakae/.cargo/bin/wr Running tests... __ (01) intro - (00) welcome (Not rechecked) __ (01) intro - (01) syntax (Not rechecked) __ (02) basic_calculator - (00) intro (Not rechecked) __ (02) basic_calculator - (01) integers (Not rechecked) __ (02) basic_calculator - (02) variables (Not rechecked) __ (02) basic_calculator - (03) if_else Eternity lies ahead of us, and behind. Your path is not yet finished. __ Do you want to open the next exercise, (02) basic_calculator - (04) panics? [y/n] y Ahead of you lies (02) basic_calculator - (04) panics Open "exercises/02_basic_calculator/04_panics" in your editor and get started! Run `wr` again to compile the exercise and execute its tests.
今テストが通った問題なんだけど、if elseを期待してたみたいだね。こんな ので澄ませてしまったけれど。
fn is_even(n: u32) -> bool { n % 2 == 0 }
if else を使用してみた。
/// Return `true` if `n` is even, `false` otherwise. fn is_even(n: u32) -> bool { if n == 2 { true } else { false } } #[cfg(test)] mod tests { use crate::is_even; #[test] fn one() { assert!(!is_even(1)); } #[test] fn two() { assert!(is_even(2)); } #[test] fn high() { assert!(!is_even(231)); } }
これでもテストはPASSする。問題を正しく分析して対策すればパスしちゃうん だよなーー。いいかげん、テスト万能論は止めませんか。
最初は進捗監理のwrをMakefileに記述しておいてemacsから利用してたんだけ ど、この方法だとエスケープシーケンスでグチャグチャになる。諦めた。個別 に問題をチョイスする事にした。
Makefile
ob$ cat src/Makefile test: cargo test
問題毎にMakefileを用意するのは面倒だけど、まあ、問題に取り組んだ目印に なるから、許せ。
問題を消化していくと、ゴミが蓄積されるんで、適当なタイミングで cargo cleanをお忘れなく。
golang
golangは常にrustと比較(オイラー的にはですが)される、運命にあるのよ。
なんか、rustはそこはかとなくhaskellの香がするんだけど、パッケージ(rust 用語ではクレートって言うんだっけ)が、ユーザー任せにされてる。 それが元で、パッケージ地獄が発現しないか心配。
その点、golangはパッケージが充実してるし、BSD上で簡単にWindows用のシン グル・バイナリーを作成できるんで、そこを買っていますよ。まあ、そうそう アプリなんて書かないけど。。。