Class: AppMap::Hook

Inherits:
Object
  • Object
show all
Defined in:
lib/appmap/hook.rb,
lib/appmap/hook/method.rb

Defined Under Namespace

Classes: Method

Constant Summary collapse

LOG =
(ENV['DEBUG'] == 'true')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Hook

Returns a new instance of Hook.



34
35
36
# File 'lib/appmap/hook.rb', line 34

def initialize(config)
  @config = config
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



33
34
35
# File 'lib/appmap/hook.rb', line 33

def config
  @config
end

Class Method Details

.qualify_method_name(method) ⇒ Object

Return the class, separator (‘.’ or ‘#’), and method name for the given method.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/appmap/hook.rb', line 12

def qualify_method_name(method)
  if method.owner.singleton_class?
    # Singleton class names can take two forms:
    # #<Class:Foo> or
    # #<Class:#<Bar:0x0123ABC>>. Retrieve the name of
    # the class from the string.
    #
    # (There really isn't a better way to do this. The
    # singleton's reference to the class it was created
    # from is stored in an instance variable named
    # '__attached__'. It doesn't have the '@' prefix, so
    # it's internal only, and not accessible from user
    # code.)
    class_name = /#<Class:((#<(?<cls>.*?):)|((?<cls>.*?)>))/.match(method.owner.to_s)['cls']
    [ class_name, '.', method.name ]
  else
    [ method.owner.name, '#', method.name ]
  end
end

Instance Method Details

#enable(&block) ⇒ Object

Observe class loading and hook all methods which match the config.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/appmap/hook.rb', line 39

def enable &block
  require 'appmap/hook/method'

  tp = TracePoint.new(:end) do |trace_point|
    cls = trace_point.self

    instance_methods = cls.public_instance_methods(false)
    class_methods = cls.singleton_class.public_instance_methods(false) - instance_methods

    hook = lambda do |hook_cls|
      lambda do |method_id|
        next if method_id.to_s =~ /_hooked_by_appmap$/

        method = hook_cls.public_instance_method(method_id)
        hook_method = Hook::Method.new(hook_cls, method)

        warn "AppMap: Examining #{hook_method.method_display_name}" if LOG

        disasm = RubyVM::InstructionSequence.disasm(method)
        # Skip methods that have no instruction sequence, as they are obviously trivial.
        next unless disasm

        # Don't try and trace the AppMap methods or there will be
        # a stack overflow in the defined hook method.
        next if /\AAppMap[:\.]/.match?(hook_method.method_display_name)

        next unless \
          config.always_hook?(hook_method.defined_class, method.name) ||
          config.included_by_location?(method)

        hook_method.activate
      end
    end

    instance_methods.each(&hook.(cls))
    class_methods.each(&hook.(cls.singleton_class))
  end

  tp.enable(&block)
end