appmap 0.44.0 → 0.47.1
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/.releaserc.yml +11 -0
- data/.travis.yml +28 -12
- data/CHANGELOG.md +42 -0
- data/README.md +39 -11
- data/README_CI.md +29 -0
- data/Rakefile +4 -2
- data/appmap.gemspec +5 -1
- data/lib/appmap/class_map.rb +7 -15
- data/lib/appmap/config.rb +203 -95
- data/lib/appmap/event.rb +29 -28
- data/lib/appmap/handler/function.rb +19 -0
- data/lib/appmap/handler/net_http.rb +107 -0
- data/lib/appmap/handler/rails/request_handler.rb +124 -0
- data/lib/appmap/handler/rails/sql_handler.rb +152 -0
- data/lib/appmap/handler/rails/template.rb +155 -0
- data/lib/appmap/hook.rb +109 -71
- data/lib/appmap/hook/method.rb +6 -8
- data/lib/appmap/railtie.rb +5 -5
- data/lib/appmap/trace.rb +47 -6
- data/lib/appmap/util.rb +41 -2
- data/lib/appmap/version.rb +2 -2
- data/package-lock.json +3 -3
- data/release.sh +17 -0
- data/spec/abstract_controller_base_spec.rb +74 -11
- data/spec/class_map_spec.rb +3 -3
- data/spec/config_spec.rb +3 -1
- data/spec/hook_spec.rb +12 -66
- data/spec/record_net_http_spec.rb +160 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/util_spec.rb +18 -1
- metadata +16 -10
- data/lib/appmap/rails/request_handler.rb +0 -140
- data/lib/appmap/rails/sql_handler.rb +0 -150
- data/patch +0 -1447
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'appmap/event'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
module Handler
|
7
|
+
module Rails
|
8
|
+
class Template
|
9
|
+
LOG = (ENV['APPMAP_TEMPLATE_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
|
10
|
+
|
11
|
+
# All the code which is touched by the AppMap is recorded in the classMap.
|
12
|
+
# This duck-typed 'method' is used to represent a view template as a package,
|
13
|
+
# class, and method in the classMap.
|
14
|
+
# The class name is generated from the template path. The package name is
|
15
|
+
# 'app/views', and the method name is 'render'. The source location of the method
|
16
|
+
# is, of course, the path to the view template.
|
17
|
+
TemplateMethod = Struct.new(:path) do
|
18
|
+
private_instance_methods :path
|
19
|
+
attr_reader :class_name
|
20
|
+
|
21
|
+
def initialize(path)
|
22
|
+
super
|
23
|
+
|
24
|
+
@class_name = path.parameterize.underscore
|
25
|
+
end
|
26
|
+
|
27
|
+
def package
|
28
|
+
'app/views'
|
29
|
+
end
|
30
|
+
|
31
|
+
def name
|
32
|
+
'render'
|
33
|
+
end
|
34
|
+
|
35
|
+
def source_location
|
36
|
+
path
|
37
|
+
end
|
38
|
+
|
39
|
+
def static
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def comment
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def labels
|
48
|
+
[ 'mvc.template' ]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# TemplateCall is a type of function call which is specialized to view template rendering. Since
|
53
|
+
# there isn't really a perfect method in Rails to hook, this one is synthesized from the available
|
54
|
+
# information.
|
55
|
+
class TemplateCall < AppMap::Event::MethodEvent
|
56
|
+
# This is basically the +self+ parameter.
|
57
|
+
attr_reader :render_instance
|
58
|
+
# Path to the view template.
|
59
|
+
attr_accessor :path
|
60
|
+
|
61
|
+
def initialize(render_instance)
|
62
|
+
super :call
|
63
|
+
|
64
|
+
AppMap::Event::MethodEvent.build_from_invocation(:call, event: self)
|
65
|
+
@render_instance = render_instance
|
66
|
+
end
|
67
|
+
|
68
|
+
def static?
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_h
|
73
|
+
super.tap do |h|
|
74
|
+
h[:defined_class] = path ? path.parameterize.underscore : 'inline_template'
|
75
|
+
h[:method_id] = 'render'
|
76
|
+
h[:path] = path
|
77
|
+
h[:static] = static?
|
78
|
+
h[:parameters] = []
|
79
|
+
h[:receiver] = {
|
80
|
+
class: AppMap::Event::MethodEvent.best_class_name(render_instance),
|
81
|
+
object_id: render_instance.__id__,
|
82
|
+
value: AppMap::Event::MethodEvent.display_string(render_instance)
|
83
|
+
}
|
84
|
+
h.compact
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
TEMPLATE_RENDERER = 'appmap.handler.rails.template.renderer'
|
90
|
+
|
91
|
+
# Hooks the ActionView::Resolver methods +find_all+, +find_all_anywhere+. The resolver is used
|
92
|
+
# during template rendering to lookup the template file path from parameters such as the
|
93
|
+
# template name, prefix, and partial (boolean).
|
94
|
+
class ResolverHandler
|
95
|
+
class << self
|
96
|
+
# Handled as a normal function call.
|
97
|
+
def handle_call(defined_class, hook_method, receiver, args)
|
98
|
+
name, prefix, partial = args
|
99
|
+
warn "Resolver: #{{ name: name, prefix: prefix, partial: partial }}" if LOG
|
100
|
+
|
101
|
+
AppMap::Handler::Function.handle_call(defined_class, hook_method, receiver, args)
|
102
|
+
end
|
103
|
+
|
104
|
+
# When the resolver returns, look to see if there is template rendering underway.
|
105
|
+
# If so, populate the template path. In all cases, add a TemplateMethod so that the
|
106
|
+
# template will be recorded in the classMap.
|
107
|
+
def handle_return(call_event_id, elapsed, return_value, exception)
|
108
|
+
renderer = Array(Thread.current[TEMPLATE_RENDERER]).last
|
109
|
+
path_obj = Array(return_value).first
|
110
|
+
|
111
|
+
warn "Resolver return: #{path_obj}" if LOG
|
112
|
+
|
113
|
+
if path_obj
|
114
|
+
path = if path_obj.respond_to?(:identifier) && path_obj.inspect.index('#<')
|
115
|
+
path_obj.identifier
|
116
|
+
else
|
117
|
+
path_obj.inspect
|
118
|
+
end
|
119
|
+
path = path[Dir.pwd.length + 1..-1] if path.index(Dir.pwd) == 0
|
120
|
+
AppMap.tracing.record_method(TemplateMethod.new(path))
|
121
|
+
renderer.path ||= path if renderer
|
122
|
+
end
|
123
|
+
|
124
|
+
AppMap::Handler::Function.handle_return(call_event_id, elapsed, return_value, exception)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Hooks the ActionView::Renderer method +render+. This method is used by Rails to perform
|
130
|
+
# template rendering. The TemplateCall event which is emitted by this handler has a
|
131
|
+
# +path+ parameter, which is nil until it's filled in by a ResolverHandler.
|
132
|
+
class RenderHandler
|
133
|
+
class << self
|
134
|
+
def handle_call(defined_class, hook_method, receiver, args)
|
135
|
+
context, options = args
|
136
|
+
|
137
|
+
warn "Renderer: #{options}" if LOG
|
138
|
+
|
139
|
+
TemplateCall.new(receiver).tap do |call|
|
140
|
+
Thread.current[TEMPLATE_RENDERER] ||= []
|
141
|
+
Thread.current[TEMPLATE_RENDERER] << call
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def handle_return(call_event_id, elapsed, return_value, exception)
|
146
|
+
Array(Thread.current[TEMPLATE_RENDERER]).pop
|
147
|
+
|
148
|
+
AppMap::Event::MethodReturnIgnoreValue.build_from_invocation(call_event_id, elapsed: elapsed)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/appmap/hook.rb
CHANGED
@@ -5,6 +5,7 @@ require 'English'
|
|
5
5
|
module AppMap
|
6
6
|
class Hook
|
7
7
|
LOG = (ENV['APPMAP_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
|
8
|
+
LOG_HOOK = (ENV['DEBUG_HOOK'] == 'true')
|
8
9
|
|
9
10
|
OBJECT_INSTANCE_METHODS = %i[! != !~ <=> == === =~ __id__ __send__ class clone define_singleton_method display dup enum_for eql? equal? extend freeze frozen? hash inspect instance_eval instance_exec instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method methods nil? object_id private_methods protected_methods public_method public_methods public_send remove_instance_variable respond_to? send singleton_class singleton_method singleton_methods taint tainted? tap then to_enum to_s to_h to_a trust untaint untrust untrusted? yield_self].freeze
|
10
11
|
OBJECT_STATIC_METHODS = %i[! != !~ < <= <=> == === =~ > >= __id__ __send__ alias_method allocate ancestors attr attr_accessor attr_reader attr_writer autoload autoload? class class_eval class_exec class_variable_defined? class_variable_get class_variable_set class_variables clone const_defined? const_get const_missing const_set constants define_method define_singleton_method deprecate_constant display dup enum_for eql? equal? extend freeze frozen? hash include include? included_modules inspect instance_eval instance_exec instance_method instance_methods instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables is_a? itself kind_of? method method_defined? methods module_eval module_exec name new nil? object_id prepend private_class_method private_constant private_instance_methods private_method_defined? private_methods protected_instance_methods protected_method_defined? protected_methods public_class_method public_constant public_instance_method public_instance_methods public_method public_method_defined? public_methods public_send remove_class_variable remove_instance_variable remove_method respond_to? send singleton_class singleton_class? singleton_method singleton_methods superclass taint tainted? tap then to_enum to_s trust undef_method untaint untrust untrusted? yield_self].freeze
|
@@ -35,73 +36,21 @@ module AppMap
|
|
35
36
|
|
36
37
|
def initialize(config)
|
37
38
|
@config = config
|
39
|
+
@trace_locations = []
|
40
|
+
# Paths that are known to be non-tracing
|
41
|
+
@notrace_paths = Set.new
|
38
42
|
end
|
39
43
|
|
40
44
|
# Observe class loading and hook all methods which match the config.
|
41
|
-
def enable
|
45
|
+
def enable(&block)
|
42
46
|
require 'appmap/hook/method'
|
43
47
|
|
44
48
|
hook_builtins
|
45
49
|
|
46
|
-
|
47
|
-
|
50
|
+
@trace_begin = TracePoint.new(:class, &method(:trace_class))
|
51
|
+
@trace_end = TracePoint.new(:end, &method(:trace_end))
|
48
52
|
|
49
|
-
|
50
|
-
# NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
|
51
|
-
class_methods = begin
|
52
|
-
if cls.respond_to?(:singleton_class)
|
53
|
-
cls.singleton_class.public_instance_methods(false) - instance_methods - OBJECT_STATIC_METHODS
|
54
|
-
else
|
55
|
-
[]
|
56
|
-
end
|
57
|
-
rescue NameError
|
58
|
-
[]
|
59
|
-
end
|
60
|
-
|
61
|
-
hook = lambda do |hook_cls|
|
62
|
-
lambda do |method_id|
|
63
|
-
# Don't try and trace the AppMap methods or there will be
|
64
|
-
# a stack overflow in the defined hook method.
|
65
|
-
return if (hook_cls&.name || '').split('::')[0] == AppMap.name
|
66
|
-
|
67
|
-
method = begin
|
68
|
-
hook_cls.public_instance_method(method_id)
|
69
|
-
rescue NameError
|
70
|
-
warn "AppMap: Method #{hook_cls} #{method.name} is not accessible" if LOG
|
71
|
-
return
|
72
|
-
end
|
73
|
-
|
74
|
-
warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
|
75
|
-
|
76
|
-
disasm = RubyVM::InstructionSequence.disasm(method)
|
77
|
-
# Skip methods that have no instruction sequence, as they are obviously trivial.
|
78
|
-
next unless disasm
|
79
|
-
|
80
|
-
next if config.never_hook?(method)
|
81
|
-
|
82
|
-
next unless \
|
83
|
-
config.always_hook?(hook_cls, method.name) ||
|
84
|
-
config.included_by_location?(method)
|
85
|
-
|
86
|
-
package = config.package_for_method(method)
|
87
|
-
|
88
|
-
hook_method = Hook::Method.new(package, hook_cls, method)
|
89
|
-
|
90
|
-
hook_method.activate
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
instance_methods.each(&hook.(cls))
|
95
|
-
# NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
|
96
|
-
begin
|
97
|
-
class_methods.each(&hook.(cls.singleton_class)) if cls.respond_to?(:singleton_class)
|
98
|
-
rescue NameError
|
99
|
-
# NameError:
|
100
|
-
# uninitialized constant Faraday::Connection
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
tp.enable(&block)
|
53
|
+
@trace_begin.enable(&block)
|
105
54
|
end
|
106
55
|
|
107
56
|
# hook_builtins builds hooks for code that is built in to the Ruby standard library.
|
@@ -115,30 +64,119 @@ module AppMap
|
|
115
64
|
end
|
116
65
|
end
|
117
66
|
|
118
|
-
config.
|
67
|
+
config.builtin_hooks.each do |class_name, hooks|
|
119
68
|
Array(hooks).each do |hook|
|
120
69
|
require hook.package.package_name if hook.package.package_name
|
121
70
|
Array(hook.method_names).each do |method_name|
|
122
71
|
method_name = method_name.to_sym
|
72
|
+
base_cls = class_from_string.(class_name)
|
123
73
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
cls.instance_method(method_name)
|
128
|
-
rescue NameError
|
129
|
-
cls.method(method_name) rescue nil
|
130
|
-
end
|
131
|
-
|
132
|
-
next if config.never_hook?(method)
|
74
|
+
hook_method = lambda do |entry|
|
75
|
+
cls, method = entry
|
76
|
+
return false if config.never_hook?(cls, method)
|
133
77
|
|
134
|
-
if method
|
135
78
|
Hook::Method.new(hook.package, cls, method).activate
|
79
|
+
end
|
80
|
+
|
81
|
+
methods = []
|
82
|
+
methods << [ base_cls, base_cls.public_instance_method(method_name) ] rescue nil
|
83
|
+
if base_cls.respond_to?(:singleton_class)
|
84
|
+
methods << [ base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name) ] rescue nil
|
85
|
+
end
|
86
|
+
methods.compact!
|
87
|
+
if methods.empty?
|
88
|
+
warn "Method #{method_name} not found on #{base_cls.name}"
|
136
89
|
else
|
137
|
-
|
90
|
+
methods.each(&hook_method)
|
138
91
|
end
|
139
92
|
end
|
140
93
|
end
|
141
94
|
end
|
142
95
|
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
def trace_class(trace_point)
|
100
|
+
path = trace_point.path
|
101
|
+
|
102
|
+
return if @notrace_paths.member?(path)
|
103
|
+
|
104
|
+
if config.path_enabled?(path)
|
105
|
+
location = trace_location(trace_point)
|
106
|
+
warn "Entering hook-enabled location #{location}" if Hook::LOG || Hook::LOG_HOOK
|
107
|
+
@trace_locations << location
|
108
|
+
unless @trace_end.enabled?
|
109
|
+
warn "Enabling hooking" if Hook::LOG || Hook::LOG_HOOK
|
110
|
+
@trace_end.enable
|
111
|
+
end
|
112
|
+
else
|
113
|
+
@notrace_paths << path
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def trace_location(trace_point)
|
118
|
+
[ trace_point.path, trace_point.lineno ].join(':')
|
119
|
+
end
|
120
|
+
|
121
|
+
def trace_end(trace_point)
|
122
|
+
cls = trace_point.self
|
123
|
+
|
124
|
+
instance_methods = cls.public_instance_methods(false) - OBJECT_INSTANCE_METHODS
|
125
|
+
# NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
|
126
|
+
class_methods = begin
|
127
|
+
if cls.respond_to?(:singleton_class)
|
128
|
+
cls.singleton_class.public_instance_methods(false) - instance_methods - OBJECT_STATIC_METHODS
|
129
|
+
else
|
130
|
+
[]
|
131
|
+
end
|
132
|
+
rescue NameError
|
133
|
+
[]
|
134
|
+
end
|
135
|
+
|
136
|
+
hook = lambda do |hook_cls|
|
137
|
+
lambda do |method_id|
|
138
|
+
# Don't try and trace the AppMap methods or there will be
|
139
|
+
# a stack overflow in the defined hook method.
|
140
|
+
next if %w[Marshal AppMap ActiveSupport].member?((hook_cls&.name || '').split('::')[0])
|
141
|
+
|
142
|
+
next if method_id == :call
|
143
|
+
|
144
|
+
method = begin
|
145
|
+
hook_cls.public_instance_method(method_id)
|
146
|
+
rescue NameError
|
147
|
+
warn "AppMap: Method #{hook_cls} #{method.name} is not accessible" if LOG
|
148
|
+
next
|
149
|
+
end
|
150
|
+
|
151
|
+
warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
|
152
|
+
|
153
|
+
disasm = RubyVM::InstructionSequence.disasm(method)
|
154
|
+
# Skip methods that have no instruction sequence, as they are obviously trivial.
|
155
|
+
next unless disasm
|
156
|
+
|
157
|
+
package = config.lookup_package(hook_cls, method)
|
158
|
+
next unless package
|
159
|
+
|
160
|
+
Hook::Method.new(package, hook_cls, method).activate
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
instance_methods.each(&hook.(cls))
|
165
|
+
begin
|
166
|
+
# NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
|
167
|
+
class_methods.each(&hook.(cls.singleton_class)) if cls.respond_to?(:singleton_class)
|
168
|
+
rescue NameError
|
169
|
+
# NameError:
|
170
|
+
# uninitialized constant Faraday::Connection
|
171
|
+
warn "NameError in #{__FILE__}: #{$!.message}"
|
172
|
+
end
|
173
|
+
|
174
|
+
location = @trace_locations.pop
|
175
|
+
warn "Leaving hook-enabled location #{location}" if Hook::LOG || Hook::LOG_HOOK
|
176
|
+
if @trace_locations.empty?
|
177
|
+
warn "Disabling hooking" if Hook::LOG || Hook::LOG_HOOK
|
178
|
+
@trace_end.disable
|
179
|
+
end
|
180
|
+
end
|
143
181
|
end
|
144
182
|
end
|
data/lib/appmap/hook/method.rb
CHANGED
@@ -76,7 +76,7 @@ module AppMap
|
|
76
76
|
raise
|
77
77
|
ensure
|
78
78
|
with_disabled_hook.call do
|
79
|
-
after_hook.call(self, call_event, start_time, return_value, exception)
|
79
|
+
after_hook.call(self, call_event, start_time, return_value, exception) if call_event
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -87,18 +87,16 @@ module AppMap
|
|
87
87
|
protected
|
88
88
|
|
89
89
|
def before_hook(receiver, defined_class, args)
|
90
|
-
|
91
|
-
|
92
|
-
AppMap.tracing.record_event call_event, package: hook_package, defined_class: defined_class, method: hook_method
|
90
|
+
call_event = hook_package.handler_class.handle_call(defined_class, hook_method, receiver, args)
|
91
|
+
AppMap.tracing.record_event(call_event, package: hook_package, defined_class: defined_class, method: hook_method) if call_event
|
93
92
|
[ call_event, TIME_NOW.call ]
|
94
93
|
end
|
95
94
|
|
96
95
|
def after_hook(_receiver, call_event, start_time, return_value, exception)
|
97
|
-
require 'appmap/event'
|
98
96
|
elapsed = TIME_NOW.call - start_time
|
99
|
-
return_event =
|
100
|
-
|
101
|
-
|
97
|
+
return_event = hook_package.handler_class.handle_return(call_event.id, elapsed, return_value, exception)
|
98
|
+
AppMap.tracing.record_event(return_event) if return_event
|
99
|
+
nil
|
102
100
|
end
|
103
101
|
|
104
102
|
def with_disabled_hook(&function)
|
data/lib/appmap/railtie.rb
CHANGED
@@ -8,12 +8,12 @@ module AppMap
|
|
8
8
|
# appmap.subscribe subscribes to ActiveSupport Notifications so that they can be recorded as
|
9
9
|
# AppMap events.
|
10
10
|
initializer 'appmap.subscribe' do |_| # params: app
|
11
|
-
require 'appmap/rails/sql_handler'
|
12
|
-
require 'appmap/rails/request_handler'
|
13
|
-
ActiveSupport::Notifications.subscribe 'sql.sequel', AppMap::Rails::SQLHandler.new
|
14
|
-
ActiveSupport::Notifications.subscribe 'sql.active_record', AppMap::Rails::SQLHandler.new
|
11
|
+
require 'appmap/handler/rails/sql_handler'
|
12
|
+
require 'appmap/handler/rails/request_handler'
|
13
|
+
ActiveSupport::Notifications.subscribe 'sql.sequel', AppMap::Handler::Rails::SQLHandler.new
|
14
|
+
ActiveSupport::Notifications.subscribe 'sql.active_record', AppMap::Handler::Rails::SQLHandler.new
|
15
15
|
|
16
|
-
AppMap::Rails::RequestHandler::HookMethod.new.activate
|
16
|
+
AppMap::Handler::Rails::RequestHandler::HookMethod.new.activate
|
17
17
|
end
|
18
18
|
|
19
19
|
# appmap.trace begins recording an AppMap trace and writes it to appmap.json.
|
data/lib/appmap/trace.rb
CHANGED
@@ -2,14 +2,36 @@
|
|
2
2
|
|
3
3
|
module AppMap
|
4
4
|
module Trace
|
5
|
-
class
|
6
|
-
attr_reader :
|
5
|
+
class RubyMethod
|
6
|
+
attr_reader :class_name, :static
|
7
7
|
|
8
|
-
def initialize(package,
|
8
|
+
def initialize(package, class_name, method, static)
|
9
9
|
@package = package
|
10
|
-
@
|
10
|
+
@class_name = class_name
|
11
|
+
@method = method
|
11
12
|
@static = static
|
12
|
-
|
13
|
+
end
|
14
|
+
|
15
|
+
def source_location
|
16
|
+
@method.source_location
|
17
|
+
end
|
18
|
+
|
19
|
+
def comment
|
20
|
+
@method.comment
|
21
|
+
rescue MethodSource::SourceNotFoundError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def package
|
26
|
+
@package.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
@method.name
|
31
|
+
end
|
32
|
+
|
33
|
+
def labels
|
34
|
+
@package.labels
|
13
35
|
end
|
14
36
|
end
|
15
37
|
|
@@ -43,6 +65,12 @@ module AppMap
|
|
43
65
|
end
|
44
66
|
end
|
45
67
|
|
68
|
+
def record_method(method)
|
69
|
+
@tracers.each do |tracer|
|
70
|
+
tracer.record_method(method)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
46
74
|
def delete(tracer)
|
47
75
|
return unless @tracers.member?(tracer)
|
48
76
|
|
@@ -82,10 +110,23 @@ module AppMap
|
|
82
110
|
|
83
111
|
@last_package_for_thread[Thread.current.object_id] = package if package
|
84
112
|
@events << event
|
85
|
-
|
113
|
+
static = event.static if event.respond_to?(:static)
|
114
|
+
@methods << Trace::RubyMethod.new(package, defined_class, method, static) \
|
86
115
|
if package && defined_class && method && (event.event == :call)
|
87
116
|
end
|
88
117
|
|
118
|
+
# +method+ should be duck-typed to respond to the following:
|
119
|
+
# * package
|
120
|
+
# * defined_class
|
121
|
+
# * name
|
122
|
+
# * static
|
123
|
+
# * comment
|
124
|
+
# * labels
|
125
|
+
# * source_location
|
126
|
+
def record_method(method)
|
127
|
+
@methods << method
|
128
|
+
end
|
129
|
+
|
89
130
|
# Gets the last package which was observed on the current thread.
|
90
131
|
def last_package_for_current_thread
|
91
132
|
@last_package_for_thread[Thread.current.object_id]
|