ddb

前回からRTCの事を、チマチマと調べている。手持ちの資料が無いかと当たった所、 『FreeBSDのブートプロセスをみる』なんていう、今は亡きユニマガの連載をまとめた 物が出てきた。

このムックの80頁の所に、cpu_startupを読むが有り、その前振りとして3つの時計が 出てた。基本はリアルタイムクロック(RTC)である。オイラーが追ってたやつ。 MC146818Aというモトローラ社のやつが元祖。

こいつにアクセスするには、I/Oポートの70Hにアクセスして、石内部のレジスタ番号を 書き込み、続いて71Hにアクセスして、データを読み書きするとな。

二番目は、プログラマブル・インターバルタイマー。巷では8254と言う24pinのICが 有名。内部には16Bitのカウンターが3本収納されるとか。1番目が、100Hzで割り込みを かけてくるように使う。カーネル変数で言うHZってやつかな。2番目は、DRAMの リフレッシュ用。3番目は、ビープ音の発信器として使ってるらしい。

三番目はタイムスタンプ・カウンター。略してTSC。CPU内に収納されてる64Bitの カウンター。CPUがリセットされるとZEROに初期化され、CPUクロックでインクリメント される。 パソコンの寿命が尽きても、オーバーフローする事が無いという強力なやつ。 RDTSCって命令で、カウンター値を読み出すそうな。

ムックをブラウジングしてたら、127ページにRTCの持ってるエラーステータスの 事が出てた。8Bitのステータスのうち上位6Bitが定義されてる。vmmな仮想PCを OpenBSDで立ち上げた時のエラーは、この表に当てはめると、構成情報エラーって 事になる。

こういった情報をWebで探し回ると大変。手元にこういう資料が有ると助かるな。

そして、このムック本の最後に、カーネルデバッグって解説が載ってた。 FreeBSDでdebuggerと言うと、アセンブラーレベルを対象にするddbとソースレベルで debug出来る、kgdb(実は普通のgdb)が有るとな。両方を上手く使い分けると いいよと、アドバイスされてた。

デバイスドライバ、inb()とoutb()

ddb

そんな訳で、OpenBSDにもddbを入れて野郎。えと、どうやるんだ? そんな時は迷わずに man ddbだな。それによると、ddbがイネーブルになったカーネルが標準らしい。

普段は隠れているけど、OSがパニクッた時に、牙を剥いてくるらしい。証拠物件を押さえる 積りなんだな。

その他にも、sysctl ddb.console=1 にしておくと、キーコンビネーションで、ddbに 落ちる事が出来るとか。

# sysctl ddb.console=1
sysctl: ddb.console: Operation not permitted

あれれ、拒否されたぞ。何故? sysctlのソースをざっと見したけど、該当箇所を発見 出来ず。多分、使ってる端末がコンソールじゃなくてはいけないとかだろうな。

もし、そうだとすると、VMWAREのコンソールって小さくて、年よりには見にくいのよね。 15インチのパソコン画面で、解像度がipad並みとはいかないけど、1900x1024ぐらい 有ったから、小さくなっちゃうんだな。

だったら、vboxではどうか? こちらの方には、今の所Debianを入れているけど、 若干、画面が広かったように思う。でも、32Bit時代にOpenBSDを入れようとして、 ことごとく失敗してたからなあ。

悩む前に手を動かせ!! 入れるOSはOpenBSDですって指定しただけで、64Bit版の OpenBSDですねって言ってきた。脈が有りそう。すんなり入った。あの32Bit時代の 失敗につぐ失敗は何だったんだろう?

折角なので、カーネルを作る時のオプションを options(4)を見て、設定してから カーネルを作り直そう。

makeoptions     DEBUG="-g
option          DDB_SAFE_CONSOLE
option          DDB_STRUCT

残念ながら、gdb似のKGDBとは排他の関係にあるので、FreeBSDみたいに同時使用は不可 だった。なお、一番大事な、option DDB は、デフォで定義されてるので、自分のconfigに 書くと、律儀な事に2重定義だって文句を言われる。テオ親分の何が何でも、ddb入りの カーネルを作れって言う、強い意志が見え隠れしてますなあ。

で、嬉しい事に、vboxのコンソールに表示メニューで、表示倍率が変えられるように なってた。150%を選んでおいた。

Alt+Ctl+Esc で、何時でもddbに落ちる事が出来たよ。

ddbのコマンド

debuggerなんで、使い方はgdbとかと一緒。BP貼って、走らせて、止まったら、どういう 経路でやって来た? 今の状況は? 次は一歩づつ進むか、次のBPに出会うまで、一気に 走れかぐらい。細かい事は、ddb(4)ね。

break hoge
    BPをhogeに置く。hogeは補完してくれないので、別OSを立ち上げておいて
    調べておく事
delete hoge
    hogeに置いたBPを取り下げる
show  breaks
    BPの一覧表示
show  registers
    レジスタの値表示
print hoge
    hogeの内容表示、特定regを見たい時は、$eipとかとする
x/i  hoge
    hoge番地を逆アセンブル
x/w  hoge
    hoge番地の内容表示
trace
    callの履歴表示、gdbで言うbt
c
    次のBPまで走れ
s
    ステップ実行
n
    今のサブルーチンの最後まで実行

取り合えず、これぐらい知ってれば、gdbユーザーがddbに乗り換え可能だな。

ddbへの突入方法

上でもちょっと書いたけど、ddbへ突入する方法は色々ある。manから拾ってみると、 ddb.consoleが1にセットされてる時に、rootがddbを呼び出せるとな。

このddb.consoleが1を満たす為には、DDB_SAFE_CONSOLをイネーブルにしてカーネルを コンパイルする必要が有る。下記は、それをやってないもの。

# sysctl | grep ddb
ddb.radix=16
ddb.max_width=80
ddb.max_line=24
ddb.tab_stop_width=8
ddb.panic=1
ddb.console=0
ddb.log=1
ddb.trigger=0
# sysctl ddb.console=1
sysctl: ddb.console: Operation not permitted

どうも、ddb.consoleの値は、コンパイル時に埋め込まれる定数になってるようだ。 このメインSWが1(on)になっていないと、ddbに気軽には入りこめない。

この事に気が付いたのは、32Bit機で実験してみたから。イネーブルになってると、 Ctrl-Alt-Escでddbが起動してくる。VMWAREのコンソールでこのコンビネーションを 試すと、VMWAREに横取りされちゃって、キーのフォーカスが外れてしまう。

Ctrl-Alt-Deleteでも、条件を満たせば、ddbに入れるそうだけど、このコンビネーションは、 今度はWindowsに横取りされちゃって、ログアウトするか等と言った無骨な画面が出て しまい、途方に暮れる事になる。

最後の手段として、sysctl ddb.trigger=1 がある。これなら、文句なくddbへ突入 出来るぞ。(但し、実行は、rootユーザーがconsoleからって条件が付くけど) aliasにでも、登録しておくと奇知。

# sysctl ddb.trigger=1
sysctl: ddb.trigger: Operation not supported by device
# tty
/dev/ttyp0

これ、sshした端末での出来事。 そりゃそうだ。ddbに落ちている間、sshでの通信が 途絶えてしまいますからねぇ。がっつり、物理的に接続された端末から実行しろってのは、 頷けますよ。

また、ddbの大きなアドバンテージとして挙げられるのが、カーネル起動の非常に早い段階 から使える事だ。それをやるには、bootのプロンプトが出てる時、すかさず、-dを入力 する事。そんなのトロいオイラーには無理。OpenBSDの名だたる開発者達の集合写真を 見た事があるけど、正直言って、ロートルばっかり。

