-
Notifications
You must be signed in to change notification settings - Fork 0
/
trace.c
160 lines (141 loc) · 4.35 KB
/
trace.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "vm_core_mini.h" /* Pulls in ruby.h */
/* What release we got? */
#define TRACE_VERSION "0.5.9"
extern VALUE rb_cRubyVM; /* RubyVM class */
extern rb_vm_t *ruby_current_vm;
extern VALUE rb_obj_is_proc(VALUE proc);
VALUE rb_cTraceHook; /* TraceHook class */
VALUE rb_eTraceHookError; /* Exception raised by TraceHook class */
/* Extra information we need to save about a hook.
FIXME: For now we will work only with vm hooks. When this
extends to thread hooks, we will need to use this.
*/
typedef struct
{
rb_thread_t *th; /* If NULL, hook is in vm. Otherwise, this
is the thread the hook belongs to. */
rb_event_hook_t *hook;
} hook_info_t;
static int is_hook_member(rb_event_hook_t *check_hook, rb_event_hook_t *hook);
static void
check_hook_valid(rb_event_hook_t *check_hook)
{
/* FIXME: in the future use check_hook to find the hook head. */
rb_event_hook_t *hook_head = GET_VM()->event_hooks;
if (!is_hook_member(check_hook, hook_head))
rb_raise(rb_eTraceHookError, "hook not found");
}
/* Return an Array of vm event hooks found from hook. */
VALUE
get_trace_hooks(rb_event_hook_t *hook)
{
VALUE ary;
for (ary = rb_ary_new(); hook; hook = hook->next)
rb_ary_push(ary, Data_Wrap_Struct(rb_cTraceHook, NULL, NULL, hook));
return ary;
}
/* Return 1 if check_hook is found in the list of hooks pointed to by
'hook', or 0 if not found. */
static int
is_hook_member(rb_event_hook_t *check_hook, rb_event_hook_t *hook)
{
for (; hook; hook = hook->next) if (check_hook == hook) return 1;
return 0; /* Not found */
}
/* Return an Array of VM event hooks objects. */
VALUE
trace_hook_s_trace_hooks()
{
return get_trace_hooks(GET_VM()->event_hooks);
}
/*
Return the event mask value for a given hook. If no hook, then return nil.
*/
static VALUE
trace_hook_event_mask(VALUE klass)
{
rb_event_hook_t *hook;
Data_Get_Struct(klass, rb_event_hook_t, hook);
if (!hook) return Qnil;
check_hook_valid(hook);
return INT2FIX(hook->flag);
}
/* Set a new mask value for given hook and return the old mask
value. Can raise an error if there is no hook installed. */
static VALUE
trace_hook_event_mask_set(VALUE klass, VALUE maskval)
{
rb_event_hook_t *hook;
rb_event_flag_t flag;
Data_Get_Struct(klass, rb_event_hook_t, hook);
if (!hook)
rb_raise(rb_eTraceHookError, "No hook installed");
if (!FIXNUM_P(maskval)) {
rb_raise(rb_eTypeError, "integer argument expected");
}
check_hook_valid(hook);
flag = hook->flag;
hook->flag = FIX2INT(maskval);
return INT2FIX(flag);
}
/*
Return the event mask value for a given hook. If no hook, then return nil.
*/
static VALUE
trace_hook_proc(VALUE klass)
{
rb_event_hook_t *hook;
Data_Get_Struct(klass, rb_event_hook_t, hook);
if (!hook) return Qnil;
check_hook_valid(hook);
return hook->data;
}
/*
Return the event mask value for a given hook. If no hook, then return nil.
*/
static VALUE
trace_hook_proc_set(VALUE klass, VALUE trace_proc)
{
rb_event_hook_t *hook;
if (!rb_obj_is_proc(trace_proc)) {
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
}
Data_Get_Struct(klass, rb_event_hook_t, hook);
if (!hook) return Qnil;
check_hook_valid(hook);
hook->data = trace_proc;
return trace_proc;
}
/*
Return true if hook is still valid or is nil), false otherwise.
*/
static VALUE
trace_hook_valid(VALUE klass)
{
rb_event_hook_t *hook;
Data_Get_Struct(klass, rb_event_hook_t, hook);
/* FIXME in the future we will need to extract whether this hook is
part of a thread or from the vm.
*/
return is_hook_member(hook, GET_VM()->event_hooks) ? Qtrue : Qfalse;
}
void
Init_trace(void)
{
rb_eTraceHookError = rb_define_class_under(rb_cRubyVM, "TraceHookError",
rb_eStandardError);
rb_cTraceHook = rb_define_class_under(rb_cRubyVM, "TraceHook",
rb_cObject);
rb_define_singleton_method(rb_cTraceHook, "trace_hooks",
trace_hook_s_trace_hooks, 0);
rb_define_method(rb_cTraceHook, "event_mask",
trace_hook_event_mask, 0);
rb_define_method(rb_cTraceHook, "event_mask=",
trace_hook_event_mask_set, 1);
rb_define_method(rb_cTraceHook, "proc",
trace_hook_proc, 0);
rb_define_method(rb_cTraceHook, "proc=",
trace_hook_proc_set, 1);
rb_define_method(rb_cTraceHook, "valid?",
trace_hook_valid, 0);
}