ruby-changes:43323
From: naruse <ko1@a...>
Date: Mon, 13 Jun 2016 21:34:11 +0900 (JST)
Subject: [ruby-changes:43323] naruse:r55397 (trunk): * thread.c (debug_deadlock_check): show thread lock dependency and
naruse 2016-06-13 21:34:06 +0900 (Mon, 13 Jun 2016) New Revision: 55397 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=55397 Log: * 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. Modified files: trunk/ChangeLog trunk/NEWS trunk/thread.c Index: NEWS =================================================================== --- NEWS (revision 55396) +++ NEWS (revision 55397) @@ -167,3 +167,5 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L167 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] Index: thread.c =================================================================== --- thread.c (revision 55396) +++ thread.c (revision 55397) @@ -2737,7 +2737,7 @@ rb_thread_group(VALUE thread) https://github.com/ruby/ruby/blob/trunk/thread.c#L2737 } 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) https://github.com/ruby/ruby/blob/trunk/thread.c#L2745 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) https://github.com/ruby/ruby/blob/trunk/thread.c#L2807 } 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) https://github.com/ruby/ruby/blob/trunk/thread.c#L2953 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) https://github.com/ruby/ruby/blob/trunk/thread.c#L4851 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) https://github.com/ruby/ruby/blob/trunk/thread.c#L4925 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); } Index: ChangeLog =================================================================== --- ChangeLog (revision 55396) +++ ChangeLog (revision 55397) @@ -1,3 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Mon Jun 13 21:09:40 2016 NARUSE, Yui <naruse@r...> + + * 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 <nobu@r...> * parse.y (reg_named_capture_assign_iter): remove named capture -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/