Bug #7032

TestSetTraceFuncの実行に失敗する

Added by Kazuki Tsujimoto over 1 year ago. Updated over 1 year ago.

[ruby-dev:46141]
Status:Closed
Priority:Normal
Assignee:-
Category:-
Target version:-
ruby -v:ruby 2.0.0dev (2012-09-16 trunk 36980) [x86_64-linux] Backport:

Description

=begin
辻本です。

以下のようにテストを実行すると、まれにTestSetTraceFuncの実行に失敗することがあります。

$ make TESTS='-vq -j1 objspace/testobjspace.rb \
json/test
jsonfixtures.rb \
json/test
json.rb \
xmlrpc/testmarshal.rb \
xmlrpc/test
features.rb \
uri/testparser.rb \
uri/test
http.rb \
uri/testftp.rb \
uri/test
generic.rb \
io/console/testioconsole.rb \
rexml/teststream.rb \
rexml/test
light.rb \
rexml/testxpathtext.rb \
rexml/test
xpathpred.rb \
rexml/test
rexmlissuezilla.rb \
ruby/test
case.rb \
ruby/testhash.rb \
ruby/test
marshal.rb \
ruby/testproc.rb \
ruby/test
yield.rb \
ruby/teststring.rb \
ruby/test
settracefunc.rb' test-all

失敗するケースは2つ確認していて、
1つめがRubyレベルで失敗するもの:

