rb-trace 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +23 -0
- data/NEWS +2 -0
- data/Rakefile +121 -0
- data/ext/extconf.rb +8 -0
- data/ext/thread_pthread.h +24 -0
- data/ext/trace.c +160 -0
- data/ext/vm_core_mini.h +133 -0
- data/lib/eventbuffer.rb +172 -0
- data/lib/trace.rb +3 -0
- data/lib/trace_mod.rb +207 -0
- data/lib/tracefilter.rb +228 -0
- data/test/unit/test-event2bitmask.rb +26 -0
- data/test/unit/test-trace-hook.rb +95 -0
- data/test/unit/test-trace_mod.rb +42 -0
- data/test/unit/test-tracefilter.rb +115 -0
- metadata +95 -0
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
|
data/lib/tracefilter.rb
ADDED
@@ -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
|