で、彼らも考えた。生命線のddbに楽に突入する方法を。

$ cat /etc/boot.conf
set timeout 10

こんな風に書いておくと、10秒の猶予が与えられるとな。また、boot時から、いきなりddbに 入りたい時は、set debug と書いておけば良いらしいんだけど、オイラーが試したら、 シンタックスエラーで、機能しなかった。まあ、そんなものだろう。特殊機能ですから。

Loading.....
probing: pc0 mem[639k 254M a20=on]
disk: hd0+
>> OpenBSD/amd64 BOOT 3.31
boot> -d
booting hd0a:/bsd: 6853429+....
entry point at 0x1001000 [7205c766, .....]
[ using 1165688 bytes of bsd ELF symbol table ]
Stopped at      Debugger+0x9:   leave
ddb> trace
Debugger() at Debugger+0x9
init_x86_64() at init_x86_64+0x740
end trace frame: 0x0, count: -2
ddb>

この表示は、コンソールに出てきたのを、手コピペしたもの。だから数字の羅列は、...で お茶を濁している。全く、VGAの端末はコピペもすんなり出来ず、困ったものです。 だったら、写真に取れと言うのは激しく拒否します。

探偵ものの小説を読んでいると、探偵が変な理屈を付けて、容疑者とおぼしきアジトに 侵入して、証拠書類をスマホ(or携帯)で、カシャカシャ撮りまくるってのが、よく出て くる。もう、写真を撮るってのは、当たり前なんだなあ。そんな事からすると、オイラーは 紛れもなく、昭和の人間。そう、昭和40年代ですかね。

あの頃は、テレタイプががちゃがちゃ動いて、何でも紙に記録してたなあ。へまをすると コンピュータが暴走して、ロール紙を1巻ぐらいあっと言う間に使い果たした。紙の 海に埋もれてしまう事有ったぞ。

ddb のログ

このblogに是非、ddbのセッション結果を張り付けてみたい。と言ってもmanを調べる 限りでは、

           $log        Controls whether the output of ddb will also appear in
                       the system message buffer.

これしか説明が無い。 $logを調べても、無常にも1って表示されるだけ。ちゃんと、説明を 嫁よ。system message bufferって書いてあるじゃん。これって、dmsgで読めるやつ かいな。ビンゴでしたよ。

下記は、OSが起動した後、ddbに落ちて、traceした結果。

Stopped at      Debugger+0x9:   leave
ddb> machine     print       examine     x           search      set
write       w           delete      d           break       dwatch
watch       step        s           continue    c           until
next        match       trace       call        ps          callout
show        boot        help        hangman     dmesg
ddb> Debugger() at Debugger+0x9
internal_command() at internal_command+0x155
wskbd_translate() at wskbd_translate+0x90
wskbd_input() at wskbd_input+0x55
pckbd_input() at pckbd_input+0xb4
pckbcintr_internal() at pckbcintr_internal+0x8e
intr_handler() at intr_handler+0x28
Xintr_ioapic_edge1() at Xintr_ioapic_edge1+0xc9
--- interrupt ---
acpicpu_idle() at acpicpu_idle+0xcb
cpu_idle_cycle() at cpu_idle_cycle+0x10
end trace frame: 0x0, count: -10
ddb>

何処を回っていたかが表示されているので、これを手掛かりにして、カーネルソースを 摘み読み出来るな。

hangman

上のhelpで、ddbコマンドとは似ても似つかないものが混じっている事に気付いた。 hangmanって、単語あてゲームだったよな。提示された、ブランクを埋めて、正しい 単語を推測するやつ。

