Skip to content

Commit c93d91c

Browse files
committed
feat: Enhance the hook log a lot
1 parent 2a8b2ed commit c93d91c

File tree

3 files changed

+90
-63
lines changed

3 files changed

+90
-63
lines changed

lib/appmap/config.rb

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require 'appmap/service/guesser'
99
require 'appmap/swagger/configuration'
1010
require 'appmap/depends/configuration'
11+
require_relative './hook_log'
1112

1213
module AppMap
1314
class Config
@@ -499,7 +500,11 @@ def lookup_package(cls, method)
499500

500501
def never_hook?(cls, method)
501502
_, separator, = ::AppMap::Hook.qualify_method_name(method)
502-
return true if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join)
503+
if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join)
504+
HookLog.log "Hooking of #{method} disabled by configuration" if HookLog.enabled?
505+
return true
506+
end
507+
false
503508
end
504509
end
505510
end

lib/appmap/hook.rb

+45-46
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def hook_builtins?
1919
Mutex.new.synchronize do
2020
@hook_builtins = true if @hook_builtins.nil?
2121

22-
return false unless @hook_builtins
22+
next false unless @hook_builtins
2323

2424
@hook_builtins = false
2525
true
@@ -101,48 +101,52 @@ def hook_builtins
101101
hook_loaded_code = lambda do |hooks_by_class, builtin|
102102
hooks_by_class.each do |class_name, hooks|
103103
Array(hooks).each do |hook|
104-
if builtin && hook.package.require_name && hook.package.require_name != 'ruby'
105-
require hook.package.require_name
106-
end
107-
108-
Array(hook.method_names).each do |method_name|
109-
method_name = method_name.to_sym
110-
104+
HookLog.builtin class_name do
105+
if builtin && hook.package.require_name && hook.package.require_name != 'ruby'
106+
begin
107+
require hook.package.require_name
108+
rescue
109+
HookLog.load_error hook.package.require_name, "Unable to require #{hook.package.require_name}: #{$!}" if HookLog.enabled?
110+
next
111+
end
112+
end
113+
111114
begin
112115
base_cls = Object.const_get class_name
113116
rescue NameError
117+
HookLog.load_error class_name, "Class #{class_name} not found in global scope" if HookLog.enabled?
114118
next
115119
end
116-
117-
HookLog.builtin_begin class_name, method_name if HookLog.enabled?
118-
119-
hook_method = lambda do |entry|
120-
cls, method = entry
121-
next if config.never_hook?(cls, method)
122-
123-
hook.package.handler_class.new(hook.package, cls, method).activate
124-
end
125-
126-
methods = []
127-
# irb(main):001:0> Kernel.public_instance_method(:system)
128-
# (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
129-
if base_cls == Kernel
130-
methods << [base_cls, base_cls.instance_method(method_name)] rescue nil
131-
end
132-
methods << [base_cls, base_cls.public_instance_method(method_name)] rescue nil
133-
methods << [base_cls, base_cls.protected_instance_method(method_name)] rescue nil
134-
if base_cls.respond_to?(:singleton_class)
135-
methods << [base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name)] rescue nil
136-
methods << [base_cls.singleton_class, base_cls.singleton_class.protected_instance_method(method_name)] rescue nil
137-
end
138-
methods.compact!
139-
if methods.empty?
140-
HookLog.log "Method #{method_name} not found on #{base_cls.name}" if HookLog.enabled?
141-
else
142-
methods.each(&hook_method)
120+
121+
Array(hook.method_names).each do |method_name|
122+
method_name = method_name.to_sym
123+
124+
hook_method = lambda do |entry|
125+
cls, method = entry
126+
next if config.never_hook?(cls, method)
127+
128+
hook.package.handler_class.new(hook.package, cls, method).activate
129+
end
130+
131+
methods = []
132+
# irb(main):001:0> Kernel.public_instance_method(:system)
133+
# (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
134+
if base_cls == Kernel
135+
methods << [base_cls, base_cls.instance_method(method_name)] rescue nil
136+
end
137+
methods << [base_cls, base_cls.public_instance_method(method_name)] rescue nil
138+
methods << [base_cls, base_cls.protected_instance_method(method_name)] rescue nil
139+
if base_cls.respond_to?(:singleton_class)
140+
methods << [base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name)] rescue nil
141+
methods << [base_cls.singleton_class, base_cls.singleton_class.protected_instance_method(method_name)] rescue nil
142+
end
143+
methods.compact!
144+
if methods.empty?
145+
HookLog.load_error [ base_cls.name, method_name ].join('[#.]'), "Method #{method_name} not found on #{base_cls.name}" if HookLog.enabled?
146+
else
147+
methods.each(&hook_method)
148+
end
143149
end
144-
145-
HookLog.builtin_end class_name, method_name if HookLog.enabled?
146150
end
147151
end
148152
end
@@ -159,17 +163,14 @@ def trace_location(trace_point)
159163

