/
tc_method_tracer.rb
237 lines (201 loc) · 7.42 KB
/
tc_method_tracer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
require File.join(File.dirname(__FILE__),'mock_agent')
require 'newrelic/agent/method_tracer'
require 'newrelic/agent/transaction_sampler'
require 'test/unit'
::RPM_TRACERS_ENABLED = true unless defined? ::RPM_TRACERS_ENABLED
module NewRelic
module Agent
# for testing, enable the stats engine to clear itself
class StatsEngine
def reset
scope_stack.clear
@stats_hash.clear
end
end
extend self
def module_method_to_be_traced (x, testcase)
testcase.assert x == "x"
testcase.assert testcase.stats_engine.peek_scope.name == "x"
end
class MethodTracerTests < Test::Unit::TestCase
attr_reader :stats_engine
def setup
@stats_engine = Agent.instance.stats_engine
@stats_engine.reset
end
def teardown
self.class.remove_method_tracer :method_to_be_traced, @metric_name if @metric_name
@metric_name = nil
end
def test_basic
metric = "hello"
t1 = Time.now
self.class.trace_method_execution metric do
sleep 0.1
assert metric == @stats_engine.peek_scope.name
end
elapsed = Time.now - t1
stats = @stats_engine.get_stats(metric)
check_time stats.total_call_time, elapsed
assert stats.call_count == 1
end
METRIC = "metric"
def test_add_method_tracer
@metric_name = METRIC
assert ::RPM_TRACERS_ENABLED
self.class.add_method_tracer :method_to_be_traced, METRIC
t1 = Time.now
method_to_be_traced 1,2,3,true,METRIC
elapsed = Time.now - t1
stats = @stats_engine.get_stats(METRIC)
check_time stats.total_call_time, elapsed
assert stats.call_count == 1
end
def test_nested_scope_tracer
Insider.add_method_tracer :catcher, "catcher", true
Insider.add_method_tracer :thrower, "thrower", true
sampler = TransactionSampler.new
@stats_engine.add_scope_stack_listener sampler
mock = Insider.new(@stats_engine)
mock.catcher(0)
mock.catcher(5)
stats = @stats_engine.get_stats("catcher")
assert_equal 2, stats.call_count
stats = @stats_engine.get_stats("thrower")
assert_equal 6, stats.call_count
sample = sampler.harvest_slowest_sample
assert_not_nil sample
end
def test_add_same_tracer_twice
@metric_name = METRIC
self.class.add_method_tracer :method_to_be_traced, METRIC
self.class.add_method_tracer :method_to_be_traced, METRIC
t1 = Time.now
method_to_be_traced 1,2,3,true,METRIC
elapsed = Time.now - t1
stats = @stats_engine.get_stats(METRIC)
check_time stats.total_call_time, elapsed
assert stats.call_count == 1
end
def test_add_tracer_with_dynamic_metric
metric_code = '#{args[0]}.#{args[1]}'
@metric_name = metric_code
expected_metric = "1.2"
self.class.add_method_tracer :method_to_be_traced, metric_code
t1 = Time.now
method_to_be_traced 1,2,3,true,expected_metric
elapsed = Time.now - t1
stats = @stats_engine.get_stats(expected_metric)
check_time stats.total_call_time, elapsed
assert stats.call_count == 1
end
def test_trace_method_with_block
self.class.add_method_tracer :method_with_block, METRIC
t1 = Time.now
method_with_block(1,2,3,true,METRIC) do |scope|
assert scope == METRIC
end
elapsed = Time.now - t1
stats = @stats_engine.get_stats(METRIC)
check_time stats.total_call_time, elapsed
assert stats.call_count == 1
end
def test_trace_module_method
NewRelic::Agent.add_method_tracer :module_method_to_be_traced, '#{args[0]}'
NewRelic::Agent.module_method_to_be_traced "x", self
NewRelic::Agent.remove_method_tracer :module_method_to_be_traced, '#{args[0]}'
end
def test_remove
self.class.add_method_tracer :method_to_be_traced, METRIC
self.class.remove_method_tracer :method_to_be_traced, METRIC
t1 = Time.now
method_to_be_traced 1,2,3,false,METRIC
elapsed = Time.now - t1
stats = @stats_engine.get_stats(METRIC)
assert stats.call_count == 0
end
def MethodTracerTests.static_method(x, testcase, is_traced)
testcase.assert x == "x"
testcase.assert((testcase.stats_engine.peek_scope.name == "x") == is_traced)
end
def trace_trace_static_method
self.add_method_tracer :static_method, '#{args[0]}'
self.class.static_method "x", self, true
self.remove_method_tracer :static_method, '#{args[0]}'
self.class.static_method "x", self, false
end
def test_execption
begin
metric = "hey there"
self.class.trace_method_execution metric do
assert @stats_engine.peek_scope.name == metric
throw Exception.new
end
assert false # should never get here
rescue Exception
# make sure the scope gets popped
assert @stats_engine.peek_scope == nil
end
stats = @stats_engine.get_stats metric
assert stats.call_count == 1
end
def test_add_multiple_tracers
self.class.add_method_tracer :method_to_be_traced, 'X', false
method_to_be_traced 1,2,3,true,nil
self.class.add_method_tracer :method_to_be_traced, 'Y'
method_to_be_traced 1,2,3,true,'Y'
self.class.remove_method_tracer :method_to_be_traced, 'Y'
method_to_be_traced 1,2,3,true,nil
self.class.remove_method_tracer :method_to_be_traced, 'X'
method_to_be_traced 1,2,3,false,'X'
end
def trace_no_push_scope
self.class.add_method_tracer :method_to_be_traced, 'X', false
method_to_be_traced 1,2,3,true,nil
self.class.remove_method_tracer :method_to_be_traced, 'X'
method_to_be_traced 1,2,3,false,'X'
end
def check_time (t1, t2)
assert((t2-t1).abs < 0.01)
end
# =======================================================
# test methods to be traced
def method_to_be_traced(x, y, z, is_traced, expected_metric)
sleep 0.1
assert x == 1
assert y == 2
assert z == 3
scope_name = @stats_engine.peek_scope ? @stats_engine.peek_scope.name : nil
assert((expected_metric == scope_name) == is_traced)
end
def method_with_block(x, y, z, is_traced, expected_metric, &block)
sleep 0.1
assert x == 1
assert y == 2
assert z == 3
block.call(@stats_engine.peek_scope.name)
scope_name = @stats_engine.peek_scope ? @stats_engine.peek_scope.name : nil
assert((expected_metric == scope_name) == is_traced)
end
end
end
end
class Insider
def initialize(stats_engine)
@stats_engine = stats_engine
end
def catcher(level=0)
thrower(level) if level>0
end
def thrower(level)
if level == 0
sampler = NewRelic::Agent::TransactionSampler.new
begin
@stats_engine.add_scope_stack_listener sampler
fail "This should not have worked."
rescue; end
else
thrower(level-1)
end
end
end