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をお忘れなく。

GopherがRust入門したので違いをまとめてみた

golang

go build を使わずにGoプログラムをビルドする

go modを使った開発の流れ

Goで社内ツールを作るならこんなふうに

golangは常にrustと比較(オイラー的にはですが)される、運命にあるのよ。

なんか、rustはそこはかとなくhaskellの香がするんだけど、パッケージ(rust 用語ではクレートって言うんだっけ)が、ユーザー任せにされてる。 それが元で、パッケージ地獄が発現しないか心配。

その点、golangはパッケージが充実してるし、BSD上で簡単にWindows用のシン グル・バイナリーを作成できるんで、そこを買っていますよ。まあ、そうそう アプリなんて書かないけど。。。


This year's Index

Home