зеркало из https://github.com/github/ruby.git
* 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:
Родитель
2dd91facca
Коммит
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>
|
||||
|
||||
* 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);
|
||||
}
|
||||
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 {
|
||||
|
@ -2433,6 +2445,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
|
|||
|
||||
type = nd_type(node);
|
||||
|
||||
if (node->flags & NODE_NEWLINE) {
|
||||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_LINE);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
||||
case NODE_METHOD:{
|
||||
|
|
|
@ -157,6 +157,11 @@ r_value(VALUE value)
|
|||
new_insn_send(iseq, line, \
|
||||
(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 */
|
||||
#define ADD_LABEL(seq, 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_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_method.h"
|
||||
#include "eval_safe.h"
|
||||
|
@ -162,8 +139,8 @@ ruby_finalize_1(void)
|
|||
{
|
||||
signal(SIGINT, SIG_DFL);
|
||||
GET_THREAD()->errinfo = 0;
|
||||
rb_gc_call_finalizer_at_exit();
|
||||
rb_clear_trace_func();
|
||||
rb_gc_call_finalizer_at_exit();
|
||||
}
|
||||
|
||||
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:
|
||||
* 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 void
|
||||
rb_longjmp(tag, mesg)
|
||||
int tag;
|
||||
VALUE mesg;
|
||||
rb_longjmp(int tag, VALUE mesg)
|
||||
{
|
||||
VALUE at;
|
||||
rb_thread_t *th = GET_THREAD();
|
||||
|
||||
/*
|
||||
//while (th->cfp->pc == 0 || th->cfp->iseq == 0) {
|
||||
//th->cfp++;
|
||||
//}
|
||||
*/
|
||||
|
||||
if (thread_set_raised(th)) {
|
||||
th->errinfo = exception_error;
|
||||
JUMP_TAG(TAG_FATAL);
|
||||
|
@ -943,7 +699,8 @@ rb_longjmp(tag, mesg)
|
|||
|
||||
rb_trap_restore_mask();
|
||||
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);
|
||||
JUMP_TAG(tag);
|
||||
|
@ -1889,18 +1646,29 @@ rb_frame_self(void)
|
|||
const char *
|
||||
rb_sourcefile(void)
|
||||
{
|
||||
rb_iseq_t *iseq = GET_THREAD()->cfp->iseq;
|
||||
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
||||
return RSTRING_PTR(iseq->filename);
|
||||
rb_thread_t *th = GET_THREAD();
|
||||
rb_control_frame_t *cfp = th_get_ruby_level_cfp(th, th->cfp);
|
||||
|
||||
if (cfp) {
|
||||
return RSTRING_PTR(cfp->iseq->filename);
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rb_sourceline(void)
|
||||
{
|
||||
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
|
||||
|
@ -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("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);
|
||||
}
|
||||
|
||||
|
|
|
@ -239,4 +239,30 @@ void rb_thread_terminate_all(void);
|
|||
|
||||
#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 */
|
||||
|
|
10
insns.def
10
insns.def
|
@ -843,7 +843,6 @@ definemethod
|
|||
get_cref(GET_ISEQ(), GET_LFP()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@c setting
|
||||
@e make alias (if v_p is Qtrue, make valias)
|
||||
|
@ -1020,15 +1019,12 @@ postexe
|
|||
*/
|
||||
DEFINE_INSN
|
||||
trace
|
||||
(num_t flag, VALUE args)
|
||||
(num_t nf)
|
||||
()
|
||||
()
|
||||
{
|
||||
/* TODO: trace instruction design */
|
||||
if (th->vm->trace_flag & flag) {
|
||||
/* */
|
||||
args = Qnil;
|
||||
}
|
||||
rb_event_flag_t flag = nf;
|
||||
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */);
|
||||
}
|
||||
|
||||
/**********************************************************/
|
||||
|
|
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_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
|
||||
OPT_STACK_CACHING, /* int stack_caching; */
|
||||
OPT_TRACE_INSTRUCTION,
|
||||
};
|
||||
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, instructions_unification);
|
||||
SET_COMPILE_OPTION(option, opt, stack_caching);
|
||||
SET_COMPILE_OPTION(option, opt, trace_instruction);
|
||||
#undef SET_COMPILE_OPTION
|
||||
}
|
||||
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_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)
|
||||
} /* extern "C" { */
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
eval("class Foo; end")
|
||||
set_trace_func nil
|
||||
|
||||
assert_equal(["c-return", 18, :set_trace_func, TestSetTraceFunc],
|
||||
events.shift) # TODO
|
||||
assert_equal(["line", 19, :test_event, TestSetTraceFunc],
|
||||
events.shift) # a = 1
|
||||
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
|
||||
|
@ -2488,6 +2771,12 @@ Init_Thread(void)
|
|||
rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
|
||||
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();
|
||||
{
|
||||
/* main thread setting */
|
||||
|
|
35
vm.c
35
vm.c
|
@ -534,25 +534,28 @@ th_call0(rb_thread_t *th, VALUE klass, VALUE recv,
|
|||
break;
|
||||
}
|
||||
case NODE_CFUNC: {
|
||||
rb_control_frame_t *reg_cfp = th->cfp;
|
||||
rb_control_frame_t *cfp =
|
||||
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
||||
recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1);
|
||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
|
||||
{
|
||||
rb_control_frame_t *reg_cfp = th->cfp;
|
||||
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->method_id = id;
|
||||
cfp->method_klass = klass;
|
||||
cfp->callee_id = oid;
|
||||
cfp->method_id = id;
|
||||
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) {
|
||||
SDR2(reg_cfp);
|
||||
SDR2(th->cfp-5);
|
||||
rb_bug("cfp consistency error - call0");
|
||||
th->cfp = reg_cfp;
|
||||
if (reg_cfp != th->cfp + 1) {
|
||||
SDR2(reg_cfp);
|
||||
SDR2(th->cfp-5);
|
||||
rb_bug("cfp consistency error - call0");
|
||||
th->cfp = reg_cfp;
|
||||
}
|
||||
pop_frame(th);
|
||||
}
|
||||
pop_frame(th);
|
||||
|
||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
|
||||
break;
|
||||
}
|
||||
case NODE_ATTRSET:{
|
||||
|
@ -1472,6 +1475,8 @@ yarv_init_redefined_flag(void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "vm_evalbody.ci"
|
||||
|
||||
/* 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)
|
||||
{
|
||||
rb_control_frame_t *cfp =
|
||||
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
||||
recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
|
||||
cfp->callee_id = id; /* TODO */
|
||||
cfp->method_id = id;
|
||||
cfp->method_klass = klass;
|
||||
|
||||
reg_cfp->sp -= num + 1;
|
||||
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
|
||||
{
|
||||
rb_control_frame_t *cfp =
|
||||
push_frame(th, 0, FRAME_MAGIC_CFUNC,
|
||||
recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
|
||||
cfp->callee_id = id; /* TODO */
|
||||
cfp->method_id = id;
|
||||
cfp->method_klass = klass;
|
||||
|
||||
val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
|
||||
if (reg_cfp != th->cfp + 1) {
|
||||
SDR2(reg_cfp);
|
||||
SDR2(th->cfp-5);
|
||||
rb_bug("cfp consistency error - send");
|
||||
th->cfp = reg_cfp;
|
||||
reg_cfp->sp -= num + 1;
|
||||
|
||||
val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
|
||||
|
||||
if (reg_cfp != th->cfp + 1) {
|
||||
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)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
/* VM running option */
|
||||
#define OPT_CHECKED_RUN 1
|
||||
#define OPT_TRACE_INSTRUCTION 1
|
||||
|
||||
/* at compile */
|
||||
#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;
|
||||
}
|
||||
|
||||
static void
|
||||
mark_event_hooks(rb_event_hook_t *hook)
|
||||
{
|
||||
while (hook) {
|
||||
rb_gc_mark(hook->data);
|
||||
hook = hook->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vm_mark(void *ptr)
|
||||
{
|
||||
|
@ -181,6 +190,8 @@ vm_mark(void *ptr)
|
|||
MARK_UNLESS_NULL(vm->mark_object_ary);
|
||||
MARK_UNLESS_NULL(vm->last_status);
|
||||
MARK_UNLESS_NULL(vm->loaded_features);
|
||||
|
||||
mark_event_hooks(vm->event_hooks);
|
||||
}
|
||||
|
||||
MARK_REPORT_LEAVE("vm");
|
||||
|
@ -289,6 +300,8 @@ thread_mark(void *ptr)
|
|||
(VALUE *)(&th->machine_regs) +
|
||||
sizeof(th->machine_regs) / sizeof(VALUE));
|
||||
}
|
||||
|
||||
mark_event_hooks(th->event_hooks);
|
||||
}
|
||||
|
||||
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 instructions_unification;
|
||||
int stack_caching;
|
||||
int trace_instruction;
|
||||
} rb_compile_option_t;
|
||||
|
||||
struct iseq_compile_data {
|
||||
|
@ -304,6 +305,29 @@ struct rb_iseq_struct {
|
|||
|
||||
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) \
|
||||
Data_Get_Struct(obj, rb_vm_t, ptr)
|
||||
|
||||
|
@ -332,6 +356,9 @@ typedef struct rb_vm_struct {
|
|||
/* signal */
|
||||
rb_atomic_t signal_buff[RUBY_NSIG];
|
||||
rb_atomic_t bufferd_signal_size;
|
||||
|
||||
/* hook */
|
||||
rb_event_hook_t *event_hooks;
|
||||
} rb_vm_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -456,6 +483,11 @@ struct rb_thread_struct
|
|||
/* statistics data for profiler */
|
||||
VALUE stat_insn_usage;
|
||||
|
||||
/* tracer */
|
||||
rb_event_hook_t *event_hooks;
|
||||
rb_event_flag_t event_flags;
|
||||
int tracing;
|
||||
|
||||
/* misc */
|
||||
int method_missing_reason;
|
||||
int abort_on_exception;
|
||||
|
|
Загрузка…
Ссылка в новой задаче