* eval.c, node.h, thread.c, yarvcore.[ch], eval_intern.h:
support set_trace_func (incomplete. id and klass don't be passed). And support Thread#set_trace_func which hook only specified thread and Thread#add_trace_func which add new trace func instead of replace old one. C level API was modified. See thread.c (logic) and yarvcore.h (data structures). * vm.c, vm_macro.def: add hook points. * compile.c, insns.def: fix "trace" instruction. * iseq.c, vm_macro.h: add compile option "trace_instruction". * test/ruby/test_settracefunc.rb: hook "c-return" of set_trace_func. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12195 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
2dd91facca
commit
a73894337a
18
ChangeLog
18
ChangeLog
@ -1,3 +1,21 @@
|
|||||||
|
Thu Apr 19 18:37:49 2007 Koichi Sasada <ko1@atdot.net>
|
||||||
|
|
||||||
|
* eval.c, node.h, thread.c, yarvcore.[ch], eval_intern.h:
|
||||||
|
support set_trace_func (incomplete. id and klass
|
||||||
|
don't be passed). And support Thread#set_trace_func
|
||||||
|
which hook only specified thread and Thread#add_trace_func
|
||||||
|
which add new trace func instead of replace old one.
|
||||||
|
C level API was modified. See thread.c (logic) and
|
||||||
|
yarvcore.h (data structures).
|
||||||
|
|
||||||
|
* vm.c, vm_macro.def: add hook points.
|
||||||
|
|
||||||
|
* compile.c, insns.def: fix "trace" instruction.
|
||||||
|
|
||||||
|
* iseq.c, vm_macro.h: add compile option "trace_instruction".
|
||||||
|
|
||||||
|
* test/ruby/test_settracefunc.rb: hook "c-return" of set_trace_func.
|
||||||
|
|
||||||
Thu Apr 19 17:46:36 2007 Koichi Sasada <ko1@atdot.net>
|
Thu Apr 19 17:46:36 2007 Koichi Sasada <ko1@atdot.net>
|
||||||
|
|
||||||
* lib/optparse.rb: fix to override conv proc.
|
* lib/optparse.rb: fix to override conv proc.
|
||||||
|
18
compile.c
18
compile.c
@ -162,7 +162,19 @@ rb_iseq_compile(VALUE self, NODE *node)
|
|||||||
ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
|
ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
COMPILE(ret, "scoped node", node->nd_body);
|
if (iseq->type == ISEQ_TYPE_CLASS) {
|
||||||
|
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CLASS);
|
||||||
|
COMPILE(ret, "scoped node", node->nd_body);
|
||||||
|
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
|
||||||
|
}
|
||||||
|
else if (iseq->type == ISEQ_TYPE_METHOD) {
|
||||||
|
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CALL);
|
||||||
|
COMPILE(ret, "scoped node", node->nd_body);
|
||||||
|
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
COMPILE(ret, "scoped node", node->nd_body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2433,6 +2445,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
|||||||
|
|
||||||
type = nd_type(node);
|
type = nd_type(node);
|
||||||
|
|
||||||
|
if (node->flags & NODE_NEWLINE) {
|
||||||
|
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
case NODE_METHOD:{
|
case NODE_METHOD:{
|
||||||
|
@ -157,6 +157,11 @@ r_value(VALUE value)
|
|||||||
new_insn_send(iseq, line, \
|
new_insn_send(iseq, line, \
|
||||||
(VALUE)id, (VALUE)argc, (VALUE)block, (VALUE)flag))
|
(VALUE)id, (VALUE)argc, (VALUE)block, (VALUE)flag))
|
||||||
|
|
||||||
|
#define ADD_TRACE(seq, line, event) \
|
||||||
|
if (iseq->compile_data->option->trace_instruction) { \
|
||||||
|
ADD_INSN1(seq, line, trace, INT2FIX(event)); \
|
||||||
|
}
|
||||||
|
|
||||||
/* add label */
|
/* add label */
|
||||||
#define ADD_LABEL(seq, label) \
|
#define ADD_LABEL(seq, label) \
|
||||||
ADD_ELEM(seq, (LINK_ELEMENT *)label)
|
ADD_ELEM(seq, (LINK_ELEMENT *)label)
|
||||||
|
274
eval.c
274
eval.c
@ -40,29 +40,6 @@ static VALUE eval _((VALUE, VALUE, VALUE, char *, int));
|
|||||||
static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int, int));
|
static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int, int));
|
||||||
static VALUE rb_call(VALUE, VALUE, ID, int, const VALUE *, int);
|
static VALUE rb_call(VALUE, VALUE, ID, int, const VALUE *, int);
|
||||||
|
|
||||||
static void rb_clear_trace_func(void);
|
|
||||||
|
|
||||||
typedef struct event_hook {
|
|
||||||
rb_event_hook_func_t func;
|
|
||||||
rb_event_t events;
|
|
||||||
struct event_hook *next;
|
|
||||||
} rb_event_hook_t;
|
|
||||||
|
|
||||||
static rb_event_hook_t *event_hooks;
|
|
||||||
|
|
||||||
#define EXEC_EVENT_HOOK(event, node, self, id, klass) \
|
|
||||||
do { \
|
|
||||||
rb_event_hook_t *hook; \
|
|
||||||
\
|
|
||||||
for (hook = event_hooks; hook; hook = hook->next) { \
|
|
||||||
if (hook->events & event) \
|
|
||||||
(*hook->func)(event, node, self, id, klass); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
static void call_trace_func _((rb_event_t, NODE *, VALUE, ID, VALUE));
|
|
||||||
|
|
||||||
|
|
||||||
#include "eval_error.h"
|
#include "eval_error.h"
|
||||||
#include "eval_method.h"
|
#include "eval_method.h"
|
||||||
#include "eval_safe.h"
|
#include "eval_safe.h"
|
||||||
@ -162,8 +139,8 @@ ruby_finalize_1(void)
|
|||||||
{
|
{
|
||||||
signal(SIGINT, SIG_DFL);
|
signal(SIGINT, SIG_DFL);
|
||||||
GET_THREAD()->errinfo = 0;
|
GET_THREAD()->errinfo = 0;
|
||||||
rb_gc_call_finalizer_at_exit();
|
|
||||||
rb_clear_trace_func();
|
rb_clear_trace_func();
|
||||||
|
rb_gc_call_finalizer_at_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -446,219 +423,6 @@ rb_frozen_class_p(VALUE klass)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef C_ALLOCA
|
|
||||||
# define TMP_PROTECT NODE * volatile tmp__protect_tmp=0
|
|
||||||
# define TMP_ALLOC(n) \
|
|
||||||
(tmp__protect_tmp = NEW_NODE(NODE_ALLOCA, \
|
|
||||||
ALLOC_N(VALUE,n),tmp__protect_tmp,n), \
|
|
||||||
(void*)tmp__protect_tmp->nd_head)
|
|
||||||
#else
|
|
||||||
# define TMP_PROTECT typedef int foobazzz
|
|
||||||
# define TMP_ALLOC(n) ALLOCA_N(VALUE,n)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MATCH_DATA *rb_svar(node->nd_cnt)
|
|
||||||
|
|
||||||
void
|
|
||||||
rb_add_event_hook(func, events)
|
|
||||||
rb_event_hook_func_t func;
|
|
||||||
rb_event_t events;
|
|
||||||
{
|
|
||||||
rb_event_hook_t *hook;
|
|
||||||
|
|
||||||
hook = ALLOC(rb_event_hook_t);
|
|
||||||
hook->func = func;
|
|
||||||
hook->events = events;
|
|
||||||
hook->next = event_hooks;
|
|
||||||
event_hooks = hook;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
rb_remove_event_hook(rb_event_hook_func_t func)
|
|
||||||
{
|
|
||||||
rb_event_hook_t *prev, *hook;
|
|
||||||
|
|
||||||
prev = NULL;
|
|
||||||
hook = event_hooks;
|
|
||||||
while (hook) {
|
|
||||||
if (hook->func == func) {
|
|
||||||
if (prev) {
|
|
||||||
prev->next = hook->next;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event_hooks = hook->next;
|
|
||||||
}
|
|
||||||
xfree(hook);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
prev = hook;
|
|
||||||
hook = hook->next;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
rb_clear_trace_func(void)
|
|
||||||
{
|
|
||||||
/* TODO: fix me */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* call-seq:
|
|
||||||
* set_trace_func(proc) => proc
|
|
||||||
* set_trace_func(nil) => nil
|
|
||||||
*
|
|
||||||
* Establishes _proc_ as the handler for tracing, or disables
|
|
||||||
* tracing if the parameter is +nil+. _proc_ takes up
|
|
||||||
* to six parameters: an event name, a filename, a line number, an
|
|
||||||
* object id, a binding, and the name of a class. _proc_ is
|
|
||||||
* invoked whenever an event occurs. Events are: <code>c-call</code>
|
|
||||||
* (call a C-language routine), <code>c-return</code> (return from a
|
|
||||||
* C-language routine), <code>call</code> (call a Ruby method),
|
|
||||||
* <code>class</code> (start a class or module definition),
|
|
||||||
* <code>end</code> (finish a class or module definition),
|
|
||||||
* <code>line</code> (execute code on a new line), <code>raise</code>
|
|
||||||
* (raise an exception), and <code>return</code> (return from a Ruby
|
|
||||||
* method). Tracing is disabled within the context of _proc_.
|
|
||||||
*
|
|
||||||
* class Test
|
|
||||||
* def test
|
|
||||||
* a = 1
|
|
||||||
* b = 2
|
|
||||||
* end
|
|
||||||
* end
|
|
||||||
*
|
|
||||||
* set_trace_func proc { |event, file, line, id, binding, classname|
|
|
||||||
* printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
|
||||||
* }
|
|
||||||
* t = Test.new
|
|
||||||
* t.test
|
|
||||||
*
|
|
||||||
* line prog.rb:11 false
|
|
||||||
* c-call prog.rb:11 new Class
|
|
||||||
* c-call prog.rb:11 initialize Object
|
|
||||||
* c-return prog.rb:11 initialize Object
|
|
||||||
* c-return prog.rb:11 new Class
|
|
||||||
* line prog.rb:12 false
|
|
||||||
* call prog.rb:2 test Test
|
|
||||||
* line prog.rb:3 test Test
|
|
||||||
* line prog.rb:4 test Test
|
|
||||||
* return prog.rb:4 test Test
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
static VALUE
|
|
||||||
set_trace_func(VALUE obj, VALUE trace)
|
|
||||||
{
|
|
||||||
rb_event_hook_t *hook;
|
|
||||||
|
|
||||||
if (NIL_P(trace)) {
|
|
||||||
rb_clear_trace_func();
|
|
||||||
rb_remove_event_hook(call_trace_func);
|
|
||||||
return Qnil;
|
|
||||||
}
|
|
||||||
if (!rb_obj_is_proc(trace)) {
|
|
||||||
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* register trace func */
|
|
||||||
/* trace_func = trace; */
|
|
||||||
|
|
||||||
for (hook = event_hooks; hook; hook = hook->next) {
|
|
||||||
if (hook->func == call_trace_func)
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL);
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *
|
|
||||||
get_event_name(rb_event_t event)
|
|
||||||
{
|
|
||||||
switch (event) {
|
|
||||||
case RUBY_EVENT_LINE:
|
|
||||||
return "line";
|
|
||||||
case RUBY_EVENT_CLASS:
|
|
||||||
return "class";
|
|
||||||
case RUBY_EVENT_END:
|
|
||||||
return "end";
|
|
||||||
case RUBY_EVENT_CALL:
|
|
||||||
return "call";
|
|
||||||
case RUBY_EVENT_RETURN:
|
|
||||||
return "return";
|
|
||||||
case RUBY_EVENT_C_CALL:
|
|
||||||
return "c-call";
|
|
||||||
case RUBY_EVENT_C_RETURN:
|
|
||||||
return "c-return";
|
|
||||||
case RUBY_EVENT_RAISE:
|
|
||||||
return "raise";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
call_trace_func(rb_event_t event, NODE *node, VALUE self, ID id, VALUE klass)
|
|
||||||
{
|
|
||||||
/* TODO: fix me */
|
|
||||||
#if 0
|
|
||||||
int state, raised;
|
|
||||||
NODE *node_save;
|
|
||||||
VALUE srcfile;
|
|
||||||
char *event_name;
|
|
||||||
|
|
||||||
if (!trace_func)
|
|
||||||
return;
|
|
||||||
if (tracing)
|
|
||||||
return;
|
|
||||||
if (id == ID_ALLOCATOR)
|
|
||||||
return;
|
|
||||||
if (!node && ruby_sourceline == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!(node_save = ruby_current_node)) {
|
|
||||||
node_save = NEW_BEGIN(0);
|
|
||||||
}
|
|
||||||
tracing = 1;
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
ruby_current_node = node;
|
|
||||||
ruby_sourcefile = node->nd_file;
|
|
||||||
ruby_sourceline = nd_line(node);
|
|
||||||
}
|
|
||||||
if (klass) {
|
|
||||||
if (TYPE(klass) == T_ICLASS) {
|
|
||||||
klass = RBASIC(klass)->klass;
|
|
||||||
}
|
|
||||||
else if (FL_TEST(klass, FL_SINGLETON)) {
|
|
||||||
klass = self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PUSH_TAG(PROT_NONE);
|
|
||||||
raised = thread_reset_raised(th);
|
|
||||||
if ((state = EXEC_TAG()) == 0) {
|
|
||||||
srcfile = rb_str_new2(ruby_sourcefile ? ruby_sourcefile : "(ruby)");
|
|
||||||
event_name = get_event_name(event);
|
|
||||||
proc_invoke(trace_func, rb_ary_new3(6, rb_str_new2(event_name),
|
|
||||||
srcfile,
|
|
||||||
INT2FIX(ruby_sourceline),
|
|
||||||
id ? ID2SYM(id) : Qnil,
|
|
||||||
self ? rb_binding_new() : Qnil,
|
|
||||||
klass ? klass : Qnil), Qundef, 0);
|
|
||||||
}
|
|
||||||
if (raised)
|
|
||||||
thread_set_raised(th);
|
|
||||||
POP_TAG();
|
|
||||||
|
|
||||||
tracing = 0;
|
|
||||||
ruby_current_node = node_save;
|
|
||||||
SET_CURRENT_SOURCE();
|
|
||||||
if (state)
|
|
||||||
JUMP_TAG(state);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* obj.respond_to?(symbol, include_private=false) => true or false
|
* obj.respond_to?(symbol, include_private=false) => true or false
|
||||||
@ -884,19 +648,11 @@ NORETURN(static void rb_longjmp _((int, VALUE)));
|
|||||||
static VALUE make_backtrace _((void));
|
static VALUE make_backtrace _((void));
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rb_longjmp(tag, mesg)
|
rb_longjmp(int tag, VALUE mesg)
|
||||||
int tag;
|
|
||||||
VALUE mesg;
|
|
||||||
{
|
{
|
||||||
VALUE at;
|
VALUE at;
|
||||||
rb_thread_t *th = GET_THREAD();
|
rb_thread_t *th = GET_THREAD();
|
||||||
|
|
||||||
/*
|
|
||||||
//while (th->cfp->pc == 0 || th->cfp->iseq == 0) {
|
|
||||||
//th->cfp++;
|
|
||||||
//}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (thread_set_raised(th)) {
|
if (thread_set_raised(th)) {
|
||||||
th->errinfo = exception_error;
|
th->errinfo = exception_error;
|
||||||
JUMP_TAG(TAG_FATAL);
|
JUMP_TAG(TAG_FATAL);
|
||||||
@ -943,7 +699,8 @@ rb_longjmp(tag, mesg)
|
|||||||
|
|
||||||
rb_trap_restore_mask();
|
rb_trap_restore_mask();
|
||||||
if (tag != TAG_FATAL) {
|
if (tag != TAG_FATAL) {
|
||||||
/* EXEC_EVENT_HOOK(RUBY_EVENT_RAISE ...) */
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self,
|
||||||
|
0 /* TODO: id */, 0 /* TODO: klass */);
|
||||||
}
|
}
|
||||||
thread_reset_raised(th);
|
thread_reset_raised(th);
|
||||||
JUMP_TAG(tag);
|
JUMP_TAG(tag);
|
||||||
@ -1889,18 +1646,29 @@ rb_frame_self(void)
|
|||||||
const char *
|
const char *
|
||||||
rb_sourcefile(void)
|
rb_sourcefile(void)
|
||||||
{
|
{
|
||||||
rb_iseq_t *iseq = GET_THREAD()->cfp->iseq;
|
rb_thread_t *th = GET_THREAD();
|
||||||
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
rb_control_frame_t *cfp = th_get_ruby_level_cfp(th, th->cfp);
|
||||||
return RSTRING_PTR(iseq->filename);
|
|
||||||
|
if (cfp) {
|
||||||
|
return RSTRING_PTR(cfp->iseq->filename);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
rb_sourceline(void)
|
rb_sourceline(void)
|
||||||
{
|
{
|
||||||
rb_thread_t *th = GET_THREAD();
|
rb_thread_t *th = GET_THREAD();
|
||||||
return th_get_sourceline(th->cfp);
|
rb_control_frame_t *cfp = th_get_ruby_level_cfp(th, th->cfp);
|
||||||
|
|
||||||
|
if (cfp) {
|
||||||
|
return th_get_sourceline(cfp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
@ -2991,8 +2759,6 @@ Init_eval(void)
|
|||||||
rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */
|
rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */
|
||||||
rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */
|
rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */
|
||||||
|
|
||||||
rb_define_global_function("set_trace_func", set_trace_func, 1);
|
|
||||||
|
|
||||||
rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
|
rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,4 +239,30 @@ void rb_thread_terminate_all(void);
|
|||||||
|
|
||||||
#define ruby_cbase() th_get_cbase(GET_THREAD())
|
#define ruby_cbase() th_get_cbase(GET_THREAD())
|
||||||
|
|
||||||
|
|
||||||
|
/* tracer */
|
||||||
|
static void inline
|
||||||
|
exec_event_hooks(rb_event_hook_t *hook, rb_event_flag_t flag, VALUE self, ID id, VALUE klass)
|
||||||
|
{
|
||||||
|
while (hook) {
|
||||||
|
(*hook->func)(flag, hook->data, self, id, klass);
|
||||||
|
hook = hook->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXEC_EVENT_HOOK(th, flag, self, id, klass) do { \
|
||||||
|
rb_event_flag_t wait_event__ = th->event_flags; \
|
||||||
|
if (UNLIKELY(wait_event__)) { \
|
||||||
|
VALUE self__ = (self), klass__ = (klass); \
|
||||||
|
ID id__ = (id); \
|
||||||
|
if (wait_event__ & flag) { \
|
||||||
|
exec_event_hooks(th->event_hooks, flag, self__, id__, klass__); \
|
||||||
|
} \
|
||||||
|
if (wait_event__ & RUBY_EVENT_VM) { \
|
||||||
|
exec_event_hooks(th->vm->event_hooks, flag, self__, id__, klass__); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
#endif /* EVAL_INTERN_H_INCLUDED */
|
#endif /* EVAL_INTERN_H_INCLUDED */
|
||||||
|
10
insns.def
10
insns.def
@ -843,7 +843,6 @@ definemethod
|
|||||||
get_cref(GET_ISEQ(), GET_LFP()));
|
get_cref(GET_ISEQ(), GET_LFP()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@c setting
|
@c setting
|
||||||
@e make alias (if v_p is Qtrue, make valias)
|
@e make alias (if v_p is Qtrue, make valias)
|
||||||
@ -1020,15 +1019,12 @@ postexe
|
|||||||
*/
|
*/
|
||||||
DEFINE_INSN
|
DEFINE_INSN
|
||||||
trace
|
trace
|
||||||
(num_t flag, VALUE args)
|
(num_t nf)
|
||||||
()
|
()
|
||||||
()
|
()
|
||||||
{
|
{
|
||||||
/* TODO: trace instruction design */
|
rb_event_flag_t flag = nf;
|
||||||
if (th->vm->trace_flag & flag) {
|
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */);
|
||||||
/* */
|
|
||||||
args = Qnil;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************/
|
/**********************************************************/
|
||||||
|
2
iseq.c
2
iseq.c
@ -200,6 +200,7 @@ static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
|
|||||||
OPT_OPERANDS_UNIFICATION, /* int operands_unification; */
|
OPT_OPERANDS_UNIFICATION, /* int operands_unification; */
|
||||||
OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
|
OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
|
||||||
OPT_STACK_CACHING, /* int stack_caching; */
|
OPT_STACK_CACHING, /* int stack_caching; */
|
||||||
|
OPT_TRACE_INSTRUCTION,
|
||||||
};
|
};
|
||||||
static const rb_compile_option_t COMPILE_OPTION_FALSE;
|
static const rb_compile_option_t COMPILE_OPTION_FALSE;
|
||||||
|
|
||||||
@ -227,6 +228,7 @@ make_compile_option(rb_compile_option_t *option, VALUE opt)
|
|||||||
SET_COMPILE_OPTION(option, opt, operands_unification);
|
SET_COMPILE_OPTION(option, opt, operands_unification);
|
||||||
SET_COMPILE_OPTION(option, opt, instructions_unification);
|
SET_COMPILE_OPTION(option, opt, instructions_unification);
|
||||||
SET_COMPILE_OPTION(option, opt, stack_caching);
|
SET_COMPILE_OPTION(option, opt, stack_caching);
|
||||||
|
SET_COMPILE_OPTION(option, opt, trace_instruction);
|
||||||
#undef SET_COMPILE_OPTION
|
#undef SET_COMPILE_OPTION
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
17
node.h
17
node.h
@ -387,23 +387,6 @@ VALUE rb_gvar_get(struct global_entry *);
|
|||||||
VALUE rb_gvar_set(struct global_entry *, VALUE);
|
VALUE rb_gvar_set(struct global_entry *, VALUE);
|
||||||
VALUE rb_gvar_defined(struct global_entry *);
|
VALUE rb_gvar_defined(struct global_entry *);
|
||||||
|
|
||||||
typedef unsigned int rb_event_t;
|
|
||||||
|
|
||||||
#define RUBY_EVENT_NONE 0x00
|
|
||||||
#define RUBY_EVENT_LINE 0x01
|
|
||||||
#define RUBY_EVENT_CLASS 0x02
|
|
||||||
#define RUBY_EVENT_END 0x04
|
|
||||||
#define RUBY_EVENT_CALL 0x08
|
|
||||||
#define RUBY_EVENT_RETURN 0x10
|
|
||||||
#define RUBY_EVENT_C_CALL 0x20
|
|
||||||
#define RUBY_EVENT_C_RETURN 0x40
|
|
||||||
#define RUBY_EVENT_RAISE 0x80
|
|
||||||
#define RUBY_EVENT_ALL 0xff
|
|
||||||
|
|
||||||
typedef void (*rb_event_hook_func_t)(rb_event_t,NODE*,VALUE,ID,VALUE);
|
|
||||||
void rb_add_event_hook(rb_event_hook_func_t,rb_event_t);
|
|
||||||
int rb_remove_event_hook(rb_event_hook_func_t);
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
} /* extern "C" { */
|
} /* extern "C" { */
|
||||||
#endif
|
#endif
|
||||||
|
@ -35,6 +35,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||||||
eval("class Foo; end")
|
eval("class Foo; end")
|
||||||
set_trace_func nil
|
set_trace_func nil
|
||||||
|
|
||||||
|
assert_equal(["c-return", 18, :set_trace_func, TestSetTraceFunc],
|
||||||
|
events.shift) # TODO
|
||||||
assert_equal(["line", 19, :test_event, TestSetTraceFunc],
|
assert_equal(["line", 19, :test_event, TestSetTraceFunc],
|
||||||
events.shift) # a = 1
|
events.shift) # a = 1
|
||||||
assert_equal(["line", 20, :test_event, TestSetTraceFunc],
|
assert_equal(["line", 20, :test_event, TestSetTraceFunc],
|
||||||
|
289
thread.c
289
thread.c
@ -2393,6 +2393,289 @@ rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* tracer */
|
||||||
|
|
||||||
|
static rb_event_hook_t *
|
||||||
|
alloc_event_fook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
||||||
|
{
|
||||||
|
rb_event_hook_t *hook = ALLOC(rb_event_hook_t);
|
||||||
|
hook->func = func;
|
||||||
|
hook->flag = events;
|
||||||
|
hook->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
thread_reset_event_flags(rb_thread_t *th)
|
||||||
|
{
|
||||||
|
rb_event_hook_t *hook = th->event_hooks;
|
||||||
|
rb_event_flag_t flag = th->event_flags & RUBY_EVENT_VM;
|
||||||
|
|
||||||
|
while (hook) {
|
||||||
|
flag |= hook->flag;
|
||||||
|
hook = hook->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_thread_add_event_hook(rb_thread_t *th,
|
||||||
|
rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
||||||
|
{
|
||||||
|
rb_event_hook_t *hook = alloc_event_fook(func, events, data);
|
||||||
|
hook->next = th->event_hooks;
|
||||||
|
th->event_hooks = hook;
|
||||||
|
thread_reset_event_flags(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_threads_event_flags_i(st_data_t key, st_data_t val, st_data_t flag)
|
||||||
|
{
|
||||||
|
VALUE thval = key;
|
||||||
|
rb_thread_t *th;
|
||||||
|
GetThreadPtr(thval, th);
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
th->event_flags |= RUBY_EVENT_VM;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
th->event_flags &= (~RUBY_EVENT_VM);
|
||||||
|
}
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_threads_event_flags(int flag)
|
||||||
|
{
|
||||||
|
st_foreach(GET_VM()->living_threads, set_threads_event_flags_i, (st_data_t) flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
|
||||||
|
{
|
||||||
|
rb_event_hook_t *hook = alloc_event_fook(func, events, data);
|
||||||
|
rb_vm_t *vm = GET_VM();
|
||||||
|
|
||||||
|
hook->next = vm->event_hooks;
|
||||||
|
vm->event_hooks = hook;
|
||||||
|
|
||||||
|
set_threads_event_flags(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
remove_event_hook(rb_event_hook_t **root, rb_event_hook_func_t func)
|
||||||
|
{
|
||||||
|
rb_event_hook_t *prev = NULL, *hook = *root;
|
||||||
|
|
||||||
|
while (hook) {
|
||||||
|
if (func == 0 || hook->func == func) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = hook->next;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*root = hook->next;
|
||||||
|
}
|
||||||
|
xfree(hook);
|
||||||
|
}
|
||||||
|
prev = hook;
|
||||||
|
hook = hook->next;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rb_thread_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func)
|
||||||
|
{
|
||||||
|
remove_event_hook(&th->event_hooks, func);
|
||||||
|
thread_reset_event_flags(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rb_remove_event_hook(rb_event_hook_func_t func)
|
||||||
|
{
|
||||||
|
rb_vm_t *vm = GET_VM();
|
||||||
|
rb_event_hook_t *hook = vm->event_hooks;
|
||||||
|
int ret = remove_event_hook(&vm->event_hooks, func);
|
||||||
|
|
||||||
|
if (hook != NULL && vm->event_hooks == NULL) {
|
||||||
|
set_threads_event_flags(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag)
|
||||||
|
{
|
||||||
|
rb_thread_t *th;
|
||||||
|
GetThreadPtr((VALUE)key, th);
|
||||||
|
rb_thread_remove_event_hook(th, 0);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_clear_trace_func(void)
|
||||||
|
{
|
||||||
|
st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0);
|
||||||
|
rb_remove_event_hook(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* set_trace_func(proc) => proc
|
||||||
|
* set_trace_func(nil) => nil
|
||||||
|
*
|
||||||
|
* Establishes _proc_ as the handler for tracing, or disables
|
||||||
|
* tracing if the parameter is +nil+. _proc_ takes up
|
||||||
|
* to six parameters: an event name, a filename, a line number, an
|
||||||
|
* object id, a binding, and the name of a class. _proc_ is
|
||||||
|
* invoked whenever an event occurs. Events are: <code>c-call</code>
|
||||||
|
* (call a C-language routine), <code>c-return</code> (return from a
|
||||||
|
* C-language routine), <code>call</code> (call a Ruby method),
|
||||||
|
* <code>class</code> (start a class or module definition),
|
||||||
|
* <code>end</code> (finish a class or module definition),
|
||||||
|
* <code>line</code> (execute code on a new line), <code>raise</code>
|
||||||
|
* (raise an exception), and <code>return</code> (return from a Ruby
|
||||||
|
* method). Tracing is disabled within the context of _proc_.
|
||||||
|
*
|
||||||
|
* class Test
|
||||||
|
* def test
|
||||||
|
* a = 1
|
||||||
|
* b = 2
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* set_trace_func proc { |event, file, line, id, binding, classname|
|
||||||
|
* printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
||||||
|
* }
|
||||||
|
* t = Test.new
|
||||||
|
* t.test
|
||||||
|
*
|
||||||
|
* line prog.rb:11 false
|
||||||
|
* c-call prog.rb:11 new Class
|
||||||
|
* c-call prog.rb:11 initialize Object
|
||||||
|
* c-return prog.rb:11 initialize Object
|
||||||
|
* c-return prog.rb:11 new Class
|
||||||
|
* line prog.rb:12 false
|
||||||
|
* call prog.rb:2 test Test
|
||||||
|
* line prog.rb:3 test Test
|
||||||
|
* line prog.rb:4 test Test
|
||||||
|
* return prog.rb:4 test Test
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
set_trace_func(VALUE obj, VALUE trace)
|
||||||
|
{
|
||||||
|
rb_vm_t *vm = GET_VM();
|
||||||
|
rb_remove_event_hook(call_trace_func);
|
||||||
|
|
||||||
|
if (NIL_P(trace)) {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rb_obj_is_proc(trace)) {
|
||||||
|
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace);
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
thread_add_trace_func(rb_thread_t *th, VALUE trace)
|
||||||
|
{
|
||||||
|
if (!rb_obj_is_proc(trace)) {
|
||||||
|
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_thread_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
thread_add_trace_func_m(VALUE obj, VALUE trace)
|
||||||
|
{
|
||||||
|
rb_thread_t *th;
|
||||||
|
GetThreadPtr(obj, th);
|
||||||
|
thread_add_trace_func(th, trace);
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
thread_set_trace_func_m(VALUE obj, VALUE trace)
|
||||||
|
{
|
||||||
|
rb_thread_t *th;
|
||||||
|
GetThreadPtr(obj, th);
|
||||||
|
rb_thread_remove_event_hook(th, call_trace_func);
|
||||||
|
|
||||||
|
if (!NIL_P(trace)) {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
thread_add_trace_func(th, trace);
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_event_name(rb_event_flag_t event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case RUBY_EVENT_LINE:
|
||||||
|
return "line";
|
||||||
|
case RUBY_EVENT_CLASS:
|
||||||
|
return "class";
|
||||||
|
case RUBY_EVENT_END:
|
||||||
|
return "end";
|
||||||
|
case RUBY_EVENT_CALL:
|
||||||
|
return "call";
|
||||||
|
case RUBY_EVENT_RETURN:
|
||||||
|
return "return";
|
||||||
|
case RUBY_EVENT_C_CALL:
|
||||||
|
return "c-call";
|
||||||
|
case RUBY_EVENT_C_RETURN:
|
||||||
|
return "c-return";
|
||||||
|
case RUBY_EVENT_RAISE:
|
||||||
|
return "raise";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
||||||
|
{
|
||||||
|
rb_thread_t *th = GET_THREAD();
|
||||||
|
int state, raised;
|
||||||
|
VALUE eventname = rb_str_new2(get_event_name(event));
|
||||||
|
VALUE filename = rb_str_new2(rb_sourcefile());
|
||||||
|
int line = rb_sourceline();
|
||||||
|
|
||||||
|
if (th->tracing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
th->tracing = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
raised = thread_reset_raised(th);
|
||||||
|
|
||||||
|
PUSH_TAG();
|
||||||
|
if ((state = EXEC_TAG()) == 0) {
|
||||||
|
proc_invoke(proc, rb_ary_new3(6,
|
||||||
|
eventname, filename, INT2FIX(line),
|
||||||
|
id ? ID2SYM(id) : Qnil,
|
||||||
|
self ? rb_binding_new() : Qnil,
|
||||||
|
klass ? klass : Qnil), Qundef, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raised) {
|
||||||
|
thread_set_raised(th);
|
||||||
|
}
|
||||||
|
POP_TAG();
|
||||||
|
|
||||||
|
th->tracing = 0;
|
||||||
|
if (state) {
|
||||||
|
JUMP_TAG(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* +Thread+ encapsulates the behavior of a thread of
|
* +Thread+ encapsulates the behavior of a thread of
|
||||||
@ -2488,6 +2771,12 @@ Init_Thread(void)
|
|||||||
rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
|
rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
|
||||||
rb_define_global_function("callcc", rb_callcc, 0);
|
rb_define_global_function("callcc", rb_callcc, 0);
|
||||||
|
|
||||||
|
/* trace */
|
||||||
|
rb_define_global_function("set_trace_func", set_trace_func, 1);
|
||||||
|
rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
|
||||||
|
rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
|
||||||
|
|
||||||
|
/* init thread core */
|
||||||
Init_native_thread();
|
Init_native_thread();
|
||||||
{
|
{
|
||||||
/* main thread setting */
|
/* main thread setting */
|
||||||
|
35
vm.c
35
vm.c
@ -534,25 +534,28 @@ th_call0(rb_thread_t *th, VALUE klass, VALUE recv,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODE_CFUNC: {
|
case NODE_CFUNC: {
|
||||||
rb_control_frame_t *reg_cfp = th->cfp;
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
|
||||||
rb_control_frame_t *cfp =
|
{
|
||||||
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
rb_control_frame_t *reg_cfp = th->cfp;
|
||||||
recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1);
|
rb_control_frame_t *cfp =
|
||||||
|
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
||||||
|
recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1);
|
||||||
|
|
||||||
cfp->callee_id = oid;
|
cfp->callee_id = oid;
|
||||||
cfp->method_id = id;
|
cfp->method_id = id;
|
||||||
cfp->method_klass = klass;
|
cfp->method_klass = klass;
|
||||||
|
|
||||||
val = call_cfunc(body->nd_cfnc, recv, body->nd_argc, argc, argv);
|
val = call_cfunc(body->nd_cfnc, recv, body->nd_argc, argc, argv);
|
||||||
|
|
||||||
if (reg_cfp != th->cfp + 1) {
|
if (reg_cfp != th->cfp + 1) {
|
||||||
SDR2(reg_cfp);
|
SDR2(reg_cfp);
|
||||||
SDR2(th->cfp-5);
|
SDR2(th->cfp-5);
|
||||||
rb_bug("cfp consistency error - call0");
|
rb_bug("cfp consistency error - call0");
|
||||||
th->cfp = reg_cfp;
|
th->cfp = reg_cfp;
|
||||||
|
}
|
||||||
|
pop_frame(th);
|
||||||
}
|
}
|
||||||
pop_frame(th);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODE_ATTRSET:{
|
case NODE_ATTRSET:{
|
||||||
@ -1472,6 +1475,8 @@ yarv_init_redefined_flag(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "vm_evalbody.ci"
|
#include "vm_evalbody.ci"
|
||||||
|
|
||||||
/* finish
|
/* finish
|
||||||
|
35
vm_macro.def
35
vm_macro.def
@ -56,23 +56,28 @@ MACRO macro_eval_setup_send_arguments(num, blockptr, flag, blockiseq)
|
|||||||
|
|
||||||
MACRO macro_eval_invoke_cfunc(num, id, recv, klass, mn, blockptr)
|
MACRO macro_eval_invoke_cfunc(num, id, recv, klass, mn, blockptr)
|
||||||
{
|
{
|
||||||
rb_control_frame_t *cfp =
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
|
||||||
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
{
|
||||||
recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
|
rb_control_frame_t *cfp =
|
||||||
cfp->callee_id = id; /* TODO */
|
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
||||||
cfp->method_id = id;
|
recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
|
||||||
cfp->method_klass = klass;
|
cfp->callee_id = id; /* TODO */
|
||||||
|
cfp->method_id = id;
|
||||||
reg_cfp->sp -= num + 1;
|
cfp->method_klass = klass;
|
||||||
|
|
||||||
val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
|
reg_cfp->sp -= num + 1;
|
||||||
if (reg_cfp != th->cfp + 1) {
|
|
||||||
SDR2(reg_cfp);
|
val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
|
||||||
SDR2(th->cfp-5);
|
|
||||||
rb_bug("cfp consistency error - send");
|
if (reg_cfp != th->cfp + 1) {
|
||||||
th->cfp = reg_cfp;
|
SDR2(reg_cfp);
|
||||||
|
SDR2(th->cfp-5);
|
||||||
|
rb_bug("cfp consistency error - send");
|
||||||
|
th->cfp = reg_cfp;
|
||||||
|
}
|
||||||
|
pop_frame(th);
|
||||||
}
|
}
|
||||||
pop_frame(th);
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
|
||||||
}
|
}
|
||||||
|
|
||||||
MACRO macro_eval_invoke_func(niseqval, recv, klass, blockptr, num)
|
MACRO macro_eval_invoke_func(niseqval, recv, klass, blockptr, num)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
/* VM running option */
|
/* VM running option */
|
||||||
#define OPT_CHECKED_RUN 1
|
#define OPT_CHECKED_RUN 1
|
||||||
|
#define OPT_TRACE_INSTRUCTION 1
|
||||||
|
|
||||||
/* at compile */
|
/* at compile */
|
||||||
#define OPT_INLINE_CONST_CACHE 1
|
#define OPT_INLINE_CONST_CACHE 1
|
||||||
|
13
yarvcore.c
13
yarvcore.c
@ -167,6 +167,15 @@ vm_mark_each_thread_func(st_data_t key, st_data_t value, st_data_t dummy)
|
|||||||
return ST_CONTINUE;
|
return ST_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mark_event_hooks(rb_event_hook_t *hook)
|
||||||
|
{
|
||||||
|
while (hook) {
|
||||||
|
rb_gc_mark(hook->data);
|
||||||
|
hook = hook->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vm_mark(void *ptr)
|
vm_mark(void *ptr)
|
||||||
{
|
{
|
||||||
@ -181,6 +190,8 @@ vm_mark(void *ptr)
|
|||||||
MARK_UNLESS_NULL(vm->mark_object_ary);
|
MARK_UNLESS_NULL(vm->mark_object_ary);
|
||||||
MARK_UNLESS_NULL(vm->last_status);
|
MARK_UNLESS_NULL(vm->last_status);
|
||||||
MARK_UNLESS_NULL(vm->loaded_features);
|
MARK_UNLESS_NULL(vm->loaded_features);
|
||||||
|
|
||||||
|
mark_event_hooks(vm->event_hooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
MARK_REPORT_LEAVE("vm");
|
MARK_REPORT_LEAVE("vm");
|
||||||
@ -289,6 +300,8 @@ thread_mark(void *ptr)
|
|||||||
(VALUE *)(&th->machine_regs) +
|
(VALUE *)(&th->machine_regs) +
|
||||||
sizeof(th->machine_regs) / sizeof(VALUE));
|
sizeof(th->machine_regs) / sizeof(VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mark_event_hooks(th->event_hooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
MARK_UNLESS_NULL(th->stat_insn_usage);
|
MARK_UNLESS_NULL(th->stat_insn_usage);
|
||||||
|
32
yarvcore.h
32
yarvcore.h
@ -186,6 +186,7 @@ typedef struct rb_compile_option_struct {
|
|||||||
int operands_unification;
|
int operands_unification;
|
||||||
int instructions_unification;
|
int instructions_unification;
|
||||||
int stack_caching;
|
int stack_caching;
|
||||||
|
int trace_instruction;
|
||||||
} rb_compile_option_t;
|
} rb_compile_option_t;
|
||||||
|
|
||||||
struct iseq_compile_data {
|
struct iseq_compile_data {
|
||||||
@ -304,6 +305,29 @@ struct rb_iseq_struct {
|
|||||||
|
|
||||||
typedef struct rb_iseq_struct rb_iseq_t;
|
typedef struct rb_iseq_struct rb_iseq_t;
|
||||||
|
|
||||||
|
#define RUBY_EVENT_NONE 0x00
|
||||||
|
#define RUBY_EVENT_LINE 0x01
|
||||||
|
#define RUBY_EVENT_CLASS 0x02
|
||||||
|
#define RUBY_EVENT_END 0x04
|
||||||
|
#define RUBY_EVENT_CALL 0x08
|
||||||
|
#define RUBY_EVENT_RETURN 0x10
|
||||||
|
#define RUBY_EVENT_C_CALL 0x20
|
||||||
|
#define RUBY_EVENT_C_RETURN 0x40
|
||||||
|
#define RUBY_EVENT_RAISE 0x80
|
||||||
|
#define RUBY_EVENT_ALL 0xff
|
||||||
|
#define RUBY_EVENT_VM 0x100
|
||||||
|
|
||||||
|
typedef unsigned int rb_event_flag_t;
|
||||||
|
typedef void (*rb_event_hook_func_t)(rb_event_flag_t, VALUE data, VALUE, ID, VALUE klass);
|
||||||
|
|
||||||
|
typedef struct rb_event_hook_struct {
|
||||||
|
rb_event_flag_t flag;
|
||||||
|
rb_event_hook_func_t func;
|
||||||
|
VALUE data;
|
||||||
|
struct rb_event_hook_struct *next;
|
||||||
|
} rb_event_hook_t;
|
||||||
|
|
||||||
|
|
||||||
#define GetVMPtr(obj, ptr) \
|
#define GetVMPtr(obj, ptr) \
|
||||||
Data_Get_Struct(obj, rb_vm_t, ptr)
|
Data_Get_Struct(obj, rb_vm_t, ptr)
|
||||||
|
|
||||||
@ -332,6 +356,9 @@ typedef struct rb_vm_struct {
|
|||||||
/* signal */
|
/* signal */
|
||||||
rb_atomic_t signal_buff[RUBY_NSIG];
|
rb_atomic_t signal_buff[RUBY_NSIG];
|
||||||
rb_atomic_t bufferd_signal_size;
|
rb_atomic_t bufferd_signal_size;
|
||||||
|
|
||||||
|
/* hook */
|
||||||
|
rb_event_hook_t *event_hooks;
|
||||||
} rb_vm_t;
|
} rb_vm_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -456,6 +483,11 @@ struct rb_thread_struct
|
|||||||
/* statistics data for profiler */
|
/* statistics data for profiler */
|
||||||
VALUE stat_insn_usage;
|
VALUE stat_insn_usage;
|
||||||
|
|
||||||
|
/* tracer */
|
||||||
|
rb_event_hook_t *event_hooks;
|
||||||
|
rb_event_flag_t event_flags;
|
||||||
|
int tracing;
|
||||||
|
|
||||||
/* misc */
|
/* misc */
|
||||||
int method_missing_reason;
|
int method_missing_reason;
|
||||||
int abort_on_exception;
|
int abort_on_exception;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user