Mountain LionでboehmgcがSEGVする件を修正
(2012/10/25 追記)8月にリリースされたgc-7.2dで修正されたようです。該当のコミットはこれかな?parent見るとけっこうおおがかりに手をいれたみたいですね。
あるいは、プログラムを理解しないまま修正する手法について。
ということで昨日のw3mのつづきでboehmgc。今日の成果物の修正パッチ。
- https://gist.github.com/3216854
- https://trac.macports.org/attachment/ticket/35429/patch-boehmgc-MoutainLion.diff
1箇所volatileを追加だけです。納得いかねーーー!
調査の記録
昨日わかったことは、boehmgcを-O0でコンパイルすればSEGVしなくなるということでした。今までの経験的に、こういうケースではvolatileを足すと直るようです。
じゃあどこなのよ、というのが問題。rubyのときには通らないテストから該当の関数をわりとすぐ特定できた(rubyのソース構成をある程度知っていた)のですが、boehmgcとかさっぱりわからん。使うコード書いたこともないし。
幸いboehmgcにもテスト(make check)が用意されているので、それを利用して問題が発生している箇所をゴリ押しで特定していきます。
まずはMakefileをいじって、最適化のフラグをパラメータで指定しやすくします。
% diff -u Makefile{.orig,} --- Makefile.orig 2012-07-31 22:24:13.000000000 +0900 +++ Makefile 2012-07-31 22:24:42.000000000 +0900 @@ -70,7 +70,7 @@ # Modified by: Petter Urkedal <petter.urkedal@nordita.dk> - +OPTFLAGS = -O2 pkgdatadir = $(datadir)/gc @@ -392,15 +392,15 @@ CC = /usr/bin/clang CCAS = /usr/bin/clang CCASDEPMODE = depmode=gcc3 -CCASFLAGS = -pipe -O2 -arch x86_64 $(DEFS) +CCASFLAGS = -pipe $(OPTFLAGS) -arch x86_64 $(DEFS) CCDEPMODE = depmode=gcc3 -CFLAGS = -pipe -O2 -arch x86_64 -fno-strict-aliasing +CFLAGS = -pipe $(OPTFLAGS) -arch x86_64 -fno-strict-aliasing CPP = /usr/bin/clang -E CPPFLAGS = -I/opt/local/include -D_XOPEN_SOURCE=600 -D_DARWIN_C_SOURCE CXX = /usr/bin/clang++ CXXCPP = /usr/bin/clang++ -E CXXDEPMODE = depmode=gcc3 -CXXFLAGS = -pipe -O2 -arch x86_64 +CXXFLAGS = -pipe $(OPTFLAGS) -arch x86_64 CXXINCLUDES = CYGPATH_W = echo DEFS = -DHAVE_CONFIG_H
これで、`make OPTFLAGS=-O0'のようにして、いつでも最適化フラグを変更することができます。
次に以下のシェルスクリプトを用意しました。
というものです。ようするに-O0でコンパイルする必要がある(=> -O2で問題が起きている)プログラム(.c)を特定するのが目的です。
% cat test-O0.sh #!/bin/zsh # % ls -1 *.o OBJFILES=' allchblk.o alloc.o : # 略 thread_local_alloc.o typd_mlc.o ' for OBJ in `echo ${OBJFILES}` do echo "testing ${OBJ} ..." rm ${OBJ} ${OBJ:r}.lo make OPTFLAGS=-O0 all >& /dev/null make check >& /dev/null if [ $? -eq 0 ] then echo "[passed] ${OBJ} - OK OK OK OK OK OK OK OK OK" exit 0 else echo "[FAILURE] ${OBJ}" fi echo "--------------------------------------" done
さて実施。
% sudo port build boehmgc % cd `port work boehmgc` % cd gc-7.2 % make clean % make % cp Makefile{,.orig} % vi Makefile # 上述のOPTFLAGSの編集 % ./test-O0.sh testing allchblk.o ... [FAILURE] allchblk.o -------------------------------------- testing alloc.o ... [FAILURE] alloc.o -------------------------------------- : # 略 -------------------------------------- testing mark_rts.o ... [FAILURE] mark_rts.o -------------------------------------- testing misc.o ... [passed] misc.o - OK OK OK OK OK OK OK OK OK %
ほいきた!
misc.cがあやしいですね。次はmisc.cだけを-O0でコンパイルしてみます。これでだめなら、この状態からまたスクリプトを実行するということを繰り返していけば、最終的に最適化の影響で問題がでている.cをひととおり特定できることになります。
% make clean && make % rm misc.o misc.lo % make OPTFLAGS=-O0 all % make check : # 略 =================== All 11 tests passed =================== %
おお、misc.cだけでいけるようですね。
あとはmisc.cのどっかにvolatileを追加すれば直ると思うのですが、misc.cは1896行あります。読めない量ではありませんが、内容のわからないプログラムであることを考えると無謀です。
とりあえず、make check中にSEGVしたときのクラッシュレポートを見てみましょう。こんだけあります。
gctest_2012-07-31-....crash middletest_2012-07-31-....crash realloc_test_2012-07-31-....crash smashtest_2012-07-31-....crash staticrootstest_2012-07-31-....crash test_cpp_2012-07-31-....crash threadkey_test_2012-07-31-....crash
Process: gctest [81258] Path: /Volumes/VOLUME/*/gctest Identifier: gctest Version: 0 Code Type: X86-64 (Native) Parent Process: sh [81257] : # 略 Thread 6 Crashed: 0 libgc.1.dylib 0x000000010d7aa6f6 GC_clear_stack_inner + 54 1 libgc.1.dylib 0x000000010d7aa706 GC_clear_stack_inner + 70 2 libgc.1.dylib 0x000000010d7aa706 GC_clear_stack_inner + 70 3 libgc.1.dylib 0x000000010d7aa706 GC_clear_stack_inner + 70 : # 略 295 libgc.1.dylib 0x000000010d7aa706 GC_clear_stack_inner + 70 296 libgc.1.dylib 0x000000010d7aa706 GC_clear_stack_inner + 70 297 libgc.1.dylib 0x000000010d7aa787 GC_clear_stack + 87 298 gctest 0x000000010d77009d run_one_test + 973 299 gctest 0x000000010d7708b9 thr_run_one_test + 9 300 libgc.1.dylib 0x000000010d7afdca GC_inner_start_routine + 90 301 libgc.1.dylib 0x000000010d7abce9 GC_call_with_stack_base + 25 302 libsystem_c.dylib 0x00007fff8ac36782 _pthread_start + 327 303 libsystem_c.dylib 0x00007fff8ac231c1 thread_start + 13
見た感じ、どれもGC_clear_stack_inner()が無限ループしてるようなログです。ここからは勘でGC_clear_stack_inner()のまわりを「んー、ここかな?」というカンジでvolatileを足してみてはmake check、という手順で効果のある場所を探していきます。今回はラッキーだったのか、30分程度でパッチをつくることができました。
テストはマジ偉大。プログラム理解してなくても修正できたかどうかがわかります。