gdb script for newfs
gdb script basic
と言うのを見て、自動実行をやってみる。何も難しい事はなく、対話的に入力してたコマンドを羅列したファイルを作る。下記は、disklabelにスイッチを与えなかった場合、現在値を表示する為に、displayが呼ばれる。そこでbreakさせて、関数の引数であるlpを表示してるだけ。
vbox# cat gscript # gdb script break display run vnd0 set print pretty print/x *lp continue quit
実行は、-x でスクリプト名を追加指定。結果をLOGにリダイレクト。リダイレクトしないと、途中にmoreが挟まって、RETで次のデータを表示って塩梅になる。
vbox# gdb -q a.out -x gscript >LOG
vbox$ lv LOG Reading symbols from a.out... Breakpoint 1 at 0x906c: file disklabel.c, line 554. Breakpoint 1, display (f=0x2a4623ec <__sF+88>, lp=0x3ac45690 <lab>, unit=0 '\000', all=1) at disklabel.c:554 554 unit = canonical_unit(lp, unit); $1 = { d_magic = 0x82564557, : d_bbsize = 0x2000, d_sbsize = 0x10000, d_partitions = {{ p_size = 0x1000, p_offset = 0x0, p_offseth = 0x0, p_sizeh = 0x0, p_fstype = 0x7, p_fragblock = 0x14, p_cpg = 0x20 }, { p_size = 0x838, p_offset = 0x1000, p_offseth = 0x0, p_sizeh = 0x0, p_fstype = 0x1, p_fragblock = 0x0, p_cpg = 0x0 } <repeats 12 times>, { p_size = 0xe7c0, p_offset = 0x1840, p_offseth = 0x0, p_sizeh = 0x0, p_fstype = 0x7, p_fragblock = 0x14, p_cpg = 0x1ca }} } # /dev/rvnd0c: type: vnd : 16 partitions: # size offset fstype [fsize bsize cpg] a: 4096 0 4.2BSD 2048 16384 32 b: 2104 4096 swap c: 65536 0 unused p: 59328 6208 4.2BSD 2048 16384 458 [Inferior 1 (process 62381) exited normally]
a: 2M, b: 1M, c: 32M, p: 29Mの構成。-h を付けると人間様用の出力に切り替わる。
FreeBSD disk
FreeBSDでも確認しておく。
[sakae@fb ~]$ dd if=/dev/zero of=myfbd bs=1m count=32 [23/23] 32+0 records in 32+0 records out 33554432 bytes transferred in 0.970158 secs (34586559 bytes/sec) root@fb:/home/sakae # mdconfig -f myfbd md0 root@fb:/home/sakae # bsdlabel -w md0 root@fb:/home/sakae # bsdlabel md0 # /dev/md0: 8 partitions: # size offset fstype [fsize bsize bps/cpg] a: 65520 16 unused 0 0 c: 65536 0 unused 0 0 # "raw" part, don't edit root@fb:/home/sakae # newfs md0a /dev/md0a: 32.0MB (65520 sectors) block size 32768, fragment size 4096 using 4 cylinder groups of 8.00MB, 256 blks, 1024 inodes. super-block backups (for fsck_ffs -b #) at: 192, 16576, 32960, 49344 root@fb:/home/sakae # mount /dev/md0a /mnt root@fb:/mnt # vi iamfreebsd root@fb:/mnt # ls -al total 16 drwxr-xr-x 3 root wheel 512 Nov 4 07:32 . drwxr-xr-x 20 root wheel 1024 Nov 4 06:40 .. drwxrwxr-x 2 root operator 512 Nov 4 07:12 .snap -rw-r--r-- 1 root wheel 800 Nov 4 07:32 iamfreebsd
Debian disk
sakae@pen:~/sim$ sudo fdisk -l Disk /dev/sda: 32 GiB, 34359738368 bytes, 67108864 sectors Disk model: VMware Virtual S Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x17c4760d Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 62916607 62914560 30G 83 Linux /dev/sda2 62918654 67106815 4188162 2G 5 Extended /dev/sda5 62918656 67106815 4188160 2G 82 Linux swap / Solaris
super block
軽くソースを見ると核心は、mkfsにありそうなので、取り敢えずBP。
(gdb) b mkfs Breakpoint 1 at 0xd8b8: file mkfs.c, line 177. (gdb) r vnd0a Starting program: /home/sakae/newfs vnd0a Breakpoint 1 at 0x1bb718b8: file mkfs.c, line 177. Breakpoint 1, mkfs (pp=0x3bb67110, fsys=0x3bb67238 "/dev/rvnd0a", fi=7, fo=6, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:177
ppとか意味不な引数があるけど(そういう時のソースだろう)、軽く分る所を見ると、fiとfoは、fsysである /dev/rvnd0a をオープンしたディスクリプターみたいだ。
んで、 newfsの挙動観察用のスクリプトを作ってみた。折角覚えた技ですから。
break mkfs.c:566 run vnd0a set print pretty set $sblock=fsun->fs p/x $sblock continue quit
そして実行。gdb機能を搭載したnewfsは、面倒なので/usr/src/slib/newfsの所で作った。成果物だけ、/tmpの所に移動してから実行。ソースが有る所は、副産物の*.o,*.dを削除して、痕跡を残さない。これが楽な方法だな。
vbox# gdb -q newfs -x gscript > LOG
次はログの確認。
$1 = { fs_firstfield = 0x0, fs_unused_1 = 0x0, fs_sblkno = 0x28, fs_cblkno = 0x30, fs_iblkno = 0x38, fs_dblkno = 0x58, fs_cgoffset = 0x0, fs_cgmask = 0x0, fs_ffs1_time = 0x0, fs_ffs1_size = 0x0, fs_ffs1_dsize = 0x0, fs_ncg = 0x4, fs_bsize = 0x4000, fs_fsize = 0x800, : fs_maxfilesize = 0x80100202ffff, fs_qbmask = 0x3fff, fs_qfmask = 0x7ff, fs_state = 0x0, fs_postblformat = 0x1, fs_nrpos = 0x1, fs_postbloff = 0x0, fs_rotbloff = 0x0, fs_magic = 0x19540119, fs_space = 0x5f8fb7b0 } /dev/rvnd0a: 8.0MB in 16384 sectors of 512 bytes 4 cylinder groups of 2.00MB, 128 blocks, 256 inodes each super-block backups (for fsck -b #) at: 160, 4256, 8352, 12448,
採取ポイントのすぐ後ろの所で、シリンダーブロック毎にDISKに書込みをしてるっぽい。その任につくのは、
/* * Initialize a cylinder group. */ void initcg(u_int cg, time_t utime)
これをざっくり見て行くと、グループ0とそれ以外で扱いが違うみたいだ。今迄の想像では、みな同じと思っていたんだけどな。で、ごちゃごちゃ始過ぎているので、DISKに書き出す所を探ってみる。それらしいのは、
/* * write a block to the file system */ void wtfs(daddr_t bno, int size, void *bf) { : n = pwrite(fso, bf, size, (off_t)bno * DEV_BSIZE); if (n != size) { err(36, "wtfs: write error on block %lld", (long long)bno); } }
肝はpwriteっぽい。多分manに有るだろう。
ssize_t write(int fd, const void *buf, size_t nbytes); ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
writeの親戚みたいだな。pwriteの方は、書込み位置を指定出来るようになってる。位置は、ブロック番号とデバイスに固有のブロックサイズの積とな。 fsysってのは、デバイスをブロック型で扱かうんじゃなくて、キャラクター型で扱かうように指定してる(/dev/rvnd0a)。DISKも普通のファイル扱いって事だ。
(gdb) b wtfs Breakpoint 1 at 0x10240: file mkfs.c, line 1054. (gdb) r vnd0a (gdb) bt #0 wtfs (bno=16383, size=512, bf=0x61582000) at mkfs.c:1054 #1 0x16770bce in mkfs (pp=0x36766110, fsys=0x36766238 "/dev/rvnd0a", fi=7, fo=6, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:210 (gdb) c #0 wtfs (bno=128, size=8192, bf=0x61582000) at mkfs.c:1054 #1 0x1677299f in mkfs (pp=0x36766110, fsys=0x36766238 "/dev/rvnd0a", fi=7, fo=6, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:533 (gdb) bt #0 wtfs (bno=160, size=65536, bf=0x46a2f000) at mkfs.c:1054 ;; initcg #1 0x16774cdc in initcg (cg=0, utime=1667860385) at mkfs.c:757 #2 0x16772be4 in mkfs (pp=0x36766110, fsys=0x36766238 "/dev/rvnd0a", fi=7, fo=6, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:568 (gdb) c Breakpoint 1, wtfs (bno=4256, size=65536, bf=0x46a2f000) at mkfs.c:1054 ;; initcg Breakpoint 1, wtfs (bno=8352, size=65536, bf=0x46a2f000) at mkfs.c:1054 ;; initcg Breakpoint 1, wtfs (bno=12448, size=65536, bf=0x46a2f000) at mkfs.c:1054 ;; initcg (gdb) bt #0 wtfs (bno=192, size=2048, bf=0x53522000) at mkfs.c:1054 #1 0x167762ad in alloc (size=2048, mode=16877) at mkfs.c:981 #2 0x16775284 in fsinit2 (utime=1667860385, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:880 (gdb) c Breakpoint 1, wtfs (bno=384, size=2048, bf=0x46a2f000) at mkfs.c:1054 Breakpoint 1, wtfs (bno=192, size=2048, bf=0x53522000) at mkfs.c:1054 Breakpoint 1, wtfs (bno=224, size=16384, bf=0x46a2f000) at mkfs.c:1054 Breakpoint 1, wtfs (bno=128, size=8192, bf=0x61582000) at mkfs.c:1054 Breakpoint 1, wtfs (bno=352, size=2048, bf=0x5ec22800) at mkfs.c:1054
gdb script for newfs
上の結果は、gdbのセッションをコピペ編集したものだ。マシンに使われていると言っても過言ではない。それって、負けた証拠、恥しいぞ。
何とか、自動化出来無いか。ハッカーらしく正しい手抜きをしたい。そこで、gdbのマニュアルを少し調べてみた。そしたらBPにヒットした時、自前のコマンドを実行出来ると説明があった。 これは、是非使ってみろ。 myscr っていうのをでっちあげた。
break wtfs commands silent bt 2 echo \n cont end run wd2e quit
BPをセットしたら、すかさず、commands ~ end の間に、自前のコマンドを羅列する。ヒットしたら、それがら順に実行されるって仕組み。silentで、Breakした時の挨拶を抑止、そしてバックトレースを2ヶ分実施。echoで空行を入れて見易くする。そして継続、ってschemeみたいに再帰呼出だな。
後は、newfsしたいデバイスを指定して実行。最後はgdbをquitだ。スクリプトの作りがschemeぽいな。
実行は、何時もと変らずって、これで2回目のスクリプト実行だけどね。
vbox# gdb -q newfs -x myscr >LOG
供試DISKは、VirtualBOXに取り付けた、新たなものだ。普通に実行すると、こんな感じ。
vbox# newfs wd2e /dev/rwd2e: 1536.0MB in 3145728 sectors of 512 bytes 8 cylinder groups of 202.50MB, 12960 blocks, 25920 inodes each super-block backups (for fsck -b #) at: 160, 414880, 829600, 1244320, 1659040, 2073760, 2488480, 2903200,
で、結果の開陳
Breakpoint 1 at 0x99e0: file mkfs.c, line 1054. Breakpoint 1 at 0x17b619e0: file mkfs.c, line 1054. Current language: auto; currently minimal #0 wtfs (bno=3145727, size=512, bf=0x4fe86000) at mkfs.c:1054 #1 0x17b5f36e in mkfs (pp=0x37b598c8, fsys=0x2a35744c "/dev/rwd2e", fi=8, fo=7, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:210 #0 wtfs (bno=128, size=8192, bf=0x4fe86000) at mkfs.c:1054 #1 0x17b6113f in mkfs (pp=0x37b598c8, fsys=0x2a35744c "/dev/rwd2e", fi=8, fo=7, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:533 #0 wtfs (bno=160, size=65536, bf=0x6c302000) at mkfs.c:1054 #1 0x17b6347c in initcg (cg=0, utime=1667888970) at mkfs.c:757 /dev/rwd2e: 1536.0MB in 3145728 sectors of 512 bytes 8 cylinder groups of 202.50MB, 12960 blocks, 25920 inodes each super-block backups (for fsck -b #) at: 160,#0 wtfs (bno=414880, size=65536, bf=0x6c302000) at mkfs.c:1054 #1 0x17b6347c in initcg (cg=1, utime=1667888970) at mkfs.c:757
これが前半部分。newfsが出力する結果とgdbの出力するものが混っているな。それでもシリンダーグループの出力前に、2回書込みが有るのがみて取れる。
これを見るとフレーム#1は、関数名だけ有れば良さそうなので、何とかしたいと思う? そんな事より、眼力に頼った方が得策っぽいな。
長いトンネルを抜けるとそこは雪国であったとしちゃうと、川端さんになっちゅうんで、長いinitcgを抜けると、そこはオイラーに取っては意味不な世界であった。もとえ、そこも、でしょう!!
2903200,#0 wtfs (bno=192, size=16384, bf=0x78c9e000) at mkfs.c:1054 #1 0x17b64a4d in alloc (size=2048, mode=16877) at mkfs.c:981 #0 wtfs (bno=13216, size=2048, bf=0x6c302000) at mkfs.c:1054 #1 0x17b63b1e in fsinit2 (utime=1667888970, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:886 #0 wtfs (bno=192, size=16384, bf=0x78c9e000) at mkfs.c:1054 #1 0x17b64d21 in iput (ip=0xcf7bc528, ino=2) at mkfs.c:1007 #0 wtfs (bno=224, size=16384, bf=0x6c302000) at mkfs.c:1054 #1 0x17b65146 in iput (ip=0xcf7bc528, ino=2) at mkfs.c:1025 #0 wtfs (bno=128, size=8192, bf=0x4fe86000) at mkfs.c:1054 #1 0x17b61727 in mkfs (pp=0x37b598c8, fsys=0x2a35744c "/dev/rwd2e", fi=8, fo=7, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:602 #0 wtfs (bno=13184, size=2048, bf=0x59b13800) at mkfs.c:1054 #1 0x17b6185e in mkfs (pp=0x37b598c8, fsys=0x2a35744c "/dev/rwd2e", fi=8, fo=7, mfsmode=0, mfsuid=0, mfsgid=0) at mkfs.c:605
ああ、こういう見方も有りか。
vbox$ grep '#0' LOG #0 wtfs (bno=3145727, size=512, bf=0x6f0ba000) at mkfs.c:1054 #0 wtfs (bno=128, size=8192, bf=0x6f0ba000) at mkfs.c:1054 #0 wtfs (bno=160, size=65536, bf=0x64b0b000) at mkfs.c:1054 : #0 wtfs (bno=192, size=16384, bf=0x58af8000) at mkfs.c:1054 #0 wtfs (bno=13216, size=2048, bf=0x64b0b000) at mkfs.c:1054 #0 wtfs (bno=192, size=16384, bf=0x58af8000) at mkfs.c:1054 #0 wtfs (bno=224, size=16384, bf=0x64b0b000) at mkfs.c:1054 #0 wtfs (bno=128, size=8192, bf=0x6f0ba000) at mkfs.c:1054 #0 wtfs (bno=13184, size=2048, bf=0x67508800) at mkfs.c:1054
vbox$ grep '#1' LOG #1 0x199da36e in mkfs (pp=0x399d48c8, fsys=0x2d4cb44c "/dev/rwd2e", fi=8, #1 0x199dc13f in mkfs (pp=0x399d48c8, fsys=0x2d4cb44c "/dev/rwd2e", fi=8, #1 0x199de47c in initcg (cg=0, utime=1667890046) at mkfs.c:757 : #1 0x199dfa4d in alloc (size=2048, mode=16877) at mkfs.c:981 #1 0x199deb1e in fsinit2 (utime=1667890046, mfsmode=0, mfsuid=0, mfsgid=0) #1 0x199dfd21 in iput (ip=0xcf7d5838, ino=2) at mkfs.c:1007 #1 0x199e0146 in iput (ip=0xcf7d5838, ino=2) at mkfs.c:1025 #1 0x199dc727 in mkfs (pp=0x399d48c8, fsys=0x2d4cb44c "/dev/rwd2e", fi=8, #1 0x199dc85e in mkfs (pp=0x399d48c8, fsys=0x2d4cb44c "/dev/rwd2e", fi=8,
mg
上で出て来た DEV_BSIZE
が幾つになってるか、確認しようとしたら
(gdb) p DEV_BSIZE No symbol "DEV_BSIZE" in current context.
と、怒られてしまった。正統にソースを当たれ。
/usr/include/sys/param.h
#define _DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #define DEV_BSIZE (1 << _DEV_BSHIFT)
どうやら、これっぽいんだけど、釈然としない。かくなる上は、実測だな。 上のスクリプトを少々変形してgdbを走らせて実測、現場に当たれです。
#0 __i386_read_tcb (offset=0) at /usr/include/machine/tcb.h:41 #1 _libc_pwrite_cancel (fd=3, buf=0x5db1d000, nbyte=512, offset=1610612224) at /usr/src/lib/libc/sys/w_pwrite.c:26 #2 0x1a89da73 in wtfs (bno=3145727, size=512, bf=0x5db1d000) at mkfs.c:1060 #0 __i386_read_tcb (offset=0) at /usr/include/machine/tcb.h:41 #1 _libc_pwrite_cancel (fd=3, buf=0x5db1d000, nbyte=8192, offset=65536) at /usr/src/lib/libc/sys/w_pwrite.c:26 #2 0x1a89da73 in wtfs (bno=128, size=8192, bf=0x5db1d000) at mkfs.c:1060
pwriteにBPをはったんだけど、少し深い所で止った。bcで計算してみる。
1610612224 / 3145727 512 65536 / 128 512
2例も調べておけば十分だろう。Q.E.D.
それはそうと、qemu上で動いているやつにemacsは荷が重過ぎる。じゃviを使えでもいいんだけど。。emacsの代替品を発見。mgってやつ。デフォで入ってる。
能書きの一部。
TAGS mg supports tag files created by ctags(1), allowing the user to quickly locate various object definitions. Note though that emacs uses etags, not ctags. CSCOPE mg supports navigating source code using cscope. However, mg requires cscope and cscope-indexer executables to be present in PATH for it to work. MG DIRED KEY BINDINGS Specific key bindings are available in dired mode. :
ctagsを使ってtagsを作れって、エコな仕様だな。cscopeはどうしようもないけど。。。 非常用設備という事で、頭の片隅に入れておく。