期限まで推測出来ないと、縛り首されると言う、恐ろしいゲーム。まるで、地獄で 閻魔様に裁きを受けているみたい。正解すると、放免されるけど、不正解だと 一生地獄で虐められる。

     hangman [/s[0-9]]
                 This is a tiny and handy tool for random kernel hangs
                 analysis, of which its depth is controlled by the optional
                 argument of the default value of five.  It uses some
                 sophisticated heuristics to spot the global symbol that
                 caused the hang.  Since the discovering algorithm is a
                 probabilistic one, you may spend substantial time to figure
                 the exact symbol name.  This smart thing requires a little of
                 your attention, the input it accepts is mostly of the same
                 format as that of the famous hangman(6) game, to which it,
                 apparently, is obliged by the name.  Hint: the nm(1) utility
                 might help.

ddbの中だとあれなんで、明るい所でプレーしてみる。

[ob: ~]$ hangman
     ______
     |    |
     |    O                                Guessed:  efhnst
     |    |
     |                                     Word #:           1
     |                                     Current Average:  2.000
   __|_____                                Overall Average:  0.000
   |      |___
   |_________|

 Word:  --n-e-s-t--n--e
Guess:
Sorry, the word was "conversationize"
Another word?

最初、何も無い所からスタートするので、ヒットしそうな文字から入力してみるのが定石。 ヒットしそうな文字って何だ? 英文の文字出現頻度だな。これを題材にした小説は、ポーの 『黄金虫』だったような。

どんな文字が高頻度だろう?そんなのググルしてみろ。ネットに毒されてますなあ。 無線屋なら、欧文モールス符号を思い出せ。頻度順にト・ツーを決めると言う合理的 な方法を取っていたぞ。

一番短いモールス符号。トがeで、トトがiでトトトがsか。ツーがtで、ツーツーがmか。 それより、トツーの方が短い? トツーならaだな。ツートがnとな。

より正確な頻度は、プログラミングHaskellの暗号解読に利用されてたな。

> table                         =  [8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0,
>                                   6.1, 7.0, 0.2, 0.8,  4.0, 2.4, 6.7,
>                                   7.5, 1.9, 0.1, 6.0,  6.3, 9.1, 2.8,
>                                   1.0, 2.4, 0.2, 2.0,  0.1]

浮動小数点の配列。最初のエレメントがaの頻度。最後がzの頻度のようにアルファベット順に 頻度が書かれている。これによると、aの頻度は、8.2%とな。 eは12.7%で一番高いのかな。

話は戻って、途中まで推測出来た場合(上のような時)、カンニングしちゃうって手も あるぞ。

[ob: ~]$ egrep '..n.e.s.t..n..e'  /usr/share/dict/words
conversationize

閻魔様だって、全ての単語を知ってる訳ではない。単語帳からこっそり単語を選んで きて、偉そうに出題してるだけ。だったら、こちとらも、文明の利器を使って、 探しだしちゃえ。まあ、バレたら、どうなるかは知らないけど。

ddbのhngmanの出題範囲はnmによると、手をばらしている。一体、nmにはいくつの単語が 登録されてるの?

[ob: ~]$ nm /bsd | wc
   30359   91077 1052773
[ob: ~]$ nm /bsd | fgrep ' T ' | wc
   20928   62784  742807

全体では3万を超える。グローバルな関数だけでも2万個以上。残りは、定数とか変数名 になるかな。

[ob: z]$ nm /bsd | fgrep ' T ' | cut -d ' ' -f 3 | ./cnt.rb | sort -nr
38882   _
28691   e
23808   t
21169   i
20928
20836   a
18358   r
17216   s
16695   c
13259   p
12691   d
12666   n
12612   o
11086   m
10143   l
 :
13      G
12      Y
5       W
4       Q

これ、関数名に使われている文字の出現頻度。アンダーバーが一番多いって、ラクダ型の 名前付けじゃなくて、スネーク型なのね。次からは、e,t,iの順で、20928の所は、空白は ありえないので、改行コードか。そうでっしゃろか > rubyさん。

cnt.rbは、遥か昔を思い出して書いたもの。

#! /usr/local/bin/ruby

cnt = Hash.new(0)

