spicycode-rcov 0.8.1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,242 @@
1
+ #include <ruby.h>
2
+ #include <env.h>
3
+ #include <node.h>
4
+ #include <st.h>
5
+ #include <stdlib.h>
6
+
7
+
8
+ static char callsite_hook_set_p;
9
+
10
+ typedef struct {
11
+ char *sourcefile;
12
+ unsigned int sourceline;
13
+ VALUE curr_meth;
14
+ } type_def_site;
15
+ static VALUE caller_info = 0;
16
+ static VALUE method_def_site_info = 0;
17
+
18
+ static caller_stack_len = 1;
19
+
20
+ /*
21
+ *
22
+ * callsite hook and associated functions
23
+ *
24
+ * */
25
+
26
+ static VALUE
27
+ record_callsite_info(VALUE args)
28
+ {
29
+ VALUE caller_ary;
30
+ VALUE curr_meth;
31
+ VALUE count_hash;
32
+ VALUE count;
33
+ VALUE *pargs = (VALUE *)args;
34
+
35
+ caller_ary = pargs[0];
36
+ curr_meth = pargs[1];
37
+ count_hash = rb_hash_aref(caller_info, curr_meth);
38
+ if(TYPE(count_hash) != T_HASH) {
39
+ /* Qnil, anything else should be impossible unless somebody's been
40
+ * messing with ObjectSpace */
41
+ count_hash = rb_hash_new();
42
+ rb_hash_aset(caller_info, curr_meth, count_hash);
43
+ }
44
+ count = rb_hash_aref(count_hash, caller_ary);
45
+ if(count == Qnil)
46
+ count = INT2FIX(0);
47
+ count = INT2FIX(FIX2UINT(count) + 1);
48
+ rb_hash_aset(count_hash, caller_ary, count);
49
+ /*
50
+ printf("CALLSITE: %s -> %s %d\n", RSTRING(rb_inspect(curr_meth))->ptr,
51
+ RSTRING(rb_inspect(caller_ary))->ptr, FIX2INT(count));
52
+ */
53
+
54
+ return Qnil;
55
+ }
56
+
57
+
58
+ static VALUE
59
+ record_method_def_site(VALUE args)
60
+ {
61
+ type_def_site *pargs = (type_def_site *)args;
62
+ VALUE def_site_info;
63
+ VALUE hash;
64
+
65
+ if( RTEST(rb_hash_aref(method_def_site_info, pargs->curr_meth)) )
66
+ return Qnil;
67
+ def_site_info = rb_ary_new();
68
+ rb_ary_push(def_site_info, rb_str_new2(pargs->sourcefile));
69
+ rb_ary_push(def_site_info, INT2NUM(pargs->sourceline+1));
70
+ rb_hash_aset(method_def_site_info, pargs->curr_meth, def_site_info);
71
+ /*
72
+ printf("DEFSITE: %s:%d for %s\n", pargs->sourcefile, pargs->sourceline+1,
73
+ RSTRING(rb_inspect(pargs->curr_meth))->ptr);
74
+ */
75
+
76
+ return Qnil;
77
+ }
78
+
79
+ static VALUE
80
+ callsite_custom_backtrace(int lev)
81
+ {
82
+ struct FRAME *frame = ruby_frame;
83
+ VALUE ary;
84
+ NODE *n;
85
+ VALUE level;
86
+ VALUE klass;
87
+
88
+ ary = rb_ary_new();
89
+ if (frame->last_func == ID_ALLOCATOR) {
90
+ frame = frame->prev;
91
+ }
92
+ for (; frame && (n = frame->node); frame = frame->prev) {
93
+ if (frame->prev && frame->prev->last_func) {
94
+ if (frame->prev->node == n) continue;
95
+ level = rb_ary_new();
96
+ klass = frame->prev->last_class ? frame->prev->last_class : Qnil;
97
+ if(TYPE(klass) == T_ICLASS) {
98
+ klass = CLASS_OF(klass);
99
+ }
100
+ rb_ary_push(level, klass);
101
+ rb_ary_push(level, ID2SYM(frame->prev->last_func));
102
+ rb_ary_push(level, rb_str_new2(n->nd_file));
103
+ rb_ary_push(level, INT2NUM(nd_line(n)));
104
+ }
105
+ else {
106
+ level = rb_ary_new();
107
+ rb_ary_push(level, Qnil);
108
+ rb_ary_push(level, Qnil);
109
+ rb_ary_push(level, rb_str_new2(n->nd_file));
110
+ rb_ary_push(level, INT2NUM(nd_line(n)));
111
+ }
112
+ rb_ary_push(ary, level);
113
+ if(--lev == 0)
114
+ break;
115
+ }
116
+
117
+ return ary;
118
+ }
119
+
120
+ static void
121
+ coverage_event_callsite_hook(rb_event_t event, NODE *node, VALUE self,
122
+ ID mid, VALUE klass)
123
+ {
124
+ VALUE caller_ary;
125
+ VALUE curr_meth;
126
+ VALUE args[2];
127
+ int status;
128
+
129
+ caller_ary = callsite_custom_backtrace(caller_stack_len);
130
+
131
+ if(TYPE(klass) == T_ICLASS) {
132
+ klass = CLASS_OF(klass);
133
+ }
134
+ curr_meth = rb_ary_new();
135
+ rb_ary_push(curr_meth, klass);
136
+ rb_ary_push(curr_meth, ID2SYM(mid));
137
+
138
+ args[0] = caller_ary;
139
+ args[1] = curr_meth;
140
+ rb_protect(record_callsite_info, (VALUE)args, &status);
141
+ if(!status && node) {
142
+ type_def_site args;
143
+
144
+ args.sourcefile = node->nd_file;
145
+ args.sourceline = nd_line(node) - 1;
146
+ args.curr_meth = curr_meth;
147
+ rb_protect(record_method_def_site, (VALUE)&args, NULL);
148
+ }
149
+ if(status)
150
+ rb_gv_set("$!", Qnil);
151
+ }
152
+
153
+
154
+ static VALUE
155
+ cov_install_callsite_hook(VALUE self)
156
+ {
157
+ if(!callsite_hook_set_p) {
158
+ if(TYPE(caller_info) != T_HASH)
159
+ caller_info = rb_hash_new();
160
+ callsite_hook_set_p = 1;
161
+ rb_add_event_hook(coverage_event_callsite_hook,
162
+ RUBY_EVENT_CALL);
163
+
164
+ return Qtrue;
165
+ } else
166
+ return Qfalse;
167
+ }
168
+
169
+
170
+ static VALUE
171
+ cov_remove_callsite_hook(VALUE self)
172
+ {
173
+ if(!callsite_hook_set_p)
174
+ return Qfalse;
175
+ else {
176
+ rb_remove_event_hook(coverage_event_callsite_hook);
177
+ callsite_hook_set_p = 0;
178
+ return Qtrue;
179
+ }
180
+ }
181
+
182
+
183
+ static VALUE
184
+ cov_generate_callsite_info(VALUE self)
185
+ {
186
+ VALUE ret;
187
+
188
+ ret = rb_ary_new();
189
+ rb_ary_push(ret, caller_info);
190
+ rb_ary_push(ret, method_def_site_info);
191
+ return ret;
192
+ }
193
+
194
+
195
+ static VALUE
196
+ cov_reset_callsite(VALUE self)
197
+ {
198
+ if(callsite_hook_set_p) {
199
+ rb_raise(rb_eRuntimeError,
200
+ "Cannot reset the callsite info in the middle of a traced run.");
201
+ return Qnil;
202
+ }
203
+
204
+ caller_info = rb_hash_new();
205
+ method_def_site_info = rb_hash_new();
206
+ return Qnil;
207
+ }
208
+
209
+ void
210
+ Init_rcov_callsite()
211
+ {
212
+ VALUE mRcov;
213
+ VALUE mRCOV__;
214
+ ID id_rcov = rb_intern("Rcov");
215
+ ID id_coverage__ = rb_intern("RCOV__");
216
+ ID id_script_lines__ = rb_intern("SCRIPT_LINES__");
217
+
218
+ if(rb_const_defined(rb_cObject, id_rcov))
219
+ mRcov = rb_const_get(rb_cObject, id_rcov);
220
+ else
221
+ mRcov = rb_define_module("Rcov");
222
+
223
+ if(rb_const_defined(mRcov, id_coverage__))
224
+ mRCOV__ = rb_const_get_at(mRcov, id_coverage__);
225
+ else
226
+ mRCOV__ = rb_define_module_under(mRcov, "RCOV__");
227
+
228
+ callsite_hook_set_p = 0;
229
+ caller_info = rb_hash_new();
230
+ method_def_site_info = rb_hash_new();
231
+ rb_gc_register_address(&caller_info);
232
+ rb_gc_register_address(&method_def_site_info);
233
+
234
+ rb_define_singleton_method(mRCOV__, "install_callsite_hook",
235
+ cov_install_callsite_hook, 0);
236
+ rb_define_singleton_method(mRCOV__, "remove_callsite_hook",
237
+ cov_remove_callsite_hook, 0);
238
+ rb_define_singleton_method(mRCOV__, "generate_callsite_info",
239
+ cov_generate_callsite_info, 0);
240
+ rb_define_singleton_method(mRCOV__, "reset_callsite", cov_reset_callsite, 0);
241
+ }
242
+ /* vim: set sw=8 expandtab: */
@@ -0,0 +1,13 @@
1
+ unless RUBY_PLATFORM == 'java' then
2
+ require 'mkmf'
3
+
4
+ dir_config("gcov")
5
+ if ENV["USE_GCOV"] and Config::CONFIG['CC'] =~ /gcc/ and
6
+ have_library("gcov", "__gcov_open")
7
+
8
+ $CFLAGS << " -fprofile-arcs -ftest-coverage"
9
+ create_makefile("rcovrt")
10
+ else
11
+ create_makefile("rcovrt")
12
+ end
13
+ end
@@ -0,0 +1,331 @@
1
+
2
+ #include <ruby.h>
3
+ #include <env.h>
4
+ #include <node.h>
5
+ #include <st.h>
6
+ #include <stdlib.h>
7
+ #include <assert.h>
8
+
9
+ #define COVERAGE_DEBUG_EVENTS 0
10
+
11
+ #define RCOVRT_VERSION_MAJOR 2
12
+ #define RCOVRT_VERSION_MINOR 0
13
+ #define RCOVRT_VERSION_REV 0
14
+
15
+ static VALUE mRcov;
16
+ static VALUE mRCOV__;
17
+ static VALUE oSCRIPT_LINES__;
18
+ static ID id_cover;
19
+ static st_table* coverinfo = 0;
20
+ static char coverage_hook_set_p;
21
+
22
+ struct cov_array {
23
+ unsigned int len;
24
+ unsigned int *ptr;
25
+ };
26
+
27
+ static struct cov_array *cached_array = 0;
28
+ static char *cached_file = 0;
29
+
30
+
31
+ /*
32
+ *
33
+ * coverage hook and associated functions
34
+ *
35
+ * */
36
+
37
+ static struct cov_array *
38
+ coverage_increase_counter_uncached(char *sourcefile, unsigned int sourceline,
39
+ char mark_only)
40
+ {
41
+ struct cov_array *carray = NULL;
42
+
43
+ if(sourcefile == NULL) {
44
+ /* "can't happen", just ignore and avoid segfault */
45
+ return NULL;
46
+ } else if(!st_lookup(coverinfo, (st_data_t)sourcefile, (st_data_t*)&carray)) {
47
+ VALUE arr;
48
+
49
+ arr = rb_hash_aref(oSCRIPT_LINES__, rb_str_new2(sourcefile));
50
+ if(NIL_P(arr))
51
+ return 0;
52
+ rb_check_type(arr, T_ARRAY);
53
+ carray = calloc(1, sizeof(struct cov_array));
54
+ carray->ptr = calloc(RARRAY(arr)->len, sizeof(unsigned int));
55
+ carray->len = RARRAY(arr)->len;
56
+ st_insert(coverinfo, (st_data_t)strdup(sourcefile),
57
+ (st_data_t) carray);
58
+ } else {
59
+ /* recovered carray, sanity check */
60
+ assert(carray && "failed to create valid carray");
61
+ }
62
+
63
+ if(mark_only) {
64
+ if(!carray->ptr[sourceline])
65
+ carray->ptr[sourceline] = 1;
66
+ } else {
67
+ if (carray && carray->len > sourceline) {
68
+ carray->ptr[sourceline]++;
69
+ }
70
+ }
71
+
72
+ return carray;
73
+ }
74
+
75
+
76
+ static void
77
+ coverage_mark_caller()
78
+ {
79
+ struct FRAME *frame = ruby_frame;
80
+ NODE *n;
81
+
82
+ if (frame->last_func == ID_ALLOCATOR) {
83
+ frame = frame->prev;
84
+ }
85
+ for (; frame && (n = frame->node); frame = frame->prev) {
86
+ if (frame->prev && frame->prev->last_func) {
87
+ if (frame->prev->node == n) {
88
+ if (frame->prev->last_func == frame->last_func) continue;
89
+ }
90
+ coverage_increase_counter_uncached(n->nd_file, nd_line(n) - 1, 1);
91
+ }
92
+ else {
93
+ coverage_increase_counter_uncached(n->nd_file, nd_line(n) - 1, 1);
94
+ }
95
+ break;
96
+ }
97
+ }
98
+
99
+
100
+ static void
101
+ coverage_increase_counter_cached(char *sourcefile, int sourceline)
102
+ {
103
+ if(cached_file == sourcefile && cached_array && cached_array->len > sourceline) {
104
+ cached_array->ptr[sourceline]++;
105
+ return;
106
+ }
107
+ cached_file = sourcefile;
108
+ cached_array = coverage_increase_counter_uncached(sourcefile, sourceline, 0);
109
+ }
110
+
111
+
112
+ static void
113
+ coverage_event_coverage_hook(rb_event_t event, NODE *node, VALUE self,
114
+ ID mid, VALUE klass)
115
+ {
116
+ char *sourcefile;
117
+ unsigned int sourceline;
118
+ static unsigned int in_hook = 0;
119
+
120
+ if(in_hook) {
121
+ return;
122
+ }
123
+
124
+ in_hook++;
125
+
126
+ #if COVERAGE_DEBUG_EVENTS
127
+ do {
128
+ int status;
129
+ VALUE old_exception;
130
+ old_exception = rb_gv_get("$!");
131
+ rb_protect(rb_inspect, klass, &status);
132
+ if(!status) {
133
+ printf("EVENT: %d %s %s %s %d\n", event,
134
+ klass ? RSTRING(rb_inspect(klass))->ptr : "",
135
+ mid ? (mid == ID_ALLOCATOR ? "ID_ALLOCATOR" : rb_id2name(mid))
136
+ : "unknown",
137
+ node ? node->nd_file : "", node ? nd_line(node) : 0);
138
+ } else {
139
+ printf("EVENT: %d %s %s %d\n", event,
140
+ mid ? (mid == ID_ALLOCATOR ? "ID_ALLOCATOR" : rb_id2name(mid))
141
+ : "unknown",
142
+ node ? node->nd_file : "", node ? nd_line(node) : 0);
143
+ }
144
+ rb_gv_set("$!", old_exception);
145
+ } while (0);
146
+ #endif
147
+
148
+ if(event & RUBY_EVENT_C_CALL) {
149
+ coverage_mark_caller();
150
+ }
151
+ if(event & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN | RUBY_EVENT_CLASS)) {
152
+ in_hook--;
153
+ return;
154
+ }
155
+
156
+ if(node == NULL) {
157
+ in_hook--;
158
+ return;
159
+ }
160
+
161
+ sourcefile = node->nd_file;
162
+ sourceline = nd_line(node) - 1;
163
+
164
+ coverage_increase_counter_cached(sourcefile, sourceline);
165
+ if(event & RUBY_EVENT_CALL)
166
+ coverage_mark_caller();
167
+ in_hook--;
168
+ }
169
+
170
+
171
+ static VALUE
172
+ cov_install_coverage_hook(VALUE self)
173
+ {
174
+ if(!coverage_hook_set_p) {
175
+ if(!coverinfo)
176
+ coverinfo = st_init_strtable();
177
+ coverage_hook_set_p = 1;
178
+ /* TODO: allow C_CALL too, since it's supported already
179
+ * the overhead is around ~30%, tested on typo */
180
+ rb_add_event_hook(coverage_event_coverage_hook,
181
+ RUBY_EVENT_ALL & ~RUBY_EVENT_C_CALL &
182
+ ~RUBY_EVENT_C_RETURN & ~RUBY_EVENT_CLASS);
183
+
184
+ return Qtrue;
185
+ }
186
+ else
187
+ return Qfalse;
188
+ }
189
+
190
+
191
+ static int
192
+ populate_cover(st_data_t key, st_data_t value, st_data_t cover)
193
+ {
194
+ VALUE rcover;
195
+ VALUE rkey;
196
+ VALUE rval;
197
+ struct cov_array *carray;
198
+ unsigned int i;
199
+
200
+ rcover = (VALUE)cover;
201
+ carray = (struct cov_array *) value;
202
+ rkey = rb_str_new2((char*) key);
203
+ rval = rb_ary_new2(carray->len);
204
+ for(i = 0; i < carray->len; i++)
205
+ RARRAY(rval)->ptr[i] = UINT2NUM(carray->ptr[i]);
206
+ RARRAY(rval)->len = carray->len;
207
+
208
+ rb_hash_aset(rcover, rkey, rval);
209
+
210
+ return ST_CONTINUE;
211
+ }
212
+
213
+
214
+ static int
215
+ free_table(st_data_t key, st_data_t value, st_data_t ignored)
216
+ {
217
+ struct cov_array *carray;
218
+
219
+ carray = (struct cov_array *) value;
220
+ free((char *)key);
221
+ free(carray->ptr);
222
+ free(carray);
223
+
224
+ return ST_CONTINUE;
225
+ }
226
+
227
+
228
+ static VALUE
229
+ cov_remove_coverage_hook(VALUE self)
230
+ {
231
+ if(!coverage_hook_set_p)
232
+ return Qfalse;
233
+ else {
234
+ rb_remove_event_hook(coverage_event_coverage_hook);
235
+ coverage_hook_set_p = 0;
236
+ return Qtrue;
237
+ }
238
+ }
239
+
240
+
241
+ static VALUE
242
+ cov_generate_coverage_info(VALUE self)
243
+ {
244
+ VALUE cover;
245
+
246
+ if(rb_const_defined_at(mRCOV__, id_cover)) {
247
+ rb_mod_remove_const(mRCOV__, ID2SYM(id_cover));
248
+ }
249
+
250
+ cover = rb_hash_new();
251
+ if(coverinfo)
252
+ st_foreach(coverinfo, populate_cover, cover);
253
+ rb_define_const(mRCOV__, "COVER", cover);
254
+
255
+ return cover;
256
+ }
257
+
258
+
259
+ static VALUE
260
+ cov_reset_coverage(VALUE self)
261
+ {
262
+ if(coverage_hook_set_p) {
263
+ rb_raise(rb_eRuntimeError,
264
+ "Cannot reset the coverage info in the middle of a traced run.");
265
+ return Qnil;
266
+ }
267
+
268
+ cached_array = 0;
269
+ cached_file = 0;
270
+ st_foreach(coverinfo, free_table, Qnil);
271
+ st_free_table(coverinfo);
272
+ coverinfo = 0;
273
+
274
+ return Qnil;
275
+ }
276
+
277
+
278
+ static VALUE
279
+ cov_ABI(VALUE self)
280
+ {
281
+ VALUE ret;
282
+
283
+ ret = rb_ary_new();
284
+ rb_ary_push(ret, INT2FIX(RCOVRT_VERSION_MAJOR));
285
+ rb_ary_push(ret, INT2FIX(RCOVRT_VERSION_MINOR));
286
+ rb_ary_push(ret, INT2FIX(RCOVRT_VERSION_REV));
287
+
288
+ return ret;
289
+ }
290
+
291
+
292
+ void
293
+ Init_rcovrt()
294
+ {
295
+ ID id_rcov = rb_intern("Rcov");
296
+ ID id_coverage__ = rb_intern("RCOV__");
297
+ ID id_script_lines__ = rb_intern("SCRIPT_LINES__");
298
+
299
+ id_cover = rb_intern("COVER");
300
+
301
+ if(rb_const_defined(rb_cObject, id_rcov))
302
+ mRcov = rb_const_get(rb_cObject, id_rcov);
303
+ else
304
+ mRcov = rb_define_module("Rcov");
305
+
306
+ if(rb_const_defined(mRcov, id_coverage__))
307
+ mRCOV__ = rb_const_get_at(mRcov, id_coverage__);
308
+ else
309
+ mRCOV__ = rb_define_module_under(mRcov, "RCOV__");
310
+
311
+ if(rb_const_defined(rb_cObject, id_script_lines__))
312
+ oSCRIPT_LINES__ = rb_const_get(rb_cObject, rb_intern("SCRIPT_LINES__"));
313
+ else {
314
+ oSCRIPT_LINES__ = rb_hash_new();
315
+ rb_const_set(rb_cObject, id_script_lines__, oSCRIPT_LINES__);
316
+ }
317
+
318
+ coverage_hook_set_p = 0;
319
+
320
+ rb_define_singleton_method(mRCOV__, "install_coverage_hook",
321
+ cov_install_coverage_hook, 0);
322
+ rb_define_singleton_method(mRCOV__, "remove_coverage_hook",
323
+ cov_remove_coverage_hook, 0);
324
+ rb_define_singleton_method(mRCOV__, "generate_coverage_info",
325
+ cov_generate_coverage_info, 0);
326
+ rb_define_singleton_method(mRCOV__, "reset_coverage", cov_reset_coverage, 0);
327
+ rb_define_singleton_method(mRCOV__, "ABI", cov_ABI, 0);
328
+
329
+ Init_rcov_callsite();
330
+ }
331
+ /* vim: set sw=8 expandtab: */