appmap 0.66.2 → 0.68.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -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/command/agent_setup/validate.rb +8 -1
- data/lib/appmap/command/inspect.rb +0 -1
- data/lib/appmap/config.rb +156 -137
- data/lib/appmap/event.rb +6 -2
- 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/handler/net_http.rb +7 -2
- data/lib/appmap/hook.rb +46 -30
- data/lib/appmap/minitest.rb +1 -0
- data/lib/appmap/rspec.rb +1 -0
- data/lib/appmap/util.rb +8 -0
- data/lib/appmap/version.rb +1 -1
- data/lib/appmap.rb +2 -2
- data/spec/config_spec.rb +224 -63
- data/spec/depends/api_spec.rb +1 -1
- data/spec/rails_spec_helper.rb +6 -1
- data/test/agent_setup_validate_test.rb +18 -10
- data/test/fixtures/mocha_mock_app/Gemfile +5 -0
- data/test/fixtures/mocha_mock_app/appmap.yml +5 -0
- data/test/fixtures/mocha_mock_app/lib/sheep.rb +5 -0
- data/test/fixtures/mocha_mock_app/test/sheep_test.rb +18 -0
- data/test/mock_compatibility_test.rb +45 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7da5837e4371cd2ce6aa6858e6ca88a50d38103657c054d0752ee7ec03d1f499
|
4
|
+
data.tar.gz: 81a5b8d75c7b4f5bad51a9f12cf3ea37ea38d1d7ca07a67073739cc96038775c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c2b4f67ecaf2f47344635cf3d16cbc8ab8821e10cd24966ff708baaa8c35c6a71bdb6a76d0eaf9415596f6d05c8c5b04c57115fede5bd6f4a0b4a1743e4ebc9
|
7
|
+
data.tar.gz: 712feeb618cea02bbaf17711a8c292f1670ef8c62974e2d3c706af88a31b27872afe139e7fc409f1da9800021035544736f2754970b4817234fabdf0190b113e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
## [0.68.1](https://github.com/applandinc/appmap-ruby/compare/v0.68.0...v0.68.1) (2021-11-12)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Support new style of `functions` syntax in appmap.yml ([dca327c](https://github.com/applandinc/appmap-ruby/commit/dca327c98db1bddf849056995541306a5fc07eea))
|
7
|
+
|
8
|
+
# [0.68.0](https://github.com/applandinc/appmap-ruby/compare/v0.67.1...v0.68.0) (2021-11-05)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Require weakref ([2f94f80](https://github.com/applandinc/appmap-ruby/commit/2f94f808bd3327aa3fc7fd8e6a3428a5da3a29bb))
|
14
|
+
|
15
|
+
|
16
|
+
### Features
|
17
|
+
|
18
|
+
* Externalize config of hooks ([8080222](https://github.com/applandinc/appmap-ruby/commit/8080222ce5b61d9824eaf20410d7b9b94b679890))
|
19
|
+
* Support loading hook config via path env vars ([4856483](https://github.com/applandinc/appmap-ruby/commit/48564837784f8b0e87c4286ad3e2f6cb2d272dcf))
|
20
|
+
|
21
|
+
## [0.67.1](https://github.com/applandinc/appmap-ruby/compare/v0.67.0...v0.67.1) (2021-11-02)
|
22
|
+
|
23
|
+
|
24
|
+
### Bug Fixes
|
25
|
+
|
26
|
+
* Don't try to index AppMaps when inspecting ([ca18861](https://github.com/applandinc/appmap-ruby/commit/ca188619bd7085caa75a06eeeb5d5a92213251ac))
|
27
|
+
|
28
|
+
# [0.67.0](https://github.com/applandinc/appmap-ruby/compare/v0.66.2...v0.67.0) (2021-10-21)
|
29
|
+
|
30
|
+
|
31
|
+
### Bug Fixes
|
32
|
+
|
33
|
+
* Ensure rack is available, and handle nil HTTP response ([5e81dc4](https://github.com/applandinc/appmap-ruby/commit/5e81dc4310c9b7f2d81c31339f8490639c845f76))
|
34
|
+
* Handle WeakRef ([852ee04](https://github.com/applandinc/appmap-ruby/commit/852ee047880f9d1492be38772ed3f0cc1a670cb5))
|
35
|
+
|
36
|
+
|
37
|
+
### Features
|
38
|
+
|
39
|
+
* APPMAP_AUTOREQUIRE and APPMAP_INITIALIZE env vars to customize loading behavior ([369807e](https://github.com/applandinc/appmap-ruby/commit/369807e4c90243e296b324e70805bd09d0f5fc4a))
|
40
|
+
* Perform GC before running each test ([84c895e](https://github.com/applandinc/appmap-ruby/commit/84c895e95fe8caa270cc1412e239599bfcc1b467))
|
41
|
+
|
1
42
|
## [0.66.2](https://github.com/applandinc/appmap-ruby/compare/v0.66.1...v0.66.2) (2021-10-07)
|
2
43
|
|
3
44
|
|
@@ -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
|
@@ -10,7 +10,14 @@ module AppMap
|
|
10
10
|
|
11
11
|
class Validate < ValidateStruct
|
12
12
|
def perform
|
13
|
-
|
13
|
+
schema_path = File.expand_path('../../../../../config-schema.yml', __FILE__)
|
14
|
+
schema = YAML.safe_load(File.read(schema_path))
|
15
|
+
result = {
|
16
|
+
version: 2,
|
17
|
+
errors: config_validator.valid? ? [] : config_validator.violations.map(&:to_h),
|
18
|
+
schema: schema
|
19
|
+
}
|
20
|
+
puts JSON.pretty_generate(result)
|
14
21
|
end
|
15
22
|
|
16
23
|
private
|
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,24 +358,15 @@ 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
|
-
end
|
369
|
+
end.flatten
|
355
370
|
end
|
356
371
|
|
357
372
|
config_params[:packages] = \
|
@@ -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
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'weakref'
|
4
|
+
|
3
5
|
module AppMap
|
4
6
|
module Event
|
5
7
|
@@id_counter = 0
|
@@ -111,10 +113,12 @@ module AppMap
|
|
111
113
|
rescue NoMethodError
|
112
114
|
begin
|
113
115
|
value.inspect
|
114
|
-
rescue
|
116
|
+
rescue
|
115
117
|
last_resort_string.call
|
116
118
|
end
|
117
|
-
rescue
|
119
|
+
rescue WeakRef::RefError
|
120
|
+
nil
|
121
|
+
rescue
|
118
122
|
last_resort_string.call
|
119
123
|
end
|
120
124
|
end
|
@@ -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
|