Skip to content

Commit c020a31

Browse files
committed
feat: Profile packaging hooking
With APPMAP_PROFILE_HOOK=true, hook.rb will periodically print a report of how much time has been spent hooking each package (above a threshold).
1 parent c9b1773 commit c020a31

File tree

1 file changed

+28
-0
lines changed

1 file changed

+28
-0
lines changed

lib/appmap/hook.rb

+28
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Hook
99

1010
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
1111
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
12+
SLOW_PACKAGE_THRESHOLD = 0.05
1213

1314
@unbound_method_arity = ::UnboundMethod.instance_method(:arity)
1415
@method_arity = ::Method.instance_method(:arity)
@@ -49,6 +50,27 @@ def enable(&block)
4950
@notrace_paths = Set.new
5051
# Locations that have already been visited.
5152
@trace_locations = Set.new
53+
@module_load_times = Hash.new {|memo,k| memo[k] = 0}
54+
@slow_packages = Set.new
55+
56+
if ENV['APPMAP_PROFILE_HOOK'] == 'true'
57+
Thread.new do
58+
sleep 1
59+
while true
60+
@module_load_times
61+
.keys
62+
.select { |key| !@slow_packages.member?(key) }
63+
.each do |key|
64+
elapsed = @module_load_times[key]
65+
if elapsed >= SLOW_PACKAGE_THRESHOLD
66+
@slow_packages.add(key)
67+
warn "AppMap: Package #{key} took #{@module_load_times[key]} seconds to hook"
68+
end
69+
end
70+
sleep 5
71+
end
72+
end
73+
end
5274

5375
@trace_end = TracePoint.new(:end, &method(:trace_end))
5476
@trace_end.enable(&block)
@@ -157,6 +179,7 @@ def trace_end(trace_point)
157179
end
158180
end
159181

182+
start = Time.now
160183
instance_methods.each(&hook.(cls))
161184
begin
162185
# NoMethodError: private method `singleton_class' called for Rack::MiniProfiler:Class
@@ -166,6 +189,11 @@ def trace_end(trace_point)
166189
# uninitialized constant Faraday::Connection
167190
warn "NameError in #{__FILE__}: #{$!.message}"
168191
end
192+
elapsed = Time.now - start
193+
if location.index(Bundler.bundle_path.to_s) == 0
194+
package_tokens = location[Bundler.bundle_path.to_s.length + 1..-1].split('/')
195+
@module_load_times[package_tokens[1]] += elapsed
196+
end
169197
end
170198
end
171199
end

0 commit comments

Comments
 (0)