TestSetTraceFunc#testbreak = 0.00 s = .
TestSetTraceFunc#test
ccall = 0.00 s = .
TestSetTraceFunc#test
call = 0.00 s = .
TestSetTraceFunc#testclass = 0.00 s = .
TestSetTraceFunc#test
invalidproc = 0.00 s = .
TestSetTraceFunc#test
raise = 0.00 s = .
TestSetTraceFunc#testraiseintrace = 0.00 s = .
TestSetTraceFunc#test
removeintrace = 0.00 s = .
TestSetTraceFunc#testreturn = 0.00 s = .
TestSetTraceFunc#test
return2 = 0.00 s = .
/home/ktsj/work/ruby/lib/test/unit/parallel.rb:40:in rescue in block in _run_suite': undefined local variable or methodlocalvar' for #Test::Unit::Worker:0x002aaaaaf8a5a0 (NameError)
xyzzy:2:in eval'
xyzzy:2:in
block in tracebytracepoint'
/home/ktsj/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#testbreak = 0.00 s = .
TestSetTraceFunc#test
ccall = 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/ktsj/work/ruby/lib/test/unit/parallel.rb:37:in `block in _runsuite'
=end

backtrace.txt Magnifier (3.09 KB) Kazuki Tsujimoto, 09/16/2012 04:08 PM

Associated revisions

Revision 37280
Added by Kazuki Tsujimoto over 1 year ago

  • vmcore.h (rbvmt::tracerunning): add a new field
    `trace_running' to store vm global tracing status.

  • vmtrace.c: fix SEGV bug. eventhook was free'd
    even when the hook is still used in another thread.
    [Bug #7032]

History

#1 Updated by Kazuki Tsujimoto over 1 year ago

=begin
SEGVの問題について調べてみました。

手元の環境では以下のコードで100%再現します。

# Thread(A)
th = Thread.new { loop { 0 } }

# Thread(B)
loop do
# testremoveintrace
func = lambda do |e, f, l, i, b, k|
set
tracefunc(nil)
eval("self", b) # allocate many objects
end
set
trace_func(func)
end

この際の流れは次のようになっているようです。

(1) Thread(B)でsettracefunc(nil)されhookにRUBYHOOKFLAGDELETEDフラグが立つ
(2) 直後にThread(A)に切り替わると、(Thread(A)のtrace
runningは0のままなので)exechooksが実行されてcleanhookされる
(3) hookと同一アドレスに新しいオブジェクトが生成される
(4) Thread(B)に戻るとhookが無効な値になっているため、参照すると落ちる

試しにフラグを立てたスレッドに限って解放処理を行うようにしたところ落ちなくなりました。

diff --git a/vmtrace.c b/vmtrace.c
index 90c2bd2..1393e89 100644
--- a/vmtrace.c
+++ b/vm
trace.c
@@ -41,6 +41,7 @@ typedef struct rbeventhookstruct {
rb
eventflagt events;
rbeventhookfunct func;
VALUE data;
+ rbthreadidt deletedthreadid;
struct rb
eventhookstruct *next;
} rbeventhook_t;

@@ -183,11 +184,13 @@ removeeventhook(rbhooklistt *list, rbeventhookfunct func, VALUE data)
{
int ret = 0;
rb
eventhookt *hook = list->hooks;
+ rbthreadt *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->deletedthreadid = th->threadid; ret+=1; list->needclean++; } @@ -250,12 +253,13 @@ static void cleanhooks(rbhooklistt list) { rbeventhook_t *hook, *nextp = &list->hooks;
  • rbthreadt *th = GET_THREAD();

    list->events = 0;
    list->need_clean = 0;

    while ((hook = *nextp) != 0) {

  • if (hook->hookflags & RUBYHOOKFLAGDELETED) {

  • if (hook->hookflags & RUBYHOOKFLAGDELETED && hook->deletedthreadid == th->threadid) {
    *nextp = hook->next;
    recalc
    removerubyvmeventflags(hook->events);
    xfree(hook);
    =end

#2 Updated by Kazuki Tsujimoto over 1 year ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r37280.
Kazuki, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • vmcore.h (rbvmt::tracerunning): add a new field
    `trace_running' to store vm global tracing status.

  • vmtrace.c: fix SEGV bug. eventhook was free'd
    even when the hook is still used in another thread.
    [Bug #7032]

#3 Updated by Kazuki Tsujimoto over 1 year ago

=begin
SEGVする問題については、すでにトレースが実行されている場合は
clean_hooksしないという形で修正しました。

"undefined local variable or method"となる問題はtrunkで
再現できないのでこのチケットはクローズします。
再発するようであれば新しくチケットを作りたいと思います。
=end

#4 Updated by Koichi Sasada over 1 year ago

ありがとうございます.
この辺,考えていたんですが,

(1) event_hooks(イベントのリスト)は thread ごとではなく,
VM 1つだけに持たせる
 (Thread ごとにフィルタするために,イベントにフィルタを設ける)

(2) event_hooks は thread ごとにもたせ,グローバルなモノは外す
 (グローバルなイベントは,全スレッドにコピー)

のどちらかにしようかなぁ,と考えています.

実は,誰も Thread#settracefunc なんて使っていないような気がするので,
(1) で十分ではないか,と.

実際のところ,どうなんでしょうね?

(2012/10/21 19:24), ktsj (Kazuki Tsujimoto) wrote:

Issue #7032 has been updated by ktsj (Kazuki Tsujimoto).

=begin
SEGVする問題については、すでにトレースが実行されている場合は
clean_hooksしないという形で修正しました。

"undefined local variable or method"となる問題はtrunkで
再現できないのでこのチケットはクローズします。
再発するようであれば新しくチケットを作りたいと思います。
=end


Bug #7032: TestSetTraceFuncの実行に失敗する
https://bugs.ruby-lang.org/issues/7032#change-31126

Author: ktsj (Kazuki Tsujimoto)
Status: Closed
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/testobjspace.rb \
json/test
jsonfixtures.rb \
json/test
json.rb \
xmlrpc/testmarshal.rb \
xmlrpc/test
features.rb \
uri/testparser.rb \
uri/test
http.rb \
uri/testftp.rb \
uri/test
generic.rb \
io/console/testioconsole.rb \
rexml/teststream.rb \
rexml/test
light.rb \
rexml/testxpathtext.rb \
rexml/test
xpathpred.rb \
rexml/test
rexmlissuezilla.rb \
ruby/test
case.rb \
ruby/testhash.rb \
ruby/test
marshal.rb \
ruby/testproc.rb \
ruby/test
yield.rb \
ruby/teststring.rb \
ruby/test
settracefunc.rb' test-all

失敗するケースは2つ確認していて、
1つめがRubyレベルで失敗するもの:

TestSetTraceFunc#testbreak = 0.00 s = .
TestSetTraceFunc#test
ccall = 0.00 s = .
TestSetTraceFunc#test
call = 0.00 s = .
TestSetTraceFunc#testclass = 0.00 s = .
TestSetTraceFunc#test
invalidproc = 0.00 s = .
TestSetTraceFunc#test
raise = 0.00 s = .
TestSetTraceFunc#testraiseintrace = 0.00 s = .
TestSetTraceFunc#test
removeintrace = 0.00 s = .
TestSetTraceFunc#testreturn = 0.00 s = .
TestSetTraceFunc#test
return2 = 0.00 s = .
/home/ktsj/work/ruby/lib/test/unit/parallel.rb:40:in rescue in block in _run_suite': undefined local variable or methodlocalvar' for #Test::Unit::Worker:0x002aaaaaf8a5a0 (NameError)
xyzzy:2:in eval'
xyzzy:2:in
block in tracebytracepoint'
/home/ktsj/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#testbreak = 0.00 s = .
TestSetTraceFunc#test
ccall = 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/ktsj/work/ruby/lib/test/unit/parallel.rb:37:in `block in _runsuite'
=end

--
// SASADA Koichi at atdot dot net

Also available in: Atom PDF