[go: up one dir, main page]
More Web Proxy on the site http://driver.im/

LinuxカーネルHack: GDBでBtrfsをデバッグする

はじめに

今回は、UML+GDBでBtrfsをデバッグできるようにしてみる。途中で失敗して多少遠回りになったけれども、デバッグはできるようになった。なお、Ubuntu 10.10でのUMLの開発環境がまだ整備できていないので、これまで使ってきたUbuntu 8.04のUMLの環境を使うことにした。

準備

Btrfsは、カーネルビルド時にmake defconfigしただけだと、デフォルトでは組み込まれない。そこで、make menuconfigからBtrfsを組み込むようにする。File systems -> Btrfs filesystem (EXPERIMENTAL) Unstable disk format を選択する。Mだとカーネルモジュールになって面倒なので、*を選択し、カーネルに完全に組み込んでしまうことにした。

これで、ビルドしたlinuxにbtrfs_*な関数にブレークポイントを打てる状態になった。

% make ARCH=um
% gdb ./linux
(gdb) b btrfs_
Display all 716 possibilities? (y or n) y
btrfs_acl_chmod                            btrfs_lookup_block_group
btrfs_add_block_group_cache                btrfs_lookup_csum
btrfs_add_dead_root                        btrfs_lookup_csums_range
btrfs_add_delayed_data_ref                 btrfs_lookup_dentry
btrfs_add_delayed_extent_op                btrfs_lookup_dir_index_item
[...]

UML上でBtrfsを実験するには、mkfs.btfsなどのユーザーランドコマンドを使えるようにする必要がある。手元のUML環境は、ビルド環境が全く整っていないので、必要なパッケージをapt-getから用意しておく。uuid-devはbtrfs-progs-unstableをビルドするのに必要。

# apt-get install build-essential git-core uuid-dev

Btrfsのユーザーランドコマンドを用意する。特に問題もなくビルドできた。

# git clone git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-progs-unstable.git
# cd btrfs-progs-unstable
# make

とにかくBtrfsをGDBで追えるようになれれば良いので、とりあえず、LoopbackデバイスからBtrfsを使えるようにする。ここではまる。/dev/loop0が無い。make menuconfigするとBlock devices -> Loopback device support がMになってる。

# dd if=/dev/zero of=btrfs.img bs=4k count=1000 
# losetup /dev/loop0 btrfs.img
/dev/loop0: No such device or address

手元の環境では、modprobeでカーネルモジュールをロードできる状態じゃない様子。解決に時間がかかりそうなので、Loopback device supportをカーネルに組み込むことで対応する。

# modprobe -l
FATAL: Could not load /lib/modules/2.6.36-rc7-00069-g6b0cd00/modules.dep: No such file or directory

カーネルをリビルドして再度試すと、今度は成功。

# losetup /dev/loop0 btrfs.img

やっとのことで、mkfs.btrfsを実行してみるものの、今度は/dev/loop0が小さすぎると。

# cd btrfs-progs-unstable/
# ./mkfs.btrfs /dev/loop0

WARNING! - Btrfs v0.19-35-g1b444cd IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using

device /dev/loop0 is too small (must be at least 256 MB)

適当に400MB確保して再挑戦。ddが遅いなと思ったら、9.2 MB/s。こんなもんか。いや、でも遅い。

# dd if=/dev/zero of=btrfs.img bs=4k count=100000

100000+0 records in
100000+0 records out
409600000 bytes (410 MB) copied, 44.5485 s, 9.2 MB/s

今度は成功した。

# losetup /dev/loop0 btrfs.img
# cd btrfs-progs-unstable/
# ./mkfs.btrfs /dev/loop0

WARNING! - Btrfs v0.19-35-g1b444cd IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using

fs created label (null) on /dev/loop0
	nodesize 4096 leafsize 4096 sectorsize 4096 size 390.62MB
Btrfs v0.19-35-g1b444cd

/dev/loop0をマウントして、dfで確認してみる。マウントに成功。

# mount -t btrfs /dev/loop0 /mnt
# df -h
Filesystem            Size  Used Avail Use% Mounted on
varrun                 15M   28K   15M   1% /var/run
varlock                15M     0   15M   0% /var/lock
udev                   15M  8.0K   15M   1% /dev
devshm                 15M     0   15M   0% /dev/shm
/dev/loop0            391M   56K  391M   1% /mnt

ioctl: ユーザーランドコマンドとカーネル(Btrfs)の接点

