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; }
実験再開
実験に戻る。起動したUMLにGDBでアタッチして、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 ?? ()
追記(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