gdb script for newfs

gdb script basic

GDB でのコマンド実行を自動化する。

と言うのを見て、自動実行をやってみる。何も難しい事はなく、対話的に入力してたコマンドを羅列したファイルを作る。下記は、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

Linux に新しくディスクを追加する方法

HDD増設手順メモ

Linuxでディスクを追加する

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はどうしようもないけど。。。 非常用設備という事で、頭の片隅に入れておく。

etc