きりかノート 3冊め

おあそびプログラミング

テーブルビューのスクロールで落ちることがある件の調査

もうずいぶん前に報告もらってるんだけど未解決。再現することは確認したのでもっと早く直せると思ったのですが手こずっています。

前のメモが見つからなかったので今日の作業を記録。

現象

  • NSTableViewをスクロールしているとEXC_BAD_ACCESSで落ちる
  • 必ず起きるという手順は見つかっていないが、スクロールを繰り返すとほぼ再現する

調査

デバッグシンボル付きのRubyCocoaでやってみる。落ちたときのログはこんな感じ。

   Exception Type:  EXC_BAD_ACCESS (SIGABRT)
   Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000d29

   Application Specific Information:
   objc[29757]: garbage collection is OFF
   abort() called

   Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
   0   libsystem_kernel.dylib              0x00007fff95ecfce2 __pthread_kill + 10
   1   libsystem_c.dylib                   0x00007fff947e87d2 pthread_kill + 95
   2   libsystem_c.dylib                   0x00007fff947d9a7a abort + 143
   3   libruby.1.dylib                     0x0000000108c9c649 rb_bug + 316
   4   libruby.1.dylib                     0x0000000108d03d8c 0x108c89000 + 503180
   5   libsystem_c.dylib                   0x00007fff9483acfa _sigtramp + 26
   6   libruby.1.dylib                     0x0000000108cd046a rb_obj_is_kind_of + 175
   7   com.apple.rubycocoa                 0x0000000108c55fcb ovmix_ffi_closure + 491 (OverrideMixin.m:143)
   8   libffi.dylib                        0x00007fff9746c7ea ffi_closure_unix64_inner + 510
   9   libffi.dylib                        0x00007fff9746bffe ffi_closure_unix64 + 70
   10  com.apple.AppKit                    0x00007fff8f2fe11b -[NSTableView _dataSourceValueForColumn:row:] + 73
   11  com.apple.AppKit                    0x00007fff8f2fdb9b -[NSTableView preparedCellAtColumn:row:] + 437

RubyCocoaのコードはframework/src/objc/OverrideMixin.mのovmix_ffi_closure()のrb_obj_is_kind_of()が問題のよう。

   138     if (!ocdata_to_rbobj(Qnil, args_octypes[i - 2], args[i], &arg, NO))
   139       rb_raise(rb_eRuntimeError, "Can't convert Objective-C argument #%d
        of octype '%s' to Ruby value", i - 2, args_octypes[i - 2]);
   140
   141     OVMIX_LOG("converted arg #%d of type '%s' to Ruby value %p", i - 2,
        args _octypes[i - 2], arg);
   142
  *143*    if (!NIL_P(arg)
   144         && rb_obj_is_kind_of(arg, objid_s_class()) == Qtrue
   145         && !OBJCID_DATA_PTR(arg)->retained) {
   146             OVMIX_LOG("retaining %p", OBJCID_ID(arg));
   147       [OBJCID_ID(arg) retain];
   148       OBJCID_DATA_PTR(arg)->retained = YES;
   149       OBJCID_DATA_PTR(arg)->can_be_released = YES;
   150     }

argはVALUEで、手前でocdata_to_rbobj()で用意した値。さすがにrb_obj_is_kind_of()/CLASS_OF()に問題があるとは思えないので、このocdata_to_rbobj()で取ってきたVALUEがおかしいってことなんだと思う。となると、

  • キャッシュから取ってきたVALUEが開放または壊れてしまっている
  • 新規につくったVALUEがここに来る前に開放されてしまっている

あたりかなと考えている。可能性が高そうなのは前者だけど、どうやって調べたものか。