# Copyright (c) 2013 AppNeta, Inc. # All rights reserved. ## # Provides the methods necessary for method profiling. Profiling # results are sent to the TraceView dashboard. # # Example usage: # class MyApp # include OboeMethodProfiling # # def process_request() # # The hard work # end # # # call syntax: profile_method , # profile_method :process_request, 'request_processor' # end module OboeMethodProfiling def self.included(klass) klass.extend ClassMethods end module ClassMethods def profile_method(method_name, profile_name, store_args = false, store_return = false, *_) begin # this only gets file and line where profiling is turned on, presumably # right after the function definition. ruby 1.9 and 2.0 has nice introspection (Method.source_location) # but its appears no such luck for ruby 1.8 file = '' line = '' if RUBY_VERSION >= '1.9' info = instance_method(method_name).source_location unless info.nil? file = info[0].to_s line = info[1].to_s end else info = Kernel.caller[0].split(':') file = info.first.to_s line = info.last.to_s end # Safety: Make sure there are no quotes or double quotes to break the class_eval file = file.gsub /[\'\"]/, '' line = line.gsub /[\'\"]/, '' # profiling via ruby-prof, is it possible to get return value of profiled code? code = "def _oboe_profiled_#{method_name}(*args, &block) entry_kvs = {} entry_kvs['Language'] = 'ruby' entry_kvs['ProfileName'] = '#{Oboe::Util.prettify(profile_name)}' entry_kvs['FunctionName'] = '#{Oboe::Util.prettify(method_name)}' entry_kvs['File'] = '#{file}' entry_kvs['LineNumber'] = '#{line}' entry_kvs['Args'] = Oboe::API.pps(*args) if #{store_args} entry_kvs.merge!(::Oboe::API.get_class_name(self)) Oboe::API.log(nil, 'profile_entry', entry_kvs) ret = _oboe_orig_#{method_name}(*args, &block) exit_kvs = {} exit_kvs['Language'] = 'ruby' exit_kvs['ProfileName'] = '#{Oboe::Util.prettify(profile_name)}' exit_kvs['ReturnValue'] = Oboe::API.pps(ret) if #{store_return} Oboe::API.log(nil, 'profile_exit', exit_kvs) ret end" rescue => e Oboe.logger.warn "[oboe/warn] profile_method: #{e.inspect}" end begin class_eval code, __FILE__, __LINE__ alias_method "_oboe_orig_#{method_name}", method_name alias_method method_name, "_oboe_profiled_#{method_name}" rescue => e Oboe.logger.warn "[oboe/warn] Fatal error profiling method (#{method_name}): #{e.inspect}" if Oboe::Config[:verbose] end end end end