appmap 0.67.1 → 0.68.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/CHANGELOG.md +13 -0
- data/lib/appmap/builtin_hooks/json.yml +4 -0
- data/lib/appmap/builtin_hooks/net/http.yml +3 -0
- data/lib/appmap/builtin_hooks/openssl.yml +16 -0
- data/lib/appmap/builtin_hooks/yaml.yml +10 -0
- data/lib/appmap/config.rb +155 -136
- data/lib/appmap/event.rb +2 -0
- data/lib/appmap/gem_hooks/actionpack.yml +40 -0
- data/lib/appmap/gem_hooks/actionview.yml +13 -0
- data/lib/appmap/gem_hooks/activesupport.yml +12 -0
- data/lib/appmap/gem_hooks/cancancan.yml +6 -0
- data/lib/appmap/hook.rb +46 -30
- data/lib/appmap/util.rb +8 -0
- data/lib/appmap/version.rb +1 -1
- data/spec/config_spec.rb +207 -65
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d54229f9f87639b89c9388722e8e9ee30e327a39f9e4effe40739033229de5f
|
4
|
+
data.tar.gz: 2a87b31d06444d8091f78ec00e0751b900f2068ee87003c945b3b6da3e58602d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a6efa5f3fbac309a6df73405f281deaaf4a577de3d22466e04e2a8587af59dc418e99c032387f5cf00d604da2d43cedfaa745f63589ae39f8edba685f9ab5b9
|
7
|
+
data.tar.gz: 390d01607fcf6ae24852758a50f4f174b90c4d59f5b008c77c60108540a9d58c65917a36242ce495430fc7bfe38bdd58a4be050d106ebbb4c7ad6a3ea7d2104e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# [0.68.0](https://github.com/applandinc/appmap-ruby/compare/v0.67.1...v0.68.0) (2021-11-05)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Require weakref ([2f94f80](https://github.com/applandinc/appmap-ruby/commit/2f94f808bd3327aa3fc7fd8e6a3428a5da3a29bb))
|
7
|
+
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* Externalize config of hooks ([8080222](https://github.com/applandinc/appmap-ruby/commit/8080222ce5b61d9824eaf20410d7b9b94b679890))
|
12
|
+
* Support loading hook config via path env vars ([4856483](https://github.com/applandinc/appmap-ruby/commit/48564837784f8b0e87c4286ad3e2f6cb2d272dcf))
|
13
|
+
|
1
14
|
## [0.67.1](https://github.com/applandinc/appmap-ruby/compare/v0.67.0...v0.67.1) (2021-11-02)
|
2
15
|
|
3
16
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
- method: OpenSSL::PKey::PKey#sign
|
2
|
+
label: crypto.pkey
|
3
|
+
- methods:
|
4
|
+
- OpenSSL::X509::Request#sign
|
5
|
+
- OpenSSL::X509::Request#verify
|
6
|
+
label: crypto.x509
|
7
|
+
- method: OpenSSL::X509::Certificate#sign
|
8
|
+
label: crypto.x509
|
9
|
+
- methods:
|
10
|
+
- OpenSSL::PKCS5#pbkdf2_hmac
|
11
|
+
- OpenSSL::PKCS5#pbkdf2_hmac_sha1
|
12
|
+
label: crypto.pkcs5
|
13
|
+
- method: OpenSSL::Cipher#encrypt
|
14
|
+
label: crypto.encrypt
|
15
|
+
- method: OpenSSL::Cipher#decrypt
|
16
|
+
label: crypto.decrypt
|
data/lib/appmap/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pathname'
|
3
4
|
require 'set'
|
4
5
|
require 'yaml'
|
5
6
|
require 'appmap/util'
|
@@ -11,17 +12,20 @@ require 'appmap/depends/configuration'
|
|
11
12
|
|
12
13
|
module AppMap
|
13
14
|
class Config
|
14
|
-
# Specifies a code
|
15
|
+
# Specifies a logical code package be mapped.
|
16
|
+
# This can be a project source folder, a Gem, or a builtin.
|
17
|
+
#
|
15
18
|
# Options:
|
16
19
|
#
|
20
|
+
# * +path+ indicates a relative path to a code folder.
|
17
21
|
# * +gem+ may indicate a gem name that "owns" the path
|
18
|
-
# * +
|
22
|
+
# * +require_name+ can be used to make sure that the code is required so that it can be loaded. This is generally used with
|
19
23
|
# builtins, or when the path to be required is not automatically required when bundler requires the gem.
|
20
24
|
# * +exclude+ can be used used to exclude sub-paths. Generally not used with +gem+.
|
21
25
|
# * +labels+ is used to apply labels to matching code. This is really only useful when the package will be applied to
|
22
26
|
# specific functions, via TargetMethods.
|
23
27
|
# * +shallow+ indicates shallow mapping, in which only the entrypoint to a gem is recorded.
|
24
|
-
Package = Struct.new(:path, :gem, :
|
28
|
+
Package = Struct.new(:name, :path, :gem, :require_name, :exclude, :labels, :shallow, :builtin) do
|
25
29
|
# This is for internal use only.
|
26
30
|
private_methods :gem
|
27
31
|
|
@@ -43,20 +47,24 @@ module AppMap
|
|
43
47
|
class << self
|
44
48
|
# Builds a package for a path, such as `app/models` in a Rails app. Generally corresponds to a `path:` entry
|
45
49
|
# in appmap.yml. Also used for mapping specific methods via TargetMethods.
|
46
|
-
def build_from_path(path, shallow: false,
|
47
|
-
Package.new(path, nil,
|
50
|
+
def build_from_path(path, shallow: false, require_name: nil, exclude: [], labels: [])
|
51
|
+
Package.new(path, path, nil, require_name, exclude, labels, shallow)
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_from_builtin(path, shallow: false, require_name: nil, exclude: [], labels: [])
|
55
|
+
Package.new(path, path, nil, require_name, exclude, labels, shallow, true)
|
48
56
|
end
|
49
57
|
|
50
58
|
# Builds a package for gem. Generally corresponds to a `gem:` entry in appmap.yml. Also used when mapping
|
51
59
|
# a builtin.
|
52
|
-
def build_from_gem(gem, shallow: true,
|
60
|
+
def build_from_gem(gem, shallow: true, require_name: nil, exclude: [], labels: [], optional: false, force: false)
|
53
61
|
if !force && %w[method_source activesupport].member?(gem)
|
54
62
|
warn "WARNING: #{gem} cannot be AppMapped because it is a dependency of the appmap gem"
|
55
63
|
return
|
56
64
|
end
|
57
65
|
path = gem_path(gem, optional)
|
58
66
|
if path
|
59
|
-
Package.new(path, gem,
|
67
|
+
Package.new(gem, path, gem, require_name, exclude, labels, shallow)
|
60
68
|
else
|
61
69
|
AppMap::Util.startup_message "#{gem} is not available in the bundle"
|
62
70
|
end
|
@@ -74,19 +82,16 @@ module AppMap
|
|
74
82
|
end
|
75
83
|
end
|
76
84
|
|
77
|
-
def name
|
78
|
-
gem || path
|
79
|
-
end
|
80
|
-
|
81
85
|
def to_h
|
82
86
|
{
|
87
|
+
name: name,
|
83
88
|
path: path,
|
84
|
-
package_name: package_name,
|
85
89
|
gem: gem,
|
86
|
-
|
90
|
+
require_name: require_name,
|
91
|
+
handler_class: handler_class ? handler_class.name : nil,
|
87
92
|
exclude: Util.blank?(exclude) ? nil : exclude,
|
88
93
|
labels: Util.blank?(labels) ? nil : labels,
|
89
|
-
shallow: shallow
|
94
|
+
shallow: shallow.nil? ? nil : shallow,
|
90
95
|
}.compact
|
91
96
|
end
|
92
97
|
end
|
@@ -96,12 +101,12 @@ module AppMap
|
|
96
101
|
attr_reader :method_names, :package
|
97
102
|
|
98
103
|
def initialize(method_names, package)
|
99
|
-
@method_names = method_names
|
104
|
+
@method_names = Array(method_names).map(&:to_sym)
|
100
105
|
@package = package
|
101
106
|
end
|
102
107
|
|
103
108
|
def include_method?(method_name)
|
104
|
-
|
109
|
+
method_names.include?(method_name.to_sym)
|
105
110
|
end
|
106
111
|
|
107
112
|
def to_h
|
@@ -110,6 +115,8 @@ module AppMap
|
|
110
115
|
method_names: method_names
|
111
116
|
}
|
112
117
|
end
|
118
|
+
|
119
|
+
alias as_json to_h
|
113
120
|
end
|
114
121
|
private_constant :TargetMethods
|
115
122
|
|
@@ -117,11 +124,11 @@ module AppMap
|
|
117
124
|
# entry in appmap.yml. When the Config is initialized, each Function is converted into
|
118
125
|
# a Package and TargetMethods. It's called a Function rather than a Method, because Function
|
119
126
|
# is the AppMap terminology.
|
120
|
-
Function = Struct.new(:package, :cls, :labels, :function_names, :builtin, :
|
127
|
+
Function = Struct.new(:package, :cls, :labels, :function_names, :builtin, :require_name) do # :nodoc:
|
121
128
|
def to_h
|
122
129
|
{
|
123
130
|
package: package,
|
124
|
-
|
131
|
+
require_name: require_name,
|
125
132
|
class: cls,
|
126
133
|
labels: labels,
|
127
134
|
functions: function_names.map(&:to_sym),
|
@@ -138,9 +145,15 @@ module AppMap
|
|
138
145
|
private_constant :MethodHook
|
139
146
|
|
140
147
|
class << self
|
141
|
-
def package_hooks(
|
148
|
+
def package_hooks(methods, path: nil, gem: nil, force: false, builtin: false, handler_class: nil, require_name: nil)
|
142
149
|
Array(methods).map do |method|
|
143
|
-
package =
|
150
|
+
package = if builtin
|
151
|
+
Package.build_from_builtin(path, require_name: require_name, labels: method.labels, shallow: false)
|
152
|
+
elsif gem
|
153
|
+
Package.build_from_gem(gem, require_name: require_name, labels: method.labels, shallow: false, force: force, optional: true)
|
154
|
+
elsif path
|
155
|
+
Package.build_from_path(path, require_name: require_name, labels: method.labels, shallow: false)
|
156
|
+
end
|
144
157
|
next unless package
|
145
158
|
|
146
159
|
package.handler_class = handler_class if handler_class
|
@@ -151,87 +164,107 @@ module AppMap
|
|
151
164
|
def method_hook(cls, method_names, labels)
|
152
165
|
MethodHook.new(cls, method_names, labels)
|
153
166
|
end
|
167
|
+
|
168
|
+
def declare_hook(hook_decl)
|
169
|
+
hook_decl = YAML.load(hook_decl) if hook_decl.is_a?(String)
|
170
|
+
|
171
|
+
methods_decl = hook_decl['methods'] || hook_decl['method']
|
172
|
+
methods_decl = Array(methods_decl) unless methods_decl.is_a?(Hash)
|
173
|
+
labels_decl = Array(hook_decl['labels'] || hook_decl['label'])
|
174
|
+
|
175
|
+
methods = methods_decl.map do |name|
|
176
|
+
class_name, method_name, static = name.include?('.') ? name.split('.', 2) + [ true ] : name.split('#', 2) + [ false ]
|
177
|
+
method_hook class_name, [ method_name ], labels_decl
|
178
|
+
end
|
179
|
+
|
180
|
+
require_name = hook_decl['require_name']
|
181
|
+
gem_name = hook_decl['gem']
|
182
|
+
path = hook_decl['path']
|
183
|
+
builtin = hook_decl['builtin']
|
184
|
+
|
185
|
+
options = {
|
186
|
+
builtin: builtin,
|
187
|
+
gem: gem_name,
|
188
|
+
path: path,
|
189
|
+
require_name: require_name || gem_name || path,
|
190
|
+
force: hook_decl['force']
|
191
|
+
}.compact
|
192
|
+
|
193
|
+
handler_class = hook_decl['handler_class']
|
194
|
+
options[:handler_class] = Util::class_from_string(handler_class) if handler_class
|
195
|
+
|
196
|
+
package_hooks(methods, **options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def declare_hook_deprecated(hook_decl)
|
200
|
+
function_name = hook_decl['name']
|
201
|
+
package, cls, functions = []
|
202
|
+
if function_name
|
203
|
+
package, cls, _, function = Util.parse_function_name(function_name)
|
204
|
+
functions = Array(function)
|
205
|
+
else
|
206
|
+
package = hook_decl['package']
|
207
|
+
cls = hook_decl['class']
|
208
|
+
functions = hook_decl['function'] || hook_decl['functions']
|
209
|
+
raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions
|
210
|
+
end
|
211
|
+
|
212
|
+
functions = Array(functions).map(&:to_sym)
|
213
|
+
labels = hook_decl['label'] || hook_decl['labels']
|
214
|
+
req = hook_decl['require']
|
215
|
+
builtin = hook_decl['builtin']
|
216
|
+
|
217
|
+
package_options = {}
|
218
|
+
package_options[:labels] = Array(labels).map(&:to_s) if labels if labels
|
219
|
+
package_options[:require_name] = req
|
220
|
+
package_options[:require_name] ||= package if builtin
|
221
|
+
tm = TargetMethods.new(functions, Package.build_from_path(package, **package_options))
|
222
|
+
ClassTargetMethods.new(cls, tm)
|
223
|
+
end
|
224
|
+
|
225
|
+
def builtin_hooks_path
|
226
|
+
[ [ __dir__, 'builtin_hooks' ].join('/') ] + ( ENV['APPMAP_BUILTIN_HOOKS_PATH'] || '').split(/[;:]/)
|
227
|
+
end
|
228
|
+
|
229
|
+
def gem_hooks_path
|
230
|
+
[ [ __dir__, 'gem_hooks' ].join('/') ] + ( ENV['APPMAP_GEM_HOOKS_PATH'] || '').split(/[;:]/)
|
231
|
+
end
|
232
|
+
|
233
|
+
def load_hooks
|
234
|
+
loader = lambda do |dir, &block|
|
235
|
+
basename = dir.split('/').compact.join('/')
|
236
|
+
[].tap do |hooks|
|
237
|
+
Dir.glob(Pathname.new(dir).join('**').join('*.yml').to_s).each do |yaml_file|
|
238
|
+
path = yaml_file[basename.length + 1...-4]
|
239
|
+
YAML.load(File.read(yaml_file)).map do |config|
|
240
|
+
block.call path, config
|
241
|
+
config
|
242
|
+
end.each do |config|
|
243
|
+
hooks << declare_hook(config)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end.compact
|
247
|
+
end
|
248
|
+
|
249
|
+
builtin_hooks = builtin_hooks_path.map do |path|
|
250
|
+
loader.(path) do |path, config|
|
251
|
+
config['path'] = path
|
252
|
+
config['builtin'] = true
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
gem_hooks = gem_hooks_path.map do |path|
|
257
|
+
loader.(path) do |path, config|
|
258
|
+
config['gem'] = path
|
259
|
+
config['builtin'] = false
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
(builtin_hooks + gem_hooks).flatten
|
264
|
+
end
|
154
265
|
end
|
155
266
|
|
156
|
-
|
157
|
-
# predefined labels specified here. If any of these hooks are not desired, they can be disabled in the +exclude+ section
|
158
|
-
# of appmap.yml.
|
159
|
-
METHOD_HOOKS = [
|
160
|
-
package_hooks('actionview',
|
161
|
-
[
|
162
|
-
method_hook('ActionView::Renderer', :render, %w[mvc.view]),
|
163
|
-
method_hook('ActionView::TemplateRenderer', :render, %w[mvc.view]),
|
164
|
-
method_hook('ActionView::PartialRenderer', :render, %w[mvc.view])
|
165
|
-
],
|
166
|
-
handler_class: AppMap::Handler::Rails::Template::RenderHandler,
|
167
|
-
package_name: 'action_view'
|
168
|
-
),
|
169
|
-
package_hooks('actionview',
|
170
|
-
[
|
171
|
-
method_hook('ActionView::Resolver', %i[find_all find_all_anywhere], %w[mvc.template.resolver])
|
172
|
-
],
|
173
|
-
handler_class: AppMap::Handler::Rails::Template::ResolverHandler,
|
174
|
-
package_name: 'action_view'
|
175
|
-
),
|
176
|
-
package_hooks('actionpack',
|
177
|
-
[
|
178
|
-
method_hook('ActionDispatch::Request::Session', %i[[] dig values fetch], %w[http.session.read]),
|
179
|
-
method_hook('ActionDispatch::Request::Session', %i[destroy []= clear update delete merge], %w[http.session.write]),
|
180
|
-
method_hook('ActionDispatch::Cookies::CookieJar', %i[[] fetch], %w[http.session.read]),
|
181
|
-
method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session.write]),
|
182
|
-
method_hook('ActionDispatch::Cookies::EncryptedCookieJar', %i[[]= clear update delete recycle], %w[http.cookie crypto.encrypt])
|
183
|
-
],
|
184
|
-
package_name: 'action_dispatch'
|
185
|
-
),
|
186
|
-
package_hooks('cancancan',
|
187
|
-
[
|
188
|
-
method_hook('CanCan::ControllerAdditions', %i[authorize! can? cannot?], %w[security.authorization]),
|
189
|
-
method_hook('CanCan::Ability', %i[authorize?], %w[security.authorization])
|
190
|
-
]
|
191
|
-
),
|
192
|
-
package_hooks('actionpack',
|
193
|
-
[
|
194
|
-
method_hook('ActionController::Instrumentation', %i[process_action send_file send_data redirect_to], %w[mvc.controller])
|
195
|
-
],
|
196
|
-
package_name: 'action_controller'
|
197
|
-
)
|
198
|
-
].flatten.freeze
|
199
|
-
|
200
|
-
OPENSSL_PACKAGES = ->(labels) { Package.build_from_path('openssl', package_name: 'openssl', labels: labels) }
|
201
|
-
|
202
|
-
# Hook functions which are builtin to Ruby. Because they are builtins, they may be loaded before appmap.
|
203
|
-
# Therefore, we can't rely on TracePoint to report the loading of this code.
|
204
|
-
BUILTIN_HOOKS = {
|
205
|
-
'OpenSSL::PKey::PKey' => TargetMethods.new(:sign, OPENSSL_PACKAGES.(%w[crypto.pkey])),
|
206
|
-
'OpenSSL::X509::Request' => TargetMethods.new(%i[sign verify], OPENSSL_PACKAGES.(%w[crypto.x509])),
|
207
|
-
'OpenSSL::PKCS5' => TargetMethods.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES.(%w[crypto.pkcs5])),
|
208
|
-
'OpenSSL::Cipher' => [
|
209
|
-
TargetMethods.new(%i[encrypt], OPENSSL_PACKAGES.(%w[crypto.encrypt])),
|
210
|
-
TargetMethods.new(%i[decrypt], OPENSSL_PACKAGES.(%w[crypto.decrypt]))
|
211
|
-
],
|
212
|
-
'ActiveSupport::Callbacks::CallbackSequence' => [
|
213
|
-
TargetMethods.new(:invoke_before, Package.build_from_gem('activesupport', force: true, package_name: 'active_support', labels: %w[mvc.before_action])),
|
214
|
-
TargetMethods.new(:invoke_after, Package.build_from_gem('activesupport', force: true, package_name: 'active_support', labels: %w[mvc.after_action])),
|
215
|
-
],
|
216
|
-
'ActiveSupport::SecurityUtils' => TargetMethods.new(:secure_compare, Package.build_from_gem('activesupport', force: true, package_name: 'active_support/security_utils', labels: %w[crypto.secure_compare])),
|
217
|
-
'OpenSSL::X509::Certificate' => TargetMethods.new(:sign, OPENSSL_PACKAGES.(%w[crypto.x509])),
|
218
|
-
'Net::HTTP' => TargetMethods.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http]).tap do |package|
|
219
|
-
package.handler_class = AppMap::Handler::NetHTTP
|
220
|
-
end),
|
221
|
-
'Net::SMTP' => TargetMethods.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.email.smtp])),
|
222
|
-
'Net::POP3' => TargetMethods.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.email.pop])),
|
223
|
-
# This is happening: Method send_command not found on Net::IMAP
|
224
|
-
# 'Net::IMAP' => TargetMethods.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])),
|
225
|
-
# 'Marshal' => TargetMethods.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal])),
|
226
|
-
'Psych' => [
|
227
|
-
TargetMethods.new(%i[load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.parse])),
|
228
|
-
TargetMethods.new(%i[dump dump_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.generate])),
|
229
|
-
],
|
230
|
-
'JSON::Ext::Parser' => TargetMethods.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.parse])),
|
231
|
-
'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
|
232
|
-
}.freeze
|
233
|
-
|
234
|
-
attr_reader :name, :appmap_dir, :packages, :exclude, :swagger_config, :depends_config, :hooked_methods, :builtin_hooks
|
267
|
+
attr_reader :name, :appmap_dir, :packages, :exclude, :swagger_config, :depends_config, :gem_hooks, :builtin_hooks
|
235
268
|
|
236
269
|
def initialize(name,
|
237
270
|
packages: [],
|
@@ -246,31 +279,22 @@ module AppMap
|
|
246
279
|
@depends_config = depends_config
|
247
280
|
@hook_paths = Set.new(packages.map(&:path))
|
248
281
|
@exclude = exclude
|
249
|
-
@builtin_hooks = BUILTIN_HOOKS.dup
|
250
282
|
@functions = functions
|
251
283
|
|
252
|
-
@
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
package_options[:labels] = func.labels if func.labels
|
259
|
-
package_options[:package_name] = func.package_name
|
260
|
-
package_options[:package_name] ||= func.package if func.builtin
|
261
|
-
hook = TargetMethods.new(func.function_names, Package.build_from_path(func.package, **package_options))
|
262
|
-
if func.builtin
|
263
|
-
@builtin_hooks[func.cls] ||= []
|
264
|
-
@builtin_hooks[func.cls] << hook
|
284
|
+
@builtin_hooks = Hash.new { |h,k| h[k] = [] }
|
285
|
+
@gem_hooks = Hash.new { |h,k| h[k] = [] }
|
286
|
+
|
287
|
+
(functions + self.class.load_hooks).each_with_object(Hash.new { |h,k| h[k] = [] }) do |cls_target_methods, gem_hooks|
|
288
|
+
hooks = if cls_target_methods.target_methods.package.builtin
|
289
|
+
@builtin_hooks
|
265
290
|
else
|
266
|
-
@
|
291
|
+
@gem_hooks
|
267
292
|
end
|
293
|
+
hooks[cls_target_methods.cls] << cls_target_methods.target_methods
|
268
294
|
end
|
269
295
|
|
270
|
-
@
|
271
|
-
Array(hooks).
|
272
|
-
@hook_paths << hook.package.path
|
273
|
-
end
|
296
|
+
@gem_hooks.each_value do |hooks|
|
297
|
+
@hook_paths += Array(hooks).map { |hook| hook.package.path }.compact
|
274
298
|
end
|
275
299
|
end
|
276
300
|
|
@@ -334,23 +358,14 @@ module AppMap
|
|
334
358
|
}.compact
|
335
359
|
|
336
360
|
if config_data['functions']
|
337
|
-
config_params[:functions] = config_data['functions'].map do |
|
338
|
-
|
339
|
-
|
340
|
-
if function_name
|
341
|
-
package, cls, _, function = Util.parse_function_name(function_name)
|
342
|
-
functions = Array(function)
|
361
|
+
config_params[:functions] = config_data['functions'].map do |hook_decl|
|
362
|
+
if hook_decl['name'] || hook_decl['package']
|
363
|
+
declare_hook_deprecated(hook_decl)
|
343
364
|
else
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions
|
365
|
+
# Support the same syntax within the 'functions' that's used for externalized
|
366
|
+
# hook config.
|
367
|
+
declare_hook(hook_decl)
|
348
368
|
end
|
349
|
-
|
350
|
-
functions = Array(functions).map(&:to_sym)
|
351
|
-
labels = function_data['label'] || function_data['labels']
|
352
|
-
labels = Array(labels).map(&:to_s) if labels
|
353
|
-
Function.new(package, cls, labels, functions, function_data['builtin'], function_data['require'])
|
354
369
|
end
|
355
370
|
end
|
356
371
|
|
@@ -366,7 +381,11 @@ module AppMap
|
|
366
381
|
shallow = package['shallow']
|
367
382
|
# shallow is true by default for gems
|
368
383
|
shallow = true if shallow.nil?
|
369
|
-
|
384
|
+
|
385
|
+
require_name = \
|
386
|
+
package['package'] || #deprecated
|
387
|
+
package['require_name']
|
388
|
+
Package.build_from_gem(gem, require_name: require_name, exclude: package['exclude'] || [], shallow: shallow)
|
370
389
|
else
|
371
390
|
Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
|
372
391
|
end
|
@@ -417,8 +436,8 @@ module AppMap
|
|
417
436
|
|
418
437
|
# Hook a method which is specified by class and method name.
|
419
438
|
def package_for_code_object
|
420
|
-
|
421
|
-
|
439
|
+
class_name = cls.to_s.index('#<Class:') == 0 ? cls.to_s['#<Class:'.length...-1] : cls.name
|
440
|
+
Array(config.gem_hooks[class_name])
|
422
441
|
.find { |hook| hook.include_method?(method.name) }
|
423
442
|
&.package
|
424
443
|
end
|
data/lib/appmap/event.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
- methods:
|
2
|
+
- ActionDispatch::Request::Session#[]
|
3
|
+
- ActionDispatch::Request::Session#dig
|
4
|
+
- ActionDispatch::Request::Session#values
|
5
|
+
- ActionDispatch::Request::Session#fetch
|
6
|
+
- ActionDispatch::Cookies::CookieJar#[]
|
7
|
+
- ActionDispatch::Cookies::CookieJar#fetch
|
8
|
+
label: http.session.read
|
9
|
+
require_name: action_dispatch
|
10
|
+
- methods:
|
11
|
+
- ActionDispatch::Request::Session#destroy
|
12
|
+
- ActionDispatch::Request::Session#[]=
|
13
|
+
- ActionDispatch::Request::Session#clear
|
14
|
+
- ActionDispatch::Request::Session#update
|
15
|
+
- ActionDispatch::Request::Session#delete
|
16
|
+
- ActionDispatch::Request::Session#merge
|
17
|
+
- ActionDispatch::Cookies::CookieJar#[]=
|
18
|
+
- ActionDispatch::Cookies::CookieJar#clear
|
19
|
+
- ActionDispatch::Cookies::CookieJar#update
|
20
|
+
- ActionDispatch::Cookies::CookieJar#delete
|
21
|
+
- ActionDispatch::Cookies::CookieJar#recycle!
|
22
|
+
label: http.session.write
|
23
|
+
require_name: action_dispatch
|
24
|
+
- methods:
|
25
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#[]=
|
26
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#clear
|
27
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#update
|
28
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#delete
|
29
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#recycle
|
30
|
+
labels:
|
31
|
+
- http.cookie
|
32
|
+
- crypto.encrypt
|
33
|
+
require_name: action_dispatch
|
34
|
+
- methods:
|
35
|
+
- ActionController::Instrumentation#process_action
|
36
|
+
- ActionController::Instrumentation#send_file
|
37
|
+
- ActionController::Instrumentation#send_data
|
38
|
+
- ActionController::Instrumentation#redirect_to
|
39
|
+
label: mvc.controller
|
40
|
+
require_name: action_controller
|
@@ -0,0 +1,13 @@
|
|
1
|
+
- methods:
|
2
|
+
- ActionView::Renderer#render
|
3
|
+
- ActionView::TemplateRenderer#render
|
4
|
+
- ActionView::PartialRenderer#render
|
5
|
+
label: mvc.view
|
6
|
+
handler_class: AppMap::Handler::Rails::Template::RenderHandler
|
7
|
+
require_name: action_view
|
8
|
+
- methods:
|
9
|
+
- ActionView::Resolver#find_all
|
10
|
+
- ActionView::Resolver#find_all_anywhere
|
11
|
+
label: mvc.template.resolver
|
12
|
+
handler_class: AppMap::Handler::Rails::Template::ResolverHandler
|
13
|
+
require_name: action_view
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- method: ActiveSupport::Callbacks::CallbackSequence#invoke_before
|
2
|
+
label: mvc.before_action
|
3
|
+
require_name: active_support
|
4
|
+
force: true
|
5
|
+
- method: ActiveSupport::Callbacks::CallbackSequence#invoke_after
|
6
|
+
label: mvc.after_action
|
7
|
+
require_name: active_support
|
8
|
+
force: true
|
9
|
+
- method: ActiveSupport::SecurityUtils#secure_compare
|
10
|
+
label: crypto.secure_compare
|
11
|
+
require_name: active_support/security_utils
|
12
|
+
force: true
|
data/lib/appmap/hook.rb
CHANGED
@@ -15,10 +15,23 @@ module AppMap
|
|
15
15
|
@method_arity = ::Method.instance_method(:arity)
|
16
16
|
|
17
17
|
class << self
|
18
|
-
def
|
19
|
-
|
18
|
+
def hook_builtins?
|
19
|
+
Mutex.new.synchronize do
|
20
|
+
@hook_builtins = true if @hook_builtins.nil?
|
20
21
|
|
21
|
-
|
22
|
+
return false unless @hook_builtins
|
23
|
+
|
24
|
+
@hook_builtins = false
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def already_hooked?(method)
|
30
|
+
# After a method is defined, the statement "module_function <the-method>" can convert that method
|
31
|
+
# into a module (class) method. The method is hooked first when it's defined, then AppMap will attempt to
|
32
|
+
# hook it again when it's redefined as a module method. So we check the method source location - if it's
|
33
|
+
# part of the AppMap source tree, we ignore it.
|
34
|
+
method.source_location && method.source_location[0].index(__dir__) == 0
|
22
35
|
end
|
23
36
|
|
24
37
|
# Return the class, separator ('.' or '#'), and method name for
|
@@ -79,42 +92,43 @@ module AppMap
|
|
79
92
|
# hook_builtins builds hooks for code that is built in to the Ruby standard library.
|
80
93
|
# No TracePoint events are emitted for builtins, so a separate hooking mechanism is needed.
|
81
94
|
def hook_builtins
|
82
|
-
return unless self.class.
|
95
|
+
return unless self.class.hook_builtins?
|
83
96
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
97
|
+
hook_loaded_code = lambda do |hooks_by_class, builtin|
|
98
|
+
hooks_by_class.each do |class_name, hooks|
|
99
|
+
Array(hooks).each do |hook|
|
100
|
+
require hook.package.require_name if builtin && hook.package.require_name && hook.package.require_name != 'ruby'
|
89
101
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
method_name = method_name.to_sym
|
95
|
-
base_cls = class_from_string.(class_name)
|
102
|
+
Array(hook.method_names).each do |method_name|
|
103
|
+
method_name = method_name.to_sym
|
104
|
+
base_cls = Util::class_from_string(class_name, must: false)
|
105
|
+
next unless base_cls
|
96
106
|
|
97
|
-
|
98
|
-
|
99
|
-
|
107
|
+
hook_method = lambda do |entry|
|
108
|
+
cls, method = entry
|
109
|
+
return false if config.never_hook?(cls, method)
|
100
110
|
|
101
|
-
|
102
|
-
|
111
|
+
Hook::Method.new(hook.package, cls, method).activate
|
112
|
+
end
|
103
113
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
+
methods = []
|
115
|
+
methods << [ base_cls, base_cls.public_instance_method(method_name) ] rescue nil
|
116
|
+
if base_cls.respond_to?(:singleton_class)
|
117
|
+
methods << [ base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name) ] rescue nil
|
118
|
+
end
|
119
|
+
methods.compact!
|
120
|
+
if methods.empty?
|
121
|
+
warn "Method #{method_name} not found on #{base_cls.name}" if LOG
|
122
|
+
else
|
123
|
+
methods.each(&hook_method)
|
124
|
+
end
|
114
125
|
end
|
115
126
|
end
|
116
127
|
end
|
117
128
|
end
|
129
|
+
|
130
|
+
hook_loaded_code.(config.builtin_hooks, true)
|
131
|
+
hook_loaded_code.(config.gem_hooks, false)
|
118
132
|
end
|
119
133
|
|
120
134
|
protected
|
@@ -165,6 +179,8 @@ module AppMap
|
|
165
179
|
next
|
166
180
|
end
|
167
181
|
|
182
|
+
next if self.class.already_hooked?(method)
|
183
|
+
|
168
184
|
warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
|
169
185
|
|
170
186
|
disasm = RubyVM::InstructionSequence.disasm(method)
|
data/lib/appmap/util.rb
CHANGED
@@ -21,6 +21,14 @@ module AppMap
|
|
21
21
|
WHITE = "\e[37m"
|
22
22
|
|
23
23
|
class << self
|
24
|
+
def class_from_string(fq_class, must: true)
|
25
|
+
fq_class.split('::').inject(Object) do |mod, class_name|
|
26
|
+
mod.const_get(class_name)
|
27
|
+
end
|
28
|
+
rescue NameError
|
29
|
+
raise if must
|
30
|
+
end
|
31
|
+
|
24
32
|
def parse_function_name(name)
|
25
33
|
package_tokens = name.split('/')
|
26
34
|
|
data/lib/appmap/version.rb
CHANGED
data/spec/config_spec.rb
CHANGED
@@ -4,58 +4,7 @@ require 'rails_spec_helper'
|
|
4
4
|
require 'appmap/config'
|
5
5
|
|
6
6
|
describe AppMap::Config, docker: false do
|
7
|
-
it 'loads
|
8
|
-
config_data = {
|
9
|
-
exclude: [],
|
10
|
-
name: 'test',
|
11
|
-
packages: [
|
12
|
-
{
|
13
|
-
path: 'path-1'
|
14
|
-
},
|
15
|
-
{
|
16
|
-
path: 'path-2',
|
17
|
-
exclude: [ 'exclude-1' ]
|
18
|
-
}
|
19
|
-
],
|
20
|
-
functions: [
|
21
|
-
{
|
22
|
-
package: 'pkg',
|
23
|
-
class: 'cls',
|
24
|
-
function: 'fn',
|
25
|
-
label: 'lbl'
|
26
|
-
}
|
27
|
-
]
|
28
|
-
}.deep_stringify_keys!
|
29
|
-
config = AppMap::Config.load(config_data)
|
30
|
-
|
31
|
-
config_expectation = {
|
32
|
-
exclude: [],
|
33
|
-
name: 'test',
|
34
|
-
packages: [
|
35
|
-
{
|
36
|
-
path: 'path-1',
|
37
|
-
handler_class: 'AppMap::Handler::Function'
|
38
|
-
},
|
39
|
-
{
|
40
|
-
path: 'path-2',
|
41
|
-
handler_class: 'AppMap::Handler::Function',
|
42
|
-
exclude: [ 'exclude-1' ]
|
43
|
-
}
|
44
|
-
],
|
45
|
-
functions: [
|
46
|
-
{
|
47
|
-
package: 'pkg',
|
48
|
-
class: 'cls',
|
49
|
-
functions: [ :fn ],
|
50
|
-
labels: ['lbl']
|
51
|
-
}
|
52
|
-
]
|
53
|
-
}.deep_stringify_keys!
|
54
|
-
|
55
|
-
expect(config.to_h.deep_stringify_keys!).to eq(config_expectation)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'interprets a function in canonical name format' do
|
7
|
+
it 'loads as expected' do
|
59
8
|
config_data = {
|
60
9
|
name: 'test',
|
61
10
|
packages: [],
|
@@ -67,20 +16,212 @@ describe AppMap::Config, docker: false do
|
|
67
16
|
}.deep_stringify_keys!
|
68
17
|
config = AppMap::Config.load(config_data)
|
69
18
|
|
70
|
-
|
71
|
-
|
72
|
-
name:
|
73
|
-
|
74
|
-
|
19
|
+
expect(JSON.parse(JSON.generate(config.as_json))).to eq(JSON.parse(<<~FIXTURE))
|
20
|
+
{
|
21
|
+
"name": "test",
|
22
|
+
"appmap_dir": "tmp/appmap",
|
23
|
+
"packages": [
|
24
|
+
],
|
25
|
+
"swagger_config": {
|
26
|
+
"project_name": null,
|
27
|
+
"project_version": "1.0",
|
28
|
+
"output_dir": "swagger",
|
29
|
+
"description": "Generate Swagger from AppMaps"
|
30
|
+
},
|
31
|
+
"depends_config": {
|
32
|
+
"base_dir": null,
|
33
|
+
"base_branches": [
|
34
|
+
"remotes/origin/main",
|
35
|
+
"remotes/origin/master"
|
36
|
+
],
|
37
|
+
"test_file_patterns": [
|
38
|
+
"spec/**/*_spec.rb",
|
39
|
+
"test/**/*_test.rb"
|
40
|
+
],
|
41
|
+
"dependent_tasks": [
|
42
|
+
"swagger"
|
43
|
+
],
|
44
|
+
"description": "Bring AppMaps up to date with local file modifications, and updated derived data such as Swagger files",
|
45
|
+
"rspec_environment_method": "AppMap::Depends.test_env",
|
46
|
+
"minitest_environment_method": "AppMap::Depends.test_env",
|
47
|
+
"rspec_select_tests_method": "AppMap::Depends.select_rspec_tests",
|
48
|
+
"minitest_select_tests_method": "AppMap::Depends.select_minitest_tests",
|
49
|
+
"rspec_test_command_method": "AppMap::Depends.rspec_test_command",
|
50
|
+
"minitest_test_command_method": "AppMap::Depends.minitest_test_command"
|
51
|
+
},
|
52
|
+
"hook_paths": [
|
53
|
+
"pkg",
|
54
|
+
"#{Gem.loaded_specs['activesupport'].gem_dir}"
|
55
|
+
],
|
56
|
+
"exclude": [
|
57
|
+
],
|
58
|
+
"functions": [
|
75
59
|
{
|
76
|
-
|
77
|
-
|
78
|
-
|
60
|
+
"cls": "cls",
|
61
|
+
"target_methods": {
|
62
|
+
"package": "pkg",
|
63
|
+
"method_names": [
|
64
|
+
"fn"
|
65
|
+
]
|
66
|
+
}
|
79
67
|
}
|
80
|
-
]
|
81
|
-
|
82
|
-
|
83
|
-
|
68
|
+
],
|
69
|
+
"builtin_hooks": {
|
70
|
+
"JSON::Ext::Parser": [
|
71
|
+
{
|
72
|
+
"package": "json",
|
73
|
+
"method_names": [
|
74
|
+
"parse"
|
75
|
+
]
|
76
|
+
}
|
77
|
+
],
|
78
|
+
"JSON::Ext::Generator::State": [
|
79
|
+
{
|
80
|
+
"package": "json",
|
81
|
+
"method_names": [
|
82
|
+
"generate"
|
83
|
+
]
|
84
|
+
}
|
85
|
+
],
|
86
|
+
"Net::HTTP": [
|
87
|
+
{
|
88
|
+
"package": "net/http",
|
89
|
+
"method_names": [
|
90
|
+
"request"
|
91
|
+
]
|
92
|
+
}
|
93
|
+
],
|
94
|
+
"OpenSSL::PKey::PKey": [
|
95
|
+
{
|
96
|
+
"package": "openssl",
|
97
|
+
"method_names": [
|
98
|
+
"sign"
|
99
|
+
]
|
100
|
+
}
|
101
|
+
],
|
102
|
+
"OpenSSL::X509::Request": [
|
103
|
+
{
|
104
|
+
"package": "openssl",
|
105
|
+
"method_names": [
|
106
|
+
"sign"
|
107
|
+
]
|
108
|
+
},
|
109
|
+
{
|
110
|
+
"package": "openssl",
|
111
|
+
"method_names": [
|
112
|
+
"verify"
|
113
|
+
]
|
114
|
+
}
|
115
|
+
],
|
116
|
+
"OpenSSL::X509::Certificate": [
|
117
|
+
{
|
118
|
+
"package": "openssl",
|
119
|
+
"method_names": [
|
120
|
+
"sign"
|
121
|
+
]
|
122
|
+
}
|
123
|
+
],
|
124
|
+
"OpenSSL::PKCS5": [
|
125
|
+
{
|
126
|
+
"package": "openssl",
|
127
|
+
"method_names": [
|
128
|
+
"pbkdf2_hmac"
|
129
|
+
]
|
130
|
+
},
|
131
|
+
{
|
132
|
+
"package": "openssl",
|
133
|
+
"method_names": [
|
134
|
+
"pbkdf2_hmac_sha1"
|
135
|
+
]
|
136
|
+
}
|
137
|
+
],
|
138
|
+
"OpenSSL::Cipher": [
|
139
|
+
{
|
140
|
+
"package": "openssl",
|
141
|
+
"method_names": [
|
142
|
+
"encrypt"
|
143
|
+
]
|
144
|
+
},
|
145
|
+
{
|
146
|
+
"package": "openssl",
|
147
|
+
"method_names": [
|
148
|
+
"decrypt"
|
149
|
+
]
|
150
|
+
}
|
151
|
+
],
|
152
|
+
"Psych": [
|
153
|
+
{
|
154
|
+
"package": "yaml",
|
155
|
+
"method_names": [
|
156
|
+
"load"
|
157
|
+
]
|
158
|
+
},
|
159
|
+
{
|
160
|
+
"package": "yaml",
|
161
|
+
"method_names": [
|
162
|
+
"load_stream"
|
163
|
+
]
|
164
|
+
},
|
165
|
+
{
|
166
|
+
"package": "yaml",
|
167
|
+
"method_names": [
|
168
|
+
"parse"
|
169
|
+
]
|
170
|
+
},
|
171
|
+
{
|
172
|
+
"package": "yaml",
|
173
|
+
"method_names": [
|
174
|
+
"parse_stream"
|
175
|
+
]
|
176
|
+
},
|
177
|
+
{
|
178
|
+
"package": "yaml",
|
179
|
+
"method_names": [
|
180
|
+
"dump"
|
181
|
+
]
|
182
|
+
},
|
183
|
+
{
|
184
|
+
"package": "yaml",
|
185
|
+
"method_names": [
|
186
|
+
"dump_stream"
|
187
|
+
]
|
188
|
+
}
|
189
|
+
]
|
190
|
+
},
|
191
|
+
"gem_hooks": {
|
192
|
+
"cls": [
|
193
|
+
{
|
194
|
+
"package": "pkg",
|
195
|
+
"method_names": [
|
196
|
+
"fn"
|
197
|
+
]
|
198
|
+
}
|
199
|
+
],
|
200
|
+
"ActiveSupport::Callbacks::CallbackSequence": [
|
201
|
+
{
|
202
|
+
"package": "activesupport",
|
203
|
+
"method_names": [
|
204
|
+
"invoke_before"
|
205
|
+
]
|
206
|
+
},
|
207
|
+
{
|
208
|
+
"package": "activesupport",
|
209
|
+
"method_names": [
|
210
|
+
"invoke_after"
|
211
|
+
]
|
212
|
+
}
|
213
|
+
],
|
214
|
+
"ActiveSupport::SecurityUtils": [
|
215
|
+
{
|
216
|
+
"package": "activesupport",
|
217
|
+
"method_names": [
|
218
|
+
"secure_compare"
|
219
|
+
]
|
220
|
+
}
|
221
|
+
]
|
222
|
+
}
|
223
|
+
}
|
224
|
+
FIXTURE
|
84
225
|
end
|
85
226
|
|
86
227
|
context do
|
@@ -94,7 +235,8 @@ describe AppMap::Config, docker: false do
|
|
94
235
|
expect(config.to_h).to eq(YAML.load(<<~CONFIG))
|
95
236
|
:name: appmap-ruby
|
96
237
|
:packages:
|
97
|
-
- :
|
238
|
+
- :name: lib
|
239
|
+
:path: lib
|
98
240
|
:handler_class: AppMap::Handler::Function
|
99
241
|
:shallow: false
|
100
242
|
:functions: []
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.68.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gilpin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -3405,6 +3405,10 @@ files:
|
|
3405
3405
|
- ext/appmap/extconf.rb
|
3406
3406
|
- lib/appmap.rb
|
3407
3407
|
- lib/appmap/agent.rb
|
3408
|
+
- lib/appmap/builtin_hooks/json.yml
|
3409
|
+
- lib/appmap/builtin_hooks/net/http.yml
|
3410
|
+
- lib/appmap/builtin_hooks/openssl.yml
|
3411
|
+
- lib/appmap/builtin_hooks/yaml.yml
|
3408
3412
|
- lib/appmap/class_map.rb
|
3409
3413
|
- lib/appmap/command/agent_setup/init.rb
|
3410
3414
|
- lib/appmap/command/agent_setup/status.rb
|
@@ -3423,6 +3427,10 @@ files:
|
|
3423
3427
|
- lib/appmap/depends/test_runner.rb
|
3424
3428
|
- lib/appmap/depends/util.rb
|
3425
3429
|
- lib/appmap/event.rb
|
3430
|
+
- lib/appmap/gem_hooks/actionpack.yml
|
3431
|
+
- lib/appmap/gem_hooks/actionview.yml
|
3432
|
+
- lib/appmap/gem_hooks/activesupport.yml
|
3433
|
+
- lib/appmap/gem_hooks/cancancan.yml
|
3426
3434
|
- lib/appmap/handler/function.rb
|
3427
3435
|
- lib/appmap/handler/net_http.rb
|
3428
3436
|
- lib/appmap/handler/rails/request_handler.rb
|