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['APPMAP_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
LOG_HOOK =
(ENV['DEBUG_HOOK'] == 'true')
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
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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Hook

Returns a new instance of Hook.



37
38
39
40
41
42
# File 'lib/appmap/hook.rb', line 37

def initialize(config)
  @config = config
  @trace_enabled = []
  # Paths that are known to be non-tracing
  @notrace_paths = Set.new
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



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

def config
  @config
end

Class Method Details

.lock_builtinsObject



17
18
19
20
21
# File 'lib/appmap/hook.rb', line 17

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.



25
26
27
28
29
30
31
32
# File 'lib/appmap/hook.rb', line 25

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



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'ext/appmap/appmap.c', line 15

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);
  }

  // Did __attached__.class return an object that's a Module or a
  // Class?
  if (CLASS_OR_MODULE_P(attached)) {
    // Yup, get it's name
    return rb_mod_name(attached);
  }

  // Nope (which seems weird, but whatever). Fall back to calling
  // #to_s on the method's owner and hope for the best.
  return rb_funcall(owner, rb_intern("to_s"), 0);
}

Instance Method Details

#enable(&block) ⇒ Object

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



45
46
47
48
49
50
51
52
# File 'lib/appmap/hook.rb', line 45

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

  hook_builtins

  @trace_end = TracePoint.new(:end, &method(:trace_end))
  @trace_end.enable(&block)
end

#hook_builtinsObject

hook_builtins builds hooks for code that is built in to the Ruby standard library. No TracePoint events are emitted for builtins, so a separate hooking mechanism is needed.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/appmap/hook.rb', line 56

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_hooks.each do |class_name, hooks|
    Array(hooks).each do |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
        base_cls = class_from_string.(class_name)

        hook_method = lambda do |entry|
          cls, method = entry
          return false if config.never_hook?(cls, method)

          Hook::Method.new(hook.package, cls, method).activate
        end

        methods = []
        methods << [ base_cls, base_cls.public_instance_method(method_name) ] rescue nil
        if base_cls.respond_to?(:singleton_class)
          methods << [ base_cls.singleton_class, base_cls.singleton_class.public_instance_method(method_name) ] rescue nil
        end
        methods.compact!
        if methods.empty?
          warn "Method #{method_name} not found on #{base_cls.name}"
        else
          methods.each(&hook_method)
        end
      end
    end
  end
end