Commit b74308c0 authored by Yorick Peterse's avatar Yorick Peterse

Correct arity for instrumented methods w/o args

This ensures that an instrumented method that doesn't take arguments
reports an arity of 0, instead of -1.

If Ruby had a proper method for finding out the required arguments of a
method (e.g. Method#required_arguments) this would not have been an
issue. Sadly the only two methods we have are Method#parameters and
Method#arity, and both are equally painful to use.

Fixes gitlab-org/gitlab-ce#12450
parent 8b3285bf
...@@ -106,20 +106,36 @@ module Gitlab ...@@ -106,20 +106,36 @@ module Gitlab
if type == :instance if type == :instance
target = mod target = mod
label = "#{mod.name}##{name}" label = "#{mod.name}##{name}"
method = mod.instance_method(name)
else else
target = mod.singleton_class target = mod.singleton_class
label = "#{mod.name}.#{name}" label = "#{mod.name}.#{name}"
method = mod.method(name)
end
# Some code out there (e.g. the "state_machine" Gem) checks the arity of
# a method to make sure it only passes arguments when the method expects
# any. If we were to always overwrite a method to take an `*args`
# signature this would break things. As a result we'll make sure the
# generated method _only_ accepts regular arguments if the underlying
# method also accepts them.
if method.arity == 0
args_signature = '&block'
else
args_signature = '*args, &block'
end end
send_signature = "__send__(#{alias_name.inspect}, #{args_signature})"
target.class_eval <<-EOF, __FILE__, __LINE__ + 1 target.class_eval <<-EOF, __FILE__, __LINE__ + 1
alias_method #{alias_name.inspect}, #{name.inspect} alias_method #{alias_name.inspect}, #{name.inspect}
def #{name}(*args, &block) def #{name}(#{args_signature})
trans = Gitlab::Metrics::Instrumentation.transaction trans = Gitlab::Metrics::Instrumentation.transaction
if trans if trans
start = Time.now start = Time.now
retval = __send__(#{alias_name.inspect}, *args, &block) retval = #{send_signature}
duration = (Time.now - start) * 1000.0 duration = (Time.now - start) * 1000.0
if duration >= Gitlab::Metrics.method_call_threshold if duration >= Gitlab::Metrics.method_call_threshold
...@@ -132,7 +148,7 @@ module Gitlab ...@@ -132,7 +148,7 @@ module Gitlab
retval retval
else else
__send__(#{alias_name.inspect}, *args, &block) #{send_signature}
end end
end end
EOF EOF
......
...@@ -66,6 +66,16 @@ describe Gitlab::Metrics::Instrumentation do ...@@ -66,6 +66,16 @@ describe Gitlab::Metrics::Instrumentation do
@dummy.foo @dummy.foo
end end
it 'generates a method with the correct arity when using methods without arguments' do
dummy = Class.new do
def self.test; end
end
described_class.instrument_method(dummy, :test)
expect(dummy.method(:test).arity).to eq(0)
end
end end
describe 'with metrics disabled' do describe 'with metrics disabled' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment