* 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:
ko1 2007-04-19 10:37:08 +00:00
Родитель 2dd91facca
Коммит a73894337a
15 изменённых файлов: 468 добавлений и 309 удалений

Просмотреть файл

@ -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.

Просмотреть файл

@ -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
Просмотреть файл

@ -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 */

Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;