From 4328f93f1bf08296115172a279e2d85a0ed80122 Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Tue, 2 Mar 2021 21:50:15 +0900 Subject: [PATCH] merge revision(s) 87437326214e4587a41946c8937e11418d983acd: [Backport #17581] Fix backtrace to not skip frames with iseq without pc Previously, frames with iseq but no pc were skipped (even before the refactoring in 3b24b7914c16930bfadc89d6aff6326a51c54295). Because the entire backtrace was procesed before the refactoring, this was handled by using later frames instead. However, after the refactoring, we need to handle those frames or they get lost. Keep two iteration counters when iterating, one for the desired backtrace size (so we generate the desired number of frames), and one for the actual backtrace size (so we don't process off the end of the stack). When skipping over an iseq frame with no pc, decrement the counter for the desired backtrace, so it will continue to process the expected number of backtrace frames. Fixes [Bug #17581] --- test/ruby/test_backtrace.rb | 12 ++++++++++++ vm_backtrace.c | 16 +++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) --- test/ruby/test_backtrace.rb | 12 ++++++++++++ version.h | 2 +- vm_backtrace.c | 16 +++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index 00c96b3b9f..aa04cf0961 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -138,6 +138,18 @@ class TestBacktrace < Test::Unit::TestCase rec[m] end + def test_caller_with_limit + x = nil + c = Class.new do + define_method(:bar) do + x = caller(1, 1) + end + end + [c.new].group_by(&:bar) + assert_equal 1, x.length + assert_equal caller(0), caller(0, nil) + end + def test_caller_with_nil_length assert_equal caller(0), caller(0, nil) end diff --git a/version.h b/version.h index bd7e54522b..92c504153f 100644 --- a/version.h +++ b/version.h @@ -12,7 +12,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 0 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 46 +#define RUBY_PATCHLEVEL 47 #define RUBY_RELEASE_YEAR 2021 #define RUBY_RELEASE_MONTH 3 diff --git a/vm_backtrace.c b/vm_backtrace.c index f2cc21294c..fae65b5c2a 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -515,7 +515,7 @@ backtrace_each(const rb_execution_context_t *ec, const rb_control_frame_t *last_cfp = ec->cfp; const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec); const rb_control_frame_t *cfp; - ptrdiff_t size, i, last, start = 0; + ptrdiff_t size, real_size, i, j, last, start = 0; int ret = 0; // In the case the thread vm_stack or cfp is not initialized, there is no backtrace. @@ -539,10 +539,10 @@ backtrace_each(const rb_execution_context_t *ec, RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */ if (start_cfp < last_cfp) { - size = last = 0; + real_size = size = last = 0; } else { - size = start_cfp - last_cfp + 1; + real_size = size = start_cfp - last_cfp + 1; if (from_last > size) { size = last = 0; @@ -568,7 +568,7 @@ backtrace_each(const rb_execution_context_t *ec, init(arg, size); /* SDR(); */ - for (i=0, cfp = start_cfp; ivm_stack + ec->vm_stack_size) - cfp); */ if (cfp->iseq) { - if (cfp->pc) { - iter_iseq(arg, cfp); - } + if (cfp->pc) { + iter_iseq(arg, cfp); + } else { + i--; + } } else if (RUBYVM_CFUNC_FRAME_P(cfp)) { const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);