ここで、Btrfsのユーザーランドコマンドからサブボリューム作成時に、ユーザーランドで何をしているのか簡単に見ておく。btrfs sub create /mnt/subvolのように実行すると、/mnt/subvolをファイルオープン、ioctlからサブボリューム作成を指示、ファイルをクローズしている。このように、やっていることは本当に単純。ここで一番重要なのは、ioctl。ioctlでBtrfs固有の機能を制御している。ioctlを使わないと、Btrfs固有の機能にアクセスできないから、Btrfsのユーザーランドコマンドを別途用意する必要があると。

% cat btrfs-progs-unstable/btrfs_cmds.c
[...]
int do_create_subvol(int argc, char **argv)
{
        int     res, fddst, len;
        char    *newname;
        char    *dstdir;
        struct btrfs_ioctl_vol_args     args;
        char    *dst = argv[1];

        res = test_isdir(dst);
        if(res >= 0 ){
                fprintf(stderr, "ERROR: '%s' exists\n", dst);
                return 12;
        }

        newname = strdup(dst);
        newname = basename(newname);
        dstdir = strdup(dst);
        dstdir = dirname(dstdir);

        if( !strcmp(newname,".") || !strcmp(newname,"..") ||
             strchr(newname, '/') ){
                fprintf(stderr, "ERROR: uncorrect subvolume name ('%s')\n",
                        newname);
                return 14;
        }

        len = strlen(newname);
        if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
                fprintf(stderr, "ERROR: subvolume name too long ('%s)\n",
                        newname);
                return 14;
        }

        fddst = open_file_or_dir(dstdir);
        if (fddst < 0) {
                fprintf(stderr, "ERROR: can't access to '%s'\n", dstdir);
                return 12;
        }

        printf("Create subvolume '%s/%s'\n", dstdir, newname);
        strcpy(args.name, newname);
        res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);

        close(fddst);

        if(res < 0 ){
                fprintf( stderr, "ERROR: cannot create subvolume\n");
                return 11;
        }

        return 0;

}

Btrfsのコードを眺めていると、btrfs_ioctlが実行されそう。btrfs_ioctlにBtrfs固有の機能を制御するためのエントリポイントが集まっている。今の所、21個も制御ポイントがある。後で実際に実行されるかどうかをGDBで確かめてみる。

% cat fs/btrfs/ioctl.c
[...]
long btrfs_ioctl(struct file *file, unsigned int
                cmd, unsigned long arg)
{
        struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
        void __user *argp = (void __user *)arg;

        switch (cmd) {
        case FS_IOC_GETFLAGS:
                return btrfs_ioctl_getflags(file, argp);
        case FS_IOC_SETFLAGS:
                return btrfs_ioctl_setflags(file, argp);
        case FS_IOC_GETVERSION:
                return btrfs_ioctl_getversion(file, argp);
        case BTRFS_IOC_SNAP_CREATE:
                return btrfs_ioctl_snap_create(file, argp, 0);
        case BTRFS_IOC_SUBVOL_CREATE:
                return btrfs_ioctl_snap_create(file, argp, 1);
        case BTRFS_IOC_SNAP_DESTROY:
                return btrfs_ioctl_snap_destroy(file, argp);
        case BTRFS_IOC_DEFAULT_SUBVOL:
                return btrfs_ioctl_default_subvol(file, argp);
        case BTRFS_IOC_DEFRAG:
                return btrfs_ioctl_defrag(file, NULL);
        case BTRFS_IOC_DEFRAG_RANGE:
                return btrfs_ioctl_defrag(file, argp);
        case BTRFS_IOC_RESIZE:
                return btrfs_ioctl_resize(root, argp);
        case BTRFS_IOC_ADD_DEV:
                return btrfs_ioctl_add_dev(root, argp);
        case BTRFS_IOC_RM_DEV:
                return btrfs_ioctl_rm_dev(root, argp);
        case BTRFS_IOC_BALANCE:
                return btrfs_balance(root->fs_info->dev_root);
        case BTRFS_IOC_CLONE:
                return btrfs_ioctl_clone(file, arg, 0, 0, 0);
        case BTRFS_IOC_CLONE_RANGE:
                return btrfs_ioctl_clone_range(file, argp);
        case BTRFS_IOC_TRANS_START:
                return btrfs_ioctl_trans_start(file);
        case BTRFS_IOC_TRANS_END:
                return btrfs_ioctl_trans_end(file);
        case BTRFS_IOC_TREE_SEARCH:
                return btrfs_ioctl_tree_search(file, argp);
        case BTRFS_IOC_INO_LOOKUP:
                return btrfs_ioctl_ino_lookup(file, argp);
        case BTRFS_IOC_SPACE_INFO:
                return btrfs_ioctl_space_info(root, argp);
        case BTRFS_IOC_SYNC:
                btrfs_sync_fs(file->f_dentry->d_sb, 1);
                return 0;
        }

        return -ENOTTY;
}

