appmap 0.23.0 → 0.25.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +17 -8
- data/.travis.yml +6 -0
- data/CHANGELOG.md +19 -0
- data/README.md +29 -12
- data/Rakefile +3 -3
- data/appmap.gemspec +3 -1
- data/exe/appmap +6 -18
- data/lib/appmap.rb +47 -6
- data/lib/appmap/algorithm/prune_class_map.rb +2 -0
- data/lib/appmap/algorithm/stats.rb +4 -2
- data/lib/appmap/class_map.rb +143 -0
- data/lib/appmap/command/record.rb +8 -6
- data/lib/appmap/command/stats.rb +2 -0
- data/lib/appmap/command/upload.rb +4 -2
- data/lib/appmap/event.rb +168 -0
- data/lib/appmap/hook.rb +151 -0
- data/lib/appmap/middleware/remote_recording.rb +14 -20
- data/lib/appmap/rails/action_handler.rb +10 -6
- data/lib/appmap/rails/sql_handler.rb +10 -8
- data/lib/appmap/railtie.rb +31 -18
- data/lib/appmap/rspec.rb +238 -261
- data/lib/appmap/trace.rb +88 -0
- data/lib/appmap/version.rb +1 -1
- data/package-lock.json +90 -92
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +7 -3
- data/spec/config_spec.rb +25 -0
- data/spec/fixtures/hook/attr_accessor.rb +5 -0
- data/spec/fixtures/hook/class_method.rb +17 -0
- data/spec/fixtures/hook/constructor.rb +7 -0
- data/spec/fixtures/hook/exception_method.rb +11 -0
- data/spec/fixtures/hook/instance_method.rb +23 -0
- data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +3 -3
- data/spec/fixtures/rails4_users_app/config/database.yml +2 -1
- data/spec/fixtures/rails4_users_app/docker-compose.yml +2 -0
- data/spec/fixtures/rails_users_app/.ruby-version +1 -1
- data/spec/fixtures/rails_users_app/app/controllers/api/users_controller.rb +2 -2
- data/spec/fixtures/rails_users_app/config/database.yml +2 -1
- data/spec/fixtures/rails_users_app/create_app +1 -0
- data/spec/fixtures/rails_users_app/docker-compose.yml +4 -0
- data/spec/fixtures/rails_users_app/spec/models/user_spec.rb +1 -1
- data/spec/hook_spec.rb +357 -0
- data/spec/rails_spec_helper.rb +25 -16
- data/spec/railtie_spec.rb +1 -1
- data/spec/record_sql_rails_pg_spec.rb +1 -2
- data/spec/remote_recording_spec.rb +117 -0
- data/spec/spec_helper.rb +1 -0
- data/test/cli_test.rb +7 -36
- data/test/fixtures/cli_record_test/appmap.yml +2 -1
- data/test/fixtures/cli_record_test/lib/cli_record_test/main.rb +4 -2
- data/test/test_helper.rb +0 -42
- metadata +46 -62
- data/exe/_appmap-record-self +0 -49
- data/lib/appmap/command/inspect.rb +0 -14
- data/lib/appmap/config.rb +0 -65
- data/lib/appmap/config/directory.rb +0 -65
- data/lib/appmap/config/file.rb +0 -13
- data/lib/appmap/config/named_function.rb +0 -21
- data/lib/appmap/config/package_dir.rb +0 -52
- data/lib/appmap/config/path.rb +0 -25
- data/lib/appmap/feature.rb +0 -262
- data/lib/appmap/inspect.rb +0 -91
- data/lib/appmap/inspect/inspector.rb +0 -99
- data/lib/appmap/inspect/parse_node.rb +0 -170
- data/lib/appmap/inspect/parser.rb +0 -15
- data/lib/appmap/parser.rb +0 -60
- data/lib/appmap/rspec/parse_node.rb +0 -41
- data/lib/appmap/rspec/parser.rb +0 -15
- data/lib/appmap/trace/event_handler/rack_handler_webrick.rb +0 -65
- data/lib/appmap/trace/tracer.rb +0 -356
- data/spec/fixtures/rails_users_app/bin/_appmap-record-self +0 -29
- data/spec/rack_handler_webrick_spec.rb +0 -59
- data/test/config_test.rb +0 -149
- data/test/explict_inspect_test.rb +0 -29
- data/test/fixtures/active_record_like/active_record.rb +0 -2
- data/test/fixtures/active_record_like/active_record/aggregations.rb +0 -4
- data/test/fixtures/active_record_like/active_record/association.rb +0 -4
- data/test/fixtures/active_record_like/active_record/associations/join_dependency.rb +0 -6
- data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_base.rb +0 -8
- data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_part.rb +0 -8
- data/test/fixtures/active_record_like/active_record/caps/caps.rb +0 -4
- data/test/fixtures/ignore_non_ruby_file/class.rb +0 -3
- data/test/fixtures/ignore_non_ruby_file/non-ruby.txt +0 -1
- data/test/fixtures/includes_excludes/lib/a/a_1.rb +0 -6
- data/test/fixtures/includes_excludes/lib/a/a_2.rb +0 -6
- data/test/fixtures/includes_excludes/lib/a/x/x_1.rb +0 -8
- data/test/fixtures/includes_excludes/lib/b/b_1.rb +0 -6
- data/test/fixtures/includes_excludes/lib/root_1.rb +0 -4
- data/test/fixtures/inspect_multiple_subdirs/module_a.rb +0 -2
- data/test/fixtures/inspect_multiple_subdirs/module_a/class_a.rb +0 -5
- data/test/fixtures/inspect_multiple_subdirs/module_b.rb +0 -2
- data/test/fixtures/inspect_multiple_subdirs/module_b/class_b.rb +0 -5
- data/test/fixtures/inspect_multiple_subdirs/module_b/class_c.rb +0 -5
- data/test/fixtures/inspect_package/module_a/module_b/class_in_module.rb +0 -6
- data/test/fixtures/parse_file/defs_static_function.rb +0 -96
- data/test/fixtures/parse_file/function_within_class.rb +0 -36
- data/test/fixtures/parse_file/include_public_methods.rb +0 -127
- data/test/fixtures/parse_file/instance_function.rb +0 -17
- data/test/fixtures/parse_file/modules.rb +0 -71
- data/test/fixtures/parse_file/sclass_static_function.rb +0 -88
- data/test/fixtures/parse_file/toplevel_class.rb +0 -13
- data/test/fixtures/parse_file/toplevel_function.rb +0 -14
- data/test/fixtures/trace_test/trace_program_1.rb +0 -44
- data/test/implicit_inspect_test.rb +0 -33
- data/test/include_exclude_test.rb +0 -48
- data/test/prerecorded_trace_test.rb +0 -76
- data/test/trace_test.rb +0 -92
data/lib/appmap/rspec/parser.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'appmap/parser'
|
|
2
|
-
require 'appmap/rspec/parse_node'
|
|
3
|
-
|
|
4
|
-
module AppMap
|
|
5
|
-
module RSpec
|
|
6
|
-
# Parser processes a Ruby into a list of parse nodes and a list of comments.
|
|
7
|
-
class Parser < ::AppMap::Parser
|
|
8
|
-
protected
|
|
9
|
-
|
|
10
|
-
def build_parse_node(node, file_path, ancestors)
|
|
11
|
-
ParseNode.from_node(node, file_path, ancestors)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
module AppMap
|
|
2
|
-
module Trace
|
|
3
|
-
module EventHandler
|
|
4
|
-
# Use the `req` and `res` parameters on Rack::Handler::WEBrick to populate the
|
|
5
|
-
# `http_server_request` and `http_server_response` info on the trace event.
|
|
6
|
-
#
|
|
7
|
-
# See https://github.com/rack/rack/blob/b72bfc9435c118c54019efae1fedd119521b76df/lib/rack/handler/webrick.rb#L26
|
|
8
|
-
module RackHandlerWebrick
|
|
9
|
-
class Call < MethodEvent
|
|
10
|
-
attr_accessor :http_server_request
|
|
11
|
-
|
|
12
|
-
class << self
|
|
13
|
-
def build_from_tracepoint(mc = Call.new, tp, path)
|
|
14
|
-
mc.tap do |_|
|
|
15
|
-
req = value_in_binding(tp, :req)
|
|
16
|
-
|
|
17
|
-
# Don't try and grab 'parameters', because:
|
|
18
|
-
# a) They aren't needed.
|
|
19
|
-
# b) We want to avoid triggering side effects like reading the request body.
|
|
20
|
-
|
|
21
|
-
mc.http_server_request = {
|
|
22
|
-
request_method: req.request_method,
|
|
23
|
-
path_info: req.path_info,
|
|
24
|
-
protocol: "HTTP/#{req.http_version}"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
MethodEvent.build_from_tracepoint(mc, tp, path)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def to_h
|
|
33
|
-
super.tap do |h|
|
|
34
|
-
h[:http_server_request] = http_server_request
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
class Return < MethodReturnIgnoreValue
|
|
40
|
-
attr_accessor :http_server_response
|
|
41
|
-
|
|
42
|
-
class << self
|
|
43
|
-
def build_from_tracepoint(mr = Return.new, tp, path, parent_id, elapsed)
|
|
44
|
-
mr.tap do |_|
|
|
45
|
-
res = value_in_binding(tp, :res)
|
|
46
|
-
|
|
47
|
-
mr.http_server_response = {
|
|
48
|
-
status: res.status
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
MethodReturnIgnoreValue.build_from_tracepoint(mr, tp, path, parent_id, elapsed)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def to_h
|
|
57
|
-
super.tap do |h|
|
|
58
|
-
h[:http_server_response] = http_server_response
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
data/lib/appmap/trace/tracer.rb
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
module AppMap
|
|
2
|
-
module Trace
|
|
3
|
-
MethodEventStruct =
|
|
4
|
-
Struct.new(:id, :event, :defined_class, :method_id, :path, :lineno, :static, :thread_id)
|
|
5
|
-
|
|
6
|
-
class Tracers
|
|
7
|
-
def initialize
|
|
8
|
-
@tracers = []
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def empty?
|
|
12
|
-
@tracers.empty?
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def trace(functions, enable: true)
|
|
16
|
-
AppMap::Trace::Tracer.new(functions).tap do |tracer|
|
|
17
|
-
@tracers << tracer
|
|
18
|
-
tracer.enable if enable
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def record_event(event)
|
|
23
|
-
@tracers.each do |tracer|
|
|
24
|
-
tracer.record_event(event)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def delete(tracer)
|
|
29
|
-
return unless @tracers.member?(tracer)
|
|
30
|
-
|
|
31
|
-
@tracers.delete(tracer)
|
|
32
|
-
tracer.disable
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
class << self
|
|
37
|
-
def tracers
|
|
38
|
-
@tracers ||= Tracers.new
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# @appmap
|
|
43
|
-
class MethodEvent < MethodEventStruct
|
|
44
|
-
LIMIT = 100
|
|
45
|
-
|
|
46
|
-
COUNTER_LOCK = Mutex.new # :nodoc:
|
|
47
|
-
@@id_counter = 0
|
|
48
|
-
|
|
49
|
-
class << self
|
|
50
|
-
# Build a new instance from a TracePoint.
|
|
51
|
-
def build_from_tracepoint(me, tp, path)
|
|
52
|
-
me.id = next_id
|
|
53
|
-
me.event = tp.event
|
|
54
|
-
|
|
55
|
-
if tp.defined_class.singleton_class?
|
|
56
|
-
me.defined_class = (tp.self.is_a?(Class) || tp.self.is_a?(Module)) ? tp.self.name : tp.self.class.name
|
|
57
|
-
else
|
|
58
|
-
me.defined_class = tp.defined_class.name
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
me.method_id = tp.method_id
|
|
62
|
-
me.path = path
|
|
63
|
-
me.lineno = tp.lineno
|
|
64
|
-
me.static = tp.defined_class.name.nil?
|
|
65
|
-
me.thread_id = Thread.current.object_id
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Gets the next serial id.
|
|
69
|
-
#
|
|
70
|
-
# This method is thread-safe.
|
|
71
|
-
# @appmap
|
|
72
|
-
def next_id
|
|
73
|
-
COUNTER_LOCK.synchronize do
|
|
74
|
-
@@id_counter += 1
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Gets a value, by key, from the trace point binding.
|
|
79
|
-
# If the method raises an error, it can be handled by the optional block.
|
|
80
|
-
def value_in_binding(tp, key, &block)
|
|
81
|
-
tp.binding.eval(key.to_s)
|
|
82
|
-
rescue NameError, ArgumentError
|
|
83
|
-
yield if block_given?
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Gets a display string for a value. This is not meant to be a machine deserializable value.
|
|
87
|
-
def display_string(value)
|
|
88
|
-
return nil unless value
|
|
89
|
-
|
|
90
|
-
last_resort_string = lambda do
|
|
91
|
-
warn "AppMap encountered an error inspecting a #{value.class.name}: #{$!.message}"
|
|
92
|
-
'*Error inspecting variable*'
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
value_string = \
|
|
96
|
-
begin
|
|
97
|
-
value.to_s
|
|
98
|
-
rescue NoMethodError
|
|
99
|
-
begin
|
|
100
|
-
value.inspect
|
|
101
|
-
rescue StandardError
|
|
102
|
-
last_resort_string.call
|
|
103
|
-
end
|
|
104
|
-
rescue StandardError
|
|
105
|
-
last_resort_string.call
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
value_string[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
alias static? static
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# @appmap
|
|
116
|
-
class MethodCall < MethodEvent
|
|
117
|
-
attr_accessor :parameters, :receiver
|
|
118
|
-
|
|
119
|
-
class << self
|
|
120
|
-
# @appmap
|
|
121
|
-
def build_from_tracepoint(mc = MethodCall.new, tp, path)
|
|
122
|
-
mc.tap do |_|
|
|
123
|
-
mc.parameters = collect_parameters(tp)
|
|
124
|
-
mc.receiver = collect_self(tp)
|
|
125
|
-
MethodEvent.build_from_tracepoint(mc, tp, path)
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def collect_self(tp)
|
|
130
|
-
{
|
|
131
|
-
class: tp.self.class.name,
|
|
132
|
-
object_id: tp.self.__id__,
|
|
133
|
-
value: display_string(tp.self)
|
|
134
|
-
}
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def collect_parameters(tp)
|
|
138
|
-
-> { tp.self.method(tp.method_id).parameters rescue [] }.call.collect do |pinfo|
|
|
139
|
-
kind, key = pinfo
|
|
140
|
-
value = value_in_binding(tp, key)
|
|
141
|
-
{
|
|
142
|
-
name: key,
|
|
143
|
-
class: value.class.name,
|
|
144
|
-
object_id: value.__id__,
|
|
145
|
-
value: display_string(value),
|
|
146
|
-
kind: kind # :req, :rest, :key, :keyrest, :block
|
|
147
|
-
}
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def to_h
|
|
153
|
-
super.tap do |h|
|
|
154
|
-
h[:parameters] = parameters
|
|
155
|
-
h[:receiver] = receiver
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
class MethodReturnIgnoreValue < MethodEvent
|
|
161
|
-
attr_accessor :parent_id, :elapsed
|
|
162
|
-
|
|
163
|
-
class << self
|
|
164
|
-
def build_from_tracepoint(mr = MethodReturnIgnoreValue.new, tp, path, parent_id, elapsed)
|
|
165
|
-
mr.tap do |_|
|
|
166
|
-
mr.parent_id = parent_id
|
|
167
|
-
mr.elapsed = elapsed
|
|
168
|
-
MethodEvent.build_from_tracepoint(mr, tp, path)
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def to_h
|
|
174
|
-
super.tap do |h|
|
|
175
|
-
h[:parent_id] = parent_id
|
|
176
|
-
h[:elapsed] = elapsed
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
class MethodReturn < MethodReturnIgnoreValue
|
|
182
|
-
attr_accessor :return_value
|
|
183
|
-
|
|
184
|
-
class << self
|
|
185
|
-
def build_from_tracepoint(mr = MethodReturn.new, tp, path, parent_id, elapsed)
|
|
186
|
-
mr.tap do |_|
|
|
187
|
-
mr.return_value = {
|
|
188
|
-
class: tp.return_value.class.name,
|
|
189
|
-
value: display_string(tp.return_value),
|
|
190
|
-
object_id: tp.return_value.__id__
|
|
191
|
-
}
|
|
192
|
-
MethodReturnIgnoreValue.build_from_tracepoint(mr, tp, path, parent_id, elapsed)
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def to_h
|
|
198
|
-
super.tap do |h|
|
|
199
|
-
h[:return_value] = return_value
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# Processes a series of calls into recorded events.
|
|
205
|
-
# Each call to the handle should provide a TracePoint (or duck-typed object) as the argument.
|
|
206
|
-
# On each call, a MethodEvent is constructed according to the nature of the TracePoint, and then
|
|
207
|
-
# stored using the record_event method.
|
|
208
|
-
# @appmap
|
|
209
|
-
class TracePointHandler
|
|
210
|
-
attr_accessor :call_constructor, :return_constructor
|
|
211
|
-
|
|
212
|
-
DEFAULT_HANDLER_CLASSES = {
|
|
213
|
-
call: MethodCall,
|
|
214
|
-
return: MethodReturn
|
|
215
|
-
}.freeze
|
|
216
|
-
|
|
217
|
-
# @appmap
|
|
218
|
-
def initialize(tracer)
|
|
219
|
-
@pwd = Dir.pwd
|
|
220
|
-
@tracer = tracer
|
|
221
|
-
@call_stack = Hash.new { |h, k| h[k] = [] }
|
|
222
|
-
@handler_classes = {}
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# @appmap
|
|
226
|
-
def handle(tp)
|
|
227
|
-
# Absoute paths which are within the current working directory are normalized
|
|
228
|
-
# to be relative paths.
|
|
229
|
-
path = tp.path
|
|
230
|
-
if path.index(@pwd) == 0
|
|
231
|
-
path = path[@pwd.length+1..-1]
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
method_event = \
|
|
235
|
-
if tp.event == :call && (function = @tracer.lookup_function(path, tp.lineno))
|
|
236
|
-
call_constructor = handler_class(function, tp.event)
|
|
237
|
-
call_constructor.build_from_tracepoint(tp, path).tap do |c|
|
|
238
|
-
@call_stack[Thread.current.object_id] << [ tp.defined_class, tp.method_id, c.id, Time.now, function ]
|
|
239
|
-
end
|
|
240
|
-
elsif (c = @call_stack[Thread.current.object_id].last) &&
|
|
241
|
-
c[0] == tp.defined_class &&
|
|
242
|
-
c[1] == tp.method_id
|
|
243
|
-
function = c[4]
|
|
244
|
-
@call_stack[Thread.current.object_id].pop
|
|
245
|
-
return_constructor = handler_class(function, tp.event)
|
|
246
|
-
return_constructor.build_from_tracepoint(tp, path, c[2], Time.now - c[3])
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
@tracer.record_event method_event if method_event
|
|
250
|
-
|
|
251
|
-
method_event
|
|
252
|
-
rescue
|
|
253
|
-
puts $!.message
|
|
254
|
-
puts $!.backtrace.join("\n")
|
|
255
|
-
# XXX If this exception doesn't get reraised, internal errors
|
|
256
|
-
# (e.g. a missing method on TracePoint) get silently
|
|
257
|
-
# ignored. This allows tests to pass that should fail, which
|
|
258
|
-
# is bad, but is it desirable otherwise?
|
|
259
|
-
raise
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
protected
|
|
263
|
-
|
|
264
|
-
# Figure out which handler class should be used for a trace event. It may be
|
|
265
|
-
# a custom handler, e.g. in case we are processing a special named function such as a
|
|
266
|
-
# web server entry point, or it may be the standard :call or :return handler.
|
|
267
|
-
def handler_class(function, event)
|
|
268
|
-
cache_key = [function.location, event]
|
|
269
|
-
cached_handler = @handler_classes[cache_key]
|
|
270
|
-
return cached_handler if cached_handler
|
|
271
|
-
|
|
272
|
-
return default_handler_class(event) unless function.handler_id
|
|
273
|
-
|
|
274
|
-
require "appmap/trace/event_handler/#{function.handler_id}"
|
|
275
|
-
|
|
276
|
-
AppMap::Trace::EventHandler
|
|
277
|
-
.const_get(function.handler_id.to_s.camelize)
|
|
278
|
-
.const_get(event.to_s.capitalize).tap do |handler|
|
|
279
|
-
@handler_classes[cache_key] = handler
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
def default_handler_class(event)
|
|
284
|
-
DEFAULT_HANDLER_CLASSES[event] or raise "No handler class for #{event.inspect}"
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
# @appmap
|
|
289
|
-
class Tracer
|
|
290
|
-
# Trace a specified set of functions.
|
|
291
|
-
#
|
|
292
|
-
# functions Array of AppMap::Feature::Function.
|
|
293
|
-
# @appmap
|
|
294
|
-
def initialize(functions)
|
|
295
|
-
@functions = functions
|
|
296
|
-
|
|
297
|
-
@functions_by_location = functions.each_with_object({}) do |m, memo|
|
|
298
|
-
path, lineno = m.location.split(':', 2)
|
|
299
|
-
memo[path] ||= {}
|
|
300
|
-
memo[path][lineno.to_i] = m
|
|
301
|
-
memo
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
@events_mutex = Mutex.new
|
|
305
|
-
@events = []
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def enable
|
|
309
|
-
handler = TracePointHandler.new(self)
|
|
310
|
-
@trace_point = TracePoint.trace(:call, :return, &handler.method(:handle))
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
# Private function. Use AppMap.tracers#delete.
|
|
314
|
-
def disable # :nodoc:
|
|
315
|
-
@trace_point.disable
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
# Whether the indicated file path and lineno is a breakpoint on which
|
|
319
|
-
# execution should interrupted.
|
|
320
|
-
# @appmap
|
|
321
|
-
def lookup_function(path, lineno)
|
|
322
|
-
(methods_by_path = @functions_by_location[path]) && methods_by_path[lineno]
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
# Record a program execution event.
|
|
326
|
-
#
|
|
327
|
-
# The event should be one of the MethodEvent subclasses.
|
|
328
|
-
#
|
|
329
|
-
# This method is thread-safe.
|
|
330
|
-
# @appmap
|
|
331
|
-
def record_event(event)
|
|
332
|
-
@events_mutex.synchronize do
|
|
333
|
-
@events << event
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
# Whether there is an event available for processing.
|
|
338
|
-
#
|
|
339
|
-
# This method is thread-safe.
|
|
340
|
-
def event?
|
|
341
|
-
@events_mutex.synchronize do
|
|
342
|
-
!@events.empty?
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
# Gets the next available event, if any.
|
|
347
|
-
#
|
|
348
|
-
# This method is thread-safe.
|
|
349
|
-
def next_event
|
|
350
|
-
@events_mutex.synchronize do
|
|
351
|
-
@events.shift
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
# This file was generated by Bundler.
|
|
6
|
-
#
|
|
7
|
-
# The application '_appmap-record-self' is installed as part of a gem, and
|
|
8
|
-
# this file is here to facilitate running it.
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
require "pathname"
|
|
12
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
-
Pathname.new(__FILE__).realpath)
|
|
14
|
-
|
|
15
|
-
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
-
|
|
17
|
-
if File.file?(bundle_binstub)
|
|
18
|
-
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
-
load(bundle_binstub)
|
|
20
|
-
else
|
|
21
|
-
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
-
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
require "rubygems"
|
|
27
|
-
require "bundler/setup"
|
|
28
|
-
|
|
29
|
-
load Gem.bin_path("appmap", "_appmap-record-self")
|