https://bugs.ruby-lang.org/
https://bugs.ruby-lang.org/favicon.ico?1711330511
2013-04-19T15:31:15Z
Ruby Issue Tracking System
Ruby master - Feature #8214: デッドロックチェックに全スレッドのバックトレースダンプの追加
https://bugs.ruby-lang.org/issues/8214?journal_id=38737
2013-04-19T15:31:15Z
nobu (Nobuyoshi Nakada)
nobu@ruby-lang.org
<ul><li><strong>Subject</strong> changed from <i>デッドロックチェックに前スレッドのバックトレースダンプの追加</i> to <i>デッドロックチェックに全スレッドのバックトレースダンプの追加</i></li><li><strong>Description</strong> updated (<a title="View differences" href="/journals/38737/diff?detail_id=27971">diff</a>)</li></ul>
Ruby master - Feature #8214: デッドロックチェックに全スレッドのバックトレースダンプの追加
https://bugs.ruby-lang.org/issues/8214?journal_id=59005
2016-06-01T16:10:45Z
naruse (Yui NARUSE)
naruse@airemix.jp
<ul></ul><p>とりあえず以下のような感じで作ってみました。<br>
やっぱりバックトレースも欲しいかな</p>
<pre><code>% ./miniruby -ve'Thread.current.name="MainThread!";z=Thread.new{Thread.stop};a,b=Thread.new{1until b;b.join},Thread.new{1until a;a.join};a.name="aaaaa";b.name="bbbbb";z.name="zzzz";a.join'
`miniruby' is up to date.
ruby 2.4.0dev (2016-06-01 trunk 55248) [x86_64-freebsd10.3]
-e:1:in `join': No live threads left. Deadlock? (fatal)
4 threads, 4 sleeps current:0x00000802c1b200 main thread:0x00000802c17300
* #<Thread:0x00000802d8ce58@MainThread! sleep_forever>
rb_thread_t:0x00000802c17300 native:0x00000802c06400 int:1
* #<Thread:0x00000802d7f780@zzzz@-e:1 sleep_forever>
rb_thread_t:0x00000802c1ac00 native:0x00000802c07800 int:0
* #<Thread:0x00000802d7f690@aaaaa@-e:1 sleep_forever>
rb_thread_t:0x00000802c1af00 native:0x00000802c07c00 int:0
depended by: tb_thread_id:0x00000802c1b200
depended by: tb_thread_id:0x00000802c17300
* #<Thread:0x00000802d7f5a0@bbbbb@-e:1 sleep_forever>
rb_thread_t:0x00000802c1b200 native:0x00000802c08000 int:0
depended by: tb_thread_id:0x00000802c1af00
from -e:1:in `<main>'
</code></pre>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/thread.c b/thread.c
index d17b663..359dada 100644
</span><span class="gd">--- a/thread.c
</span><span class="gi">+++ b/thread.c
</span><span class="p">@@ -2618,7 +2618,7 @@</span> rb_thread_group(VALUE thread)
}
static const char *
<span class="gd">-thread_status_name(rb_thread_t *th)
</span><span class="gi">+thread_status_name(rb_thread_t *th, int detail)
</span> {
switch (th->status) {
case THREAD_RUNNABLE:
<span class="p">@@ -2626,8 +2626,9 @@</span> thread_status_name(rb_thread_t *th)
return "aborting";
else
return "run";
<span class="gd">- case THREAD_STOPPED:
</span> case THREAD_STOPPED_FOREVER:
<span class="gi">+ if (detail) return "sleep_forever";
+ case THREAD_STOPPED:
</span> return "sleep";
case THREAD_KILLED:
return "dead";
<span class="p">@@ -2687,7 +2688,7 @@</span> rb_thread_status(VALUE thread)
}
return Qfalse;
}
<span class="gd">- return rb_str_new2(thread_status_name(th));
</span><span class="gi">+ return rb_str_new2(thread_status_name(th, FALSE));
</span> }
<span class="p">@@ -2833,7 +2834,7 @@</span> rb_thread_inspect(VALUE thread)
VALUE str;
GetThreadPtr(thread, th);
<span class="gd">- status = thread_status_name(th);
</span><span class="gi">+ status = thread_status_name(th, FALSE);
</span> str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
if (!NIL_P(th->name)) {
rb_str_catf(str, "@%"PRIsVALUE, th->name);
<span class="p">@@ -4728,26 +4729,36 @@</span> ruby_native_thread_p(void)
}
static void
<span class="gd">-debug_deadlock_check(rb_vm_t *vm)
</span><span class="gi">+debug_deadlock_check(rb_vm_t *vm, VALUE msg)
</span> {
<span class="gd">-#ifdef DEBUG_DEADLOCK_CHECK
</span> rb_thread_t *th = 0;
<span class="gd">- printf("%d %d %p %p\n", vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
</span><span class="gi">+ rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
+ vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
</span> list_for_each(&vm->living_threads, th, vmlt_node) {
<span class="gd">- printf("th:%p %d %d", th, th->status, th->interrupt_flag);
</span> if (th->locking_mutex) {
rb_mutex_t *mutex;
<span class="gi">+ struct rb_thread_struct volatile *mth;
+ int waiting;
</span> GetMutexPtr(th->locking_mutex, mutex);
native_mutex_lock(&mutex->lock);
<span class="gd">- printf(" %p %d\n", mutex->th, mutex->cond_waiting);
</span><span class="gi">+ mth = mutex->th;
+ waiting = mutex->cond_waiting;
</span> native_mutex_unlock(&mutex->lock);
<span class="gi">+ rb_str_catf(msg, " rb_thread_t:%p %s %u waiting:%p cond_waiting:%d", th, thread_status_name(th, TRUE), th->interrupt_flag, mth, waiting);
+ }
+ else {
+ rb_str_catf(msg, " rb_thread_t:%p %s %u\n", th, thread_status_name(th, TRUE), th->interrupt_flag);
+ }
+ {
+ rb_thread_list_t *list = th->join_list;
+ while (list) {
+ rb_str_catf(msg, " depended by: %p\n", list->th);
+ list = list->next;
+ }
</span> }
<span class="gd">- else
- puts("");
</span> }
<span class="gd">-#endif
</span> }
static void
<span class="p">@@ -4782,7 +4793,7 @@</span> rb_check_deadlock(rb_vm_t *vm)
VALUE argv[2];
argv[0] = rb_eFatal;
argv[1] = rb_str_new2("No live threads left. Deadlock?");
<span class="gd">- debug_deadlock_check(vm);
</span><span class="gi">+ debug_deadlock_check(vm, argv[1]);
</span> vm->sleeper--;
rb_threadptr_raise(vm->main_thread, 2, argv);
}
</code></pre>
Ruby master - Feature #8214: デッドロックチェックに全スレッドのバックトレースダンプの追加
https://bugs.ruby-lang.org/issues/8214?journal_id=59006
2016-06-01T16:54:17Z
naruse (Yui NARUSE)
naruse@airemix.jp
<ul></ul><p>バックトレースも欲しいらしいので</p>
<pre><code>% ./miniruby -ve'Thread.current.name="MainThread!";z=Thread.new{Thread.stop};a,b=Thread.new{1until b;b.join},Thread.new{1until a;a.join};a.name="aaaaa";b.name="bbbbb";z.name="zzzz";a.join'
--- thread.o ---
compiling ../../ruby/thread.c
--- miniruby ---
linking miniruby
ruby 2.4.0dev (2016-06-01 trunk 55248) [x86_64-freebsd10.3]
-e:1:in `join': No live threads left. Deadlock? (fatal)
4 threads, 4 sleeps current:0x00000802c1b200 main thread:0x00000802c17300
* #<Thread:0x00000802d8ce58@MainThread! sleep_forever>
rb_thread_t:0x00000802c17300 native:0x00000802c06400 int:1
-e:1:in `join'
-e:1:in `<main>'
* #<Thread:0x00000802d7f780@zzzz@-e:1 sleep_forever>
rb_thread_t:0x00000802c1ac00 native:0x00000802c07800 int:0
-e:1:in `stop'
-e:1:in `block in <main>'
* #<Thread:0x00000802d7f690@aaaaa@-e:1 sleep_forever>
rb_thread_t:0x00000802c1af00 native:0x00000802c07c00 int:0
depended by: tb_thread_id:0x00000802c1b200
depended by: tb_thread_id:0x00000802c17300
-e:1:in `join'
-e:1:in `block in <main>'
* #<Thread:0x00000802d7f5a0@bbbbb@-e:1 sleep_forever>
rb_thread_t:0x00000802c1b200 native:0x00000802c08000 int:0
depended by: tb_thread_id:0x00000802c1af00
-e:1:in `join'
-e:1:in `block in <main>'
from -e:1:in `<main>'
</code></pre>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gh">diff --git a/thread.c b/thread.c
index d17b663..84f76d1 100644
</span><span class="gd">--- a/thread.c
</span><span class="gi">+++ b/thread.c
</span><span class="p">@@ -2618,7 +2618,7 @@</span> rb_thread_group(VALUE thread)
}
static const char *
<span class="gd">-thread_status_name(rb_thread_t *th)
</span><span class="gi">+thread_status_name(rb_thread_t *th, int detail)
</span> {
switch (th->status) {
case THREAD_RUNNABLE:
<span class="p">@@ -2626,8 +2626,9 @@</span> thread_status_name(rb_thread_t *th)
return "aborting";
else
return "run";
<span class="gd">- case THREAD_STOPPED:
</span> case THREAD_STOPPED_FOREVER:
<span class="gi">+ if (detail) return "sleep_forever";
+ case THREAD_STOPPED:
</span> return "sleep";
case THREAD_KILLED:
return "dead";
<span class="p">@@ -2687,7 +2688,7 @@</span> rb_thread_status(VALUE thread)
}
return Qfalse;
}
<span class="gd">- return rb_str_new2(thread_status_name(th));
</span><span class="gi">+ return rb_str_new2(thread_status_name(th, FALSE));
</span> }
<span class="p">@@ -2833,7 +2834,7 @@</span> rb_thread_inspect(VALUE thread)
VALUE str;
GetThreadPtr(thread, th);
<span class="gd">- status = thread_status_name(th);
</span><span class="gi">+ status = thread_status_name(th, TRUE);
</span> str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
if (!NIL_P(th->name)) {
rb_str_catf(str, "@%"PRIsVALUE, th->name);
<span class="p">@@ -4727,27 +4728,46 @@</span> ruby_native_thread_p(void)
return th != 0;
}
<span class="gi">+VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, long lev, long n);
</span> static void
<span class="gd">-debug_deadlock_check(rb_vm_t *vm)
</span><span class="gi">+debug_deadlock_check(rb_vm_t *vm, VALUE msg)
</span> {
<span class="gd">-#ifdef DEBUG_DEADLOCK_CHECK
</span> rb_thread_t *th = 0;
<span class="gi">+ VALUE sep = rb_str_new_cstr("\n ");
</span>
<span class="gd">- printf("%d %d %p %p\n", vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
</span><span class="gi">+ rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
+ vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread);
</span> list_for_each(&vm->living_threads, th, vmlt_node) {
<span class="gd">- printf("th:%p %d %d", th, th->status, th->interrupt_flag);
</span> if (th->locking_mutex) {
rb_mutex_t *mutex;
<span class="gi">+ struct rb_thread_struct volatile *mth;
+ int waiting;
</span> GetMutexPtr(th->locking_mutex, mutex);
native_mutex_lock(&mutex->lock);
<span class="gd">- printf(" %p %d\n", mutex->th, mutex->cond_waiting);
</span><span class="gi">+ mth = mutex->th;
+ waiting = mutex->cond_waiting;
</span> native_mutex_unlock(&mutex->lock);
<span class="gi">+ rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p native:%p int:%u mutex:%p cond:%d\n",
+ th->self, th, th->thread_id,
+ th->interrupt_flag, mth, waiting);
</span> }
<span class="gd">- else
- puts("");
</span><span class="gi">+ else {
+ rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p native:%p int:%u\n",
+ th->self, th, th->thread_id,
+ th->interrupt_flag);
+ }
+ {
+ rb_thread_list_t *list = th->join_list;
+ while (list) {
+ rb_str_catf(msg, " depended by: tb_thread_id:%p\n", list->th);
+ list = list->next;
+ }
+ }
+ rb_str_catf(msg, " ");
+ rb_str_concat(msg, rb_ary_join(rb_vm_backtrace_str_ary(th, 0, 0), sep));
+ rb_str_catf(msg, "\n");
</span> }
<span class="gd">-#endif
</span> }
static void
<span class="p">@@ -4782,7 +4802,7 @@</span> rb_check_deadlock(rb_vm_t *vm)
VALUE argv[2];
argv[0] = rb_eFatal;
argv[1] = rb_str_new2("No live threads left. Deadlock?");
<span class="gd">- debug_deadlock_check(vm);
</span><span class="gi">+ debug_deadlock_check(vm, argv[1]);
</span> vm->sleeper--;
rb_threadptr_raise(vm->main_thread, 2, argv);
}
</code></pre>
Ruby master - Feature #8214: デッドロックチェックに全スレッドのバックトレースダンプの追加
https://bugs.ruby-lang.org/issues/8214?journal_id=59007
2016-06-02T04:53:19Z
tagomoris (Satoshi Tagomori)
tagomoris@gmail.com
<ul></ul><p>デッドロック関連のデバッグであれば A -> B の順にロックを取ろうとしているスレッドと B -> A の順に<br>
ロックを取ろうとしているスレッドが両方いる、という事実が重要だと思うので、バックトレースは極めて重要だと考えます。</p>
Ruby master - Feature #8214: デッドロックチェックに全スレッドのバックトレースダンプの追加
https://bugs.ruby-lang.org/issues/8214?journal_id=59183
2016-06-13T09:13:13Z
shyouhei (Shyouhei Urabe)
shyouhei@ruby-lang.org
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>naruse (Yui NARUSE)</i></li></ul>
Ruby master - Feature #8214: デッドロックチェックに全スレッドのバックトレースダンプの追加
https://bugs.ruby-lang.org/issues/8214?journal_id=59191
2016-06-13T12:34:11Z
naruse (Yui NARUSE)
naruse@airemix.jp
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Closed</i></li></ul><p>Applied in changeset r55397.</p>
<hr>
<ul>
<li>
<p>thread.c (debug_deadlock_check): show thread lock dependency and<br>
backtrace [Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: デッドロックチェックに全スレッドのバックトレースダンプの追加 (Closed)" href="https://bugs.ruby-lang.org/issues/8214">#8214</a>] <a href="/issues/8214">[ruby-dev:47217]</a></p>
</li>
<li>
<p>thread.c (thread_status_name): show "sleep_forever" instead of<br>
"sleep" if called from inspect.</p>
</li>
</ul>