実験再開

実験に戻る。起動したUMLGDBでアタッチして、btrfs_ioctlにブレークポイントを打ってみる。

(gdb) b btrfs_ioctl
Breakpoint 1 at 0x816b3a2: file fs/btrfs/ctree.h, line 1988.
(gdb) c
Continuing.

サブボリュームを作成。

# ./btrfs sub create /mnt/subvol

サブボリューム作成を実行しら、GDBがブレークした。おー!

Breakpoint 1, btrfs_ioctl (file=0x9fc7da0, cmd=1342215182, arg=3213134472)
    at fs/btrfs/ctree.h:1988
1988            return file->f_path.dentry;
(gdb) bt
#0  btrfs_ioctl (file=0x9fc7da0, cmd=1342215182, arg=3213134472) at fs/btrfs/ctree.h:1988
#1  0x080bfcc1 in vfs_ioctl (filp=0x9fc7da0, cmd=1342215182, arg=3213134472)
    at fs/ioctl.c:44
#2  0x080c032e in do_vfs_ioctl (filp=0x9fc7da0, fd=3, cmd=1342215182, arg=3213134472)
    at fs/ioctl.c:597
#3  0x080c0397 in sys_ioctl (fd=3, cmd=1342215182, arg=3213134472) at fs/ioctl.c:617
#4  0x0805ab18 in handle_syscall (r=0x9f08260) at arch/um/kernel/skas/syscall.c:35
#5  0x0806832d in userspace (regs=0x9f08260) at arch/um/os-Linux/skas/process.c:201
#6  0x08058964 in fork_handler () at arch/um/kernel/process.c:181
#7  0x00000000 in ?? ()

最後に

Btrfsのデバッグができるようになった。ファイルシステム固有の機能をioctlから制御される所をGDBで確かめることができた。ここまでできれば、これから色々解析ができそう。楽しみ。

追記(2008/10/09)

irc.freenode.netの#btrfsチャネルで、ちょっと質問してみたところ、Btrfs Wikiにページを書くのは君の自由だよ、と言ってもらえたので、ここで書いた内容を練り直して、本家のWikiに書いておいた。
https://btrfs.wiki.kernel.org/index.php/Debugging_Btrfs_with_GDB

以下はIRCでのやりとり。はじめて、久しぶりに英語でIRCして緊張したけど、結果的に、良い方向に行って良かった^^

18:48 (fixme) Hi, I'm a newbie of Btrfs. I'm interested in Btrfs.
18:48 (fixme) So, blogged. Linux Kernel Hack: Debugging Btrfs with GDB on UML. (in Japanese)
18:48 (fixme) http://d.hatena.ne.jp/fixme/20101009/1286615871
18:49 (fixme) The entry explains how to debug Btrfs with GDB on UML, but this might be little bit boring for Btrfs hackers on this IRC channel... And sorry, this is japanese.
18:49 (fixme) So, does anyone want to read such topics in English? Are there documents(ex: Btrfs Wiki) on debugging Btrfs?
18:49 (cwillu) fixme, sure
18:50 (cwillu) fixme, might poll the mailing list
18:52 (fixme) cwillu, you mean I should post an mail on this topic to the Btrfs mailing list?
18:52 (cwillu) well, asking for opinions at least, yes
18:53 (fixme) thank you
18:53 (cwillu) Nobody would stop you from making a wiki page either :p
18:54 (fixme) Oh! Really?
18:54 (cwillu) fixme, that's the point of a wiki
18:55 (cwillu) just remember that it extends to the person who wants to fix your page too :)
18:56 (fixme) Yes :D
18:57 (fixme) I'll create an account on the wiki.
[...]
20:15 (fixme) cwillu, created wiki :D https://btrfs.wiki.kernel.org/index.php/Debugging_Btrfs_with_GDB
20:15 (fixme) thank you

追記(2008/10/10)

Btrfs以外のファイルシステムのioctl.cを見てみると、Btrfsのioctlがいかに多様な機能を提供しいるのかがわかる。

ファイルシステム エントリー数(ioctlのスイッチ数) 行数
ext2 6 178
ext3 9 330
ext4 13 383
reiserfs 5 229
btrfs 21 2040