From edb80dfe3e6086fb9d5d905a40cdd6ec61a2e1ee Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Thu, 5 Dec 2019 14:39:03 +0900 Subject: [PATCH] add additional CF info for CI env Introduce new RUBY_DEBUG option 'ci' to inform Ruby interpreter that an interpreter is running on CI environment. With this option, `rb_bug()` shows more information includes method entry information, local variables information for each control frame. --- debug.c | 2 ++ gc.c | 8 +++--- test/ruby/test_rubyoptions.rb | 2 +- vm_dump.c | 50 ++++++++++++++++++++++++++++++----- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/debug.c b/debug.c index e4ad163ad1..a54be27152 100644 --- a/debug.c +++ b/debug.c @@ -147,6 +147,7 @@ extern int ruby_w32_rtc_error; UINT ruby_w32_codepage[2]; #endif extern int ruby_rgengc_debug; +extern int ruby_on_ci; int ruby_env_debug_option(const char *str, int len, void *arg) @@ -192,6 +193,7 @@ ruby_env_debug_option(const char *str, int len, void *arg) SET_WHEN("gc_stress", *ruby_initial_gc_stress_ptr, Qtrue); SET_WHEN("core", ruby_enable_coredump, 1); + SET_WHEN("ci", ruby_on_ci, 1); if (NAME_MATCH_VALUE("rgengc")) { if (!len) ruby_rgengc_debug = 1; else SET_UINT_LIST("rgengc", &ruby_rgengc_debug, 1); diff --git a/gc.c b/gc.c index 4d99261e39..3a62f57e36 100644 --- a/gc.c +++ b/gc.c @@ -11344,8 +11344,8 @@ obj_type_name(VALUE obj) return type_name(TYPE(obj), obj); } -static const char * -method_type_name(rb_method_type_t type) +const char * +rb_method_type_name(rb_method_type_t type) { switch (type) { case VM_METHOD_TYPE_ISEQ: return "iseq"; @@ -11361,7 +11361,7 @@ method_type_name(rb_method_type_t type) case VM_METHOD_TYPE_UNDEF: return "undef"; case VM_METHOD_TYPE_NOTIMPLEMENTED: return "notimplemented"; } - rb_bug("method_type_name: unreachable (type: %d)", type); + rb_bug("rb_method_type_name: unreachable (type: %d)", type); } /* from array.c */ @@ -11564,7 +11564,7 @@ rb_raw_obj_info(char *buff, const int buff_size, VALUE obj) if (me->def) { APPENDF((BUFF_ARGS, "(called_id: %s, type: %s, alias: %d, owner: %s, defined_class: %s)", rb_id2name(me->called_id), - method_type_name(me->def->type), + rb_method_type_name(me->def->type), me->def->alias_count, obj_info(me->owner), obj_info(me->defined_class))); diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index c7aa287705..496a51b970 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -674,7 +674,7 @@ class TestRubyOptions < Test::Unit::TestCase %r( (?:--\s(?:.+\n)*\n)? --\sControl\sframe\sinformation\s-+\n - (?:c:.*\n)* + (?:(?:c:.*\n)|(?:^\s+.+\n))* \n )x, %r( diff --git a/vm_dump.c b/vm_dump.c index 4d7bd54444..a8d9af5a82 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -13,6 +13,8 @@ #include "addr2line.h" #include "vm_core.h" #include "iseq.h" +#include "gc.h" + #ifdef HAVE_UCONTEXT_H #include #endif @@ -38,6 +40,9 @@ ((rb_control_frame_t *)((ec)->vm_stack + (ec)->vm_stack_size) - \ (rb_control_frame_t *)(cfp)) +const char *rb_method_type_name(rb_method_type_t type); +int ruby_on_ci; + static void control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *cfp) { @@ -46,11 +51,10 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c char ep_in_heap = ' '; char posbuf[MAX_POSBUF+1]; int line = 0; - const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-"; VALUE tmp; - - const rb_callable_method_entry_t *me; + const rb_iseq_t *iseq = NULL; + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); if (ep < 0 || (size_t)ep > ec->vm_stack_size) { ep = (ptrdiff_t)cfp->ep; @@ -110,15 +114,16 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c line = -1; } else { - pc = cfp->pc - cfp->iseq->body->iseq_encoded; - iseq_name = RSTRING_PTR(cfp->iseq->body->location.label); + iseq = cfp->iseq; + pc = cfp->pc - iseq->body->iseq_encoded; + iseq_name = RSTRING_PTR(iseq->body->location.label); line = rb_vm_get_sourceline(cfp); if (line) { - snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(rb_iseq_path(cfp->iseq)), line); + snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(rb_iseq_path(iseq)), line); } } } - else if ((me = rb_vm_frame_method_entry(cfp)) != NULL) { + else if (me != NULL) { iseq_name = rb_id2name(me->def->original_id); snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name); line = -1; @@ -148,6 +153,37 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c fprintf(stderr, "%-1s ", biseq_name); } fprintf(stderr, "\n"); + + // additional information for CI machines + if (ruby_on_ci) { + char buff[0x100]; + + if (me) { + if (imemo_type_p((VALUE)me, imemo_ment)) { + fprintf(stderr, " me:\n"); + fprintf(stderr, " called_id: %s, type: %s\n", rb_id2name(me->called_id), rb_method_type_name(me->def->type)); + fprintf(stderr, " owner class: %s\n", rb_raw_obj_info(buff, 0x100, me->owner)); + if (me->owner != me->defined_class) { + fprintf(stderr, " defined_class: %s\n", rb_raw_obj_info(buff, 0x100, me->defined_class)); + } + } + else { + fprintf(stderr, " me is corrupted (%s)\n", rb_raw_obj_info(buff, 0x100, (VALUE)me)); + } + } + + if (iseq) { + if (iseq->body->local_table_size > 0) { + fprintf(stderr, " lvars:\n"); + for (unsigned int i=0; ibody->local_table_size; i++) { + const VALUE *argv = cfp->ep - cfp->iseq->body->local_table_size - VM_ENV_DATA_SIZE + 1; + fprintf(stderr, " %s: %s\n", + rb_id2name(iseq->body->local_table[i]), + rb_raw_obj_info(buff, 0x100, argv[i])); + } + } + } + } } void