Class: AppMap::Hook

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

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.



29
30
31
# File 'lib/appmap/hook.rb', line 29

def initialize(config)
  @config = config
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



28
29
30
# File 'lib/appmap/hook.rb', line 28

def config
  @config
end

Class Method Details

.lock_builtinsObject



10
11
12
13
14
# File 'lib/appmap/hook.rb', line 10

def lock_builtins
  return if @builtins_hooked

  @builtins_hooked = true
end

.qualify_method_name(method) ⇒ Object

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



18
19
20
21
22
23
24
25
# File 'lib/appmap/hook.rb', line 18

def qualify_method_name(method)
  if method.owner.singleton_class?
    class_name = singleton_method_owner_name(method)
    [ class_name, '.', method.name ]
  else
    [ method.owner.name, '#', method.name ]
  end
end

.singleton_method_owner_name(method) ⇒ Object



11
12
13
14
15
16
17
18
19
# File 'ext/appmap/appmap.c', line 11

static VALUE singleton_method_owner_name(VALUE klass, VALUE method)
{
  VALUE owner = rb_funcall(method, rb_intern("owner"), 0);
  VALUE attached = rb_ivar_get(owner, rb_intern("__attached__"));
  if (!CLASS_OR_MODULE_P(attached)) {
    attached = rb_funcall(attached, rb_intern("class"), 0);
  }
  return rb_mod_name(attached);
}

Instance Method Details

#enable(&block) ⇒ Object

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



34
35
36
37
38
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
# File 'lib/appmap/hook.rb', line 34

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

  hook_builtins

  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|
        method = hook_cls.public_instance_method(method_id)
        hook_method = Hook::Method.new(hook_cls, method)

        warn "AppMap: Examining #{hook_cls} #{method.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_cls, 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

#hook_builtinsObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/appmap/hook.rb', line 75

def hook_builtins
  return unless self.class.lock_builtins

  class_from_string = lambda do |fq_class|
    fq_class.split('::').inject(Object) do |mod, class_name|
      mod.const_get(class_name)
    end
  end

  Config::BUILTIN_METHODS.each do |class_name, hook|
    require hook.package.package_name if hook.package.package_name
    Array(hook.method_names).each do |method_name|
      method_name = method_name.to_sym
      cls = class_from_string.(class_name)
      method = \
        begin
          cls.instance_method(method_name)
        rescue NameError
          cls.method(method_name) rescue nil
        end

      if method
        Hook::Method.new(cls, method).activate
      else
        warn "Method #{method_name} not found on #{cls.name}" 
      end
    end
  end
end