lib/sqreen/instrumentation.rb in sqreen-alt-1.10.3 vs lib/sqreen/instrumentation.rb in sqreen-alt-1.10.4

- old
+ new

@@ -174,14 +174,33 @@ end returns end end + def self.guard_multi_call(instance, method, original_method, args, block) + @sqreen_multi_instr ||= nil + key = [method] + Instrumentation.guard_call(nil, :guard_multi_call) do + args.each{|e| key.push(e.object_id)} + end + if key && @sqreen_multi_instr && @sqreen_multi_instr[instance.object_id].member?(key) + return instance.send(original_method, *args, &block) + end + @sqreen_multi_instr ||= Hash.new {|h, k| h[k]=Set.new } # TODO this should probably be a thread local + @sqreen_multi_instr[instance.object_id].add(key) + r = yield + return r + ensure + if @sqreen_multi_instr && @sqreen_multi_instr[instance.object_id] && @sqreen_multi_instr[instance.object_id].delete(key).empty? + @sqreen_multi_instr.delete(instance.object_id) + end + end + def self.guard_call(method, retval) @sqreen_in_instr ||= nil return retval if @sqreen_in_instr && @sqreen_in_instr.member?(method) - @sqreen_in_instr ||= Set.new + @sqreen_in_instr ||= Set.new # TODO this should probably be a thread local @sqreen_in_instr.add(method) r = yield @sqreen_in_instr.delete(method) return r rescue Exception => e @@ -195,10 +214,11 @@ Sqreen.log.debug do "Instrumented #{Instrumentation.instrumented_pid} != PID #{Process.pid}" end return send(original_meth, *args, &block) end + Instrumentation.guard_multi_call(self, meth, original_meth, args, block) do Sqreen.stats.callbacks_calls += 1 skip = false result = nil @@ -269,10 +289,11 @@ end end result end end + end end def override_class_method(klass, meth) # FIXME: This is somehow ugly. We should reduce the amount of # `evaled` code. @@ -323,12 +344,12 @@ alias_method meth, saved_meth_name send(method_kind, meth) end end - def get_saved_method_name(meth) - "#{meth}_not_modified".to_sym + def get_saved_method_name(meth, suffix=nil) + "#{meth}_sq#{suffix}_not_modified".to_sym end def override_instance_method(klass_name, meth) saved_meth_name = get_saved_method_name(meth) new_method = "#{meth}_modified".to_sym @@ -401,10 +422,34 @@ return true if is_class_method?(obj, method) return false unless obj.respond_to?(:instance_methods) is_instance_method?(obj, method) end + # Override a singleton method on an instance + def override_singleton_method(instance, klass_name, meth) + saved_meth_name = get_saved_method_name(meth, 'singleton') + if instance.respond_to?(saved_meth_name, true) + Sqreen.log.debug { "#{saved_meth_name} found #{instance.class}##{instance.object_id} already instrumented" } + return nil + elsif instance.frozen? + Sqreen.log.debug { "#{instance.class}##{instance.object_id} is frozen, not reinstrumenting" } + return nil + end + raise Sqreen::NotImplementedYet, "#{instance.inspect} doesn't respond to define_singleton_method" unless instance.respond_to?(:define_singleton_method) + p = Instrumentation.define_callback_method(meth, saved_meth_name, + klass_name) + instance.define_singleton_method(saved_meth_name, instance.method(meth)) + instance.define_singleton_method(meth, p) + # Hide saved method (its only available in this syntax) + eval <<-RUBY, binding, __FILE__, __LINE__ + 1 + class << instance + private :#{saved_meth_name} + end + saved_meth_name + RUBY + end + def add_callback(cb) @@override_semaphore.synchronize do klass = cb.klass method = cb.method key = [klass, method] @@ -425,16 +470,28 @@ # - define_method # - method_added # - method_missing # ... # - msg = "#{cb} is neither singleton or instance" + msg = "#{cb} is neither class or instance" raise Sqreen::NotImplementedYet, msg end @@overriden_methods += [key] if success else Sqreen.log.debug "#{key} was already overriden" + end + + if klass != Object && klass != Kernel && !Sqreen.features['instrument_all_instances'] && !defined?(::JRUBY_VERSION) + insts = 0 + ObjectSpace.each_object(klass) do |e| + next if e.is_a?(Class) || e.is_a?(Module) + next unless e.singleton_methods.include?(method.to_sym) + insts += 1 if override_singleton_method(e, klass, method) + end + if insts > 0 + Sqreen.log.debug { "Reinstrumented #{insts} instances of #{klass}" } + end end @@registered_callbacks.add(cb) @@instrumented_pid = Process.pid end