From: "ktsj (Kazuki Tsujimoto)" Date: 2012-10-14T09:34:12+09:00 Subject: [ruby-dev:46218] [ruby-trunk - Bug #7032] TestSetTraceFuncの実行に失敗する Issue #7032 has been updated by ktsj (Kazuki Tsujimoto). =begin SEGVの問題について調べてみました。 手元の環境では以下のコードで100%再現します。 # Thread(A) th = Thread.new { loop { 0 } } # Thread(B) loop do # test_remove_in_trace func = lambda do |e, f, l, i, b, k| set_trace_func(nil) eval("self", b) # allocate many objects end set_trace_func(func) end この際の流れは次のようになっているようです。 (1) Thread(B)でset_trace_func(nil)されhookにRUBY_HOOK_FLAG_DELETEDフラグが立つ (2) 直後にThread(A)に切り替わると、(Thread(A)のtrace_runningは0のままなので)exec_hooksが実行されてclean_hookされる (3) hookと同一アドレスに新しいオブジェクトが生成される (4) Thread(B)に戻るとhookが無効な値になっているため、参照すると落ちる 試しにフラグを立てたスレッドに限って解放処理を行うようにしたところ落ちなくなりました。 diff --git a/vm_trace.c b/vm_trace.c index 90c2bd2..1393e89 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -41,6 +41,7 @@ typedef struct rb_event_hook_struct { rb_event_flag_t events; rb_event_hook_func_t func; VALUE data; + rb_thread_id_t deleted_thread_id; struct rb_event_hook_struct *next; } rb_event_hook_t; @@ -183,11 +184,13 @@ remove_event_hook(rb_hook_list_t *list, rb_event_hook_func_t func, VALUE data) { int ret = 0; rb_event_hook_t *hook = list->hooks; + rb_thread_t *th = GET_THREAD(); while (hook) { if (func == 0 || hook->func == func) { if (data == Qundef || hook->data == data) { hook->hook_flags |= RUBY_HOOK_FLAG_DELETED; + hook->deleted_thread_id = th->thread_id; ret+=1; list->need_clean++; } @@ -250,12 +253,13 @@ static void clean_hooks(rb_hook_list_t *list) { rb_event_hook_t *hook, **nextp = &list->hooks; + rb_thread_t *th = GET_THREAD(); list->events = 0; list->need_clean = 0; while ((hook = *nextp) != 0) { - if (hook->hook_flags & RUBY_HOOK_FLAG_DELETED) { + if (hook->hook_flags & RUBY_HOOK_FLAG_DELETED && hook->deleted_thread_id == th->thread_id) { *nextp = hook->next; recalc_remove_ruby_vm_event_flags(hook->events); xfree(hook); =end ---------------------------------------- Bug #7032: TestSetTraceFuncの実行に失敗する https://bugs.ruby-lang.org/issues/7032#change-30588 Author: ktsj (Kazuki Tsujimoto) Status: Open Priority: Normal Assignee: Category: Target version: ruby -v: ruby 2.0.0dev (2012-09-16 trunk 36980) [x86_64-linux] =begin 辻本です。 以下のようにテストを実行すると、まれにTestSetTraceFuncの実行に失敗することがあります。 $ make TESTS='-vq -j1 objspace/test_objspace.rb \ json/test_json_fixtures.rb \ json/test_json.rb \ xmlrpc/test_marshal.rb \ xmlrpc/test_features.rb \ uri/test_parser.rb \ uri/test_http.rb \ uri/test_ftp.rb \ uri/test_generic.rb \ io/console/test_io_console.rb \ rexml/test_stream.rb \ rexml/test_light.rb \ rexml/test_xpathtext.rb \ rexml/test_xpath_pred.rb \ rexml/test_rexml_issuezilla.rb \ ruby/test_case.rb \ ruby/test_hash.rb \ ruby/test_marshal.rb \ ruby/test_proc.rb \ ruby/test_yield.rb \ ruby/test_string.rb \ ruby/test_settracefunc.rb' test-all 失敗するケースは2つ確認していて、 1つめがRubyレベルで失敗するもの: TestSetTraceFunc#test_break = 0.00 s = . TestSetTraceFunc#test_c_call = 0.00 s = . TestSetTraceFunc#test_call = 0.00 s = . TestSetTraceFunc#test_class = 0.00 s = . TestSetTraceFunc#test_invalid_proc = 0.00 s = . TestSetTraceFunc#test_raise = 0.00 s = . TestSetTraceFunc#test_raise_in_trace = 0.00 s = . TestSetTraceFunc#test_remove_in_trace = 0.00 s = . TestSetTraceFunc#test_return = 0.00 s = . TestSetTraceFunc#test_return2 = 0.00 s = . /home/k_tsj/work/ruby/lib/test/unit/parallel.rb:40:in `rescue in block in _run_suite': undefined local variable or method `local_var' for # (NameError) xyzzy:2:in `eval' xyzzy:2:in `block in trace_by_tracepoint' /home/k_tsj/work/ruby/lib/test/unit/parallel.rb:40:in `rescue in block in _run_suite' /home/k_tsj/work/ruby/lib/test/unit/parallel.rb:36:in `block in _run_suite' 2つめがSEGVを起こすものになります。 TestSetTraceFunc#test_break = 0.00 s = . TestSetTraceFunc#test_c_call = 0.00 s = . /home/k_tsj/work/ruby/lib/test/unit/parallel.rb:37: [BUG] Segmentation fault ruby 2.0.0dev (2012-09-16 trunk 36980) [x86_64-linux] -- Control frame information ----------------------------------------------- c:0002 p:0045 s:0006 b:0005 e:000e40 BLOCK /home/k_tsj/work/ruby/lib/test/unit/parallel.rb:37 [FINISH] c:0001 p:---- s:0002 b:0002 e:000001 TOP [FINISH] /home/k_tsj/work/ruby/lib/test/unit/parallel.rb:37:in `block in _run_suite' =end -- http://bugs.ruby-lang.org/