[ruby-dev:25870] Re: C level set_trace_func
From:
Shugo Maeda <shugo@...>
Date:
2005-03-13 00:18:42 UTC
List:
ruby-dev #25870
前田です。 Shugo Maeda wrote: >>|今Cでプロファイラを書いているのですが、Cレベルでset_trace_funcのような >>|ことができるようにしていただけないでしょうか。 >> >>パッチを当ててもよいですが、せっかく手を入れるんなら複数のイ >>ベントフックを登録したいような気がするんですが、どう思います >>か? > > > そう思いつつ手を抜いていました。手が空いたら書き直します。 実装してみました。 パッチを添付します。 > それはそれとして、今のAPIだとRubyで定義されたメソッドで例外が発生する > と、メソッドからの脱出を追えないのですが、これはどうしたものでしょう。 > rescueもフックできるようにして、raise時に保存した$!.backtraceとrescue時 > の$!.backtraceを比較する、とかですかね。 こっちはどうでしょうね。 -- 前田 修吾
Attachments (1)
event-hook.diff
(9.19 KB, text/x-diff)
Index: eval.c
===================================================================
RCS file: /src/ruby/eval.c,v
retrieving revision 1.764
diff -u -p -r1.764 eval.c
--- eval.c 8 Mar 2005 01:52:15 -0000 1.764
+++ eval.c 12 Mar 2005 23:51:45 -0000
@@ -1040,9 +1040,27 @@ static VALUE module_setup _((VALUE,NODE*
static VALUE massign _((VALUE,NODE*,VALUE,int));
static void assign _((VALUE,NODE*,VALUE,int));
+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 VALUE trace_func = 0;
static int tracing = 0;
-static void call_trace_func _((char*,NODE*,VALUE,ID,VALUE));
+static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE));
#if 0
#define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \
@@ -2415,6 +2433,42 @@ rb_obj_is_proc(proc)
return Qfalse;
}
+void
+rb_add_event_hook(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;
+}
+
/*
* call-seq:
* set_trace_func(proc) => proc
@@ -2463,19 +2517,53 @@ static VALUE
set_trace_func(obj, trace)
VALUE obj, trace;
{
+ rb_event_hook_t *hook;
+
if (NIL_P(trace)) {
trace_func = 0;
+ 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");
}
- return trace_func = trace;
+ 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(event, node, self, id, klass)
- char *event;
+ rb_event_t event;
NODE *node;
VALUE self;
ID id;
@@ -2485,6 +2573,7 @@ call_trace_func(event, node, self, id, k
struct FRAME *prev;
NODE *node_save;
VALUE srcfile;
+ char *event_name;
if (!trace_func) return;
if (tracing) return;
@@ -2519,7 +2608,8 @@ call_trace_func(event, node, self, id, k
raised = thread_reset_raised();
if ((state = EXEC_TAG()) == 0) {
srcfile = rb_str_new2(ruby_sourcefile?ruby_sourcefile:"(ruby)");
- proc_invoke(trace_func, rb_ary_new3(6, rb_str_new2(event),
+ 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,
@@ -2686,8 +2776,8 @@ rb_eval(self, n)
if (!node) RETURN(Qnil);
ruby_current_node = node;
- if (trace_func && (node->flags & NODE_NEWLINE)) {
- call_trace_func("line", node, self,
+ if (node->flags & NODE_NEWLINE) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
ruby_frame->this_func,
ruby_frame->this_class);
}
@@ -2783,11 +2873,9 @@ rb_eval(self, n)
RETURN(ruby_errinfo);
case NODE_IF:
- if (trace_func) {
- call_trace_func("line", node, self,
- ruby_frame->this_func,
- ruby_frame->this_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
if (RTEST(rb_eval(self, node->nd_cond))) {
node = node->nd_body;
}
@@ -2803,11 +2891,9 @@ rb_eval(self, n)
if (nd_type(node) != NODE_WHEN) goto again;
tag = node->nd_head;
while (tag) {
- if (trace_func) {
- call_trace_func("line", tag, self,
- ruby_frame->this_func,
- ruby_frame->this_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
VALUE v = rb_eval(self, tag->nd_head->nd_head);
long i;
@@ -2846,11 +2932,9 @@ rb_eval(self, n)
}
tag = node->nd_head;
while (tag) {
- if (trace_func) {
- call_trace_func("line", tag, self,
- ruby_frame->this_func,
- ruby_frame->this_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
VALUE v = rb_eval(self, tag->nd_head->nd_head);
long i;
@@ -3961,9 +4045,8 @@ module_setup(module, n)
PUSH_CREF(module);
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
- if (trace_func) {
- call_trace_func("class", n, ruby_cbase, ruby_frame->this_func, ruby_frame->this_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_CLASS, n, ruby_cbase,
+ ruby_frame->this_func, ruby_frame->this_class);
result = rb_eval(ruby_cbase, node->nd_next);
}
POP_TAG();
@@ -3973,9 +4056,8 @@ module_setup(module, n)
POP_CLASS();
ruby_frame = frame.tmp;
- if (trace_func) {
- call_trace_func("end", n, 0, ruby_frame->this_func, ruby_frame->this_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_END, n, 0, ruby_frame->this_func,
+ ruby_frame->this_class);
if (state) JUMP_TAG(state);
return result;
@@ -4370,8 +4452,8 @@ rb_longjmp(tag, mesg)
}
rb_trap_restore_mask();
- if (trace_func && tag != TAG_FATAL) {
- call_trace_func("raise", ruby_current_node,
+ if (tag != TAG_FATAL) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_RAISE, ruby_current_node,
ruby_frame->self,
ruby_frame->this_func,
ruby_frame->this_class);
@@ -4591,10 +4673,9 @@ return_jump(retval)
{
tt->dst = (VALUE)tt->frame->uniq;
tt->retval = retval;
- if (trace_func) {
- struct FRAME *f = tt->frame;
- call_trace_func("return", f->node, f->self, f->this_func, f->this_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_RETURN,
+ tt->frame->node, tt->frame->self,
+ tt->frame->this_func, tt->frame->this_class);
JUMP_TAG(TAG_RETURN);
}
if (tt->tag == PROT_THREAD) {
@@ -5589,17 +5670,19 @@ rb_call0(klass, recv, id, oid, argc, arg
rb_bug("bad argc (%d) specified for `%s(%s)'",
len, rb_class2name(klass), rb_id2name(id));
}
- if (trace_func) {
+ if (event_hooks) {
int state;
- call_trace_func("c-call", ruby_current_node, recv, id, klass);
+ EXEC_EVENT_HOOK(RUBY_EVENT_C_CALL, ruby_current_node,
+ recv, id, klass);
PUSH_TAG(PROT_FUNC);
if ((state = EXEC_TAG()) == 0) {
result = call_cfunc(body->nd_cfnc, recv, len, argc, argv);
}
POP_TAG();
ruby_current_node = ruby_frame->node;
- call_trace_func("c-return", ruby_current_node, recv, id, klass);
+ EXEC_EVENT_HOOK(RUBY_EVENT_C_RETURN, ruby_current_node,
+ recv, id, klass);
if (state) JUMP_TAG(state);
}
else {
@@ -5729,13 +5812,9 @@ rb_call0(klass, recv, id, oid, argc, arg
ruby_frame->argc = -(ruby_frame->argc - argc)-1;
}
- if (trace_func) {
- call_trace_func("call", b2, recv, id, klass);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_CALL, b2, recv, id, klass);
result = rb_eval(recv, body);
- if (trace_func) {
- call_trace_func("return", body, recv, id, klass);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass);
}
else if (state == TAG_RETURN && TAG_DST()) {
result = prot_tag->retval;
Index: node.h
===================================================================
RCS file: /src/ruby/node.h,v
retrieving revision 1.62
diff -u -p -r1.62 node.h
--- node.h 2 Mar 2005 03:21:30 -0000 1.62
+++ node.h 12 Mar 2005 23:51:45 -0000
@@ -366,6 +366,23 @@ 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