spicycode-rcov 0.8.1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: */