gdbのDynamic Printfを使う

# はじめに
gdbのDynamic pinrtf機能を使うことで動的にprintfを挿入できます。
breakすることなくプログラムの変数 / 実行パス を確認できます。

# 使い方
[QEMU / gdb で Linux kernel の動きを確認する](http://qiita.com/tobira-code/items/4ac30b34453da7afbbd5)を例に説明します。

consoleAでQEMUを起動します。

```bash:consoleA
TOP_LINUX=~/linux-3.13.0
TOP_BUSYBOX=~/busybox-1.26.2
qemu-system-x86_64 -s -nographic \
  -kernel $TOP_LINUX/arch/x86/boot/bzImage \
  -initrd $TOP_BUSYBOX/rootfs.img \
  -append "root=/dev/ram rdinit=/sbin/init console=ttyS0"
```

別のconsoleBでgdbを起動してtarget remoteします。

```bash:consoleB
gdb vmlinux
GNU gdb (Ubuntu 7.7-0ubuntu3) 7.7
(snip)
Reading symbols from vmlinux...done.
(gdb) target remote :1234
```

プログラムを実行するとkernel の do_execveがcallされます。
do_execveの引数 filenameを表示してみます。
> dprintf do_execve, "do_execve fname=%s\n", filename

```bash:consoleB
(gdb) l do_execve
1577	}
1578	
1579	int do_execve(const char *filename,
1580		const char __user *const __user *__argv,
1581		const char __user *const __user *__envp)
1582	{
1583		struct user_arg_ptr argv = { .ptr.native = __argv };
1584		struct user_arg_ptr envp = { .ptr.native = __envp };
1585		return do_execve_common(filename, argv, envp);
1586	}
(gdb) dprintf do_execve, "do_execve fname=%s\n", filename
(gdb) c
```

consoleAで ls / find / catのコマンドを入力するとconsoleBにdprintfの出力が表示されます。

```bash:consoleB
do_execve fname=/bin/ls
do_execve fname=/usr/bin/find
do_execve fname=/bin/cat
```

# address指定
dprintfで表示する場所をaddress指定できます。
do_execveのアドレスを調べます。アドレス指定でdprintfを設定します。
> dprintf *0xffffffff8108bc62, "do_execve rax=%llx\n", $rax

```bash:consoleB
(gdb) disas do_execve 
Dump of assembler code for function do_execve:
   0xffffffff8108bc60 <+0>:	push   %r15
   0xffffffff8108bc62 <+2>:	mov    0xffffffff8141d080,%rax
   0xffffffff8108bc6a <+10>:	push   %r14
   0xffffffff8108bc6c <+12>:	mov    %rsi,%r14
(snip)
(gdb) dprintf *0xffffffff8108bc62, "do_execve rax=%llx\n", $rax
(gdb) c
Continuing.
do_execve rax=ffff88000005800
```

consoleAでコマンドを入力するとdprintfの結果が表示されます。
> do_execve rax=ffff88000005800

# 削除
breakの削除と同じです。

```
(gdb) i b
Num     Type           Disp Enb Address            What
8       dprintf        keep y   0xffffffff8108bc62 in do_execve 
                                                   at /home/user/linux-3.13.0/arch/x86/include/asm/current.h:14
	breakpoint already hit 2 times
        printf "do_execve rax=%llx\n", $rax
(gdb) d 8
(gdb) i b
No breakpoints or watchpoints.
(gdb) 
```

# Breakpoint Command Lists
break pointを通過したときに実行したいCommand Listsを登録できます。
Breakpoint Command Listsといいます。この機能を使うとdprintfと類似のことができます。

```bash:consoleB
(gdb) b do_execve 
Breakpoint 18 at 0xffffffff8108bc60: file fs/exec.c, line 1582.
(gdb) commands 18
Type commands for breakpoint(s) 18, one per line.
End with a line saying just "end".
>silent
>p filename
>c
>end
(gdb) c
Continuing.
$7 = 0xffff880000058020 "/bin/ls"
$8 = 0xffff880000058020 "/bin/ls"
$9 = 0xffff880000058020 "/bin/cat"

(gdb) i b
Num     Type           Disp Enb Address            What
18      breakpoint     keep y   0xffffffff8108bc60 in do_execve 
                                                   at fs/exec.c:1582
	breakpoint already hit 4 times
        silent
        p filename
        c
(gdb) 
```

dprintfとの違いについてはこちらを参照ください。

- http://stackoverflow.com/questions/31653903/what-is-the-difference-between-dprintf-vs-break-commands-continue

# references
- https://sourceware.org/gdb/onlinedocs/gdb/Dynamic-Printf.html
- https://sourceware.org/gdb/onlinedocs/gdb/Break-Commands.html#Break-Commands
- https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=e7e0cddfb0d4c4823cc7ed48c727e12f0e11a600