Class: AppMap::Hook
- Inherits:
-
Object
- Object
- AppMap::Hook
- 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')
- 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
-
#config ⇒ Object
readonly
Returns the value of attribute config.
Class Method Summary collapse
- .lock_builtins ⇒ Object
-
.qualify_method_name(method) ⇒ Object
Return the class, separator (‘.’ or ‘#’), and method name for the given method.
- .singleton_method_owner_name(method) ⇒ Object
Instance Method Summary collapse
-
#enable(&block) ⇒ Object
Observe class loading and hook all methods which match the config.
- #hook_builtins ⇒ Object
-
#initialize(config) ⇒ Hook
constructor
A new instance of Hook.
Constructor Details
#initialize(config) ⇒ Hook
Returns a new instance of Hook.
35 36 37 |
# File 'lib/appmap/hook.rb', line 35 def initialize(config) @config = config end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
34 35 36 |
# File 'lib/appmap/hook.rb', line 34 def config @config end |
Class Method Details
.lock_builtins ⇒ Object
16 17 18 19 20 |
# File 'lib/appmap/hook.rb', line 16 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.
24 25 26 27 28 29 30 31 |
# File 'lib/appmap/hook.rb', line 24 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.
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 79 80 81 82 83 84 85 |
# File 'lib/appmap/hook.rb', line 40 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) - OBJECT_INSTANCE_METHODS class_methods = cls.singleton_class.public_instance_methods(false) - instance_methods - OBJECT_STATIC_METHODS hook = lambda do |hook_cls| lambda do |method_id| method = begin hook_cls.public_instance_method(method_id) rescue NameError warn "AppMap: Method #{hook_cls} #{method.name} is not accessible" if LOG return end 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 next unless \ config.always_hook?(hook_cls, method.name) || config.included_by_location?(method) hook_method = Hook::Method.new(config.package_for_method(method), hook_cls, method) # 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) hook_method.activate end end instance_methods.each(&hook.(cls)) class_methods.each(&hook.(cls.singleton_class)) end tp.enable(&block) end |
#hook_builtins ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/appmap/hook.rb', line 87 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(hook.package, cls, method).activate else warn "Method #{method_name} not found on #{cls.name}" end end end end |