Debian BTS経由でバグレポートをいただいたので、調査してみました。
自分の思考の過程を書くだけなので、あんまり他の人への参考にはならないと思います。
まずgdbで追いかけられるようコンパイラのフラグを適切に設定して
buildします。
$ apt-get source anthy
$ cd anthy-9000e
$ CFLAGS="-g -O0" ./configure
$ make
次に、問題となる変換テキストを用意します。
itioku
(space)
PRINT_CONTEXT
そして、それを使ってgdb経由で実行します。
$ libtool --mode=execute gdb anthy-agent
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) run --conffile /etc/anthy/anthy-conf < foo.txt
Starting program: /home/knok/draft/debian/build/anthy/anthy-9100e/src-util/.libs/lt-anthy-agent --conffile /etc/anthy/anthy-conf < foo.txt
(2 ((UL) "
Program received signal SIGSEGV, Segmentation fault.
0xb7f40ea6 in anthy_get_nth_dic_ent_str (se=0x80a5250, orig=0x80b01a4, n=-1,
x=0xbffb6408) at word_dic.c:389
389 x->len = se->dic_ents[n]->str.len;
(gdb) p x
$1 = (xstr *) 0xbffb6408
(gdb) p x->len
$2 = 1
(gdb) p se
$3 = (seq_ent_t) 0x80a5250
(gdb) p n
$4 = -1
nが-1となっているのがおかしいようです。実行している箇所は
anthy_get_nth_dic_ent_str()です。バックトレースは以下のとおり。
#0 0xb7f40ea6 in anthy_get_nth_dic_ent_str (se=0x80a5250, orig=0x80b01a4,
n=-1, x=0xbffb6408) at word_dic.c:389
#1 0xb7f89c98 in proc_swap_candidate_indep (se=0x80ac498) at candswap.c:172
#2 0xb7f89d3e in anthy_proc_swap_candidate (seg=0x80ac498) at candswap.c:203
#3 0xb7f8a2c0 in apply_learning (sl=0x80a4040, nth=0) at candsort.c:233
#4 0xb7f8a374 in anthy_sort_candidate (sl=0x80a4040, nth=0) at candsort.c:261
#5 0xb7f823dd in make_candidates (ac=0x80a4038, from=0, from2=0, is_reverse=0)
at context.c:349
#6 0xb7f82477 in anthy_do_context_set_str (ac=0x80a4038, s=0x80a35a8,
is_reverse=0) at context.c:366
#7 0xb7f8134d in anthy_set_string (ac=0x80a4038, s=0x8077ad8 " at main.c:230
#8 0xb7f9437e in enter_conv_state (ictx=0x8077628) at input.c:241
#9 0xb7f95fb8 in anthy_input_space (ictx=0x8077628) at input.c:1151
#10 0x0804b027 in dispatch_command (ictx=0x8077628, cmd=0x8077600)
at agent.c:1013
#11 0x0804b104 in main_loop () at agent.c:1063
#12 0x0804b419 in main (argc=-1, argv=0x0) at agent.c:1150
一つ上の関数を調べてみます。
(gdb) up
#1 0xb7f89c98 in proc_swap_candidate_indep (se=0x80ac498) at candswap.c:172
172 res = anthy_get_nth_dic_ent_str(se->cands[i]->elm[core_elm_idx].se,
(gdb) l
167 /* 168 for (i = 1; i < se->nr_cands; i++) {
169 if (se->cands[i]->nr_words == se->cands[0]->nr_words &&
170 se->cands[i]->core_elm_index == core_elm_idx) {
171 xstr cand;
172 res = anthy_get_nth_dic_ent_str(se->cands[i]->elm[core_elm_idx].se,
173 &se->cands[i]->elm[core_elm_idx].str,
174 se->cands[i]->elm[core_elm_idx].nth,
175 &cand);
176 if (res == 0 &&
(gdb) p i
$5 = 5
(gdb) p *se->cands[5]->elm
$9 = {nth = -1, wt = {pos = 0, cos = 0, scos = 0, cc = 0, ct = 0, wf = 0},
se = 0x0, ratio = 0, str = {str = 0x0, len = 0}, id = -1}
このelm(エレメント)は空っぽとなっているようです。これは妙なので、
他のバージョンで実行してみます。
$ anthy-agent < foo.txt
(2 ((UL) "(3 ((UL RV) "|
anthy 9100cでは正しく動作しています。なので、どこかでバグが入り込んだ
ようです。同じ条件でgdbをかけてみます。
(gdb) b anthy_get_nth_dic_ent_str
Function "anthy_get_nth_dic_ent_str" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (anthy_get_nth_dic_ent_str) pending.
(gdb) run --conffile /etc/anthy/anthy-conf < ../../foo.txt
Starting program: /home/knok/draft/debian/build/anthy/anthy-9100-c/src-util/.libs/lt-anthy-agent --conffile /etc/anthy/anthy-conf < ../../foo.txt
Breakpoint 2 at 0xb7f516ec: file word_dic.c, line 382.
Pending breakpoint "anthy_get_nth_dic_ent_str" resolved
(2 ((UL) "
Breakpoint 2, anthy_get_nth_dic_ent_str (se=0x80a66e8, orig=0xbfac3020, n=0,
x=0xbfac3028) at word_dic.c:382
382 if (!se) {
(以下略)
基本的にnの値は常に0以上です。また、anthy-agentの出力も異なります。
(2 ((UL) "(3 ((UL RV) "|
バックトレースも見てみましょう。
#0 anthy_get_nth_dic_ent_str (se=0x80a6a48, orig=0xbfacd240, n=0,
x=0xbfacd248) at word_dic.c:382
#1 0xb7fa4912 in enum_candidates (seg=0x80a68a8, ce=0x80a6958, from=0, n=1)
at compose.c:162
#2 0xb7fa4abd in enum_candidates (seg=0x80a68a8, ce=0x80adc98, from=0, n=0)
at compose.c:188
#3 0xb7fa4f93 in make_candidate_from_simple_metaword (se=0x80a68a8,
mw=0x80a60f8, top_mw=0x80a60f8, is_reverse=0) at compose.c:336
#4 0xb7fa5148 in proc_splitter_info (se=0x80a68a8, mw=0x80a60f8,
top_mw=0x80a60f8, is_reverse=0) at compose.c:393
#5 0xb7fa5347 in anthy_do_make_candidates (sc=0x80a57f4, se=0x80a68a8,
is_reverse=0) at compose.c:464
#6 0xb7f9f10d in make_candidates (ac=0x80a57b0, from=0, from2=0, is_reverse=0)
at context.c:344
#7 0xb7f9f1cb in anthy_do_context_set_str (ac=0x80a57b0, s=0x80a4e88,
is_reverse=0) at context.c:366
#8 0xb7f9e09c in anthy_set_string (ac=0x80a57b0, s=0x8077b10 " at main.c:230
#9 0xb7fb015e in enter_conv_state (ictx=0x8077660) at input.c:241
#10 0xb7fb1d14 in anthy_input_space (ictx=0x8077660) at input.c:1151
#11 0x0804af21 in dispatch_command (ictx=0x8077660, cmd=0x8077638)
at agent.c:1013
#12 0x0804affe in main_loop () at agent.c:1063
---Type <return> to continue, or q <return> to quit---
ずいぶん挙動が異なっているようです。比較をしてみると、make_candidatesから
先が違うようなので、そこを重点的に見てみます。
anthy_sort_candidateの中でanthy_get_nth_dic_ent_strを呼び出し、
この時にsegfaultしています。
その前にanthy_do_make_candidatesを呼び出しており、ここで候補一覧を
生成しているようです。
この辺で面倒になってきたのでltraceで比較してみることにします。
すると、一点大きな違いがあることに気づきました。
9100c:
anthy_input_get_preedit(0x8077660, 0xb7e874c0, 0x8077600, 0xbf8157a8, 0xb7dbe022) = 0x8077600
9100e:
anthy_input_get_preedit(0x8077628, 1, 0xbf8ead68, 0xb7e9074c, 0xb7f5f160) = 0x80775c8
2番目の引数が妙です。しかしよくみると、実際にはこの関数は構造体へのポインタ
のみを受け取る、引数1種類の関数でした。その時の内容をみるとこんな
違いがありました。
9100c:
(gdb) p *ictx
$2 = {state = 2, rkctx = 0x80776c8, map_no = 2, hbuf = 0x8077b10 " n_hbuf = 8, s_hbuf = 9, hbuf_follow = 0x0, n_hbuf_follow = 0,
s_hbuf_follow = 0, actx = 0x0, segment = 0x0, cur_segment = 0x0,
enum_cand_count = 0, enum_cand_limit = 3, enum_reverse = 0,
last_gotten_cand = 7185, commit = 0x0, n_commit = 0, s_commit = 0,
cut = 0x0, n_cut = 0, s_cut = 0, cfg = 0x8054a40, next_cfg_owner = 0x0}
9100e:
$1 = {state = 2, rkctx = 0x8077690, map_no = 2, hbuf = 0x8077ad8 " n_hbuf = 8, s_hbuf = 9, hbuf_follow = 0x0, n_hbuf_follow = 0,
s_hbuf_follow = 0, actx = 0x0, segment = 0x0, cur_segment = 0x0,
enum_cand_count = 0, enum_cand_limit = 3, enum_reverse = 0,
last_gotten_cand = 0, commit = 0x0, n_commit = 0, s_commit = 0, cut = 0x0,
n_cut = 0, s_cut = 0, cfg = 0x8054a18, next_cfg_owner = 0x0}
(gdb) p *ictx
last_gotten_candが0となっているのがおかしい気がします。
加えて、9100cではanthy_input_get_preeditを二回呼んでいるのに対し、
9100eでは一度しか呼んでいません。
anthy_input_preeditを呼ぶ前の段階で問題が起きているようです。
さらに細かく見てみると、anthy-agentの構造はトークンを一つづつ読み込んで
それらをコマンドとして解釈するdispatch処理をひたすら繰り返す、という
形になっていることを把握できました。
(space)が変換コマンドなので、そこで発生している何かを掴めば
デバッグできそうです。
そう思ってcmd_spaceにブレークポイントをおいてみたら、この段階で
すでにictx->last_gotten_candの値が異なっていました。
ではこの値が問題か? と思ったのですが、面倒になってきて
emacsのgdb-modeで追いかけてみたら最終的にspaceを押した段階で
0にクリアされるのでどうも関係なさそうな感じです。
ここで視点を再度segfault発生点に向けてみます。
anthy_get_nth_dic_ent_strの引数int nに問題がある(-1)ことが問題ですが、
その親関数はproc_swap_candidate_indepです。一つ一つ親関数で値が
どう設定されているかをみてゆけば、問題が発見できるはずです。
そして、ようやく鍵となる関数anthy_do_make_candidatesを発見しました。
ここで文節の変換を行い、候補として記録して行くようです。
デバッグ用の処理が含まれていたので、それを有効化してみます。
環境変数ANTHY_ENABLE_DEBUG_PRINTを定義し、もう一つ環境変数
ANTHY_SPLITTER_PRINTに'c'を設定すればよいようです
(src-splitter/splitter.c)。他にもいろいろ出力できるようなので、
とりあえず'wmlic'全部定義しておきます。
これで大量の出力が出るようになりましたが、少なくとも
途中経過で「一億」という候補を出しているようではあります。
さらにたどって、ようやくcandswap.c内の関数proc_swap_candidate_indep()
で呼ばれているprepare_swap_candidate(&key)の結果が異なることがわかりました。
さらに先を調べると、anthy_select_rowの返す値が異なっています。
ここは個人辞書の排他制御が入るので、2つのバージョンを見比べながら
gdbで追いかけるのは無理のようです。まあとりあえず、原因はある程度
絞り込めてきました。