* thread.c: add `Thread#backtrace_locations' method.

This method is similart to `caller_locations' method for
  specific method.
  And fix to accept `level' and `n' parameters for `Thread#backtrace'
  and `Thread#backtrace_locations'.
  `caller' (and `caller_locations') do not return `caller' method
  frame.
  However, `Thread#backtrace' (and `Thread#backtrace_locations')
  return `Thread#backtrace' method frame itself
  if `Thread.current.backtrace' was called.
* vm_backtrace.c: ditto.
* internal.h: ditto.
* test/ruby/test_backtrace.rb: add tests.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37716 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2012-11-19 06:07:06 +00:00
parent 3918bbfac4
commit 15d940b8b0
6 changed files with 124 additions and 58 deletions

View File

@ -1,3 +1,22 @@
Mon Nov 19 14:55:51 2012 Koichi Sasada <ko1@atdot.net>
* thread.c: add `Thread#backtrace_locations' method.
This method is similart to `caller_locations' method for
specific method.
And fix to accept `level' and `n' parameters for `Thread#backtrace'
and `Thread#backtrace_locations'.
`caller' (and `caller_locations') do not return `caller' method
frame.
However, `Thread#backtrace' (and `Thread#backtrace_locations')
return `Thread#backtrace' method frame itself
if `Thread.current.backtrace' was called.
* vm_backtrace.c: ditto.
* internal.h: ditto.
* test/ruby/test_backtrace.rb: add tests.
Mon Nov 19 14:54:32 2012 NAKAMURA Usaku <usa@ruby-lang.org>
* Makefile.in, common.mk (probes.h): moved to common.mk and changed to

4
NEWS
View File

@ -56,6 +56,8 @@ with all sufficient information, see the ChangeLog file.
* added Kernel#using, which imports refinements into the current scope.
[experimental]
* added Kernel#__dir__ which returns a current dirname.
* added Kernel#caller_locations which returns an array of
frame information objects.
* extended method:
* Kernel#warn accepts multiple args in like puts.
* Kernel#caller accepts second optional argument `n' which specify
@ -125,6 +127,8 @@ with all sufficient information, see the ChangeLog file.
variable keys.
* added Thread#thread_variable? for testing to see if a particular thread
variable has been set.
* added Thread#backtrace_locations which returns similar information of
Kernel#caller_locations.
* Time
* change return value:

View File

@ -310,7 +310,9 @@ void Init_prelude(void);
/* vm_backtrace.c */
void Init_vm_backtrace(void);
VALUE rb_thread_backtrace(VALUE thval);
VALUE vm_thread_backtrace(int argc, VALUE *argv, VALUE thval);
VALUE vm_thread_backtrace_locations(int argc, VALUE *argv, VALUE thval);
VALUE rb_make_backtrace(void);
void rb_backtrace_print_as_bugreport(void);
int rb_backtrace_p(VALUE obj);

View File

@ -1,5 +1,5 @@
require 'test/unit'
require 'thread'
class TestBacktrace < Test::Unit::TestCase
def test_exception
@ -91,4 +91,39 @@ class TestBacktrace < Test::Unit::TestCase
}
assert_equal(cs, locs)
end
def th_rec q, n=10
if n > 1
th_rec q, n-1
else
q.pop
end
end
def test_thread_backtrace
begin
q = Queue.new
th = Thread.new{
th_rec q
}
sleep 0.5
th_backtrace = th.backtrace
th_locations = th.backtrace_locations
assert_equal(10, th_backtrace.count{|e| e =~ /th_rec/})
assert_equal(th_backtrace, th_locations.map{|e| e.to_s})
assert_equal(th_backtrace, th.backtrace(0))
assert_equal(th_locations.map{|e| e.to_s},
th.backtrace_locations(0).map{|e| e.to_s})
th_backtrace.size.times{|n|
assert_equal(n, th.backtrace(0, n).size)
assert_equal(n, th.backtrace_locations(0, n).size)
}
n = th_backtrace.size
assert_equal(n, th.backtrace(0, n + 1).size)
assert_equal(n, th.backtrace_locations(0, n + 1).size)
ensure
q << true
end
end
end

View File

