call_stack 0.1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,56 @@
1
+ call_stack is copyrighted free software by Mauricio Fernandez <mfp@acm.org>.
2
+ You can redistribute it and/or modify it under either the terms of the GPL
3
+ (see the file GPL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
@@ -0,0 +1,76 @@
1
+ call_stack copyright (c) 2006 Mauricio Fernandez <mfp@acm.org>
2
+ Use and redistribution subject to the same terms as Ruby.
3
+
4
+ == Overview
5
+
6
+ call_stack allows you to obtain backtrace data in your Ruby code.
7
+ It is used as follows:
8
+
9
+ require 'call_stack'
10
+
11
+ call_stack_on # start recording information
12
+
13
+ #.... somewhere else
14
+ def b; foo end
15
+ def foo
16
+ backtrace = call_stack(-1)
17
+ end
18
+
19
+ b # => [[:unknown, :unknown, "-", 3, nil, nil],
20
+ [Object, :b, "-", 7, #<Binding:0xa7dbe6b8>, :Ruby],
21
+ [Object, :foo, "-", 8, #<Binding:0xa7dbe690>, :Ruby]]
22
+
23
+ call_stack_off
24
+
25
+ Kernel#call_stack returns an array of
26
+ [class, mid, filename, line, binding, language]
27
+ arrays, where language is either :Ruby, :C or nil. Older stack frames come
28
+ first, newer last.
29
+
30
+ Information from stack frames which existed before #call_stack_on was called
31
+ will be incomplete.
32
+
33
+ With no arguments, #call_stack will return an array with only one element
34
+ (with the structure described above), corresponding to the context where
35
+ #call_stack was called, e.g.
36
+
37
+ require 'call_stack'
38
+ call_stack_on
39
+ def foo; call_stack end
40
+ foo # => [[Object, :foo, "-", 4, #<Binding:0xa7d90880>, :Ruby]]
41
+
42
+ With a negative argument, #call_stack returns the whole call stack.
43
+ If a positive argument is given, as many levels as indicated will be returned;
44
+ call_stack() is equivalent to call_stack(1).
45
+
46
+
47
+ == Binding.of_caller compatibility
48
+
49
+ binding_of_caller.rb contains an implementation of Binding.of_caller built on
50
+ top of call_stack. It will make your program somewhat slower, so you might
51
+ want to write your own (it takes 3 lines of code) and postpone the call to
52
+ #call_stack_on until it's needed.
53
+
54
+ == Use with Ruby on Rails
55
+
56
+ Rails' breakpoint functionality doesn't work with 1.8.5, since it depends on
57
+ the "standard" Binding.of_caller, which won't run on Ruby > 1.8.4.
58
+
59
+ This can be overcome by using call_stack with Rails' breakpointer as follows:
60
+ ruby -rbreakpoint185 script/server
61
+ and then, in another console
62
+ script/breakpointer
63
+
64
+ == Installing
65
+
66
+ De-compress the archive and enter its top directory.
67
+ Then type:
68
+
69
+ ($ su)
70
+ # ruby setup.rb
71
+
72
+ Run ruby setup.rb --help for information on the install options.
73
+
74
+ == License
75
+
76
+ call_stack is licensed under the same terms as Ruby. See LICENSE.
@@ -0,0 +1,95 @@
1
+
2
+ # {{{ Package tasks
3
+
4
+ CALL_STACK_VERSION = File.read("VERSION").chomp
5
+
6
+ PKG_REVISION = ".0"
7
+ PKG_FILES = FileList[
8
+ "ext/call_stack/*.c",
9
+ "ext/call_stack/*.h",
10
+ "ext/call_stack/extconf.rb",
11
+ "lib/**/*.rb",
12
+ "LICENSE", "Rakefile", "README.*", "THANKS", "VERSION", "example.rb",
13
+ "mingw-rbconfig.rb", "setup.rb"
14
+ ]
15
+
16
+ require 'rake/gempackagetask'
17
+ Spec = Gem::Specification.new do |s|
18
+ s.name = "call_stack"
19
+ s.version = CALL_STACK_VERSION + PKG_REVISION
20
+ s.summary = "Call stack information and Binding.of_caller/breakpoint workaround for Ruby 1.8.5"
21
+ s.description = <<EOF
22
+ call_stack can be used to obtain backtrace information in a running program.
23
+ It also contains a new Binding.of_caller implementation that works with Ruby 1.8.5.
24
+
25
+ It can be used with Rails' breakpointer as follows:
26
+ ruby -rbreakpoint185 script/server
27
+ and then, in another console
28
+ script/breakpointer
29
+ EOF
30
+ s.files = PKG_FILES.to_a
31
+ s.require_path = 'lib'
32
+ s.extensions << "ext/call_stack/extconf.rb"
33
+ s.author = "Mauricio Fernandez"
34
+ s.email = "mfp@acm.org"
35
+ s.homepage = "http://eigenclass.org/"
36
+ s.has_rdoc = true
37
+ s.extra_rdoc_files = %w[README.en]
38
+ s.rdoc_options << "--main" << "README.en" << "--title" << 'call_stack'
39
+ s.post_install_message = <<EOF
40
+
41
+ =============================================================================
42
+
43
+ call_stack provides an alternative implementation of Binding.of_caller that
44
+ works on Ruby > 1.8.4. You can use it by loading breakpoint185.rb before
45
+ running your program.
46
+
47
+ If you're using Rails, you can do
48
+ ruby -rbreakpoint185 script/server
49
+ and then, in another console
50
+ script/breakpointer
51
+
52
+ =============================================================================
53
+
54
+ EOF
55
+ end
56
+
57
+ Rake::GemPackageTask.new(Spec) do |p|
58
+ p.need_tar = true
59
+ end
60
+
61
+ # {{{ Cross-compilation and building of a binary RubyGems package for mswin32
62
+
63
+ require 'rake/clean'
64
+
65
+ WIN32_PKG_DIR = "call_stack-" + CALL_STACK_VERSION + PKG_REVISION
66
+
67
+ file "#{WIN32_PKG_DIR}" => [:package] do
68
+ sh "tar zxf pkg/#{WIN32_PKG_DIR}.tgz"
69
+ end
70
+
71
+ desc "Cross-compile the rcovrt.so extension for win32"
72
+ file "call_stack_win32" => ["#{WIN32_PKG_DIR}"] do
73
+ cp "mingw-rbconfig.rb", "#{WIN32_PKG_DIR}/ext/call_stack/rbconfig.rb"
74
+ sh "cd #{WIN32_PKG_DIR}/ext/call_stack/ && ruby -I. extconf.rb && make"
75
+ mv "#{WIN32_PKG_DIR}/ext/call_stack/call_stack.so", "#{WIN32_PKG_DIR}/lib"
76
+ end
77
+
78
+ Win32Spec = Spec.clone
79
+ Win32Spec.platform = Gem::Platform::WIN32
80
+ Win32Spec.extensions = []
81
+ Win32Spec.files += ["lib/call_stack.so"]
82
+
83
+ desc "Build the binary RubyGems package for win32"
84
+ task :rubygems_win32 => ["call_stack_win32"] do
85
+ Dir.chdir("#{WIN32_PKG_DIR}") do
86
+ Gem::Builder.new(Win32Spec).build
87
+ verbose(true) {
88
+ mv Dir["*.gem"].first, "../pkg/call_stack-#{CALL_STACK_VERSION + PKG_REVISION}-mswin32.gem"
89
+ }
90
+ end
91
+ end
92
+
93
+ CLEAN.include "#{WIN32_PKG_DIR}"
94
+
95
+ # vim: set sw=2 ft=ruby:
data/THANKS ADDED
@@ -0,0 +1,5 @@
1
+
2
+ Kent Sibilev
3
+ * create_binding() was stolen from his excellent ruby-debug, replacing
4
+ an rb_funcall()
5
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,42 @@
1
+
2
+ require 'call_stack'
3
+ def a; _a = 1; b end
4
+ def b; _b = 2; c end
5
+ def c; _c = 3; d end
6
+ def d; _d = 4; send(:e) end
7
+ def e
8
+ levels = call_stack(10)
9
+ levels.each do |klass, id, file, line, binding, lang|
10
+ puts "=" * 80
11
+ p [klass, id, file, line, lang]
12
+ next unless binding
13
+ p eval("local_variables", binding).map{|x| [x, eval(x, binding)]}
14
+ end
15
+ end
16
+
17
+ def x
18
+ call_stack_on
19
+ a
20
+ call_stack_off
21
+ end
22
+
23
+ x
24
+
25
+
26
+ # binding of caller
27
+ require 'binding_of_caller'
28
+
29
+ def foo
30
+ a = b = 1
31
+ puts bar.inspect, a, b
32
+ end
33
+
34
+ def bar
35
+ Binding.of_caller do |binding|
36
+ ret = eval("[a,b]", binding)
37
+ eval("a, b = 2, 3", binding)
38
+ ret
39
+ end
40
+ end
41
+
42
+ foo
@@ -0,0 +1,493 @@
1
+
2
+ #include <ruby.h>
3
+ #include <node.h>
4
+ #include <st.h>
5
+ #include <stdlib.h>
6
+
7
+ #define CALLSITE_DEBUG 0
8
+ #define CALLSITE_DEBUG_EVENTS 0
9
+
10
+ static char callsite_hook_set_p;
11
+
12
+ typedef struct {
13
+ char *sourcefile;
14
+ unsigned int sourceline;
15
+ ID mid;
16
+ VALUE klass;
17
+ VALUE binding;
18
+ VALUE type;
19
+ } code_site_t;
20
+ static VALUE additional_gc_roots = 0;
21
+
22
+ static caller_stack_len = 1;
23
+ static ID id_unknown;
24
+ static ID id_binding;
25
+ static VALUE sym_C;
26
+ static VALUE sym_Ruby;
27
+
28
+ typedef struct {
29
+ code_site_t *start;
30
+ code_site_t *end;
31
+ code_site_t *ptr;
32
+ } callsite_stack_t;
33
+ static callsite_stack_t *callsite_stack;
34
+ static VALUE callsite_last_thread;
35
+ static st_table* callsite_stack_tbl;
36
+ static st_table* callsite_filename_tbl;
37
+
38
+ static st_table* callsite_defsite_data;
39
+ static st_table* callsite_caller_data;
40
+
41
+ static VALUE module_to_s_proc;
42
+
43
+ #define INITIAL_STACK_SIZE 8
44
+
45
+
46
+ static int
47
+ value_cmp(VALUE x, VALUE y)
48
+ {
49
+ return x != y;
50
+ }
51
+
52
+ static int
53
+ value_hash(VALUE v)
54
+ {
55
+ return v;
56
+ }
57
+
58
+ static struct st_hash_type type_value_hash = {
59
+ value_cmp,
60
+ value_hash,
61
+ #if RUBY_VERSION_CODE >= 190
62
+ st_nothing_key_free,
63
+ st_nothing_key_clone
64
+ #endif
65
+ };
66
+
67
+
68
+ typedef struct {
69
+ VALUE klass;
70
+ ID mid;
71
+ } meth_selector;
72
+
73
+ static int
74
+ meth_selector_cmp(meth_selector* x, meth_selector* y)
75
+ {
76
+ return (x->klass != y->klass || x->mid != y->mid);
77
+ }
78
+
79
+ static int
80
+ meth_selector_hash(meth_selector* x)
81
+ {
82
+ return (x->mid << 10) | (x->klass >> 2);
83
+ }
84
+
85
+ static struct st_hash_type type_meth_selector_hash = {
86
+ meth_selector_cmp,
87
+ meth_selector_hash,
88
+ #if RUBY_VERSION_CODE >= 190
89
+ HeyFIXME,
90
+ HeyFIXME
91
+ #endif
92
+ };
93
+
94
+
95
+ /*
96
+ *
97
+ * callsite hook and associated functions
98
+ *
99
+ * */
100
+
101
+ static VALUE
102
+ callsite_gen_backtrace_info(int argc, VALUE *argv, VALUE self)
103
+ {
104
+ VALUE backtrace;
105
+ VALUE level;
106
+ VALUE klass;
107
+ VALUE depth = Qnil;
108
+ unsigned int i;
109
+ code_site_t *csite;
110
+ int len;
111
+
112
+ if(!callsite_hook_set_p) {
113
+ return Qnil;
114
+ }
115
+ rb_scan_args(argc, argv, "01", &depth);
116
+ if(NIL_P(depth)) {
117
+ len = 1;
118
+ } else {
119
+ len = NUM2INT(depth);
120
+ }
121
+ if(len < 0) {
122
+ len = 0x7FFFFFFF;
123
+ }
124
+ backtrace = rb_ary_new();
125
+ for(i = 0, csite = callsite_stack->ptr-2; i < len; i++, csite--) {
126
+ if(csite < callsite_stack->start)
127
+ break;
128
+ level = rb_ary_new();
129
+ klass = csite->klass ? csite->klass : Qnil;
130
+ rb_ary_push(level, klass);
131
+ rb_ary_push(level, csite->mid ? ID2SYM(csite->mid) : Qnil);
132
+ if(csite->sourcefile) {
133
+ rb_ary_push(level, rb_str_new2(csite->sourcefile));
134
+ rb_ary_push(level, UINT2NUM(csite->sourceline));
135
+ }
136
+ else {
137
+ rb_ary_push(level, Qnil);
138
+ rb_ary_push(level, Qnil);
139
+ }
140
+ rb_ary_push(level, csite->binding);
141
+ rb_ary_push(level, csite->type);
142
+
143
+ rb_ary_unshift(backtrace, level);
144
+ }
145
+
146
+
147
+ return backtrace;
148
+ }
149
+
150
+
151
+ static callsite_stack_t *
152
+ callsite_stack_create()
153
+ {
154
+ callsite_stack_t *stack;
155
+
156
+ stack = ALLOC(callsite_stack_t);
157
+ stack->start = stack->ptr =
158
+ ALLOC_N(code_site_t, INITIAL_STACK_SIZE);
159
+ stack->end = stack->start + INITIAL_STACK_SIZE;
160
+ return stack;
161
+ }
162
+
163
+ static void
164
+ callsite_stack_free(callsite_stack_t *stack)
165
+ {
166
+ xfree(stack->start);
167
+ xfree(stack);
168
+ }
169
+
170
+ static void
171
+ callsite_stack_push(VALUE klass, ID mid,
172
+ char *filename, unsigned int line, VALUE binding,
173
+ VALUE type)
174
+ {
175
+ char *sourcefile;
176
+
177
+ if (callsite_stack->ptr == callsite_stack->end) {
178
+ code_site_t *new_start;
179
+ int len, new_capa;
180
+
181
+ len = callsite_stack->ptr - callsite_stack->start;
182
+ new_capa = (callsite_stack->end - callsite_stack->start) * 2;
183
+ new_start = ALLOC_N(code_site_t, new_capa);
184
+ memcpy(new_start, callsite_stack->start, sizeof(code_site_t) * len);
185
+ callsite_stack->start = new_start;
186
+ callsite_stack->end = new_start + new_capa;
187
+ callsite_stack->ptr = new_start + len;
188
+ }
189
+ callsite_stack->ptr->klass = klass ? klass : Qnil;
190
+ callsite_stack->ptr->mid = mid ? mid : id_unknown;
191
+ if(filename) {
192
+ /* have to copy the filename, since it could be reclaimed by Ruby */
193
+ if(!st_lookup(callsite_filename_tbl, (st_data_t)filename, (st_data_t*)&sourcefile)) {
194
+ sourcefile = strdup(filename);
195
+ st_insert(callsite_filename_tbl, (st_data_t)sourcefile, (st_data_t)sourcefile);
196
+ }
197
+ callsite_stack->ptr->sourcefile = sourcefile;
198
+ } else {
199
+ callsite_stack->ptr->sourcefile = 0;
200
+ }
201
+ callsite_stack->ptr->sourceline = line ? line : 0;
202
+ callsite_stack->ptr->binding = binding;
203
+ callsite_stack->ptr->type = type;
204
+ rb_hash_aset(additional_gc_roots, binding, binding);
205
+
206
+ callsite_stack->ptr++;
207
+ }
208
+
209
+
210
+ static VALUE
211
+ remove_gc_root(VALUE arg)
212
+ {
213
+ rb_hash_delete(additional_gc_roots, arg);
214
+ }
215
+
216
+ static VALUE
217
+ dummy(VALUE arg1, VALUE arg2)
218
+ {
219
+ return Qnil;
220
+ }
221
+
222
+ static inline code_site_t *
223
+ callsite_stack_pop(callsite_stack_t *stack)
224
+ {
225
+ if (stack->ptr == stack->start) {
226
+ return stack->ptr;
227
+ //rb_raise(rb_eException, "empty stack");
228
+ }
229
+ /* this is what we want to do, but it's not safe because rb_hash_delete
230
+ * will reuse the current block ... */
231
+ /* rb_hash_delete(additional_gc_roots, (stack->ptr-1)->binding); */
232
+ rb_iterate(remove_gc_root, (stack->ptr-1)->binding, dummy, Qnil);
233
+ return --stack->ptr;
234
+ }
235
+
236
+
237
+ static st_table *
238
+ callsite_stack_table_create()
239
+ {
240
+ return st_init_table(&type_value_hash);
241
+ }
242
+
243
+
244
+ static inline callsite_stack_t*
245
+ callsite_stack_find(VALUE thread)
246
+ {
247
+ st_data_t val;
248
+
249
+ if(st_lookup(callsite_stack_tbl, (st_data_t)thread, &val))
250
+ return (callsite_stack_t *) val;
251
+ else
252
+ return NULL;
253
+ }
254
+
255
+
256
+ static inline int
257
+ callsite_stack_table_insert(VALUE thread, callsite_stack_t *stack)
258
+ {
259
+ return st_insert(callsite_stack_tbl, (st_data_t ) thread, (st_data_t) stack);
260
+ }
261
+
262
+
263
+ static void
264
+ callsite_stack_dump()
265
+ {
266
+ code_site_t *csite;
267
+ int status;
268
+ VALUE klass_desc;
269
+ char *desc;
270
+ VALUE old_exception;
271
+
272
+ old_exception = rb_gv_get("$!");
273
+ printf("+++++++++\n");
274
+ printf("CALL STACK FOR CURRENT THREAD:\n");
275
+ for(csite = callsite_stack->start; csite < callsite_stack->ptr; csite++) {
276
+ klass_desc = rb_protect(rb_inspect, csite->klass, &status);
277
+ if(!status) {
278
+ desc = RSTRING(klass_desc)->ptr;
279
+ } else {
280
+ desc = rb_obj_classname(csite->klass);
281
+ }
282
+ printf("%30s %20s %s %d\n", desc,
283
+ csite->mid ? rb_id2name(csite->mid) : "",
284
+ csite->sourcefile ? csite->sourcefile : "unknown",
285
+ csite->sourceline);
286
+ }
287
+ printf("---------\n");
288
+
289
+ rb_gv_set("$!", old_exception);
290
+
291
+ }
292
+
293
+ /* This function stolen from Kent Sibilev's ruby-debug. */
294
+ /*
295
+ * This is a NASTY HACK. For some reasons rb_f_binding is declared
296
+ * static in eval.c
297
+ */
298
+ static VALUE
299
+ create_binding(VALUE self)
300
+ {
301
+ typedef VALUE (*bind_func_t)(VALUE);
302
+ static bind_func_t f_binding = NULL;
303
+
304
+ if(f_binding == NULL)
305
+ {
306
+ NODE *body, *method;
307
+ st_lookup(RCLASS(rb_mKernel)->m_tbl, rb_intern("binding"), (st_data_t *)&body);
308
+ method = (NODE *)body->u2.value;
309
+ f_binding = (bind_func_t)method->u1.value;
310
+ }
311
+ return f_binding(self);
312
+ }
313
+
314
+ static void
315
+ callsite_select_callsite_stack()
316
+ {
317
+ VALUE curr_thread;
318
+ curr_thread = rb_thread_current();
319
+ if (curr_thread != callsite_last_thread) {
320
+ callsite_stack = callsite_stack_find(curr_thread);
321
+ if (!callsite_stack) {
322
+ callsite_stack = callsite_stack_create();
323
+ callsite_stack_table_insert(curr_thread, callsite_stack);
324
+ /* no need to add curr_thread to the additional root list for
325
+ * the GC: we will never it if the Thread disappears anyway,
326
+ * so no access is done to the (dead) Ruby object. */
327
+ }
328
+ callsite_last_thread = curr_thread;
329
+ }
330
+ }
331
+
332
+
333
+ static void
334
+ coverage_event_callsite_hook(rb_event_t event, NODE *node, VALUE self,
335
+ ID mid, VALUE klass)
336
+ {
337
+ VALUE curr_meth;
338
+ VALUE args[4];
339
+ VALUE binding;
340
+ int status;
341
+ static int in_hook = 0;
342
+
343
+ if(in_hook)
344
+ return;
345
+ in_hook++;
346
+ callsite_select_callsite_stack();
347
+
348
+ if(TYPE(klass) == T_ICLASS) {
349
+ klass = RBASIC(klass)->klass;
350
+ }
351
+ #if CALLSITE_DEBUG_EVENTS
352
+ do {
353
+ VALUE old_exception;
354
+ old_exception = rb_gv_get("$!");
355
+ rb_protect(rb_inspect, klass, &status);
356
+ if(!status) {
357
+ printf("EVENT: %d %s %s %s %d\n", event,
358
+ klass ? RSTRING(rb_inspect(klass))->ptr : "",
359
+ mid ? (mid == ID_ALLOCATOR ? "ID_ALLOCATOR" : rb_id2name(mid))
360
+ : "unknown",
361
+ node ? node->nd_file : "", node ? nd_line(node) : 0);
362
+ } else {
363
+ printf("EVENT: %d %s %s %d\n", event,
364
+ mid ? (mid == ID_ALLOCATOR ? "ID_ALLOCATOR" : rb_id2name(mid))
365
+ : "unknown",
366
+ node ? node->nd_file : "", node ? nd_line(node) : 0);
367
+ }
368
+ rb_gv_set("$!", old_exception);
369
+ } while (0);
370
+ #endif
371
+ switch(event) {
372
+ case RUBY_EVENT_CALL:
373
+ binding = create_binding(rb_mKernel);
374
+ callsite_stack_push(klass, mid, node->nd_file, nd_line(node) + 1, binding, sym_Ruby);
375
+ #if CALLSITE_DEBUG
376
+ callsite_stack_dump();
377
+ #endif
378
+ break;
379
+ case RUBY_EVENT_C_CALL:
380
+ callsite_stack_push(klass, mid, 0, 0, Qnil, sym_C);
381
+ break;
382
+ case RUBY_EVENT_RETURN:
383
+ case RUBY_EVENT_C_RETURN:
384
+ callsite_stack_pop(callsite_stack);
385
+ #if CALLSITE_DEBUG
386
+ callsite_stack_dump();
387
+ #endif
388
+ break;
389
+ }
390
+ in_hook--;
391
+ }
392
+
393
+
394
+ static void
395
+ callsite_prefill_callsite_stack()
396
+ {
397
+ VALUE backtrace;
398
+ VALUE str;
399
+ VALUE parts;
400
+ int i;
401
+ char *sourcefile;
402
+ unsigned int sourceline;
403
+ VALUE meth;
404
+ ID aref;
405
+ VALUE regex;
406
+ ID mid;
407
+ VALUE sym_unknown;
408
+
409
+ callsite_select_callsite_stack();
410
+ aref = rb_intern("[]");
411
+ regex = rb_eval_string("/in `([^']*)'/");
412
+ sym_unknown = ID2SYM(rb_intern("unknown"));
413
+ /* reset before refilling */
414
+ callsite_stack->ptr = callsite_stack->start;
415
+
416
+ backtrace = rb_funcall(rb_mKernel, rb_intern("caller"), 0);
417
+ if(TYPE(backtrace) == T_ARRAY) {
418
+ for(i = RARRAY(backtrace)->len - 1; i >= 0; i--) {
419
+ str = RARRAY(backtrace)->ptr[i];
420
+ parts = rb_str_split(str, ":");
421
+ if(RARRAY(parts)->len < 2) {
422
+ sourcefile = "unknown";
423
+ sourceline = 0;
424
+ } else {
425
+ sourcefile = RSTRING(RARRAY(parts)->ptr[0])->ptr;
426
+ sscanf(RSTRING((RARRAY(parts)->ptr[1]))->ptr,
427
+ "%d", &sourceline);
428
+ }
429
+ if( RTEST(meth = rb_funcall(str, aref, 2, regex, INT2FIX(1))) ) {
430
+ /* FIXME: check type */
431
+ mid = rb_intern(RSTRING(meth)->ptr);
432
+ } else {
433
+ mid = rb_intern("unknown");
434
+ }
435
+ callsite_stack_push(sym_unknown, mid, sourcefile, sourceline,
436
+ Qnil, Qnil);
437
+ }
438
+
439
+ }
440
+ else {
441
+ rb_raise(rb_eRuntimeError, "Your Kernel#caller is weird.");
442
+ }
443
+ }
444
+
445
+ static VALUE
446
+ cov_install_callsite_hook(VALUE self)
447
+ {
448
+ if(!callsite_hook_set_p) {
449
+ callsite_hook_set_p = 1;
450
+ /* for each thread */
451
+ callsite_prefill_callsite_stack();
452
+ rb_add_event_hook(coverage_event_callsite_hook,
453
+ RUBY_EVENT_CALL | RUBY_EVENT_C_CALL |
454
+ RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN);
455
+
456
+ return Qtrue;
457
+ } else
458
+ return Qfalse;
459
+ }
460
+
461
+
462
+ static VALUE
463
+ cov_remove_callsite_hook(VALUE self)
464
+ {
465
+ if(!callsite_hook_set_p)
466
+ return Qfalse;
467
+ else {
468
+ rb_remove_event_hook(coverage_event_callsite_hook);
469
+ callsite_hook_set_p = 0;
470
+ return Qtrue;
471
+ }
472
+ }
473
+
474
+
475
+
476
+ void
477
+ Init_call_stack()
478
+ {
479
+ id_unknown = rb_intern("unknown");
480
+ sym_C = ID2SYM(rb_intern("C"));
481
+ sym_Ruby = ID2SYM(rb_intern("Ruby"));
482
+ id_binding = rb_intern("binding");
483
+
484
+ additional_gc_roots = rb_hash_new();
485
+ rb_gc_register_address(&additional_gc_roots);
486
+ callsite_stack_tbl = st_init_table(&type_value_hash);
487
+ callsite_filename_tbl = st_init_strtable();
488
+
489
+ rb_define_global_function("call_stack_on", cov_install_callsite_hook, 0);
490
+ rb_define_global_function("call_stack_off", cov_remove_callsite_hook, 0);
491
+ rb_define_global_function("call_stack", callsite_gen_backtrace_info, -1);
492
+ }
493
+ /* vim: set sw=8 expandtab: */