From a475df2f25acd975c711ed538148b5da02181809 Mon Sep 17 00:00:00 2001 From: naruse Date: Mon, 13 Jun 2016 12:34:06 +0000 Subject: * thread.c (debug_deadlock_check): show thread lock dependency and backtrace [Feature #8214] [ruby-dev:47217] * thread.c (thread_status_name): show "sleep_forever" instead of "sleep" if called from inspect. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55397 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 8 ++++++++ NEWS | 2 ++ thread.c | 46 +++++++++++++++++++++++++++++++++------------- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index f7487a30b9..bf84b77c4d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Mon Jun 13 21:09:40 2016 NARUSE, Yui + + * thread.c (debug_deadlock_check): show thread lock dependency and + backtrace [Feature #8214] [ruby-dev:47217] + + * thread.c (thread_status_name): show "sleep_forever" instead of + "sleep" if called from inspect. + Mon Jun 13 20:50:07 2016 Nobuyoshi Nakada * parse.y (reg_named_capture_assign_iter): remove named capture diff --git a/NEWS b/NEWS index a8d868bdb0..673ad1577b 100644 --- a/NEWS +++ b/NEWS @@ -167,3 +167,5 @@ with all sufficient information, see the ChangeLog file or Redmine be <= 0x100, and Array#max and min must not be redefined. It will work in most casual and real-life use case where it is written with intent to `Math.max(x, y)`. + +* Thread deadlock detection now shows their backtrace and dependency. [Feature #8214] diff --git a/thread.c b/thread.c index 7a157639ac..bc573e596d 100644 --- a/thread.c +++ b/thread.c @@ -2737,7 +2737,7 @@ rb_thread_group(VALUE thread) } static const char * -thread_status_name(rb_thread_t *th) +thread_status_name(rb_thread_t *th, int detail) { switch (th->status) { case THREAD_RUNNABLE: @@ -2745,8 +2745,9 @@ thread_status_name(rb_thread_t *th) return "aborting"; else return "run"; - case THREAD_STOPPED: case THREAD_STOPPED_FOREVER: + if (detail) return "sleep_forever"; + case THREAD_STOPPED: return "sleep"; case THREAD_KILLED: return "dead"; @@ -2806,7 +2807,7 @@ rb_thread_status(VALUE thread) } return Qfalse; } - return rb_str_new2(thread_status_name(th)); + return rb_str_new2(thread_status_name(th, FALSE)); } @@ -2952,7 +2953,7 @@ rb_thread_inspect(VALUE thread) VALUE str; GetThreadPtr(thread, th); - status = thread_status_name(th); + status = thread_status_name(th, TRUE); str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread); if (!NIL_P(th->name)) { rb_str_catf(str, "@%"PRIsVALUE, th->name); @@ -4850,27 +4851,46 @@ ruby_native_thread_p(void) return th != 0; } +VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, long lev, long n); static void -debug_deadlock_check(rb_vm_t *vm) +debug_deadlock_check(rb_vm_t *vm, VALUE msg) { -#ifdef DEBUG_DEADLOCK_CHECK rb_thread_t *th = 0; + VALUE sep = rb_str_new_cstr("\n "); - printf("%d %d %p %p\n", vm_living_thread_num(vm), vm->sleeper, GET_THREAD(), vm->main_thread); + 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); list_for_each(&vm->living_threads, th, vmlt_node) { - printf("th:%p %d %d", th, th->status, th->interrupt_flag); if (th->locking_mutex) { rb_mutex_t *mutex; + struct rb_thread_struct volatile *mth; + int waiting; GetMutexPtr(th->locking_mutex, mutex); native_mutex_lock(&mutex->lock); - printf(" %p %d\n", mutex->th, mutex->cond_waiting); + mth = mutex->th; + waiting = mutex->cond_waiting; native_mutex_unlock(&mutex->lock); + 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); } - else - puts(""); + 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"); } -#endif } static void @@ -4905,7 +4925,7 @@ rb_check_deadlock(rb_vm_t *vm) VALUE argv[2]; argv[0] = rb_eFatal; argv[1] = rb_str_new2("No live threads left. Deadlock?"); - debug_deadlock_check(vm); + debug_deadlock_check(vm, argv[1]); vm->sleeper--; rb_threadptr_raise(vm->main_thread, 2, argv); } -- cgit v1.2.3