@ -4623,9 +4623,15 @@ rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
*/
static VALUE
rb_thread_backtrace_m(VALUE thval)
rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
{
return rb_thread_backtrace(thval);
return vm_thread_backtrace(argc, argv, thval);
}
static VALUE
rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
{
return vm_thread_backtrace_locations(argc, argv, thval);
}
/*
@ -4705,7 +4711,8 @@ Init_Thread(void)
rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
rb_define_method(rb_cThread, "group", rb_thread_group, 0);
rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, 0);
rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
rb_define_method(rb_cThread, "backtrace_locations", rb_thread_backtrace_locations_m, -1);
rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0);

View File

@ -697,8 +697,43 @@ rb_make_backtrace(void)
return vm_backtrace_str_ary(GET_THREAD(), 0, 0);
}
VALUE
rb_thread_backtrace(VALUE thval)
static VALUE
vm_backtrace_to_ary(rb_thread_t *th, int argc, VALUE *argv, int lev_deafult, int lev_plus, int to_str)
{
VALUE level, vn;
int lev, n;
rb_scan_args(argc, argv, "02", &level, &vn);
lev = NIL_P(level) ? lev_deafult : NUM2INT(level);
if (NIL_P(vn)) {
n = 0;
}
else {
n = NUM2INT(vn);
if (n == 0) {
return rb_ary_new();
}
}
if (lev < 0) {
rb_raise(rb_eArgError, "negative level (%d)", lev);
}
if (n < 0) {
rb_raise(rb_eArgError, "negative n (%d)", n);
}
if (to_str) {
return vm_backtrace_str_ary(th, lev+lev_plus, n);
}
else {
return vm_backtrace_frame_ary(th, lev+lev_plus, n);
}
}
static VALUE
thread_backtrace_to_ary(int argc, VALUE *argv, VALUE thval, int to_str)
{
rb_thread_t *th;
GetThreadPtr(thval, th);
@ -713,7 +748,19 @@ rb_thread_backtrace(VALUE thval)
return Qnil;
}
return vm_backtrace_str_ary(th, 0, 0);
return vm_backtrace_to_ary(th, argc, argv, 0, 0, to_str);
}
VALUE
vm_thread_backtrace(int argc, VALUE *argv, VALUE thval)
{
return thread_backtrace_to_ary(argc, argv, thval, 1);
}
VALUE
vm_thread_backtrace_locations(int argc, VALUE *argv, VALUE thval)
{
return thread_backtrace_to_ary(argc, argv, thval, 0);
}
/*
@ -749,61 +796,13 @@ rb_thread_backtrace(VALUE thval)
static VALUE
rb_f_caller(int argc, VALUE *argv)
{
VALUE level, vn;
int lev, n;
rb_scan_args(argc, argv, "02", &level, &vn);
lev = NIL_P(level) ? 1 : NUM2INT(level);
if (NIL_P(vn)) {
n = 0;
}
else {
n = NUM2INT(vn);
if (n == 0) {
return rb_ary_new();
}
}
if (lev < 0) {
rb_raise(rb_eArgError, "negative level (%d)", lev);
}
if (n < 0) {
rb_raise(rb_eArgError, "negative n (%d)", n);
}
return vm_backtrace_str_ary(GET_THREAD(), lev+1, n);
return vm_backtrace_to_ary(GET_THREAD(), argc, argv, 1, 1, 1);
}
static VALUE
rb_f_caller_locations(int argc, VALUE *argv)
{
VALUE level, vn;
int lev, n;
rb_scan_args(argc, argv, "02", &level, &vn);
lev = NIL_P(level) ? 1 : NUM2INT(level);
if (NIL_P(vn)) {
n = 0;
}
else {
n = NUM2INT(vn);
if (n == 0) {
return rb_ary_new();
}
}
if (lev < 0) {
rb_raise(rb_eArgError, "negative level (%d)", lev);
}
if (n < 0) {
rb_raise(rb_eArgError, "negative n (%d)", n);
}
return vm_backtrace_frame_ary(GET_THREAD(), lev+1, n);
return vm_backtrace_to_ary(GET_THREAD(), argc, argv, 1, 1, 0);
}
/* called from Init_vm() in vm.c */