[ruby-dev:25647] C level set_trace_func
From:
Shugo Maeda <shugo@...>
Date:
2005-02-07 02:46:45 UTC
List:
ruby-dev #25647
前田です。
今Cでプロファイラを書いているのですが、Cレベルでset_trace_funcのような
ことができるようにしていただけないでしょうか。
今考えているのは、
static void
prof_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE
klass)
{
...
}
のような関数を用意しておいて、
rb_set_event_hook(prof_event_hook,
RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
とすると、rb_set_event_hook()の第二引数で指定したイベントだけフック
できる、というものです。
パッチを添付していますが、オーバーへッドはほとんどないと思います。
ちなみに、このパッチではset_trace_funcも上記の機能で実現しています。
--
前田 修吾
Attachments (1)
ruby-event-hook.diff
(7.91 KB, text/x-diff)
Index: eval.c
===================================================================
RCS file: /src/ruby/eval.c,v
retrieving revision 1.748
diff -u -r1.748 eval.c
--- eval.c 5 Jan 2005 03:49:50 -0000 1.748
+++ eval.c 5 Feb 2005 15:47:36 -0000
@@ -1017,9 +1017,18 @@
static VALUE massign _((VALUE,NODE*,VALUE,int));
static void assign _((VALUE,NODE*,VALUE,int));
+static rb_event_t hooked_events;
+static rb_event_hook_t event_hook;
+
+#define EXEC_EVENT_HOOK(event, node, self, id, klass) \
+ do { \
+ if (hooked_events & event) \
+ (*event_hook)(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, \
@@ -2370,6 +2379,18 @@
return Qfalse;
}
+void
+rb_set_event_hook(rb_event_hook_t hook, rb_event_t events)
+{
+ event_hook = hook;
+ if (event_hook == 0) {
+ hooked_events = RUBY_EVENT_NONE;
+ }
+ else {
+ hooked_events = events;
+ }
+}
+
/*
* call-seq:
* set_trace_func(proc) => proc
@@ -2420,17 +2441,45 @@
{
if (NIL_P(trace)) {
trace_func = 0;
+ rb_set_event_hook(0, RUBY_EVENT_NONE);
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;
+ rb_set_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;
@@ -2440,6 +2489,7 @@
struct FRAME *prev;
NODE *node_save;
VALUE srcfile;
+ char *event_name;
if (!trace_func) return;
if (tracing) return;
@@ -2474,7 +2524,8 @@
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,
@@ -2641,8 +2692,8 @@
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->last_func,
ruby_frame->last_class);
}
@@ -2738,11 +2789,9 @@
RETURN(ruby_errinfo);
case NODE_IF:
- if (trace_func) {
- call_trace_func("line", node, self,
- ruby_frame->last_func,
- ruby_frame->last_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
if (RTEST(rb_eval(self, node->nd_cond))) {
node = node->nd_body;
}
@@ -2758,11 +2807,9 @@
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->last_func,
- ruby_frame->last_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
VALUE v = rb_eval(self, tag->nd_head->nd_head);
long i;
@@ -2801,11 +2848,9 @@
}
tag = node->nd_head;
while (tag) {
- if (trace_func) {
- call_trace_func("line", tag, self,
- ruby_frame->last_func,
- ruby_frame->last_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->last_func,
+ ruby_frame->last_class);
if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
VALUE v = rb_eval(self, tag->nd_head->nd_head);
long i;
@@ -3911,9 +3956,7 @@
PUSH_CREF(module);
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
- if (trace_func) {
- call_trace_func("class", n, ruby_cbase, ruby_frame->last_func, ruby_frame->last_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_CLASS, n, ruby_cbase, ruby_frame->last_func, ruby_frame->last_class);
result = rb_eval(ruby_cbase, node->nd_next);
}
POP_TAG();
@@ -3923,9 +3966,7 @@
POP_CLASS();
ruby_frame = frame.tmp;
- if (trace_func) {
- call_trace_func("end", n, 0, ruby_frame->last_func, ruby_frame->last_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_END, n, 0, ruby_frame->last_func, ruby_frame->last_class);
if (state) JUMP_TAG(state);
return result;
@@ -4320,8 +4361,8 @@
}
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->last_func,
ruby_frame->last_class);
@@ -4541,10 +4582,9 @@
{
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->last_func, f->last_class);
- }
+ EXEC_EVENT_HOOK(RUBY_EVENT_RETURN,
+ tt->frame->node, tt->frame->self,
+ tt->frame->last_func, tt->frame->last_class);
JUMP_TAG(TAG_RETURN);
}
if (tt->tag == PROT_THREAD) {
@@ -5542,17 +5582,19 @@
rb_bug("bad argc(%d) specified for `%s(%s)'",
len, rb_class2name(klass), rb_id2name(id));
}
- if (trace_func) {
+ if (hooked_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN)) {
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 {
@@ -5682,13 +5724,9 @@
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.61
diff -u -r1.61 node.h
--- node.h 16 Dec 2004 15:01:49 -0000 1.61
+++ node.h 5 Feb 2005 15:47:36 -0000
@@ -364,6 +364,22 @@
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_t)_((rb_event_t,NODE*,VALUE,ID,VALUE));
+void rb_set_event_hook(rb_event_hook_t,rb_event_t);
+
#if defined(__cplusplus)
} /* extern "C" { */
#endif