2012-06-02 11:23:37 -04:00
|
|
|
/**********************************************************************
|
|
|
|
|
2012-06-08 18:44:01 -04:00
|
|
|
vm_backtrace.c -
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
$Author: ko1 $
|
|
|
|
created at: Sun Jun 03 00:14:20 2012
|
|
|
|
|
|
|
|
Copyright (C) 1993-2012 Yukihiro Matsumoto
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
2012-11-29 02:05:27 -05:00
|
|
|
#include "eval_intern.h"
|
2019-12-04 03:16:30 -05:00
|
|
|
#include "internal.h"
|
2021-02-15 01:58:45 -05:00
|
|
|
#include "internal/error.h"
|
2019-12-04 03:16:30 -05:00
|
|
|
#include "internal/vm.h"
|
2012-06-02 11:59:37 -04:00
|
|
|
#include "iseq.h"
|
2019-12-04 03:16:30 -05:00
|
|
|
#include "ruby/debug.h"
|
|
|
|
#include "ruby/encoding.h"
|
|
|
|
#include "vm_core.h"
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
static VALUE rb_cBacktrace;
|
2012-06-02 12:46:08 -04:00
|
|
|
static VALUE rb_cBacktraceLocation;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2014-10-15 10:37:54 -04:00
|
|
|
static VALUE
|
|
|
|
id2str(ID id)
|
|
|
|
{
|
|
|
|
VALUE str = rb_id2str(id);
|
2014-10-16 09:45:53 -04:00
|
|
|
if (!str) return Qnil;
|
2014-10-15 10:37:54 -04:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
#define rb_id2str(id) id2str(id)
|
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
#define BACKTRACE_START 0
|
|
|
|
#define ALL_BACKTRACE_LINES -1
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
inline static int
|
2012-06-02 12:46:08 -04:00
|
|
|
calc_lineno(const rb_iseq_t *iseq, const VALUE *pc)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2019-07-31 10:15:56 -04:00
|
|
|
VM_ASSERT(iseq);
|
|
|
|
VM_ASSERT(iseq->body);
|
|
|
|
VM_ASSERT(iseq->body->iseq_encoded);
|
|
|
|
VM_ASSERT(iseq->body->iseq_size);
|
|
|
|
if (! pc) {
|
|
|
|
/* This can happen during VM bootup. */
|
|
|
|
VM_ASSERT(iseq->body->type == ISEQ_TYPE_TOP);
|
|
|
|
VM_ASSERT(! iseq->body->local_table);
|
|
|
|
VM_ASSERT(! iseq->body->local_table_size);
|
|
|
|
return 0;
|
2018-11-07 03:04:33 -05:00
|
|
|
}
|
|
|
|
else {
|
2019-07-31 10:15:56 -04:00
|
|
|
ptrdiff_t n = pc - iseq->body->iseq_encoded;
|
|
|
|
VM_ASSERT(n <= iseq->body->iseq_size);
|
|
|
|
VM_ASSERT(n >= 0);
|
|
|
|
ASSUME(n >= 0);
|
|
|
|
size_t pos = n; /* no overflow */
|
|
|
|
if (LIKELY(pos)) {
|
|
|
|
/* use pos-1 because PC points next instruction at the beginning of instruction */
|
|
|
|
pos--;
|
|
|
|
}
|
|
|
|
#if VMDEBUG && defined(HAVE_BUILTIN___BUILTIN_TRAP)
|
|
|
|
else {
|
|
|
|
/* SDR() is not possible; that causes infinite loop. */
|
|
|
|
rb_print_backtrace();
|
|
|
|
__builtin_trap();
|
|
|
|
}
|
2018-11-07 03:04:33 -05:00
|
|
|
#endif
|
2019-07-31 10:15:56 -04:00
|
|
|
return rb_iseq_line_no(iseq, pos);
|
|
|
|
}
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_vm_get_sourceline(const rb_control_frame_t *cfp)
|
|
|
|
{
|
2016-08-02 21:50:50 -04:00
|
|
|
if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->iseq) {
|
2017-11-14 07:58:36 -05:00
|
|
|
const rb_iseq_t *iseq = cfp->iseq;
|
|
|
|
int line = calc_lineno(iseq, cfp->pc);
|
|
|
|
if (line != 0) {
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return FIX2INT(rb_iseq_first_lineno(iseq));
|
|
|
|
}
|
2016-08-02 21:50:50 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
typedef struct rb_backtrace_location_struct {
|
|
|
|
enum LOCATION_TYPE {
|
|
|
|
LOCATION_TYPE_ISEQ = 1,
|
|
|
|
LOCATION_TYPE_ISEQ_CALCED,
|
|
|
|
LOCATION_TYPE_CFUNC,
|
2012-06-02 11:23:37 -04:00
|
|
|
} type;
|
|
|
|
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
const rb_iseq_t *iseq;
|
|
|
|
union {
|
|
|
|
const VALUE *pc;
|
2012-06-02 12:46:08 -04:00
|
|
|
int lineno;
|
|
|
|
} lineno;
|
2012-06-02 11:23:37 -04:00
|
|
|
} iseq;
|
|
|
|
struct {
|
|
|
|
ID mid;
|
2012-06-02 12:46:08 -04:00
|
|
|
struct rb_backtrace_location_struct *prev_loc;
|
2012-06-02 11:23:37 -04:00
|
|
|
} cfunc;
|
|
|
|
} body;
|
2012-06-02 12:46:08 -04:00
|
|
|
} rb_backtrace_location_t;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
struct valued_frame_info {
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_backtrace_location_t *loc;
|
2012-06-02 11:23:37 -04:00
|
|
|
VALUE btobj;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
2012-06-02 12:46:08 -04:00
|
|
|
location_mark(void *ptr)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2014-09-13 01:14:51 -04:00
|
|
|
struct valued_frame_info *vfi = (struct valued_frame_info *)ptr;
|
|
|
|
rb_gc_mark(vfi->btobj);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-02 12:46:08 -04:00
|
|
|
location_mark_entry(rb_backtrace_location_t *fi)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
switch (fi->type) {
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
2020-05-07 13:15:35 -04:00
|
|
|
rb_gc_mark_movable((VALUE)fi->body.iseq.iseq);
|
2012-06-02 11:23:37 -04:00
|
|
|
break;
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_CFUNC:
|
2012-06-02 11:23:37 -04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
2012-06-02 12:46:08 -04:00
|
|
|
location_memsize(const void *ptr)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
/* rb_backtrace_location_t *fi = (rb_backtrace_location_t *)ptr; */
|
|
|
|
return sizeof(rb_backtrace_location_t);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
static const rb_data_type_t location_data_type = {
|
2012-06-02 11:23:37 -04:00
|
|
|
"frame_info",
|
2014-09-13 01:14:51 -04:00
|
|
|
{location_mark, RUBY_TYPED_DEFAULT_FREE, location_memsize,},
|
2014-12-01 01:38:04 -05:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
2012-06-02 11:23:37 -04:00
|
|
|
};
|
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
static inline rb_backtrace_location_t *
|
|
|
|
location_ptr(VALUE locobj)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
struct valued_frame_info *vloc;
|
|
|
|
GetCoreDataFromValue(locobj, struct valued_frame_info, vloc);
|
|
|
|
return vloc->loc;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2012-06-02 12:46:08 -04:00
|
|
|
location_lineno(rb_backtrace_location_t *loc)
|
|
|
|
{
|
|
|
|
switch (loc->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
loc->type = LOCATION_TYPE_ISEQ_CALCED;
|
|
|
|
return (loc->body.iseq.lineno.lineno = calc_lineno(loc->body.iseq.iseq, loc->body.iseq.lineno.pc));
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
|
|
|
return loc->body.iseq.lineno.lineno;
|
|
|
|
case LOCATION_TYPE_CFUNC:
|
|
|
|
if (loc->body.cfunc.prev_loc) {
|
|
|
|
return location_lineno(loc->body.cfunc.prev_loc);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
default:
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_bug("location_lineno: unreachable");
|
2012-06-02 11:23:37 -04:00
|
|
|
UNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* Returns the line number of this frame.
|
|
|
|
*
|
|
|
|
* For example, using +caller_locations.rb+ from Thread::Backtrace::Location
|
|
|
|
*
|
|
|
|
* loc = c(0..1).first
|
|
|
|
* loc.lineno #=> 2
|
|
|
|
*/
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_lineno_m(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
return INT2FIX(location_lineno(location_ptr(self)));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_label(rb_backtrace_location_t *loc)
|
|
|
|
{
|
|
|
|
switch (loc->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
2015-07-21 18:52:59 -04:00
|
|
|
return loc->body.iseq.iseq->body->location.label;
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_CFUNC:
|
|
|
|
return rb_id2str(loc->body.cfunc.mid);
|
2012-06-02 11:23:37 -04:00
|
|
|
default:
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_bug("location_label: unreachable");
|
2012-06-02 11:23:37 -04:00
|
|
|
UNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* Returns the label of this frame.
|
|
|
|
*
|
|
|
|
* Usually consists of method, class, module, etc names with decoration.
|
|
|
|
*
|
2013-06-17 01:31:28 -04:00
|
|
|
* Consider the following example:
|
|
|
|
*
|
|
|
|
* def foo
|
|
|
|
* puts caller_locations(0).first.label
|
|
|
|
*
|
|
|
|
* 1.times do
|
|
|
|
* puts caller_locations(0).first.label
|
|
|
|
*
|
|
|
|
* 1.times do
|
|
|
|
* puts caller_locations(0).first.label
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* The result of calling +foo+ is this:
|
|
|
|
*
|
|
|
|
* label: foo
|
|
|
|
* label: block in foo
|
|
|
|
* label: block (2 levels) in foo
|
2012-12-31 01:09:57 -05:00
|
|
|
*
|
|
|
|
*/
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_label_m(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
return location_label(location_ptr(self));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_base_label(rb_backtrace_location_t *loc)
|
|
|
|
{
|
|
|
|
switch (loc->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
2015-07-21 18:52:59 -04:00
|
|
|
return loc->body.iseq.iseq->body->location.base_label;
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_CFUNC:
|
2015-04-13 23:33:00 -04:00
|
|
|
return rb_id2str(loc->body.cfunc.mid);
|
2012-06-02 11:23:37 -04:00
|
|
|
default:
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_bug("location_base_label: unreachable");
|
2012-06-02 11:23:37 -04:00
|
|
|
UNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* Returns the base label of this frame.
|
|
|
|
*
|
|
|
|
* Usually same as #label, without decoration.
|
|
|
|
*/
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_base_label_m(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
return location_base_label(location_ptr(self));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_path(rb_backtrace_location_t *loc)
|
|
|
|
{
|
|
|
|
switch (loc->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
2017-05-31 20:05:33 -04:00
|
|
|
return rb_iseq_path(loc->body.iseq.iseq);
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_CFUNC:
|
|
|
|
if (loc->body.cfunc.prev_loc) {
|
|
|
|
return location_path(loc->body.cfunc.prev_loc);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
return Qnil;
|
|
|
|
default:
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_bug("location_path: unreachable");
|
2012-06-02 11:23:37 -04:00
|
|
|
UNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
2020-09-23 18:52:54 -04:00
|
|
|
* Returns the file name of this frame. This will generally be an absolute
|
|
|
|
* path, unless the frame is in the main script, in which case it will be the
|
|
|
|
* script location passed on the command line.
|
2012-12-31 01:09:57 -05:00
|
|
|
*
|
|
|
|
* For example, using +caller_locations.rb+ from Thread::Backtrace::Location
|
|
|
|
*
|
|
|
|
* loc = c(0..1).first
|
|
|
|
* loc.path #=> caller_locations.rb
|
|
|
|
*/
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_path_m(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
return location_path(location_ptr(self));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2017-05-31 20:05:33 -04:00
|
|
|
location_realpath(rb_backtrace_location_t *loc)
|
2012-06-02 12:46:08 -04:00
|
|
|
{
|
|
|
|
switch (loc->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
2017-05-31 20:05:33 -04:00
|
|
|
return rb_iseq_realpath(loc->body.iseq.iseq);
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_CFUNC:
|
|
|
|
if (loc->body.cfunc.prev_loc) {
|
2017-05-31 20:05:33 -04:00
|
|
|
return location_realpath(loc->body.cfunc.prev_loc);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
return Qnil;
|
|
|
|
default:
|
2017-05-31 20:05:33 -04:00
|
|
|
rb_bug("location_realpath: unreachable");
|
2012-06-02 11:23:37 -04:00
|
|
|
UNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* Returns the full file path of this frame.
|
|
|
|
*
|
2020-09-23 18:52:54 -04:00
|
|
|
* Same as #path, except that it will return absolute path
|
|
|
|
* even if the frame is in the main script.
|
2012-12-31 01:09:57 -05:00
|
|
|
*/
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_absolute_path_m(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2017-05-31 20:05:33 -04:00
|
|
|
return location_realpath(location_ptr(self));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_format(VALUE file, int lineno, VALUE name)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2014-10-15 10:37:51 -04:00
|
|
|
VALUE s = rb_enc_sprintf(rb_enc_compatible(file, name), "%s", RSTRING_PTR(file));
|
2012-06-02 12:46:08 -04:00
|
|
|
if (lineno != 0) {
|
2014-10-15 10:37:51 -04:00
|
|
|
rb_str_catf(s, ":%d", lineno);
|
|
|
|
}
|
|
|
|
rb_str_cat_cstr(s, ":in ");
|
|
|
|
if (NIL_P(name)) {
|
|
|
|
rb_str_cat_cstr(s, "unknown method");
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
else {
|
2014-10-15 10:37:51 -04:00
|
|
|
rb_str_catf(s, "`%s'", RSTRING_PTR(name));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
2014-10-15 10:37:51 -04:00
|
|
|
return s;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_to_str(rb_backtrace_location_t *loc)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
VALUE file, name;
|
2012-06-02 12:46:08 -04:00
|
|
|
int lineno;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
switch (loc->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
2017-05-31 20:05:33 -04:00
|
|
|
file = rb_iseq_path(loc->body.iseq.iseq);
|
2015-07-21 18:52:59 -04:00
|
|
|
name = loc->body.iseq.iseq->body->location.label;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
lineno = loc->body.iseq.lineno.lineno = calc_lineno(loc->body.iseq.iseq, loc->body.iseq.lineno.pc);
|
|
|
|
loc->type = LOCATION_TYPE_ISEQ_CALCED;
|
2012-06-02 11:23:37 -04:00
|
|
|
break;
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
2017-05-31 20:05:33 -04:00
|
|
|
file = rb_iseq_path(loc->body.iseq.iseq);
|
2012-06-02 12:46:08 -04:00
|
|
|
lineno = loc->body.iseq.lineno.lineno;
|
2015-07-21 18:52:59 -04:00
|
|
|
name = loc->body.iseq.iseq->body->location.label;
|
2012-06-02 11:23:37 -04:00
|
|
|
break;
|
2012-06-02 12:46:08 -04:00
|
|
|
case LOCATION_TYPE_CFUNC:
|
|
|
|
if (loc->body.cfunc.prev_loc) {
|
2017-05-31 20:05:33 -04:00
|
|
|
file = rb_iseq_path(loc->body.cfunc.prev_loc->body.iseq.iseq);
|
2012-06-02 12:46:08 -04:00
|
|
|
lineno = location_lineno(loc->body.cfunc.prev_loc);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
else {
|
2017-10-29 09:19:53 -04:00
|
|
|
file = GET_VM()->progname;
|
2019-03-21 01:59:16 -04:00
|
|
|
lineno = 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
2012-06-02 12:46:08 -04:00
|
|
|
name = rb_id2str(loc->body.cfunc.mid);
|
2012-06-02 11:23:37 -04:00
|
|
|
break;
|
|
|
|
default:
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_bug("location_to_str: unreachable");
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
return location_format(file, lineno, name);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* Returns a Kernel#caller style string representing this frame.
|
|
|
|
*/
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_to_str_m(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
return location_to_str(location_ptr(self));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* Returns the same as calling +inspect+ on the string representation of
|
|
|
|
* #to_str
|
|
|
|
*/
|
2012-12-01 07:09:17 -05:00
|
|
|
static VALUE
|
|
|
|
location_inspect_m(VALUE self)
|
|
|
|
{
|
|
|
|
return rb_str_inspect(location_to_str(location_ptr(self)));
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
typedef struct rb_backtrace_struct {
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_backtrace_location_t *backtrace;
|
2012-06-02 11:23:37 -04:00
|
|
|
int backtrace_size;
|
|
|
|
VALUE strary;
|
2013-12-12 23:31:06 -05:00
|
|
|
VALUE locary;
|
2012-06-02 11:23:37 -04:00
|
|
|
} rb_backtrace_t;
|
|
|
|
|
|
|
|
static void
|
|
|
|
backtrace_mark(void *ptr)
|
|
|
|
{
|
2014-09-13 01:14:51 -04:00
|
|
|
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
|
|
|
|
size_t i, s = bt->backtrace_size;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2014-09-13 01:14:51 -04:00
|
|
|
for (i=0; i<s; i++) {
|
|
|
|
location_mark_entry(&bt->backtrace[i]);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
2020-05-07 13:15:35 -04:00
|
|
|
rb_gc_mark_movable(bt->strary);
|
|
|
|
rb_gc_mark_movable(bt->locary);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
backtrace_free(void *ptr)
|
|
|
|
{
|
2014-09-13 01:14:51 -04:00
|
|
|
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
if (bt->backtrace) ruby_xfree(bt->backtrace);
|
2014-09-13 01:14:51 -04:00
|
|
|
ruby_xfree(bt);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2020-05-07 13:15:35 -04:00
|
|
|
static void
|
|
|
|
location_update_entry(rb_backtrace_location_t *fi)
|
|
|
|
{
|
|
|
|
switch (fi->type) {
|
|
|
|
case LOCATION_TYPE_ISEQ:
|
|
|
|
case LOCATION_TYPE_ISEQ_CALCED:
|
|
|
|
fi->body.iseq.iseq = (rb_iseq_t*)rb_gc_location((VALUE)fi->body.iseq.iseq);
|
|
|
|
break;
|
|
|
|
case LOCATION_TYPE_CFUNC:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
backtrace_update(void *ptr)
|
|
|
|
{
|
|
|
|
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
|
|
|
|
size_t i, s = bt->backtrace_size;
|
|
|
|
|
|
|
|
for (i=0; i<s; i++) {
|
|
|
|
location_update_entry(&bt->backtrace[i]);
|
|
|
|
}
|
|
|
|
bt->strary = rb_gc_location(bt->strary);
|
|
|
|
bt->locary = rb_gc_location(bt->locary);
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
static size_t
|
|
|
|
backtrace_memsize(const void *ptr)
|
|
|
|
{
|
|
|
|
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
|
2012-06-02 12:46:08 -04:00
|
|
|
return sizeof(rb_backtrace_t) + sizeof(rb_backtrace_location_t) * bt->backtrace_size;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static const rb_data_type_t backtrace_data_type = {
|
|
|
|
"backtrace",
|
2020-05-07 13:15:35 -04:00
|
|
|
{backtrace_mark, backtrace_free, backtrace_memsize, backtrace_update},
|
2014-12-01 01:38:04 -05:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
2012-06-02 11:23:37 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_backtrace_p(VALUE obj)
|
|
|
|
{
|
|
|
|
return rb_typeddata_is_kind_of(obj, &backtrace_data_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
backtrace_alloc(VALUE klass)
|
|
|
|
{
|
|
|
|
rb_backtrace_t *bt;
|
|
|
|
VALUE obj = TypedData_Make_Struct(klass, rb_backtrace_t, &backtrace_data_type, bt);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
static long
|
|
|
|
backtrace_size(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);
|
|
|
|
|
|
|
|
if (start_cfp == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_cfp =
|
|
|
|
RUBY_VM_NEXT_CONTROL_FRAME(
|
|
|
|
RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */
|
|
|
|
|
|
|
|
if (start_cfp < last_cfp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return start_cfp - last_cfp + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-10-26 06:57:16 -04:00
|
|
|
backtrace_each(const rb_execution_context_t *ec,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
ptrdiff_t from_last,
|
|
|
|
long num_frames,
|
2012-06-02 11:23:37 -04:00
|
|
|
void (*init)(void *arg, size_t size),
|
2012-11-29 02:05:27 -05:00
|
|
|
void (*iter_iseq)(void *arg, const rb_control_frame_t *cfp),
|
|
|
|
void (*iter_cfunc)(void *arg, const rb_control_frame_t *cfp, ID mid),
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
void (*iter_skip)(void *arg, const rb_control_frame_t *cfp),
|
2012-06-02 11:23:37 -04:00
|
|
|
void *arg)
|
|
|
|
{
|
2017-10-26 06:57:16 -04:00
|
|
|
const rb_control_frame_t *last_cfp = ec->cfp;
|
|
|
|
const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
|
2017-10-26 04:41:34 -04:00
|
|
|
const rb_control_frame_t *cfp;
|
2021-01-25 17:56:03 -05:00
|
|
|
ptrdiff_t size, real_size, i, j, last, start = 0;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
int ret = 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2019-06-05 17:54:29 -04:00
|
|
|
// In the case the thread vm_stack or cfp is not initialized, there is no backtrace.
|
|
|
|
if (start_cfp == NULL) {
|
|
|
|
init(arg, 0);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
return ret;
|
2019-06-05 17:54:29 -04:00
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
/* <- start_cfp (end control frame)
|
|
|
|
* top frame (dummy)
|
|
|
|
* top frame (dummy)
|
|
|
|
* top frame <- start_cfp
|
|
|
|
* top frame
|
|
|
|
* ...
|
|
|
|
* 2nd frame <- lev:0
|
2017-10-26 06:57:16 -04:00
|
|
|
* current frame <- ec->cfp
|
2012-06-02 11:23:37 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
start_cfp =
|
|
|
|
RUBY_VM_NEXT_CONTROL_FRAME(
|
* vm_core.h: remove VM_FRAME_MAGIC_FINISH (finish frame type).
Before this commit:
`finish frame' was place holder which indicates that VM loop
needs to return function.
If a C method calls a Ruby methods (a method written by Ruby),
then VM loop will be (re-)invoked. When the Ruby method returns,
then also VM loop should be escaped. `finish frame' has only
one instruction `finish', which returns VM loop function.
VM loop function executes `finish' instruction, then VM loop
function returns itself.
With such mechanism, `leave' instruction (which returns one
frame from current scope) doesn't need to check that this `leave'
should also return from VM loop function.
Strictly, one branch can be removed from `leave' instructon.
Consideration:
However, pushing the `finish frame' needs costs because
it needs several memory accesses. The number of pushing
`finish frame' is greater than I had assumed. Of course,
pushing `finish frame' consumes additional control frame.
Moreover, recent processors has good branch prediction,
with which we can ignore such trivial checking.
After this commit:
Finally, I decide to remove `finish frame' and `finish'
instruction. Some parts of VM depend on `finish frame',
so the new frame flag VM_FRAME_FLAG_FINISH is introduced.
If this frame should escape from VM function loop, then
the result of VM_FRAME_TYPE_FINISH_P(cfp) is true.
`leave' instruction checks this flag every time.
I measured performance on it. However on my environments,
it improves some benchmarks and slows some benchmarks down.
Maybe it is because of C compiler optimization parameters.
I'll re-visit here if this cause problems.
* insns.def (leave, finish): remove finish instruction.
* vm.c, vm_eval.c, vm_exec.c, vm_backtrace.c, vm_dump.c:
apply above changes.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-15 06:22:34 -04:00
|
|
|
RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
if (start_cfp < last_cfp) {
|
2021-01-25 17:56:03 -05:00
|
|
|
real_size = size = last = 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
else {
|
2021-03-04 12:59:28 -05:00
|
|
|
/* Ensure we don't look at frames beyond the ones requested */
|
|
|
|
for(; from_last > 0 && start_cfp >= last_cfp; from_last--) {
|
|
|
|
last_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(last_cfp);
|
|
|
|
}
|
|
|
|
|
2021-01-25 17:56:03 -05:00
|
|
|
real_size = size = start_cfp - last_cfp + 1;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
|
|
|
|
if (from_last > size) {
|
|
|
|
size = last = 0;
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
else if (num_frames >= 0 && num_frames < size) {
|
|
|
|
if (from_last + num_frames > size) {
|
|
|
|
size -= from_last;
|
|
|
|
last = size;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
start = size - from_last - num_frames;
|
|
|
|
size = num_frames;
|
|
|
|
last = start + size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
size -= from_last;
|
|
|
|
last = size;
|
|
|
|
}
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
init(arg, size);
|
|
|
|
|
2021-03-04 12:59:28 -05:00
|
|
|
/* If a limited number of frames is requested, scan the VM stack for
|
|
|
|
* ignored frames (iseq without pc). Then adjust the start for the
|
|
|
|
* backtrace to account for skipped frames.
|
|
|
|
*/
|
|
|
|
if (start > 0 && num_frames >= 0 && num_frames < real_size) {
|
|
|
|
ptrdiff_t ignored_frames;
|
|
|
|
bool ignored_frames_before_start = false;
|
|
|
|
for (i=0, j=0, cfp = start_cfp; i<last && j<real_size; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
|
|
|
|
if (cfp->iseq && !cfp->pc) {
|
|
|
|
if (j < start)
|
|
|
|
ignored_frames_before_start = true;
|
|
|
|
else
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ignored_frames = j - i;
|
|
|
|
|
|
|
|
if (ignored_frames) {
|
|
|
|
if (ignored_frames_before_start) {
|
|
|
|
/* There were ignored frames before start. So just decrementing
|
|
|
|
* start for ignored frames could still result in not all desired
|
|
|
|
* frames being captured.
|
|
|
|
*
|
|
|
|
* First, scan to the CFP of the desired start frame.
|
|
|
|
*
|
|
|
|
* Then scan backwards to previous frames, skipping the number of
|
|
|
|
* frames ignored after start and any additional ones before start,
|
|
|
|
* so the number of desired frames will be correctly captured.
|
|
|
|
*/
|
|
|
|
for (i=0, j=0, cfp = start_cfp; i<last && j<real_size && j < start; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
|
|
|
|
/* nothing */
|
|
|
|
}
|
|
|
|
for (; start > 0 && ignored_frames > 0 && j > 0; j--, ignored_frames--, start--, cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
|
|
|
|
if (cfp->iseq && !cfp->pc) {
|
|
|
|
ignored_frames++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No ignored frames before start frame, just decrement start */
|
|
|
|
start -= ignored_frames;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-25 17:56:03 -05:00
|
|
|
for (i=0, j=0, cfp = start_cfp; i<last && j<real_size; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
|
2021-03-04 12:59:28 -05:00
|
|
|
if (j < start) {
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
if (iter_skip) {
|
|
|
|
iter_skip(arg, cfp);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-10-26 06:57:16 -04:00
|
|
|
/* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp); */
|
2012-06-02 11:23:37 -04:00
|
|
|
if (cfp->iseq) {
|
2021-01-25 17:56:03 -05:00
|
|
|
if (cfp->pc) {
|
|
|
|
iter_iseq(arg, cfp);
|
|
|
|
} else {
|
|
|
|
i--;
|
|
|
|
}
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
|
* method.h: introduce rb_callable_method_entry_t to remove
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-07-03 07:24:50 -04:00
|
|
|
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
|
2015-06-02 00:20:30 -04:00
|
|
|
ID mid = me->def->original_id;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-11-29 02:05:27 -05:00
|
|
|
iter_cfunc(arg, cfp, mid);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
}
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
|
|
|
|
return ret;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct bt_iter_arg {
|
|
|
|
rb_backtrace_t *bt;
|
|
|
|
VALUE btobj;
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_backtrace_location_t *prev_loc;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
const rb_control_frame_t *prev_cfp;
|
|
|
|
rb_backtrace_location_t *init_loc;
|
2012-06-02 11:23:37 -04:00
|
|
|
};
|
|
|
|
|
2020-10-11 07:36:25 -04:00
|
|
|
static bool
|
|
|
|
is_internal_location(const rb_control_frame_t *cfp)
|
|
|
|
{
|
|
|
|
static const char prefix[] = "<internal:";
|
|
|
|
const size_t prefix_len = sizeof(prefix) - 1;
|
|
|
|
VALUE file = rb_iseq_path(cfp->iseq);
|
|
|
|
return strncmp(prefix, RSTRING_PTR(file), prefix_len) == 0;
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
static void
|
|
|
|
bt_init(void *ptr, size_t size)
|
|
|
|
{
|
|
|
|
struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr;
|
|
|
|
arg->btobj = backtrace_alloc(rb_cBacktrace);
|
|
|
|
GetCoreDataFromValue(arg->btobj, rb_backtrace_t, arg->bt);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
arg->bt->backtrace = ZALLOC_N(rb_backtrace_location_t, size+1);
|
|
|
|
arg->bt->backtrace_size = 1;
|
|
|
|
arg->prev_cfp = NULL;
|
|
|
|
arg->init_loc = &arg->bt->backtrace[size];
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-29 02:05:27 -05:00
|
|
|
bt_iter_iseq(void *ptr, const rb_control_frame_t *cfp)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-11-29 02:05:27 -05:00
|
|
|
const rb_iseq_t *iseq = cfp->iseq;
|
|
|
|
const VALUE *pc = cfp->pc;
|
2012-06-02 11:23:37 -04:00
|
|
|
struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
rb_backtrace_location_t *loc = &arg->bt->backtrace[arg->bt->backtrace_size++-1];
|
2012-06-02 12:46:08 -04:00
|
|
|
loc->type = LOCATION_TYPE_ISEQ;
|
|
|
|
loc->body.iseq.iseq = iseq;
|
|
|
|
loc->body.iseq.lineno.pc = pc;
|
|
|
|
arg->prev_loc = loc;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2020-10-11 07:36:25 -04:00
|
|
|
static void
|
|
|
|
bt_iter_iseq_skip_internal(void *ptr, const rb_control_frame_t *cfp)
|
|
|
|
{
|
|
|
|
struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr;
|
|
|
|
rb_backtrace_location_t *loc = &arg->bt->backtrace[arg->bt->backtrace_size++-1];
|
|
|
|
|
|
|
|
if (!is_internal_location(cfp)) {
|
|
|
|
loc->type = LOCATION_TYPE_ISEQ;
|
|
|
|
loc->body.iseq.iseq = cfp->iseq;
|
|
|
|
loc->body.iseq.lineno.pc = cfp->pc;
|
|
|
|
arg->prev_loc = loc;
|
|
|
|
} else if (arg->prev_cfp) {
|
|
|
|
loc->type = LOCATION_TYPE_ISEQ;
|
|
|
|
loc->body.iseq.iseq = arg->prev_cfp->iseq;
|
|
|
|
loc->body.iseq.lineno.pc = arg->prev_cfp->pc;
|
|
|
|
arg->prev_loc = loc;
|
|
|
|
} else {
|
|
|
|
rb_bug("No non-internal backtrace entry before an <internal: backtrace entry");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
static void
|
2012-11-29 02:05:27 -05:00
|
|
|
bt_iter_cfunc(void *ptr, const rb_control_frame_t *cfp, ID mid)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
rb_backtrace_location_t *loc = &arg->bt->backtrace[arg->bt->backtrace_size++-1];
|
2012-06-02 12:46:08 -04:00
|
|
|
loc->type = LOCATION_TYPE_CFUNC;
|
|
|
|
loc->body.cfunc.mid = mid;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
if (arg->prev_loc) {
|
|
|
|
loc->body.cfunc.prev_loc = arg->prev_loc;
|
|
|
|
}
|
|
|
|
else if (arg->prev_cfp) {
|
|
|
|
const rb_iseq_t *iseq = arg->prev_cfp->iseq;
|
|
|
|
const VALUE *pc = arg->prev_cfp->pc;
|
|
|
|
arg->init_loc->type = LOCATION_TYPE_ISEQ;
|
|
|
|
arg->init_loc->body.iseq.iseq = iseq;
|
|
|
|
arg->init_loc->body.iseq.lineno.pc = pc;
|
|
|
|
loc->body.cfunc.prev_loc = arg->prev_loc = arg->init_loc;
|
|
|
|
} else {
|
|
|
|
loc->body.cfunc.prev_loc = NULL;
|
|
|
|
}
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
static void
|
|
|
|
bt_iter_skip(void *ptr, const rb_control_frame_t *cfp)
|
|
|
|
{
|
|
|
|
if (cfp->iseq && cfp->pc) {
|
|
|
|
((struct bt_iter_arg *)ptr)->prev_cfp = cfp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-11 07:36:25 -04:00
|
|
|
static void
|
|
|
|
bt_iter_skip_skip_internal(void *ptr, const rb_control_frame_t *cfp)
|
|
|
|
{
|
|
|
|
if (cfp->iseq && cfp->pc) {
|
|
|
|
if (!is_internal_location(cfp)) {
|
|
|
|
((struct bt_iter_arg *) ptr)->prev_cfp = cfp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
static VALUE
|
2020-10-11 07:36:25 -04:00
|
|
|
rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long lev, long n, int* level_too_large, bool skip_internal)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
struct bt_iter_arg arg;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
int too_large;
|
2012-06-02 12:46:08 -04:00
|
|
|
arg.prev_loc = 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
too_large = backtrace_each(ec,
|
|
|
|
lev,
|
|
|
|
n,
|
|
|
|
bt_init,
|
2020-10-11 07:36:25 -04:00
|
|
|
skip_internal ? bt_iter_iseq_skip_internal : bt_iter_iseq,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
bt_iter_cfunc,
|
2020-10-11 07:36:25 -04:00
|
|
|
skip_internal ? bt_iter_skip_skip_internal : bt_iter_skip,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
&arg);
|
|
|
|
|
|
|
|
if (level_too_large) *level_too_large = too_large;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
return arg.btobj;
|
|
|
|
}
|
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
MJIT_FUNC_EXPORTED VALUE
|
|
|
|
rb_ec_backtrace_object(const rb_execution_context_t *ec)
|
|
|
|
{
|
2020-10-11 07:36:25 -04:00
|
|
|
return rb_ec_partial_backtrace_object(ec, BACKTRACE_START, ALL_BACKTRACE_LINES, NULL, FALSE);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
backtrace_collect(rb_backtrace_t *bt, VALUE (*func)(rb_backtrace_location_t *, void *arg), void *arg)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
VALUE btary;
|
|
|
|
int i;
|
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
btary = rb_ary_new2(bt->backtrace_size-1);
|
2012-06-02 11:23:37 -04:00
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
for (i=0; i<bt->backtrace_size-1; i++) {
|
|
|
|
rb_backtrace_location_t *loc = &bt->backtrace[bt->backtrace_size - 2 - i];
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_ary_push(btary, func(loc, arg));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return btary;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_to_str_dmyarg(rb_backtrace_location_t *loc, void *dmy)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-06-02 12:46:08 -04:00
|
|
|
return location_to_str(loc);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
backtrace_to_str_ary(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2020-08-12 14:43:11 -04:00
|
|
|
VALUE r;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
rb_backtrace_t *bt;
|
2012-06-02 11:23:37 -04:00
|
|
|
GetCoreDataFromValue(self, rb_backtrace_t, bt);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
r = backtrace_collect(bt, location_to_str_dmyarg, 0);
|
2012-11-29 19:38:24 -05:00
|
|
|
RB_GC_GUARD(self);
|
2012-11-29 17:35:09 -05:00
|
|
|
return r;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2012-11-28 15:30:01 -05:00
|
|
|
VALUE
|
|
|
|
rb_backtrace_to_str_ary(VALUE self)
|
|
|
|
{
|
|
|
|
rb_backtrace_t *bt;
|
|
|
|
GetCoreDataFromValue(self, rb_backtrace_t, bt);
|
|
|
|
|
|
|
|
if (!bt->strary) {
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
bt->strary = backtrace_to_str_ary(self);
|
2012-11-28 15:30:01 -05:00
|
|
|
}
|
|
|
|
return bt->strary;
|
|
|
|
}
|
|
|
|
|
mjit_compile.c: merge initial JIT compiler
which has been developed by Takashi Kokubun <takashikkbn@gmail> as
YARV-MJIT. Many of its bugs are fixed by wanabe <s.wanabe@gmail.com>.
This JIT compiler is designed to be a safe migration path to introduce
JIT compiler to MRI. So this commit does not include any bytecode
changes or dynamic instruction modifications, which are done in original
MJIT.
This commit even strips off some aggressive optimizations from
YARV-MJIT, and thus it's slower than YARV-MJIT too. But it's still
fairly faster than Ruby 2.5 in some benchmarks (attached below).
Note that this JIT compiler passes `make test`, `make test-all`, `make
test-spec` without JIT, and even with JIT. Not only it's perfectly safe
with JIT disabled because it does not replace VM instructions unlike
MJIT, but also with JIT enabled it stably runs Ruby applications
including Rails applications.
I'm expecting this version as just "initial" JIT compiler. I have many
optimization ideas which are skipped for initial merging, and you may
easily replace this JIT compiler with a faster one by just replacing
mjit_compile.c. `mjit_compile` interface is designed for the purpose.
common.mk: update dependencies for mjit_compile.c.
internal.h: declare `rb_vm_insn_addr2insn` for MJIT.
vm.c: exclude some definitions if `-DMJIT_HEADER` is provided to
compiler. This avoids to include some functions which take a long time
to compile, e.g. vm_exec_core. Some of the purpose is achieved in
transform_mjit_header.rb (see `IGNORED_FUNCTIONS`) but others are
manually resolved for now. Load mjit_helper.h for MJIT header.
mjit_helper.h: New. This is a file used only by JIT-ed code. I'll
refactor `mjit_call_cfunc` later.
vm_eval.c: add some #ifdef switches to skip compiling some functions
like Init_vm_eval.
win32/mkexports.rb: export thread/ec functions, which are used by MJIT.
include/ruby/defines.h: add MJIT_FUNC_EXPORTED macro alis to clarify
that a function is exported only for MJIT.
array.c: export a function used by MJIT.
bignum.c: ditto.
class.c: ditto.
compile.c: ditto.
error.c: ditto.
gc.c: ditto.
hash.c: ditto.
iseq.c: ditto.
numeric.c: ditto.
object.c: ditto.
proc.c: ditto.
re.c: ditto.
st.c: ditto.
string.c: ditto.
thread.c: ditto.
variable.c: ditto.
vm_backtrace.c: ditto.
vm_insnhelper.c: ditto.
vm_method.c: ditto.
I would like to improve maintainability of function exports, but I
believe this way is acceptable as initial merging if we clarify the
new exports are for MJIT (so that we can use them as TODO list to fix)
and add unit tests to detect unresolved symbols.
I'll add unit tests of JIT compilations in succeeding commits.
Author: Takashi Kokubun <takashikkbn@gmail.com>
Contributor: wanabe <s.wanabe@gmail.com>
Part of [Feature #14235]
---
* Known issues
* Code generated by gcc is faster than clang. The benchmark may be worse
in macOS. Following benchmark result is provided by gcc w/ Linux.
* Performance is decreased when Google Chrome is running
* JIT can work on MinGW, but it doesn't improve performance at least
in short running benchmark.
* Currently it doesn't perform well with Rails. We'll try to fix this
before release.
---
* Benchmark reslts
Benchmarked with:
Intel 4.0GHz i7-4790K with 16GB memory under x86-64 Ubuntu 8 Cores
- 2.0.0-p0: Ruby 2.0.0-p0
- r62186: Ruby trunk (early 2.6.0), before MJIT changes
- JIT off: On this commit, but without `--jit` option
- JIT on: On this commit, and with `--jit` option
** Optcarrot fps
Benchmark: https://github.com/mame/optcarrot
| |2.0.0-p0 |r62186 |JIT off |JIT on |
|:--------|:--------|:--------|:--------|:--------|
|fps |37.32 |51.46 |51.31 |58.88 |
|vs 2.0.0 |1.00x |1.38x |1.37x |1.58x |
** MJIT benchmarks
Benchmark: https://github.com/benchmark-driver/mjit-benchmarks
(Original: https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch/MJIT-benchmarks)
| |2.0.0-p0 |r62186 |JIT off |JIT on |
|:----------|:--------|:--------|:--------|:--------|
|aread |1.00 |1.09 |1.07 |2.19 |
|aref |1.00 |1.13 |1.11 |2.22 |
|aset |1.00 |1.50 |1.45 |2.64 |
|awrite |1.00 |1.17 |1.13 |2.20 |
|call |1.00 |1.29 |1.26 |2.02 |
|const2 |1.00 |1.10 |1.10 |2.19 |
|const |1.00 |1.11 |1.10 |2.19 |
|fannk |1.00 |1.04 |1.02 |1.00 |
|fib |1.00 |1.32 |1.31 |1.84 |
|ivread |1.00 |1.13 |1.12 |2.43 |
|ivwrite |1.00 |1.23 |1.21 |2.40 |
|mandelbrot |1.00 |1.13 |1.16 |1.28 |
|meteor |1.00 |2.97 |2.92 |3.17 |
|nbody |1.00 |1.17 |1.15 |1.49 |
|nest-ntimes|1.00 |1.22 |1.20 |1.39 |
|nest-while |1.00 |1.10 |1.10 |1.37 |
|norm |1.00 |1.18 |1.16 |1.24 |
|nsvb |1.00 |1.16 |1.16 |1.17 |
|red-black |1.00 |1.02 |0.99 |1.12 |
|sieve |1.00 |1.30 |1.28 |1.62 |
|trees |1.00 |1.14 |1.13 |1.19 |
|while |1.00 |1.12 |1.11 |2.41 |
** Discourse's script/bench.rb
Benchmark: https://github.com/discourse/discourse/blob/v1.8.7/script/bench.rb
NOTE: Rails performance was somehow a little degraded with JIT for now.
We should fix this.
(At least I know opt_aref is performing badly in JIT and I have an idea
to fix it. Please wait for the fix.)
*** JIT off
Your Results: (note for timings- percentile is first, duration is second in millisecs)
categories_admin:
50: 17
75: 18
90: 22
99: 29
home_admin:
50: 21
75: 21
90: 27
99: 40
topic_admin:
50: 17
75: 18
90: 22
99: 32
categories:
50: 35
75: 41
90: 43
99: 77
home:
50: 39
75: 46
90: 49
99: 95
topic:
50: 46
75: 52
90: 56
99: 101
*** JIT on
Your Results: (note for timings- percentile is first, duration is second in millisecs)
categories_admin:
50: 19
75: 21
90: 25
99: 33
home_admin:
50: 24
75: 26
90: 30
99: 35
topic_admin:
50: 19
75: 20
90: 25
99: 30
categories:
50: 40
75: 44
90: 48
99: 76
home:
50: 42
75: 48
90: 51
99: 89
topic:
50: 49
75: 55
90: 58
99: 99
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62197 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 06:22:28 -05:00
|
|
|
MJIT_FUNC_EXPORTED void
|
2017-11-09 00:22:51 -05:00
|
|
|
rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self)
|
|
|
|
{
|
|
|
|
const rb_backtrace_t *bt;
|
|
|
|
const rb_iseq_t *iseq;
|
|
|
|
rb_backtrace_location_t *loc;
|
|
|
|
|
|
|
|
GetCoreDataFromValue(self, rb_backtrace_t, bt);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
VM_ASSERT(bt->backtrace_size > 1);
|
2017-11-09 00:22:51 -05:00
|
|
|
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
loc = &bt->backtrace[bt->backtrace_size - 2];
|
2017-11-09 00:22:51 -05:00
|
|
|
iseq = loc->body.iseq.iseq;
|
|
|
|
|
|
|
|
VM_ASSERT(loc->type == LOCATION_TYPE_ISEQ);
|
|
|
|
|
|
|
|
loc->body.iseq.lineno.lineno = FIX2INT(iseq->body->location.first_lineno);
|
|
|
|
loc->type = LOCATION_TYPE_ISEQ_CALCED;
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
2012-06-02 12:46:08 -04:00
|
|
|
location_create(rb_backtrace_location_t *srcloc, void *btobj)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
VALUE obj;
|
2012-06-02 12:46:08 -04:00
|
|
|
struct valued_frame_info *vloc;
|
|
|
|
obj = TypedData_Make_Struct(rb_cBacktraceLocation, struct valued_frame_info, &location_data_type, vloc);
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
vloc->loc = srcloc;
|
|
|
|
vloc->btobj = (VALUE)btobj;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
backtrace_to_location_ary(VALUE self)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2020-08-12 14:43:11 -04:00
|
|
|
VALUE r;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
rb_backtrace_t *bt;
|
2012-06-02 11:23:37 -04:00
|
|
|
GetCoreDataFromValue(self, rb_backtrace_t, bt);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
r = backtrace_collect(bt, location_create, (void *)self);
|
2012-11-29 19:38:24 -05:00
|
|
|
RB_GC_GUARD(self);
|
2012-11-29 17:35:09 -05:00
|
|
|
return r;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2013-12-12 23:31:06 -05:00
|
|
|
VALUE
|
|
|
|
rb_backtrace_to_location_ary(VALUE self)
|
|
|
|
{
|
|
|
|
rb_backtrace_t *bt;
|
|
|
|
GetCoreDataFromValue(self, rb_backtrace_t, bt);
|
|
|
|
|
|
|
|
if (!bt->locary) {
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
bt->locary = backtrace_to_location_ary(self);
|
2013-12-12 23:31:06 -05:00
|
|
|
}
|
|
|
|
return bt->locary;
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:23:37 -04:00
|
|
|
static VALUE
|
|
|
|
backtrace_dump_data(VALUE self)
|
|
|
|
{
|
|
|
|
VALUE str = rb_backtrace_to_str_ary(self);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
backtrace_load_data(VALUE self, VALUE str)
|
|
|
|
{
|
|
|
|
rb_backtrace_t *bt;
|
|
|
|
GetCoreDataFromValue(self, rb_backtrace_t, bt);
|
|
|
|
bt->strary = str;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2021-02-15 01:58:45 -05:00
|
|
|
static VALUE
|
|
|
|
backtrace_limit(VALUE self)
|
|
|
|
{
|
|
|
|
return LONG2NUM(rb_backtrace_length_limit);
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:59:37 -04:00
|
|
|
VALUE
|
2017-10-28 09:22:04 -04:00
|
|
|
rb_ec_backtrace_str_ary(const rb_execution_context_t *ec, long lev, long n)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2020-10-11 07:36:25 -04:00
|
|
|
return backtrace_to_str_ary(rb_ec_partial_backtrace_object(ec, lev, n, NULL, FALSE));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2019-12-10 06:54:01 -05:00
|
|
|
VALUE
|
2020-10-11 07:36:25 -04:00
|
|
|
rb_ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, long n, bool skip_internal)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2020-10-11 07:36:25 -04:00
|
|
|
return backtrace_to_location_ary(rb_ec_partial_backtrace_object(ec, lev, n, NULL, skip_internal));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
2012-06-03 22:49:37 -04:00
|
|
|
/* make old style backtrace directly */
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
struct oldbt_arg {
|
|
|
|
VALUE filename;
|
2012-06-02 12:46:08 -04:00
|
|
|
int lineno;
|
|
|
|
void (*func)(void *data, VALUE file, int lineno, VALUE name);
|
2012-06-02 11:23:37 -04:00
|
|
|
void *data; /* result */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
oldbt_init(void *ptr, size_t dmy)
|
|
|
|
{
|
|
|
|
struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
|
2017-10-29 09:19:53 -04:00
|
|
|
arg->filename = GET_VM()->progname;
|
2012-06-02 12:46:08 -04:00
|
|
|
arg->lineno = 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-29 02:05:27 -05:00
|
|
|
oldbt_iter_iseq(void *ptr, const rb_control_frame_t *cfp)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
2012-11-29 02:05:27 -05:00
|
|
|
const rb_iseq_t *iseq = cfp->iseq;
|
|
|
|
const VALUE *pc = cfp->pc;
|
2012-06-02 11:23:37 -04:00
|
|
|
struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
|
2017-05-31 20:05:33 -04:00
|
|
|
VALUE file = arg->filename = rb_iseq_path(iseq);
|
2015-07-21 18:52:59 -04:00
|
|
|
VALUE name = iseq->body->location.label;
|
2012-06-02 12:46:08 -04:00
|
|
|
int lineno = arg->lineno = calc_lineno(iseq, pc);
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
(arg->func)(arg->data, file, lineno, name);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-29 02:05:27 -05:00
|
|
|
oldbt_iter_cfunc(void *ptr, const rb_control_frame_t *cfp, ID mid)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
struct oldbt_arg *arg = (struct oldbt_arg *)ptr;
|
|
|
|
VALUE file = arg->filename;
|
|
|
|
VALUE name = rb_id2str(mid);
|
2012-06-02 12:46:08 -04:00
|
|
|
int lineno = arg->lineno;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-06-02 12:46:08 -04:00
|
|
|
(arg->func)(arg->data, file, lineno, name);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-02 12:46:08 -04:00
|
|
|
oldbt_print(void *data, VALUE file, int lineno, VALUE name)
|
2012-06-02 11:23:37 -04:00
|
|
|
{
|
|
|
|
FILE *fp = (FILE *)data;
|
|
|
|
|
|
|
|
if (NIL_P(name)) {
|
|
|
|
fprintf(fp, "\tfrom %s:%d:in unknown method\n",
|
2012-06-02 12:46:08 -04:00
|
|
|
RSTRING_PTR(file), lineno);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(fp, "\tfrom %s:%d:in `%s'\n",
|
2012-06-02 12:46:08 -04:00
|
|
|
RSTRING_PTR(file), lineno, RSTRING_PTR(name));
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vm_backtrace_print(FILE *fp)
|
|
|
|
{
|
|
|
|
struct oldbt_arg arg;
|
|
|
|
|
|
|
|
arg.func = oldbt_print;
|
|
|
|
arg.data = (void *)fp;
|
2017-10-26 06:57:16 -04:00
|
|
|
backtrace_each(GET_EC(),
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
BACKTRACE_START,
|
|
|
|
ALL_BACKTRACE_LINES,
|
2012-06-02 11:23:37 -04:00
|
|
|
oldbt_init,
|
|
|
|
oldbt_iter_iseq,
|
|
|
|
oldbt_iter_cfunc,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
NULL,
|
2012-06-02 11:23:37 -04:00
|
|
|
&arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
oldbt_bugreport(void *arg, VALUE file, int line, VALUE method)
|
|
|
|
{
|
|
|
|
const char *filename = NIL_P(file) ? "ruby" : RSTRING_PTR(file);
|
|
|
|
if (!*(int *)arg) {
|
|
|
|
fprintf(stderr, "-- Ruby level backtrace information "
|
|
|
|
"----------------------------------------\n");
|
|
|
|
*(int *)arg = 1;
|
|
|
|
}
|
|
|
|
if (NIL_P(method)) {
|
|
|
|
fprintf(stderr, "%s:%d:in unknown method\n", filename, line);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(stderr, "%s:%d:in `%s'\n", filename, line, RSTRING_PTR(method));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_backtrace_print_as_bugreport(void)
|
|
|
|
{
|
|
|
|
struct oldbt_arg arg;
|
2014-01-24 02:17:17 -05:00
|
|
|
int i = 0;
|
2012-06-02 11:23:37 -04:00
|
|
|
|
|
|
|
arg.func = oldbt_bugreport;
|
|
|
|
arg.data = (int *)&i;
|
|
|
|
|
2017-10-26 06:57:16 -04:00
|
|
|
backtrace_each(GET_EC(),
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
BACKTRACE_START,
|
|
|
|
ALL_BACKTRACE_LINES,
|
2012-06-02 11:23:37 -04:00
|
|
|
oldbt_init,
|
|
|
|
oldbt_iter_iseq,
|
|
|
|
oldbt_iter_cfunc,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
NULL,
|
2012-06-02 11:23:37 -04:00
|
|
|
&arg);
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:59:37 -04:00
|
|
|
void
|
|
|
|
rb_backtrace(void)
|
|
|
|
{
|
|
|
|
vm_backtrace_print(stderr);
|
|
|
|
}
|
|
|
|
|
2017-04-25 04:17:24 -04:00
|
|
|
struct print_to_arg {
|
|
|
|
VALUE (*iter)(VALUE recv, VALUE str);
|
|
|
|
VALUE output;
|
|
|
|
};
|
|
|
|
|
2013-10-08 11:56:01 -04:00
|
|
|
static void
|
|
|
|
oldbt_print_to(void *data, VALUE file, int lineno, VALUE name)
|
|
|
|
{
|
2017-04-25 04:17:24 -04:00
|
|
|
const struct print_to_arg *arg = data;
|
2013-10-08 11:56:01 -04:00
|
|
|
VALUE str = rb_sprintf("\tfrom %"PRIsVALUE":%d:in ", file, lineno);
|
|
|
|
|
|
|
|
if (NIL_P(name)) {
|
|
|
|
rb_str_cat2(str, "unknown method\n");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_str_catf(str, " `%"PRIsVALUE"'\n", name);
|
|
|
|
}
|
2017-04-25 04:17:24 -04:00
|
|
|
(*arg->iter)(arg->output, str);
|
2013-10-08 11:56:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-04-25 04:17:24 -04:00
|
|
|
rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output)
|
2013-10-08 11:56:01 -04:00
|
|
|
{
|
|
|
|
struct oldbt_arg arg;
|
2017-04-25 04:17:24 -04:00
|
|
|
struct print_to_arg parg;
|
2013-10-08 11:56:01 -04:00
|
|
|
|
2017-04-25 04:17:24 -04:00
|
|
|
parg.iter = iter;
|
|
|
|
parg.output = output;
|
2013-10-08 11:56:01 -04:00
|
|
|
arg.func = oldbt_print_to;
|
2017-04-25 04:17:24 -04:00
|
|
|
arg.data = &parg;
|
2017-10-26 06:57:16 -04:00
|
|
|
backtrace_each(GET_EC(),
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
BACKTRACE_START,
|
|
|
|
ALL_BACKTRACE_LINES,
|
2013-10-08 11:56:01 -04:00
|
|
|
oldbt_init,
|
|
|
|
oldbt_iter_iseq,
|
|
|
|
oldbt_iter_cfunc,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
NULL,
|
2013-10-08 11:56:01 -04:00
|
|
|
&arg);
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:59:37 -04:00
|
|
|
VALUE
|
|
|
|
rb_make_backtrace(void)
|
|
|
|
{
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
return rb_ec_backtrace_str_ary(GET_EC(), BACKTRACE_START, ALL_BACKTRACE_LINES);
|
2012-06-02 11:59:37 -04:00
|
|
|
}
|
|
|
|
|
2012-11-19 01:07:06 -05:00
|
|
|
static VALUE
|
2017-10-28 09:22:04 -04:00
|
|
|
ec_backtrace_to_ary(const rb_execution_context_t *ec, int argc, const VALUE *argv, int lev_default, int lev_plus, int to_str)
|
2012-11-19 01:07:06 -05:00
|
|
|
{
|
|
|
|
VALUE level, vn;
|
2012-11-28 20:26:38 -05:00
|
|
|
long lev, n;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
VALUE btval;
|
2012-11-29 17:35:09 -05:00
|
|
|
VALUE r;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
int too_large;
|
2012-11-19 01:07:06 -05:00
|
|
|
|
|
|
|
rb_scan_args(argc, argv, "02", &level, &vn);
|
|
|
|
|
2013-09-10 01:37:39 -04:00
|
|
|
if (argc == 2 && NIL_P(vn)) argc--;
|
|
|
|
|
2012-11-28 19:12:49 -05:00
|
|
|
switch (argc) {
|
|
|
|
case 0:
|
|
|
|
lev = lev_default + lev_plus;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
n = ALL_BACKTRACE_LINES;
|
2012-11-28 19:12:49 -05:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
{
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
long beg, len, bt_size = backtrace_size(ec);
|
|
|
|
switch (rb_range_beg_len(level, &beg, &len, bt_size - lev_plus, 0)) {
|
2012-11-28 19:12:49 -05:00
|
|
|
case Qfalse:
|
|
|
|
lev = NUM2LONG(level);
|
|
|
|
if (lev < 0) {
|
2012-11-28 20:30:42 -05:00
|
|
|
rb_raise(rb_eArgError, "negative level (%ld)", lev);
|
2012-11-28 19:12:49 -05:00
|
|
|
}
|
|
|
|
lev += lev_plus;
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
n = ALL_BACKTRACE_LINES;
|
2012-11-28 19:12:49 -05:00
|
|
|
break;
|
|
|
|
case Qnil:
|
|
|
|
return Qnil;
|
|
|
|
default:
|
|
|
|
lev = beg + lev_plus;
|
|
|
|
n = len;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
2012-11-28 19:12:49 -05:00
|
|
|
case 2:
|
|
|
|
lev = NUM2LONG(level);
|
2012-12-03 02:23:57 -05:00
|
|
|
n = NUM2LONG(vn);
|
2012-11-28 19:12:49 -05:00
|
|
|
if (lev < 0) {
|
2012-11-28 20:30:42 -05:00
|
|
|
rb_raise(rb_eArgError, "negative level (%ld)", lev);
|
2012-11-28 19:12:49 -05:00
|
|
|
}
|
2012-12-03 02:23:57 -05:00
|
|
|
if (n < 0) {
|
|
|
|
rb_raise(rb_eArgError, "negative size (%ld)", n);
|
|
|
|
}
|
2012-11-28 19:12:49 -05:00
|
|
|
lev += lev_plus;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lev = n = 0; /* to avoid warning */
|
|
|
|
break;
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
|
|
|
|
2012-11-28 19:12:49 -05:00
|
|
|
if (n == 0) {
|
|
|
|
return rb_ary_new();
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
|
|
|
|
2020-10-11 07:36:25 -04:00
|
|
|
btval = rb_ec_partial_backtrace_object(ec, lev, n, &too_large, FALSE);
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
|
|
|
|
if (too_large) {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2012-11-19 01:07:06 -05:00
|
|
|
if (to_str) {
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
r = backtrace_to_str_ary(btval);
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
|
|
|
else {
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
r = backtrace_to_location_ary(btval);
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
2012-11-29 19:38:24 -05:00
|
|
|
RB_GC_GUARD(btval);
|
2012-11-29 17:35:09 -05:00
|
|
|
return r;
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2014-06-18 02:16:39 -04:00
|
|
|
thread_backtrace_to_ary(int argc, const VALUE *argv, VALUE thval, int to_str)
|
2012-06-02 11:59:37 -04:00
|
|
|
{
|
2017-06-28 00:49:30 -04:00
|
|
|
rb_thread_t *target_th = rb_thread_ptr(thval);
|
2012-06-02 11:59:37 -04:00
|
|
|
|
2017-06-28 00:49:30 -04:00
|
|
|
if (target_th->to_kill || target_th->status == THREAD_KILLED)
|
|
|
|
return Qnil;
|
2012-06-02 11:59:37 -04:00
|
|
|
|
2017-10-28 09:22:04 -04:00
|
|
|
return ec_backtrace_to_ary(target_th->ec, argc, argv, 0, 0, to_str);
|
2012-11-19 01:07:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2014-06-18 02:16:39 -04:00
|
|
|
rb_vm_thread_backtrace(int argc, const VALUE *argv, VALUE thval)
|
2012-11-19 01:07:06 -05:00
|
|
|
{
|
|
|
|
return thread_backtrace_to_ary(argc, argv, thval, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2014-06-18 02:16:39 -04:00
|
|
|
rb_vm_thread_backtrace_locations(int argc, const VALUE *argv, VALUE thval)
|
2012-11-19 01:07:06 -05:00
|
|
|
{
|
|
|
|
return thread_backtrace_to_ary(argc, argv, thval, 0);
|
2012-06-02 11:59:37 -04:00
|
|
|
}
|
|
|
|
|
2020-08-14 23:36:18 -04:00
|
|
|
VALUE rb_vm_backtrace(int argc, const VALUE * argv, struct rb_execution_context_struct * ec)
|
|
|
|
{
|
|
|
|
return ec_backtrace_to_ary(ec, argc, argv, 0, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE rb_vm_backtrace_locations(int argc, const VALUE * argv, struct rb_execution_context_struct * ec)
|
|
|
|
{
|
|
|
|
return ec_backtrace_to_ary(ec, argc, argv, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
2012-06-02 11:59:37 -04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2012-12-31 01:09:57 -05:00
|
|
|
* caller(start=1, length=nil) -> array or nil
|
|
|
|
* caller(range) -> array or nil
|
2012-06-02 11:59:37 -04:00
|
|
|
*
|
|
|
|
* Returns the current execution stack---an array containing strings in
|
2012-12-31 01:09:57 -05:00
|
|
|
* the form <code>file:line</code> or <code>file:line: in
|
|
|
|
* `method'</code>.
|
|
|
|
*
|
|
|
|
* The optional _start_ parameter determines the number of initial stack
|
|
|
|
* entries to omit from the top of the stack.
|
|
|
|
*
|
|
|
|
* A second optional +length+ parameter can be used to limit how many entries
|
|
|
|
* are returned from the stack.
|
2012-06-02 11:59:37 -04:00
|
|
|
*
|
|
|
|
* Returns +nil+ if _start_ is greater than the size of
|
|
|
|
* current execution stack.
|
|
|
|
*
|
2012-12-31 01:09:57 -05:00
|
|
|
* Optionally you can pass a range, which will return an array containing the
|
|
|
|
* entries within the specified range.
|
|
|
|
*
|
2012-06-02 11:59:37 -04:00
|
|
|
* def a(skip)
|
|
|
|
* caller(skip)
|
|
|
|
* end
|
|
|
|
* def b(skip)
|
|
|
|
* a(skip)
|
|
|
|
* end
|
|
|
|
* def c(skip)
|
|
|
|
* b(skip)
|
|
|
|
* end
|
|
|
|
* c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10:in `<main>'"]
|
|
|
|
* c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11:in `<main>'"]
|
|
|
|
* c(2) #=> ["prog:8:in `c'", "prog:12:in `<main>'"]
|
|
|
|
* c(3) #=> ["prog:13:in `<main>'"]
|
|
|
|
* c(4) #=> []
|
|
|
|
* c(5) #=> nil
|
|
|
|
*/
|
|
|
|
|
|
|
|
static VALUE
|
2019-08-28 05:19:11 -04:00
|
|
|
rb_f_caller(int argc, VALUE *argv, VALUE _)
|
2012-06-02 11:59:37 -04:00
|
|
|
{
|
2017-10-28 09:22:04 -04:00
|
|
|
return ec_backtrace_to_ary(GET_EC(), argc, argv, 1, 1, 1);
|
2012-06-02 11:59:37 -04:00
|
|
|
}
|
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* caller_locations(start=1, length=nil) -> array or nil
|
|
|
|
* caller_locations(range) -> array or nil
|
|
|
|
*
|
|
|
|
* Returns the current execution stack---an array containing
|
|
|
|
* backtrace location objects.
|
|
|
|
*
|
|
|
|
* See Thread::Backtrace::Location for more information.
|
|
|
|
*
|
|
|
|
* The optional _start_ parameter determines the number of initial stack
|
|
|
|
* entries to omit from the top of the stack.
|
|
|
|
*
|
|
|
|
* A second optional +length+ parameter can be used to limit how many entries
|
|
|
|
* are returned from the stack.
|
|
|
|
*
|
|
|
|
* Returns +nil+ if _start_ is greater than the size of
|
|
|
|
* current execution stack.
|
|
|
|
*
|
|
|
|
* Optionally you can pass a range, which will return an array containing the
|
|
|
|
* entries within the specified range.
|
|
|
|
*/
|
2012-06-02 11:59:37 -04:00
|
|
|
static VALUE
|
2019-08-28 05:19:11 -04:00
|
|
|
rb_f_caller_locations(int argc, VALUE *argv, VALUE _)
|
2012-06-02 11:59:37 -04:00
|
|
|
{
|
2017-10-28 09:22:04 -04:00
|
|
|
return ec_backtrace_to_ary(GET_EC(), argc, argv, 1, 1, 0);
|
2012-06-02 11:59:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* called from Init_vm() in vm.c */
|
|
|
|
void
|
2012-06-02 11:23:37 -04:00
|
|
|
Init_vm_backtrace(void)
|
|
|
|
{
|
2012-12-31 01:09:57 -05:00
|
|
|
/* :nodoc: */
|
2012-11-28 14:46:44 -05:00
|
|
|
rb_cBacktrace = rb_define_class_under(rb_cThread, "Backtrace", rb_cObject);
|
2012-06-02 11:23:37 -04:00
|
|
|
rb_define_alloc_func(rb_cBacktrace, backtrace_alloc);
|
|
|
|
rb_undef_method(CLASS_OF(rb_cBacktrace), "new");
|
|
|
|
rb_marshal_define_compat(rb_cBacktrace, rb_cArray, backtrace_dump_data, backtrace_load_data);
|
2021-02-15 01:58:45 -05:00
|
|
|
rb_define_singleton_method(rb_cBacktrace, "limit", backtrace_limit, 0);
|
2012-06-02 11:23:37 -04:00
|
|
|
|
2012-12-31 01:09:57 -05:00
|
|
|
/*
|
|
|
|
* An object representation of a stack frame, initialized by
|
|
|
|
* Kernel#caller_locations.
|
|
|
|
*
|
|
|
|
* For example:
|
|
|
|
*
|
|
|
|
* # caller_locations.rb
|
|
|
|
* def a(skip)
|
|
|
|
* caller_locations(skip)
|
|
|
|
* end
|
|
|
|
* def b(skip)
|
|
|
|
* a(skip)
|
|
|
|
* end
|
|
|
|
* def c(skip)
|
|
|
|
* b(skip)
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* c(0..2).map do |call|
|
|
|
|
* puts call.to_s
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* Running <code>ruby caller_locations.rb</code> will produce:
|
|
|
|
*
|
|
|
|
* caller_locations.rb:2:in `a'
|
|
|
|
* caller_locations.rb:5:in `b'
|
|
|
|
* caller_locations.rb:8:in `c'
|
|
|
|
*
|
|
|
|
* Here's another example with a slightly different result:
|
|
|
|
*
|
|
|
|
* # foo.rb
|
|
|
|
* class Foo
|
|
|
|
* attr_accessor :locations
|
|
|
|
* def initialize(skip)
|
|
|
|
* @locations = caller_locations(skip)
|
|
|
|
* end
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* Foo.new(0..2).locations.map do |call|
|
|
|
|
* puts call.to_s
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* Now run <code>ruby foo.rb</code> and you should see:
|
|
|
|
*
|
|
|
|
* init.rb:4:in `initialize'
|
|
|
|
* init.rb:8:in `new'
|
|
|
|
* init.rb:8:in `<main>'
|
|
|
|
*/
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_cBacktraceLocation = rb_define_class_under(rb_cBacktrace, "Location", rb_cObject);
|
|
|
|
rb_undef_alloc_func(rb_cBacktraceLocation);
|
|
|
|
rb_undef_method(CLASS_OF(rb_cBacktraceLocation), "new");
|
|
|
|
rb_define_method(rb_cBacktraceLocation, "lineno", location_lineno_m, 0);
|
|
|
|
rb_define_method(rb_cBacktraceLocation, "label", location_label_m, 0);
|
|
|
|
rb_define_method(rb_cBacktraceLocation, "base_label", location_base_label_m, 0);
|
|
|
|
rb_define_method(rb_cBacktraceLocation, "path", location_path_m, 0);
|
|
|
|
rb_define_method(rb_cBacktraceLocation, "absolute_path", location_absolute_path_m, 0);
|
|
|
|
rb_define_method(rb_cBacktraceLocation, "to_s", location_to_str_m, 0);
|
2012-12-01 07:09:17 -05:00
|
|
|
rb_define_method(rb_cBacktraceLocation, "inspect", location_inspect_m, 0);
|
2012-06-02 11:59:37 -04:00
|
|
|
|
|
|
|
rb_define_global_function("caller", rb_f_caller, -1);
|
2012-06-02 12:46:08 -04:00
|
|
|
rb_define_global_function("caller_locations", rb_f_caller_locations, -1);
|
2012-06-02 11:23:37 -04:00
|
|
|
}
|
2012-11-29 02:05:27 -05:00
|
|
|
|
|
|
|
/* debugger API */
|
|
|
|
|
2013-04-05 06:29:38 -04:00
|
|
|
RUBY_SYMBOL_EXPORT_BEGIN
|
2012-11-29 02:05:27 -05:00
|
|
|
|
2013-04-05 06:29:38 -04:00
|
|
|
RUBY_SYMBOL_EXPORT_END
|
2012-11-29 02:05:27 -05:00
|
|
|
|
|
|
|
struct rb_debug_inspector_struct {
|
2017-11-07 00:06:50 -05:00
|
|
|
rb_execution_context_t *ec;
|
2012-11-29 02:05:27 -05:00
|
|
|
rb_control_frame_t *cfp;
|
|
|
|
VALUE backtrace;
|
|
|
|
VALUE contexts; /* [[klass, binding, iseq, cfp], ...] */
|
2013-01-28 04:02:19 -05:00
|
|
|
long backtrace_size;
|
2012-11-29 02:05:27 -05:00
|
|
|
};
|
|
|
|
|
2013-01-29 03:25:32 -05:00
|
|
|
enum {
|
|
|
|
CALLER_BINDING_SELF,
|
|
|
|
CALLER_BINDING_CLASS,
|
|
|
|
CALLER_BINDING_BINDING,
|
|
|
|
CALLER_BINDING_ISEQ,
|
|
|
|
CALLER_BINDING_CFP
|
|
|
|
};
|
|
|
|
|
2012-11-29 02:05:27 -05:00
|
|
|
struct collect_caller_bindings_data {
|
|
|
|
VALUE ary;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
collect_caller_bindings_init(void *arg, size_t size)
|
|
|
|
{
|
|
|
|
/* */
|
|
|
|
}
|
|
|
|
|
2013-01-29 03:25:32 -05:00
|
|
|
static VALUE
|
|
|
|
get_klass(const rb_control_frame_t *cfp)
|
|
|
|
{
|
|
|
|
VALUE klass;
|
* vm_trace.c (tracepoint_attr_callee_id, rb_tracearg_callee_id):
add TracePoint#callee_id. [ruby-core:77241] [Feature #12747]
* cont.c, eval.c, gc.c, include/ruby/intern.h, insns.def, thread.c,
vm.c, vm_backtrace.c, vm_core.h, vm_eval.c, vm_insnhelper.c, vm_trace.c: ditto.
* test/ruby/test_settracefunc.rb: tests for above.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-11-05 09:15:27 -04:00
|
|
|
if (rb_vm_control_frame_id_and_class(cfp, 0, 0, &klass)) {
|
2013-01-29 03:25:32 -05:00
|
|
|
if (RB_TYPE_P(klass, T_ICLASS)) {
|
|
|
|
return RBASIC(klass)->klass;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return klass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-29 02:05:27 -05:00
|
|
|
static void
|
|
|
|
collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp)
|
|
|
|
{
|
|
|
|
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
|
2013-01-29 03:25:32 -05:00
|
|
|
VALUE frame = rb_ary_new2(5);
|
|
|
|
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_BINDING, GC_GUARDED_PTR(cfp)); /* create later */
|
2015-07-21 18:52:59 -04:00
|
|
|
rb_ary_store(frame, CALLER_BINDING_ISEQ, cfp->iseq ? (VALUE)cfp->iseq : Qnil);
|
2013-01-29 03:25:32 -05:00
|
|
|
rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
|
|
|
|
|
|
|
|
rb_ary_push(data->ary, frame);
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
|
|
|
|
{
|
|
|
|
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
|
2013-01-29 03:25:32 -05:00
|
|
|
VALUE frame = rb_ary_new2(5);
|
|
|
|
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_BINDING, Qnil); /* not available */
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_ISEQ, Qnil); /* not available */
|
|
|
|
rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
|
|
|
|
|
|
|
|
rb_ary_push(data->ary, frame);
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2017-11-07 00:06:50 -05:00
|
|
|
collect_caller_bindings(const rb_execution_context_t *ec)
|
2012-11-29 02:05:27 -05:00
|
|
|
{
|
|
|
|
struct collect_caller_bindings_data data;
|
2013-01-29 03:25:32 -05:00
|
|
|
VALUE result;
|
|
|
|
int i;
|
|
|
|
|
2012-11-29 02:05:27 -05:00
|
|
|
data.ary = rb_ary_new();
|
2013-01-29 03:25:32 -05:00
|
|
|
|
2017-11-07 00:06:50 -05:00
|
|
|
backtrace_each(ec,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
BACKTRACE_START,
|
|
|
|
ALL_BACKTRACE_LINES,
|
2012-11-29 02:05:27 -05:00
|
|
|
collect_caller_bindings_init,
|
|
|
|
collect_caller_bindings_iseq,
|
|
|
|
collect_caller_bindings_cfunc,
|
Improve performance of partial backtraces
Previously, backtrace_each fully populated the rb_backtrace_t with all
backtrace frames, even if caller only requested a partial backtrace
(e.g. Kernel#caller_locations(1, 1)). This changes backtrace_each to
only add the requested frames to the rb_backtrace_t.
To do this, backtrace_each needs to be passed the starting frame and
number of frames values passed to Kernel#caller or #caller_locations.
backtrace_each works from the top of the stack to the bottom, where the
bottom is the current frame. Due to how the location for cfuncs is
tracked using the location of the previous iseq, we need to store an
extra frame for the previous iseq if we are limiting the backtrace and
final backtrace frame (the first one stored) would be a cfunc and not
an iseq.
To limit the amount of work in this case, while scanning until the start
of the requested backtrace, for each iseq, store the cfp. If the first
backtrace frame we care about is a cfunc, use the stored cfp to find the
related iseq. Use a function pointer to handle the storage of the cfp
in the iteration arg, and also store the location of the extra frame
in the iteration arg.
backtrace_each needs to return int instead of void in order to signal
when a starting frame larger than backtrace size is given, as caller
and caller_locations needs to return nil and not the empty array in
these cases.
To handle cases where a range is provided with a negative end, and the
backtrace size is needed to calculate the result to pass to
rb_range_beg_len, add a backtrace_size static function to calculate
the size, which copies the logic from backtrace_each.
As backtrace_each only adds the backtrace lines requested,
backtrace_to_*_ary can be simplified to always operate on the entire
backtrace.
Previously, caller_locations(1,1) was about 6.2 times slower for an
800 deep callstack compared to an empty callstack. With this new
approach, it is only 1.3 times slower. It will always be somewhat
slower as it still needs to scan the cfps from the top of the stack
until it finds the first requested backtrace frame.
This initializes the backtrace memory to zero. I do not think this is
necessary, as from my analysis, nothing during the setting of the
backtrace entries can cause a garbage collection, but it seems the
safest approach, and it's unlikely the performance decrease is
significant.
This removes the rb_backtrace_t backtrace_base member. backtrace
and backtrace_base were initialized to the same value, and neither
is modified, so it doesn't make sense to have two pointers.
This also removes LOCATION_TYPE_IFUNC from vm_backtrace.c, as
the value is never set.
Fixes [Bug #17031]
2020-08-27 18:17:36 -04:00
|
|
|
NULL,
|
2012-11-29 02:05:27 -05:00
|
|
|
&data);
|
2013-01-29 03:25:32 -05:00
|
|
|
|
|
|
|
result = rb_ary_reverse(data.ary);
|
|
|
|
|
|
|
|
/* bindings should be created from top of frame */
|
|
|
|
for (i=0; i<RARRAY_LEN(result); i++) {
|
|
|
|
VALUE entry = rb_ary_entry(result, i);
|
|
|
|
VALUE cfp_val = rb_ary_entry(entry, CALLER_BINDING_BINDING);
|
|
|
|
|
|
|
|
if (!NIL_P(cfp_val)) {
|
|
|
|
rb_control_frame_t *cfp = GC_GUARDED_PTR_REF(cfp_val);
|
2017-11-07 00:06:50 -05:00
|
|
|
rb_ary_store(entry, CALLER_BINDING_BINDING, rb_vm_make_binding(ec, cfp));
|
2013-01-29 03:25:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that the passed `rb_debug_inspector_t' will be disabled
|
|
|
|
* after `rb_debug_inspector_open'.
|
|
|
|
*/
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data)
|
|
|
|
{
|
|
|
|
rb_debug_inspector_t dbg_context;
|
2017-11-07 00:06:50 -05:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
2017-06-23 03:25:52 -04:00
|
|
|
enum ruby_tag_type state;
|
2016-09-16 02:15:55 -04:00
|
|
|
volatile VALUE MAYBE_UNUSED(result);
|
2012-11-29 02:05:27 -05:00
|
|
|
|
2018-09-21 02:41:07 -04:00
|
|
|
/* escape all env to heap */
|
|
|
|
rb_vm_stack_to_heap(ec);
|
|
|
|
|
2017-11-07 00:06:50 -05:00
|
|
|
dbg_context.ec = ec;
|
|
|
|
dbg_context.cfp = dbg_context.ec->cfp;
|
2020-10-11 07:36:25 -04:00
|
|
|
dbg_context.backtrace = rb_ec_backtrace_location_ary(ec, BACKTRACE_START, ALL_BACKTRACE_LINES, FALSE);
|
2013-01-28 04:02:19 -05:00
|
|
|
dbg_context.backtrace_size = RARRAY_LEN(dbg_context.backtrace);
|
2017-11-07 00:06:50 -05:00
|
|
|
dbg_context.contexts = collect_caller_bindings(ec);
|
2012-11-29 02:05:27 -05:00
|
|
|
|
2017-11-07 00:06:50 -05:00
|
|
|
EC_PUSH_TAG(ec);
|
2017-12-05 22:16:08 -05:00
|
|
|
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
2012-11-29 02:05:27 -05:00
|
|
|
result = (*func)(&dbg_context, data);
|
|
|
|
}
|
2017-10-26 07:02:13 -04:00
|
|
|
EC_POP_TAG();
|
2012-11-29 02:05:27 -05:00
|
|
|
|
|
|
|
/* invalidate bindings? */
|
|
|
|
|
|
|
|
if (state) {
|
2017-11-07 00:06:50 -05:00
|
|
|
EC_JUMP_TAG(ec, state);
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2013-01-28 04:02:19 -05:00
|
|
|
frame_get(const rb_debug_inspector_t *dc, long index)
|
2012-11-29 02:05:27 -05:00
|
|
|
{
|
|
|
|
if (index < 0 || index >= dc->backtrace_size) {
|
|
|
|
rb_raise(rb_eArgError, "no such frame");
|
|
|
|
}
|
|
|
|
return rb_ary_entry(dc->contexts, index);
|
|
|
|
}
|
|
|
|
|
2013-01-29 03:25:32 -05:00
|
|
|
VALUE
|
|
|
|
rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index)
|
|
|
|
{
|
|
|
|
VALUE frame = frame_get(dc, index);
|
|
|
|
return rb_ary_entry(frame, CALLER_BINDING_SELF);
|
|
|
|
}
|
|
|
|
|
2012-11-29 02:05:27 -05:00
|
|
|
VALUE
|
2013-01-28 04:02:19 -05:00
|
|
|
rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index)
|
2012-11-29 02:05:27 -05:00
|
|
|
{
|
|
|
|
VALUE frame = frame_get(dc, index);
|
2013-01-29 03:25:32 -05:00
|
|
|
return rb_ary_entry(frame, CALLER_BINDING_CLASS);
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2013-01-28 04:02:19 -05:00
|
|
|
rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index)
|
2012-11-29 02:05:27 -05:00
|
|
|
{
|
|
|
|
VALUE frame = frame_get(dc, index);
|
2013-01-29 03:25:32 -05:00
|
|
|
return rb_ary_entry(frame, CALLER_BINDING_BINDING);
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2013-01-28 04:02:19 -05:00
|
|
|
rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index)
|
2012-11-29 02:05:27 -05:00
|
|
|
{
|
|
|
|
VALUE frame = frame_get(dc, index);
|
2015-07-21 18:52:59 -04:00
|
|
|
VALUE iseq = rb_ary_entry(frame, CALLER_BINDING_ISEQ);
|
|
|
|
|
|
|
|
return RTEST(iseq) ? rb_iseqw_new((rb_iseq_t *)iseq) : Qnil;
|
2012-11-29 02:05:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc)
|
|
|
|
{
|
|
|
|
return dc->backtrace;
|
|
|
|
}
|
|
|
|
|
2013-10-07 03:21:11 -04:00
|
|
|
int
|
|
|
|
rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
|
|
|
|
{
|
|
|
|
int i;
|
2017-10-26 04:41:34 -04:00
|
|
|
const rb_execution_context_t *ec = GET_EC();
|
|
|
|
const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
|
2015-12-20 20:18:48 -05:00
|
|
|
const rb_callable_method_entry_t *cme;
|
2013-10-07 03:21:11 -04:00
|
|
|
|
2018-04-26 20:13:51 -04:00
|
|
|
for (i=0; i<limit && cfp != end_cfp;) {
|
2019-08-01 03:04:09 -04:00
|
|
|
if (VM_FRAME_RUBYFRAME_P(cfp)) {
|
2013-10-07 03:21:11 -04:00
|
|
|
if (start > 0) {
|
|
|
|
start--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* record frame info */
|
2015-12-20 20:18:48 -05:00
|
|
|
cme = rb_vm_frame_method_entry(cfp);
|
|
|
|
if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) {
|
2015-07-06 14:44:54 -04:00
|
|
|
buff[i] = (VALUE)cme;
|
|
|
|
}
|
|
|
|
else {
|
2015-07-21 18:52:59 -04:00
|
|
|
buff[i] = (VALUE)cfp->iseq;
|
2015-07-06 14:44:54 -04:00
|
|
|
}
|
|
|
|
|
2015-12-20 20:18:48 -05:00
|
|
|
if (lines) lines[i] = calc_lineno(cfp->iseq, cfp->pc);
|
2015-07-06 14:44:54 -04:00
|
|
|
|
2013-10-07 03:21:11 -04:00
|
|
|
i++;
|
|
|
|
}
|
2020-07-08 04:32:28 -04:00
|
|
|
else {
|
|
|
|
cme = rb_vm_frame_method_entry(cfp);
|
|
|
|
if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
|
|
|
buff[i] = (VALUE)cme;
|
|
|
|
if (lines) lines[i] = 0;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
2018-04-26 20:13:51 -04:00
|
|
|
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2015-07-21 18:52:59 -04:00
|
|
|
static const rb_iseq_t *
|
2015-07-06 14:44:54 -04:00
|
|
|
frame2iseq(VALUE frame)
|
|
|
|
{
|
2015-07-21 18:52:59 -04:00
|
|
|
if (frame == Qnil) return NULL;
|
2015-07-06 14:44:54 -04:00
|
|
|
|
|
|
|
if (RB_TYPE_P(frame, T_IMEMO)) {
|
2015-07-21 18:52:59 -04:00
|
|
|
switch (imemo_type(frame)) {
|
|
|
|
case imemo_iseq:
|
|
|
|
return (const rb_iseq_t *)frame;
|
|
|
|
case imemo_ment:
|
|
|
|
{
|
2019-10-02 23:26:41 -04:00
|
|
|
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
2015-07-21 18:52:59 -04:00
|
|
|
switch (cme->def->type) {
|
|
|
|
case VM_METHOD_TYPE_ISEQ:
|
|
|
|
return cme->def->body.iseq.iseqptr;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2015-07-06 14:44:54 -04:00
|
|
|
default:
|
2015-07-21 18:52:59 -04:00
|
|
|
break;
|
2015-07-06 14:44:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rb_bug("frame2iseq: unreachable");
|
|
|
|
}
|
2013-10-07 03:21:11 -04:00
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_path(VALUE frame)
|
|
|
|
{
|
2015-07-21 18:52:59 -04:00
|
|
|
const rb_iseq_t *iseq = frame2iseq(frame);
|
|
|
|
return iseq ? rb_iseq_path(iseq) : Qnil;
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 04:32:28 -04:00
|
|
|
static const rb_callable_method_entry_t *
|
|
|
|
cframe(VALUE frame)
|
|
|
|
{
|
|
|
|
if (frame == Qnil) return NULL;
|
|
|
|
|
|
|
|
if (RB_TYPE_P(frame, T_IMEMO)) {
|
|
|
|
switch (imemo_type(frame)) {
|
|
|
|
case imemo_ment:
|
|
|
|
{
|
|
|
|
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
|
|
|
switch (cme->def->type) {
|
|
|
|
case VM_METHOD_TYPE_CFUNC:
|
|
|
|
return cme;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-07 03:21:11 -04:00
|
|
|
VALUE
|
|
|
|
rb_profile_frame_absolute_path(VALUE frame)
|
|
|
|
{
|
2020-07-08 04:32:28 -04:00
|
|
|
if (cframe(frame)) {
|
|
|
|
static VALUE cfunc_str = Qfalse;
|
|
|
|
if (!cfunc_str) {
|
|
|
|
cfunc_str = rb_str_new_literal("<cfunc>");
|
|
|
|
rb_gc_register_mark_object(cfunc_str);
|
|
|
|
}
|
|
|
|
return cfunc_str;
|
|
|
|
}
|
2015-07-21 18:52:59 -04:00
|
|
|
const rb_iseq_t *iseq = frame2iseq(frame);
|
2017-05-31 20:05:33 -04:00
|
|
|
return iseq ? rb_iseq_realpath(iseq) : Qnil;
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_label(VALUE frame)
|
|
|
|
{
|
2015-07-21 18:52:59 -04:00
|
|
|
const rb_iseq_t *iseq = frame2iseq(frame);
|
|
|
|
return iseq ? rb_iseq_label(iseq) : Qnil;
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_base_label(VALUE frame)
|
|
|
|
{
|
2015-07-21 18:52:59 -04:00
|
|
|
const rb_iseq_t *iseq = frame2iseq(frame);
|
|
|
|
return iseq ? rb_iseq_base_label(iseq) : Qnil;
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_first_lineno(VALUE frame)
|
|
|
|
{
|
2015-07-21 18:52:59 -04:00
|
|
|
const rb_iseq_t *iseq = frame2iseq(frame);
|
|
|
|
return iseq ? rb_iseq_first_lineno(iseq) : Qnil;
|
2015-07-06 14:44:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
frame2klass(VALUE frame)
|
|
|
|
{
|
|
|
|
if (frame == Qnil) return Qnil;
|
|
|
|
|
|
|
|
if (RB_TYPE_P(frame, T_IMEMO)) {
|
2019-10-02 23:26:41 -04:00
|
|
|
const rb_callable_method_entry_t *cme = (rb_callable_method_entry_t *)frame;
|
2016-04-11 07:19:52 -04:00
|
|
|
|
|
|
|
if (imemo_type(frame) == imemo_ment) {
|
|
|
|
return cme->defined_class;
|
|
|
|
}
|
2015-07-06 14:44:54 -04:00
|
|
|
}
|
2016-04-11 07:19:52 -04:00
|
|
|
return Qnil;
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_classpath(VALUE frame)
|
|
|
|
{
|
2015-07-06 14:44:54 -04:00
|
|
|
VALUE klass = frame2klass(frame);
|
2013-10-07 03:21:11 -04:00
|
|
|
|
|
|
|
if (klass && !NIL_P(klass)) {
|
|
|
|
if (RB_TYPE_P(klass, T_ICLASS)) {
|
|
|
|
klass = RBASIC(klass)->klass;
|
|
|
|
}
|
|
|
|
else if (FL_TEST(klass, FL_SINGLETON)) {
|
|
|
|
klass = rb_ivar_get(klass, id__attached__);
|
2020-05-06 12:49:25 -04:00
|
|
|
if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
|
2013-10-27 18:48:34 -04:00
|
|
|
return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
|
2013-10-07 03:21:11 -04:00
|
|
|
}
|
|
|
|
return rb_class_path(klass);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_singleton_method_p(VALUE frame)
|
|
|
|
{
|
2015-07-06 14:44:54 -04:00
|
|
|
VALUE klass = frame2klass(frame);
|
|
|
|
|
2013-10-07 03:21:11 -04:00
|
|
|
if (klass && !NIL_P(klass) && FL_TEST(klass, FL_SINGLETON)) {
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qfalse;
|
|
|
|
}
|
|
|
|
}
|
2013-10-08 08:08:20 -04:00
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_profile_frame_method_name(VALUE frame)
|
|
|
|
{
|
2020-07-08 04:32:28 -04:00
|
|
|
const rb_callable_method_entry_t *cme = cframe(frame);
|
|
|
|
if (cme) {
|
|
|
|
ID mid = cme->def->original_id;
|
|
|
|
return id2str(mid);
|
|
|
|
}
|
2015-07-21 18:52:59 -04:00
|
|
|
const rb_iseq_t *iseq = frame2iseq(frame);
|
|
|
|
return iseq ? rb_iseq_method_name(iseq) : Qnil;
|
2013-10-08 08:08:20 -04:00
|
|
|
}
|
|
|
|
|
2020-07-08 04:32:28 -04:00
|
|
|
static VALUE
|
|
|
|
qualified_method_name(VALUE frame, VALUE method_name)
|
2013-10-08 08:08:20 -04:00
|
|
|
{
|
|
|
|
if (method_name != Qnil) {
|
|
|
|
VALUE classpath = rb_profile_frame_classpath(frame);
|
|
|
|
VALUE singleton_p = rb_profile_frame_singleton_method_p(frame);
|
|
|
|
|
|
|
|
if (classpath != Qnil) {
|
|
|
|
return rb_sprintf("%"PRIsVALUE"%s%"PRIsVALUE,
|
|
|
|
classpath, singleton_p == Qtrue ? "." : "#", method_name);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return method_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
2013-10-08 20:21:51 -04:00
|
|
|
|
2020-07-08 04:32:28 -04:00
|
|
|
VALUE
|
|
|
|
rb_profile_frame_qualified_method_name(VALUE frame)
|
|
|
|
{
|
|
|
|
VALUE method_name = rb_profile_frame_method_name(frame);
|
|
|
|
|
|
|
|
return qualified_method_name(frame, method_name);
|
|
|
|
}
|
|
|
|
|
2013-10-08 20:21:51 -04:00
|
|
|
VALUE
|
|
|
|
rb_profile_frame_full_label(VALUE frame)
|
|
|
|
{
|
2020-07-08 04:32:28 -04:00
|
|
|
const rb_callable_method_entry_t *cme = cframe(frame);
|
|
|
|
if (cme) {
|
|
|
|
ID mid = cme->def->original_id;
|
|
|
|
VALUE method_name = id2str(mid);
|
|
|
|
return qualified_method_name(frame, method_name);
|
|
|
|
}
|
|
|
|
|
2013-10-08 20:21:51 -04:00
|
|
|
VALUE label = rb_profile_frame_label(frame);
|
|
|
|
VALUE base_label = rb_profile_frame_base_label(frame);
|
|
|
|
VALUE qualified_method_name = rb_profile_frame_qualified_method_name(frame);
|
|
|
|
|
|
|
|
if (NIL_P(qualified_method_name) || base_label == qualified_method_name) {
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
long label_length = RSTRING_LEN(label);
|
|
|
|
long base_label_length = RSTRING_LEN(base_label);
|
2013-10-09 00:27:39 -04:00
|
|
|
int prefix_len = rb_long2int(label_length - base_label_length);
|
2013-10-08 20:21:51 -04:00
|
|
|
|
2013-10-09 00:27:39 -04:00
|
|
|
return rb_sprintf("%.*s%"PRIsVALUE, prefix_len, RSTRING_PTR(label), qualified_method_name);
|
2013-10-08 20:21:51 -04:00
|
|
|
}
|
|
|
|
}
|