call_stack 0.1.0.0-mswin32

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.
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: */