VimのソースコードをVimで解析しよう(Part1-4)
(Part1-1)、(Part1-2)、(Part1-3)
さぁ、デバッガで動かして関連性を見てみましょう。デバッガは昔ながらのGDBを使います。GDBのフロントエンドはいくつか存在しますが、「昔ながら」でいきましょう。
man gdb
GDBマニュアル
ファイヤープロジェクト GDB ←わかりやすい!!
デバッグ用バイナリの作成
GDBでCソースコードデバッグするにはデバッグ情報入りのバイナリを生成する必要があります。
bash
$ cd vimソースをゲットしたディレクトリ # 再生成可能なファイルを削除 $ make distclean # デバッグ情報あり(-g)でconfigure (configureオプションはお好みで) $ ./configure CFLAGS="-g" --enable-multibyte --with-gnome --with-features=big $ make
デバッグ準備
実行中のvimプロセスのデバッグをするので端末(ターミナルエミュレータ)が2つ必要です。*1
端末Vで先ほど生成したVimを起動しましょう。
$ cd vimソースをゲットしたディレクトリ/src
$ ./vim
j でちゃんとカーソル下移動になるように下準備しましょう。
" 相対行番号表示on :set relativenumber[Enter] " インサートモードへ移行→ a改行b を入力→ノーマルモードへ移行 ia<CR>b<Esc> " カーソルを1行目へ移動 k
# 現在実行中のvimプロセスのIDを表示 $ pgrep vim 22063 # GDBでプロセスID 22063の ./vim に接続 gdb ./vim 22063
「分離されたデバッグ情報がないよ」とか言われてますが、ライブラリのデバッグをする訳じゃないのでスルーして、これまで調べた3つの処理にbreakをセットしましょう。
(gdb) b screen.c:3482 Breakpoint 1 at 0x53a26b: file screen.c, line 3482. (gdb) b screen.c:8644 Breakpoint 2 at 0x543b6e: file screen.c, line 8644. (gdb) b edit.c:7116 Breakpoint 3 at 0x42caa2: file edit.c, line 7116. (gdb)
breakpoint # | 関数名 | ファイル名 | 行番号 | 処理内容 |
---|---|---|---|---|
1 | win_line() | screen.c | 3482 | 相対行番号の描画データ設定処理 |
2 | setcursor() | screen.c | 8644 | カーソル描画処理 |
3 | cursor_down() | edit.c | 7116 | カーソル移動処理 |
デバッグ開始
Vimの実行を継続させます。
(gdb) c
Continuing.
端末VのVimに j を入力すると、端末GのGDBがbreakしました。変数の値を表示したりした後、バックトレース表示させ継続(c)を繰り返すと以下のような流れになりました。
Breakpoint 2, setcursor () at screen.c:8644 8644 windgoto(W_WINROW(curwin) + curwin->w_wrow, (gdb) p curwin->w_wrow $2 = 0 (gdb) bt #0 setcursor () at screen.c:8644 #1 0x00000000004f1377 in display_showcmd () at normal.c:4038 #2 0x00000000004f1210 in add_to_showcmd (c=106) at normal.c:3960 #3 0x00000000004eb78a in normal_cmd (oap=0x7fff51cd2a90, toplevel=1) at normal.c:707 #4 0x00000000005c6169 in main_loop (cmdwin=0, noexmode=0) at main.c:1263 #5 0x00000000005c5b44 in main (argc=1, argv=0x7fff51cd2db8) at main.c:964 (gdb) c Continuing. Breakpoint 3, cursor_down (n=1, upd_topline=1) at edit.c:7116 7116 curwin->w_cursor.lnum = lnum; (gdb) p lnum $4 = 2 (gdb) bt #0 cursor_down (n=1, upd_topline=1) at edit.c:7116 #1 0x00000000004f5231 in nv_down (cap=0x7fff51cd29d0) at normal.c:6191 #2 0x00000000004ec65f in normal_cmd (oap=0x7fff51cd2a90, toplevel=1) at normal.c:1193 #3 0x00000000005c6169 in main_loop (cmdwin=0, noexmode=0) at main.c:1263 #4 0x00000000005c5b44 in main (argc=1, argv=0x7fff51cd2db8) at main.c:964 (gdb) c Continuing. Breakpoint 2, setcursor () at screen.c:8644 8644 windgoto(W_WINROW(curwin) + curwin->w_wrow, (gdb) p curwin->w_wrow $5 = 1 (gdb) bt #0 setcursor () at screen.c:8644 #1 0x00000000004f1377 in display_showcmd () at normal.c:4038 #2 0x00000000004f10e8 in clear_showcmd () at normal.c:3897 #3 0x00000000004ec99c in normal_cmd (oap=0x7fff51cd2a90, toplevel=1) at normal.c:1333 #4 0x00000000005c6169 in main_loop (cmdwin=0, noexmode=0) at main.c:1263 #5 0x00000000005c5b44 in main (argc=1, argv=0x7fff51cd2db8) at main.c:964 (gdb) c Continuing. Breakpoint 1, win_line (wp=0x20a97d0, lnum=1, startrow=0, endrow=21, nochange=1) at screen.c:3482 3482 sprintf((char *)extra, "%*ld ", (gdb) bt #0 win_line (wp=0x20a97d0, lnum=1, startrow=0, endrow=21, nochange=1) at screen.c:3482 #1 0x0000000000536c14 in win_update (wp=0x20a97d0) at screen.c:1850 #2 0x00000000005342fb in update_screen (type=35) at screen.c:531 #3 0x00000000005c5f28 in main_loop (cmdwin=0, noexmode=0) at main.c:1166 #4 0x00000000005c5b44 in main (argc=1, argv=0x7fff51cd2db8) at main.c:964 (gdb) c Continuing. Breakpoint 1, win_line (wp=0x20a97d0, lnum=2, startrow=1, endrow=21, nochange=1) at screen.c:3482 3482 sprintf((char *)extra, "%*ld ", (gdb) bt #0 win_line (wp=0x20a97d0, lnum=2, startrow=1, endrow=21, nochange=1) at screen.c:3482 #1 0x0000000000536c14 in win_update (wp=0x20a97d0) at screen.c:1850 #2 0x00000000005342fb in update_screen (type=35) at screen.c:531 #3 0x00000000005c5f28 in main_loop (cmdwin=0, noexmode=0) at main.c:1166 #4 0x00000000005c5b44 in main (argc=1, argv=0x7fff51cd2db8) at main.c:964 (gdb) c Continuing. Breakpoint 2, setcursor () at screen.c:8644 8644 windgoto(W_WINROW(curwin) + curwin->w_wrow, (gdb) p curwin->w_wrow $9 = 1 (gdb) p curwin->w_wcol $10 = 4 (gdb) bt #0 setcursor () at screen.c:8644 #1 0x00000000005c6087 in main_loop (cmdwin=0, noexmode=0) at main.c:1214 #2 0x00000000005c5b44 in main (argc=1, argv=0x7fff51cd2db8) at main.c:964 (gdb) c Continuing.
デバッグ終了
端末Gのgdbはctrl-Cして止めた後、qで終了します。
^C Program received signal SIGINT, Interrupt. 0x0000003a35ad9073 in __select_nocancel () from /lib64/libc.so.6 (gdb) q A debugging session is active. Inferior 1 [process 26051] will be detached. Quit anyway? (y or n) y Detaching from program: /home/h_east/samba/Mercurial/vim_new/src/vim, process 26051
考察
No. | bp# | 解説 |
---|---|---|
1 | 2 | 私の環境ではset showcmdしてるのでその描画処理内で呼ばれている (今回の件とはたぶん関係ないのでスルー) |
2 | 3 | カーソル下移動 |
3 | 2 | showcmdの消去処理内で呼ばれている (今回の件とはたぶん関係ないのでスルー) |
4 | 1 | ウィンドウの1行目の描画 |
5 | 1 | ウィンドウの2行目の描画 |
6 | 2 | 2行目5カラム目にカーソル描画(相対行番号表示エリアも込みの位置) |
今回の表示位置がずれる件に関係ありそうなのは No.2, 5, 6 ですね。
現象が起こせないのがもどかしいですが、ずれが発生する要因としては以下のようなパターンが考えられます。
- No.2, No.5は処理されたが No.6が処理されなかった。
ソースコード的には setcursor()内すぐの redrawing() の判定がFALSEの場合に上記パターンになります。
redrawing()を見てみましょう。
/* * Return TRUE if redrawing should currently be done. */ int redrawing() { return (!RedrawingDisabled && !(p_lz && char_avail() && !KeyTyped && !do_redraw)); }
コメントには「再描画が必要な場合は TRUE を返す」って書いてます。
NOT(!)とAND(&&)で分かりずらい。。えーと、TRUEになる条件は
- RedrawingDisabled が FALSE である
- かつ (
- 'lazyredraw'オプション がoff または
- char_avail() が FALSE または
- KeyTyped が TRUE または
- do_redraw が TRUE )
'lazyredraw'オプションはよっぽどのことがない限り off のはずなので(要確認)、結局 RedrawingDisabled の状態に依存していると思われます。
再現手順を確立させてGDBのwatchコマンドで追いかければ原因がわかるかも!?