rb-trace 0.2

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/lib/trace_mod.rb ADDED
@@ -0,0 +1,207 @@
1
+ require_relative '../ext/trace'
2
+
3
+ # A module for interfacing with Kernel::set_trace which adds
4
+ # filtering of events inside Ruby (as opposed to inside your trace
5
+ # hook which is slower).
6
+ module Trace
7
+
8
+
9
+ unless defined?(NO_EVENT_MASK)
10
+ NO_EVENT_MASK = 0x0000
11
+ # Event masks from +ruby.h+
12
+ LINE_EVENT_MASK = 0x0001
13
+ CLASS_EVENT_MASK = 0x0002
14
+ END_EVENT_MASK = 0x0004
15
+ CALL_EVENT_MASK = 0x0008
16
+ RETURN_EVENT_MASK = 0x0010
17
+ C_CALL_EVENT_MASK = 0x0020
18
+ C_RETURN_EVENT_MASK = 0x0040
19
+ RAISE_EVENT_MASK = 0x0080
20
+ INSN_EVENT_MASK = 0x0100
21
+ BRKPT_EVENT_MASK = 0x0200
22
+ ALL_EVENT_MASKS = (0xffff & ~INSN_EVENT_MASK)
23
+ VM_EVENT_MASK = 0x10000
24
+ SWITCH_EVENT_MASK = 0x20000
25
+ COVERAGE_EVENT_MASK = 0x40000
26
+
27
+ # Events that will trigger if you don't specify any
28
+ DEFAULT_EVENT_MASK =
29
+ BRKPT_EVENT_MASK |
30
+ CALL_EVENT_MASK |
31
+ CLASS_EVENT_MASK |
32
+ C_CALL_EVENT_MASK |
33
+ C_RETURN_EVENT_MASK |
34
+ END_EVENT_MASK |
35
+ LINE_EVENT_MASK |
36
+ RAISE_EVENT_MASK |
37
+ RETURN_EVENT_MASK
38
+
39
+
40
+ # Symbols we use to represent the individual bits inside a Fixnum bitmask
41
+ EVENT2MASK = {
42
+ :brkpt => BRKPT_EVENT_MASK,
43
+ :c_call => C_CALL_EVENT_MASK,
44
+ :c_return => C_RETURN_EVENT_MASK,
45
+ :call => CALL_EVENT_MASK,
46
+ :class => CLASS_EVENT_MASK,
47
+ :coverage => COVERAGE_EVENT_MASK,
48
+ :end => END_EVENT_MASK,
49
+ :insn => INSN_EVENT_MASK,
50
+ :line => LINE_EVENT_MASK,
51
+ :raise => RAISE_EVENT_MASK,
52
+ :return => RETURN_EVENT_MASK,
53
+ :switch => SWITCH_EVENT_MASK,
54
+ :vm => VM_EVENT_MASK
55
+ }
56
+
57
+ EVENTS = EVENT2MASK.keys.sort
58
+ end
59
+
60
+ # Convert +events+ into a Fixnum bitmask used internally by Ruby.
61
+ # Parameter +events+ should be Enumerable and each element should
62
+ # either be a Fixnum mask value or something that can be converted
63
+ # to a symbol. If the latter, the case is not important as we'll
64
+ # downcase the string representation.
65
+ def events2bitmask(events)
66
+ bitmask = NO_EVENT_MASK
67
+ bad_events = []
68
+ events.each do |event|
69
+ nextbit = nil
70
+ if event.is_a?(Fixnum)
71
+ # Assume a bit mask
72
+ nextbit = event
73
+ elsif event.respond_to?(:to_sym)
74
+ # Assume an event name
75
+ sym = event.to_sym.to_s.downcase.to_sym
76
+ nextbit = EVENT2MASK[sym] if EVENT2MASK.member?(sym)
77
+ end
78
+ if nextbit
79
+ bitmask |= nextbit
80
+ else
81
+ bad_events << event
82
+ end
83
+ end
84
+ return bitmask, bad_events
85
+ end
86
+ module_function :events2bitmask
87
+
88
+ # Convert Fixnum +bitmask+ used internally by Ruby, into an Array of
89
+ # event names.
90
+ def bitmask2events(bitmask)
91
+ mask = []
92
+ EVENT2MASK.each do |key, value|
93
+ mask << key if (value & bitmask) != 0
94
+ end
95
+ return mask
96
+ end
97
+ module_function :bitmask2events
98
+
99
+ def convert_event_mask(mask_arg)
100
+ if mask_arg.is_a?(Fixnum)
101
+ event_mask = mask_arg
102
+ elsif mask_arg.kind_of?(Enumerable)
103
+ event_mask, bad_events = events2bitmask(mask_arg)
104
+ raise ArgumentError, "Bad set elements: #{bad_events.inspect}" unless
105
+ bad_events.empty?
106
+ else
107
+ raise ArgumentError, "Bad set mask arg: #{mask_arg}"
108
+ end
109
+ event_mask
110
+ end
111
+ module_function :convert_event_mask
112
+
113
+ # Return a list of the global event masks in effect
114
+ def event_masks
115
+ RubyVM::TraceHook::trace_hooks.map do |hook|
116
+ hook.event_mask
117
+ end
118
+ end
119
+ module_function :event_masks
120
+
121
+ # Set event masks
122
+ def event_masks=(mask_arg)
123
+ RubyVM::TraceHook::trace_hooks.map do |hook|
124
+ event_mask = convert_event_mask(mask_arg)
125
+ hook.event_mask = event_mask
126
+ end
127
+ end
128
+ module_function :'event_masks='
129
+
130
+ # Replacement for Kernel::set_trace We allow for a more flexible
131
+ # event mask parameters to be set.
132
+ def add_trace_func(*args)
133
+ if args.size > 3 or args.size < 1
134
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)"
135
+ end
136
+ proc, mask_arg = args
137
+ if proc.nil?
138
+ if args.size != 1
139
+ raise ArgumentError,
140
+ "When first argument (Proc) is nil, there should be only one argument"
141
+ end
142
+ return Kernel.set_trace_func(proc)
143
+ elsif mask_arg.nil?
144
+ Kernel.add_trace_func(proc)
145
+ else # args.size == 2
146
+ event_mask = convert_event_mask(mask_arg)
147
+ Kernel.add_trace_func(proc, event_mask)
148
+ end
149
+ end
150
+ module_function :add_trace_func
151
+
152
+ # Replacement for Kernel::set_trace We allow for a more flexible
153
+ # event mask parameters to be set.
154
+ def set_trace_func(*args)
155
+ if args.size > 3 or args.size < 1
156
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)"
157
+ end
158
+ proc, mask_arg = args
159
+ if proc.nil?
160
+ if args.size != 1
161
+ raise ArgumentError,
162
+ "When first argument (Proc) is nil, there should be only one argument"
163
+ end
164
+ return Kernel.set_trace_func(proc)
165
+ elsif mask_arg.nil?
166
+ Kernel.set_trace_func(proc)
167
+ else # args.size == 2
168
+ event_mask = convert_event_mask(mask_arg)
169
+ Kernel.set_trace_func(proc, event_mask)
170
+ end
171
+ end
172
+ module_function :set_trace_func
173
+ end
174
+
175
+ if __FILE__ == $0
176
+ # Demo it
177
+ include Trace
178
+ require 'set'
179
+ mask, unhandled = events2bitmask([LINE_EVENT_MASK, CLASS_EVENT_MASK])
180
+ p bitmask2events(mask)
181
+ p [mask, unhandled]
182
+ p events2bitmask(Set.new([LINE_EVENT_MASK, CLASS_EVENT_MASK]))
183
+ p events2bitmask(['LINE', 'Class'])
184
+ p events2bitmask(Set.new(['LINE', 'Class']))
185
+ p events2bitmask([:foo, 'bar'])
186
+ p events2bitmask(['C call', ['bar']])
187
+ p events2bitmask(['C call', ['bar']])
188
+ def foo # :nodoc:
189
+ end
190
+ Trace::set_trace_func(Proc.new {|e, tf| p e}, [:call, :return])
191
+ foo
192
+ Trace::event_masks.each { |m| print "event mask: 0x%x\n" % m }
193
+ Trace::event_masks = [:call]
194
+ p '=' * 40
195
+ foo
196
+ Trace::event_masks.each { |m| print "event mask: 0x%x\n" % m }
197
+ p '=' * 40
198
+ Trace::set_trace_func(Proc.new {|e, tf| p e})
199
+ Trace::event_masks.each { |m| print "event mask: 0x%x\n" % m }
200
+ foo
201
+ Trace::set_trace_func(nil)
202
+ p '=' * 40
203
+ foo
204
+ p '=' * 40
205
+ Trace::event_masks.each { |m| print "event mask: 0x%x\n" % m }
206
+
207
+ end
@@ -0,0 +1,228 @@
1
+ require 'set'
2
+ # require '/src/external-vcs/rb-threadframe/ext/thread_frame'
3
+ require 'thread_frame'
4
+ require_relative 'trace_mod'
5
+
6
+ module Trace
7
+
8
+ # A class that can be used to test whether certain methods should be
9
+ # excluded. We also convert the hook call to pass a threadframe and
10
+ # an event rather than event and those other 5 parameters.
11
+ class Filter
12
+
13
+ attr_reader :excluded
14
+ attr_reader :hook_proc
15
+
16
+ def initialize(excluded_meths = [])
17
+ excluded_meths = excluded_meths.select{|fn| valid_meth?(fn)}
18
+ excluded_meths << self.method(:set_trace_func).to_s
19
+ @excluded = Set.new(excluded_meths.map{|m| m.to_s})
20
+ end
21
+
22
+ # +fn+ should be a RubyVM::ThreadFrame object or a Proc which has an
23
+ # instruction sequence
24
+ def valid_meth?(fn)
25
+ fn.is_a?(Method)
26
+ end
27
+
28
+ # Add +meth+ to list of trace filters.
29
+ def <<(meth)
30
+ if valid_meth?(meth)
31
+ @excluded << meth.to_s
32
+ return true
33
+ else
34
+ return false
35
+ end
36
+ end
37
+
38
+ # Null out list of excluded methods
39
+ def clear
40
+ @excluded = Set.new
41
+ end
42
+
43
+ # Returns +true+ if +meth+ is a member of the trace filter set.
44
+ def member?(meth)
45
+ if valid_meth?(meth)
46
+ @excluded.member?(meth.to_s)
47
+ else
48
+ false
49
+ end
50
+ end
51
+
52
+ # Remove +meth+ from the list of functions to include.
53
+ def remove(meth)
54
+ return nil unless valid_meth?(meth)
55
+ @excluded -= [meth.to_s]
56
+ end
57
+
58
+ # A shim to convert between older-style trace hook call to newer
59
+ # style trace hook using RubyVM::ThreadFrame. Methods stored in
60
+ # @+excluded+ are ignored.
61
+ def trace_hook(event, file, line, id, binding, klass)
62
+ tf = RubyVM::ThreadFrame::current.prev
63
+
64
+ # FIXME: Clean this mess up. And while your at it, understand
65
+ # what's going on better.
66
+ tf_check = tf
67
+
68
+ while %w(BLOCK IFUNC CFUNC).member?(tf_check.type) do
69
+ tf_check = tf_check.prev
70
+ end
71
+ return unless tf_check
72
+
73
+ begin
74
+ if tf_check.method && !tf_check.method.empty?
75
+ meth_name = tf_check.method.gsub(/^.* in /, '')
76
+ meth = eval("self.method(:#{meth_name})", tf_check.binding)
77
+ if @excluded.member?(meth.to_s)
78
+ # Turn off tracing for any calls from this frame. Note
79
+ # that Ruby turns of tracing in the thread of a hook while
80
+ # it is running, but I don't think this is as good as
81
+ # turning it off in the frame and frames called from that.
82
+ # Example: if a trace hook yields to or calls a block
83
+ # outside not derived from the frame, then tracing should
84
+ # start again. But either way, since RubyVM::ThreadFrame
85
+ # allows control over tracing *any* decision is not
86
+ # irrevocable, just possibly unhelpful.
87
+ tf_check.trace_off = true
88
+ return
89
+ end
90
+ end
91
+ rescue NameError
92
+ rescue SyntaxError
93
+ rescue ArgumentError
94
+ end
95
+ while %w(IFUNC).member?(tf.type) do
96
+ tf = tf.prev
97
+ end
98
+
99
+ # There is what looks like a a bug in Ruby where self.class for C
100
+ # functions are not set correctly. Until this is fixed in what I
101
+ # consider a more proper way, we'll hack around this by passing
102
+ # the binding as the optional arg parameter.
103
+ arg =
104
+ if 'CFUNC' == tf.type && NilClass != klass
105
+ klass
106
+ elsif 'raise' == event
107
+ # As a horrible hack to be able to get the raise message on a
108
+ # 'raise' event before the event occurs, I changed RubyVM to store
109
+ # the message in the class field.
110
+ klass
111
+ else
112
+ nil
113
+ end
114
+
115
+ retval = @hook_proc.call(event, tf, arg)
116
+ if retval.respond_to?(:ancestors) && retval.ancestors.include?(Exception)
117
+ raise retval
118
+ end
119
+ end
120
+
121
+ # Replacement for Kernel.add_trace_func. hook_proc should be a Proc that
122
+ # takes two arguments, a string event, and threadframe object.
123
+ def add_trace_func(hook_proc, event_mask=nil)
124
+ if hook_proc.nil?
125
+ Kernel.set_trace_func(nil)
126
+ return
127
+ end
128
+ raise TypeError,
129
+ "trace_func needs to be Proc or nil (is #{hook_proc.class})" unless
130
+ hook_proc.is_a?(Proc)
131
+ raise TypeError, "arity of hook_proc should be 2 or -3 (is #{hook_proc.arity})" unless
132
+ 2 == hook_proc.arity || -3 == hook_proc.arity
133
+ @hook_proc = hook_proc
134
+ if event_mask
135
+ Trace::add_trace_func(method(:trace_hook).to_proc, event_mask)
136
+ else
137
+ Kernel.add_trace_func(method(:trace_hook).to_proc)
138
+ end
139
+ end
140
+
141
+ # FIXME: remove just this trace function
142
+ def remove_trace_func
143
+ Kernel.clear_trace_func
144
+ end
145
+
146
+
147
+ # Replacement for Kernel.set_trace_func. proc should be a Proc that
148
+ # takes two arguments, a string event, and threadframe object.
149
+ def set_trace_func(hook_proc, event_mask=nil)
150
+ if hook_proc.nil?
151
+ Kernel.set_trace_func(nil)
152
+ return
153
+ end
154
+ raise TypeError,
155
+ "trace_func needs to be Proc or nil (is #{hook_proc.class})" unless
156
+ hook_proc.is_a?(Proc)
157
+ raise TypeError, "arity of hook_proc should be 2 or -3 (is #{hook_proc.arity})" unless
158
+ 2 == hook_proc.arity || -3 == hook_proc.arity
159
+ @hook_proc = hook_proc
160
+ if event_mask
161
+ Trace::set_trace_func(method(:trace_hook).to_proc, event_mask)
162
+ else
163
+ Kernel.set_trace_func(method(:trace_hook).to_proc)
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ if __FILE__ == $0
170
+ include Trace
171
+
172
+ def square(x) # :nodoc:
173
+ y = x * x
174
+ puts "the square of #{x} is #{y}\n"
175
+ y
176
+ end
177
+
178
+ def my_hook(event, tf, arg=nil) # :nodoc:
179
+ puts "#{event} #{tf.source_container[1]} #{tf.source_location[0]}"
180
+ @events << event
181
+ @line_nos << ((tf.source_location) ? tf.source_location[0] : '?')
182
+ end
183
+
184
+ def trace_test(dont_trace_me) # :nodoc:
185
+ @start_line = __LINE__
186
+ @trace_filter << self.method(:trace_test) if dont_trace_me
187
+ p @trace_filter.member?(self.method(:trace_test))
188
+
189
+ # Start tracing.
190
+ event_mask = DEFAULT_EVENT_MASK # & ~(C_RETURN_EVENT_MASK | RETURN_EVENT_MASK)
191
+ @trace_filter.set_trace_func(method(:my_hook).to_proc, event_mask)
192
+ square(10)
193
+ x = 100
194
+ square(x)
195
+ @end_line = __LINE__
196
+ end
197
+
198
+ def setup # :nodoc:
199
+ @events = []
200
+ @line_nos = []
201
+ @trace_filter.clear
202
+ end
203
+
204
+ def print_trace # :nodoc:
205
+ @line_nos.each_with_index do |line_no, i|
206
+ print "%2d %s %s\n" % [i, @events[i], line_no]
207
+ end
208
+ end
209
+
210
+ @trace_filter = Trace::Filter.new
211
+ markers = '*' * 10
212
+
213
+ [[false, 'out'], [true, '']].each do |arg, suffix|
214
+ setup
215
+ puts "%s with%s ignore %s" % [markers, suffix, markers]
216
+ trace_test(arg)
217
+ @trace_filter.set_trace_func(nil)
218
+ print_trace
219
+ end
220
+
221
+ @line_nos.each_with_index do
222
+ |line_no, i|
223
+ if (@start_line..@end_line).member?(line_no)
224
+ puts "We should not have found a line number in #{@start_line}..#{@end_line}."
225
+ puts "Got line number #{line_no} at index #{i}, event #{@events[i]}"
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require 'set'
4
+ require_relative '../../lib/trace_mod'
5
+
6
+ class TestEvent2Bitmask < Test::Unit::TestCase
7
+ include Trace
8
+
9
+ def test_basic
10
+ test_pair = events2bitmask([LINE_EVENT_MASK, CLASS_EVENT_MASK])
11
+ test_mask, unhandled = test_pair
12
+ assert_equal(test_pair,
13
+ events2bitmask(Set.new([LINE_EVENT_MASK, CLASS_EVENT_MASK])))
14
+ assert_equal([:class, :line], bitmask2events(test_mask))
15
+
16
+ EVENT2MASK.each do |mask_name, bit_value|
17
+ assert_equal([mask_name], bitmask2events(bit_value))
18
+ end
19
+
20
+ assert_equal(test_pair, events2bitmask(['LINE', 'Class']))
21
+ assert_equal(test_pair, events2bitmask(Set.new(['LINE', 'Class'])))
22
+ [[:foo, 'bar'], ['C call', ['bar']]].each do |bad|
23
+ assert_equal([0, bad], events2bitmask(bad))
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require_relative '../../ext/trace'
4
+
5
+ # Testing RubyVM::TraceHook
6
+ class TestTraceHook < Test::Unit::TestCase
7
+
8
+ def setup
9
+ set_trace_func(nil)
10
+ end
11
+
12
+ def test_trace_hooks
13
+ tt = RubyVM::TraceHook::trace_hooks
14
+ assert_equal([], tt)
15
+
16
+ set_trace_func(Proc.new { 1 })
17
+ tt = RubyVM::TraceHook::trace_hooks
18
+
19
+ assert_equal(1, tt.size)
20
+ assert tt[0].event_mask.is_a?(Fixnum)
21
+
22
+ set_trace_func(Proc.new { }, 5)
23
+ tt = RubyVM::TraceHook::trace_hooks
24
+
25
+ assert_equal(1, tt.size)
26
+
27
+ set_trace_func(nil)
28
+ tt = RubyVM::TraceHook::trace_hooks
29
+ assert_equal([], tt)
30
+
31
+ end
32
+
33
+ # Test getting and setting event mask of a trace hook
34
+ def test_event
35
+ set_trace_func(Proc.new { }, 5)
36
+ tt = RubyVM::TraceHook::trace_hooks
37
+ assert_equal(5, tt[0].event_mask)
38
+
39
+ tt[0].event_mask = 6;
40
+ assert_equal(6, tt[0].event_mask)
41
+
42
+ set_trace_func(nil)
43
+ assert_raises RubyVM::TraceHookError do
44
+ tt[0].event_mask
45
+ end
46
+
47
+ assert_raises RubyVM::TraceHookError do
48
+ tt[0].event_mask = 10
49
+ end
50
+
51
+ end
52
+
53
+ # Test getting and setting proc of a trace hook
54
+ def test_proc
55
+ p = Proc.new { 1 }
56
+ set_trace_func(p)
57
+ tt = RubyVM::TraceHook::trace_hooks
58
+ assert_equal(p, tt[0].proc)
59
+ assert_equal 1, tt[0].proc.call
60
+
61
+ p2 = Proc.new { 2 }
62
+ assert_raises TypeError do
63
+ tt[0].proc = 5
64
+ end
65
+ tt[0].proc = p2
66
+
67
+ assert_equal(p2, tt[0].proc)
68
+ assert_equal 2, tt[0].proc.call
69
+
70
+ set_trace_func(nil)
71
+ assert_raises RubyVM::TraceHookError do
72
+ tt[0].proc
73
+ end
74
+
75
+ assert_raises TypeError do
76
+ tt[0].proc = 6
77
+ end
78
+
79
+ # Test valid?
80
+ def test_valid
81
+ tt = RubyVM::TraceHook::trace_hooks
82
+ assert_equal(true, tt[0].valid?)
83
+ set_trace_func(Proc.new {} )
84
+ tt = RubyVM::TraceHook::trace_hooks
85
+ assert_equal(true, tt[0].valid?)
86
+ set_trace_func(Proc.new { 1 } )
87
+ assert_equal(false, tt[0].valid?)
88
+ tt = RubyVM::TraceHook::trace_hooks
89
+ assert_equal(true, tt[0].valid?)
90
+ set_trace_func(nil)
91
+ GC.start
92
+ assert_equal(false, tt[0].valid?)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require_relative '../../lib/trace_mod'
4
+
5
+ # Testing Trace Module
6
+ class TestTrace < Test::Unit::TestCase
7
+
8
+ def test_set_trace_func
9
+ @events = []
10
+ def foo ; end
11
+
12
+ event_set = [:call, :return]
13
+ Trace::set_trace_func(Proc.new {|e, tf| @events << e}, event_set)
14
+ foo
15
+ call_return_mask = Trace::event_masks[0]
16
+ assert_equal(Fixnum, call_return_mask.class)
17
+
18
+ Trace::event_masks = [:call]
19
+ foo
20
+ call_mask = Trace::event_masks[0]
21
+ Trace::set_trace_func(nil)
22
+
23
+ @events.each do |e|
24
+ assert_equal(true, event_set.member?(e.to_sym),
25
+ "#{e} should be in #{event_set}")
26
+ end
27
+
28
+ assert_equal(true,
29
+ ((call_return_mask > call_mask) &&
30
+ (0 != (call_return_mask & call_mask))))
31
+
32
+ @events = []
33
+ Trace::set_trace_func(nil)
34
+ @events.each do |e|
35
+ assert_equal(true, event_set.member?(e.to_sym),
36
+ "#{e} should be in #{event_set}")
37
+ end
38
+
39
+ assert_equal(nil, Trace::event_masks[0])
40
+ end
41
+
42
+ end
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+ require 'test/unit'
3
+ require_relative '../../lib/tracefilter'
4
+
5
+ # Testing Trace Filter class
6
+ class TestTraceFilter < Test::Unit::TestCase
7
+
8
+ def setup
9
+ $args = []
10
+ $events = []
11
+ $line_nos = []
12
+ @trace_filter = Trace::Filter.new unless defined?(@trace_filter)
13
+ @trace_filter.clear
14
+ end
15
+
16
+ def teardown
17
+ $args = []
18
+ $events = []
19
+ $line_nos = []
20
+ end
21
+
22
+ # Just something to trace
23
+ def square(x)
24
+ x * x
25
+ end
26
+
27
+ # Save stuff from the trace for inspection later.
28
+ def my_hook(event, tf, arg=nil)
29
+ $args << arg if arg
30
+ $events << event
31
+ $line_nos << tf.source_location[0] if tf.source_location
32
+ end
33
+
34
+ def trace_test(dont_trace_me)
35
+ $start_line = __LINE__
36
+ me = self.method(:trace_test)
37
+ assert_equal(false, @trace_filter.member?(me))
38
+ @trace_filter << self.method(:trace_test) if dont_trace_me
39
+ assert_equal(dont_trace_me, @trace_filter.member?(me))
40
+
41
+ # Start tracing.
42
+ @trace_filter.set_trace_func(method(:my_hook).to_proc)
43
+ square(10)
44
+ x = 100
45
+ square(x)
46
+ $end_line = __LINE__
47
+ end
48
+
49
+ def print_trace
50
+ $line_nos.each_with_index do |line_no, i|
51
+ print "%2d %s %s\n" % [i, $events[i], line_no]
52
+ end
53
+ p $args
54
+ end
55
+
56
+ def test_basic
57
+ trace_test(true)
58
+ @trace_filter.set_trace_func(nil)
59
+ print_trace if $DEBUG
60
+
61
+ assert_equal(false, $events.empty?,
62
+ 'We should have gotting some trace output')
63
+ assert_equal([Kernel], $args,
64
+ 'C call/returns set $args')
65
+
66
+ $line_nos.each_with_index do
67
+ |line_no, i|
68
+ assert_equal(false, ($start_line..$end_line).member?(line_no),
69
+ "We should not find a line number in " +
70
+ "#{$start_line}..#{$end_line}; " +
71
+ "got line number #{line_no} at index #{i}, " +
72
+ "event #{$events[i]}")
73
+ end
74
+ untraced_line_nos = $line_nos
75
+ untrace_events = $events
76
+
77
+ setup
78
+ trace_test(false)
79
+ @trace_filter.set_trace_func(nil)
80
+ print_trace if $DEBUG
81
+
82
+ assert_equal(true, $line_nos.size > untraced_line_nos.size,
83
+ 'We should have traced more stuff than untraced output')
84
+
85
+ found_one = false
86
+ $line_nos.each_with_index do
87
+ |line_no, i|
88
+ if ($start_line..$end_line).member?(line_no)
89
+ found_one = true
90
+ break
91
+ end
92
+ end
93
+ assert_equal(true, found_one,
94
+ 'We should have found a line number for at least one event ' +
95
+ 'in traced output.')
96
+
97
+ assert_raises TypeError do
98
+ @trace_filter.set_trace_func(method(:trace_test))
99
+ end
100
+ end
101
+
102
+ # Save stuff from the trace for inspection later.
103
+ def raise_hook(event, tf, arg=nil)
104
+ return unless 'raise' == event
105
+ $args << arg.to_s
106
+ end
107
+
108
+ def test_raise_sets_errmsg
109
+ @trace_filter.set_trace_func(method(:raise_hook).to_proc)
110
+ 1/0 rescue nil
111
+ @trace_filter.set_trace_func(nil)
112
+ assert_equal(['divided by 0'], $args)
113
+ end
114
+
115
+ end