VimのデバグにGDBを使う

はじめまして、Quipperに再来週に入社予定のujihisaと申します。 本記事では、Vim本体のC実装をデバグするのにGNU Debugger (GDB)を用いる方法について説明します。 巷には「GDBVimに統合させて何らかのプログラムをデバグする方法」はいくつか解説記事がありますが、本記事はそうではなく、Vim自体のデバグについてに着目します。

Quipperでは社内のソフトウェアエンジニアのうち4割 *1Vimを使用しており、我々はVim本体の未解決バグを踏んだときに自力で解決しVimコア開発者にフィードバックできるようになっているべきです。

Vimdebug_info, not stripped でビルドする

お使いのVimのバイナリは、おそらくdebug buildではないです。ちょっと確認してみましょう。

$ file `which vim`
/usr/sbin/vim: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=453c03888b6bf2b68fb92ba2efb59b139a9b0b76, stripped

末尾の stripped に注目してください。このバイナリに対してはGDBを使うことはほぼ出来ません *2

  1. Vimソースコードを取得します。単に https://github.com/vim/vim からgit cloneするだけで大丈夫です
    • 以下ではこれを ~/git/vim に展開したものとします
  2. configureのあと、 src/Makefile ファイルを直接編集します (以下のパッチを参照せよ)
  3. :terminal make -C src reconfig clean all install
diff --git a/src/Makefile b/src/Makefile
index f2fafa4dc..8b41d0648 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -591,7 +591,7 @@ CClink = $(CC)
 # When using -g with some older versions of Linux you might get a
 # statically linked executable.
 # When not defined, configure will try to use -O2 -g for gcc and -O for cc.
-#CFLAGS = -g
+CFLAGS = -g
 #CFLAGS = -O

 # Optimization limits - depends on the compiler.  Automatic check in configure
@@ -1006,6 +1006,7 @@ TOOLS = xxd/xxd$(EXEEXT)
 #
 # Uncomment the next line to install Vim in your home directory.
 #prefix = $(HOME)
+prefix = $(HOME)/git/vim/local

 ### exec_prefix    is the top directory for the executable (default $(prefix))
 #
@@ -1156,7 +1157,7 @@ INSTALL_DATA_R = cp -r

 ### Program to run on installed binary.  Use the second one to disable strip.
 #STRIP = strip
-#STRIP = /bin/true
+STRIP = /bin/true

 ### Permissions for binaries  {{{1
 BINMOD = 755

なお、ここの prefix 部分は僕用の設定です。これをやるときは別に全く同じよう設定する必要はありません。こうしておくと /usr/local 以下ではないところにmake installすることになりますので、sudoが不要になって便利です。

このようにして生成されたVimバイナリに改めて file をかけて、 debug_info, not stripped がついていることを確認してください。

$ file ~/git/vim/local/bin/vim
/home/ujihisa/git/vim/local/bin/vim: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=c8da01b6b400e7f8e37046f5313145fc2e15be2b, with debug_info, not stripped

2. GDBを使う

以下で実行します。勝利が約束されています。

$ gdb ~/git/vim/local/bin/vim -q --tui

以下のように見ることができます。音声のない短い動画ですので気軽にみてみてください。


vim gdb

このデモでは以下のGDBのコマンドを利用しました。詳しくはGDBの中で help してみてください。

  • start (Vimコマンドラインオプション -u NONE などを渡しています。)
  • break でbreakpointを貼っています
  • c = continue = breakpointまで一気に実行
  • n = next = Cで一行分実行

おまけ: :Termdebug を使う

すでにGDBは使えているので、ここからは必須ではありません。

Vim 8から搭載されている, Vimの中でターミナルを起動する :terminal。これを前提としたVimプラギンのtermdebugというものがあり、これはなんとVim 8にすでに標準搭載されています。

packadd termdebug

この一行を~/.vimrcに書くだけで、 :Termdebug コマンドが使えるようになります。こうすることで、Vimの中でVimのデバグを行うことができます。この場合新しく起動する子Vimは親の :terminal 内で起動されるため、それぞれが端末描画を競い合うなどといったことを回避でき、大変便利です。

おわりに

ujihisa個人ブログ (英語) にて同様の内容の記事を先日投稿しました。が、日本語では相当する記事がないため、需要があると判断して執筆してみました。

Quipperに入社したらVimなどを用いてQuipperのあれやこれをこれから開発したり手伝ったり便利したりと貢献していく所存です。みなさま今後共よろしくおねがいします。

ujihisa

*1:あとは多い順にvscode, rubymine, emacs, atomです

*2:厳密には可能です。が、breakpointは貼れないし、実行に対応するソースコードの表示などもできません

*3:[UPDATED] もともと src/READMEと書いてたけど実はsrc/Makefile内に直接書かれてました。thx @h-east!