while ln = ARGF.gets()
   ln.split('').each {|c| cnt[c] += 1 }
end

for k in cnt.keys do
   printf("%d\t%s\n", cnt[k], k)
end

それじゃ、合成語で出来た関数名がどれぐらいあるか、調べてみる。合成語は、アンダーバー で語を接続してるんで、各行毎にアンダーバーの個数を数えればいいんだな。

[ob: z]$ nm /bsd | fgrep ' T ' | cut -d ' ' -f 3 | ./comp.rb | sort -nr
8030    2
6725    1
3281    3
1440    0
1119    4
229     5
96      6
7       7
1       8

一番多いのは、3語の単語を、アンダーバー2個で接続してるやつか。これは意外な 結果だな。まてまて、説明的な関数名を積極的に採用してるって事だ。

[ob: z]$ cat comp.rb
#! /usr/local/bin/ruby

cnt = Hash.new(0)

while ln = ARGF.gets()
   cnt[ ln.count('_') ] += 1
end

for k in cnt.keys do
   printf("%d\t%s\n", cnt[k], k)
end

一つ、ひな型を作っておくと、ちょっと変形するだけで、色々使えて便利だな。 matzさん、ありがとう。

[ob: ~]$ nm /bsd|fgrep ' T '|cut -d ' ' -f 3|egrep '.*_.*_.*_.*_.*_.*_.*_.*'
ixgbe_set_lan_id_multi_port_pcie_82598
radeon_connector_encoder_get_dp_bridge_encoder_id
radeon_get_atom_connector_info_from_object_table
radeon_get_atom_connector_info_from_supported_devices_table
radeon_legacy_get_ext_tmds_info_from_combios
radeon_legacy_get_ext_tmds_info_from_table
ttm_bo_device_buffer_objects_RB_INSERT_COLOR
ttm_bo_device_buffer_objects_RB_REMOVE_COLOR
[ob: ~]$ nm /bsd|fgrep ' T '|cut -d ' ' -f 3|egrep '.*_.*_.*_.*_.*_.*_.*_.*_.*'
radeon_get_atom_connector_info_from_supported_devices_table

もう、笑っちゃうぐらい素敵な(病的な)関数名を炙り出してみました。こんな問題を、 hangmanが出してきたら、絶対に答えられないよ。ddbでhangmanは封印しとけ。 触るな危険 だから。。。。

おまけで、Linux方面はどうなってるかちょっと当たってみた。 リナのカーネルは圧縮されてて nm なんでやるもんじゃないぞってスタンス。 その代わり、Debian君では、System.mapが提供されてた。これをnmの代わりにしろって事だ。

sakae@debian:/boot$ cat System.map-3.16.0-4-amd64|awk '$2 ~ /T/ {print $3}'|wc
  13423   13423  256786

カーネルの機能がモジュールに追い出されているんで、以外にすっきりしてるっぽいな。

そして、冒頭からアンダーラインで始まる(例えば、__check_regionみたいなの)やつが 結構居るな。OpenBSDには、こういうの居なかったような。文化の違いなのかな。

etc

なるべく書かないawkの使い方

久しぶりにawkを使ってみたけど、なかなか良いね。BSDの正統なawkとごちゃごちゃ機能 てんこ盛りのgawkがあるけど、gawkに毒されるなってのが、今のオイラーの主張。 gawkを使いたくなったら、それはrubyにスイッチしろと言う赤信号が点灯したと 思っておこう。

上で書いたawkの一行野郎は、ちょっと冗長だった。awk '/ T /{print $3}' の方が スマートだな。/ T / が、grep相当で、対象行の選択。大カッコの中が、cut相当。

printは長すぎるんで、pぐらいでも良いようにして欲しかったけど、そこまでやると、 後継のperlや、そのまた後継のrubyは、この世に誕生していなかったかも。そうすると、 何処を向いても、python一色の世界で、つまらない世になったかも。