rb-trace 0.2

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