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/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
|