160164
def trace_end(trace_point)
161165
location = trace_location(trace_point)
162-
begin
163-
HookLog.usercode_begin location if HookLog.enabled?
164-
165-
return unless @trace_locations.add?(location)
166-
166+
return unless @trace_locations.add?(location)
167+
HookLog.on_load location do
167168
path = trace_point.path
168169
enabled = !@notrace_paths.member?(path) && config.path_enabled?(path)
169170
unless enabled
170171
HookLog.log 'Not hooking - path is not enabled' if HookLog.enabled?
171172
@notrace_paths << path
172-
return
173+
next
173174
end
174175

175176
cls = trace_point.self
@@ -191,7 +192,7 @@ def trace_end(trace_point)
191192
method = begin
192193
hook_cls.instance_method(method_id)
193194
rescue NameError
194-
HookLog.log "Method #{hook_cls} #{fn} is not accessible: #{$!}" if HookLog.enabled?
195+
HookLog.load_error [ hook_cls, method_id ].join('[#.]'), "Method #{hook_cls} #{method_id} is not accessible: #{$!}" if HookLog.enabled?
195196
next
196197
end
197198

@@ -239,8 +240,6 @@ def trace_end(trace_point)
239240
@module_load_times[location] += elapsed
240241
end
241242
end
242-
ensure
243-
HookLog.usercode_end(location) if HookLog.enabled?
244243
end
245244
end
246245
end

lib/appmap/hook_log.rb

+39-16
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,62 @@ def initialize
1919
end
2020

2121
def start_time(timer)
22-
@elapsed[timer] << [Time.now.to_f]
22+
@elapsed[timer] << [Util.gettime]
2323
end
2424

2525
def end_time(timer)
26-
@elapsed[timer][-1] = Time.now.to_f - @elapsed[timer].last[0]
26+
unless @elapsed[timer].last.is_a?(Array)
27+
warn "AppMap: Unbalanced timing data in hook log"
28+
@elapsed[timer].pop
29+
return
30+
end
31+
32+
@elapsed[timer][-1] = Util.gettime - @elapsed[timer].last[0]
2733
end
2834

2935
class << self
3036
def enabled?
3137
LOG || LOG_HOOK
3238
end
3339

34-
def builtin_begin(class_name, method_name)
35-
log "builtin\tbegin\tInitiating hook for builtin #{class_name} #{method_name}"
36-
@hook_log.start_time :builtin
37-
end
40+
def builtin(class_name, &block)
41+
return yield unless enabled?
3842

39-
def builtin_end(class_name, method_name)
40-
@hook_log.end_time :builtin
41-
log "builtin\tend\tCompleted hook for builtin #{class_name} #{method_name}"
43+
begin
44+
log "eager\tbegin\tInitiating eager hook for #{class_name}"
45+
@hook_log.start_time :eager
46+
47+
yield
48+
ensure
49+
@hook_log.end_time :eager
50+
log "eager\tend\tCompleted eager hook for #{class_name}"
51+
end
4252
end
4353

44-
def usercode_begin(location)
45-
log "usercode\tbegin\tClass or module defined at location #{location}"
46-
@hook_log.start_time :usercode
54+
def on_load(location, &block)
55+
return yield unless enabled?
56+
57+
begin
58+
log "on-load\tbegin\tInitiating on-load hook for class or module defined at location #{location}"
59+
@hook_log.start_time :on_load
60+
61+
yield
62+
ensure
63+
@hook_log.end_time :on_load
64+
log "on-load\tend\tCompleted on-load hook for location #{location}"
65+
end
4766
end
4867

49-
def usercode_end(location)
50-
@hook_log.end_time :usercode
51-
log "usercode\tend\tCompleted location #{location}"
68+
def load_error(name, msg)
69+
log "load_error\t#{name}\t#{msg}"
5270
end
5371

5472
def log(msg)
73+
unless HookLog.enabled?
74+
warn "AppMap: HookLog is not enabled. Disregarding message #{msg}"
75+
return
76+
end
77+
5578
@hook_log ||= HookLog.new
5679
@hook_log.log msg
5780
end
@@ -69,7 +92,7 @@ def log(msg)
6992
if LOG_HOOK_FILE == 'stderr'
7093
msg = "AppMap: #{msg}"
7194
end
72-
msg = "#{Time.new.to_f}\t#{msg}"
95+
msg = "#{Util.gettime}\t#{msg}"
7396
@file_handle.puts(msg)
7497
end
7598
end

0 commit comments

Comments
 (0)