Module: TraceView::API::Profiling

Defined in:
lib/traceview/api/profiling.rb

Overview

Module that provides profiling of arbitrary blocks of code

Instance Method Summary collapse

Instance Method Details

#profile(profile_name, report_kvs = {}, with_backtrace = false) ⇒ Object

Public: Profile a given block of code. Detect any exceptions thrown by the block and report errors.

profile_name - A name used to identify the block being profiled. report_kvs - A hash containing key/value pairs that will be reported along

with the event of this profile (optional).

with_backtrace - Boolean to indicate whether a backtrace should

be collected with this trace event.

Example

def computation(n)
  TraceView::API.profile('fib', { :n => n }) do
    fib(n)
  end
end

Returns the result of the block.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/traceview/api/profiling.rb', line 28

def profile(profile_name, report_kvs = {}, with_backtrace = false)
  report_kvs[:Language] ||= :ruby
  report_kvs[:ProfileName] ||= profile_name
  report_kvs[:Backtrace] = TraceView::API.backtrace if with_backtrace

  TraceView::API.log(nil, :profile_entry, report_kvs)

  begin
    yield
  rescue => e
    log_exception(nil, e)
    raise
  ensure
    exit_kvs = {}
    exit_kvs[:Language] = :ruby
    exit_kvs[:ProfileName] = report_kvs[:ProfileName]

    TraceView::API.log(nil, :profile_exit, exit_kvs)
  end
end

#profile_method(klass, method, opts = {}, extra_kvs = {}) ⇒ Object

Public: Profile a method on a class or module. That method can be of any (accessible) type (instance, singleton, private, protected etc.).

klass - the class or module that has the method to profile method - the method to profile. Can be singleton, instance, private etc… opts - a hash specifying the one or more of the following options:

* :arguments  - report the arguments passed to <tt>method</tt> on each profile (default: false)
* :result     - report the return value of <tt>method</tt> on each profile (default: false)
* :backtrace  - report the return value of <tt>method</tt> on each profile (default: false)
* :name       - alternate name for the profile reported in the dashboard (default: method name)

extra_kvs - a hash containing any additional KVs you would like reported with the profile

Example

opts = {}
opts[:backtrace] = true
opts[:arguments] = false
opts[:name] = :array_sort

TraceView::API.profile_method(Array, :sort, opts)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/traceview/api/profiling.rb', line 71

def profile_method(klass, method, opts = {}, extra_kvs = {})
  # If we're on an unsupported platform (ahem Mac), just act
  # like we did something to nicely play the no-op part.
  return true unless TraceView.loaded

  if RUBY_VERSION < '1.9.3'
    TraceView.logger.warn '[traceview/error] profile_method: Use the legacy method profiling for Ruby versions before 1.9.3'
    return false

  elsif !klass.is_a?(Module)
    TraceView.logger.warn "[traceview/error] profile_method: Not sure what to do with #{klass}.  Send a class or module."
    return false

  elsif !method.is_a?(Symbol)
    if method.is_a?(String)
      method = method.to_sym
    else
      TraceView.logger.warn "[traceview/error] profile_method: Not sure what to do with #{method}.  Send a string or symbol for method."
      return false
    end
  end

  instance_method = klass.instance_methods.include?(method) || klass.private_instance_methods.include?(method)
  class_method = klass.singleton_methods.include?(method)

  # Make sure the request klass::method exists
  if !instance_method && !class_method
    TraceView.logger.warn "[traceview/error] profile_method: Can't instrument #{klass}.#{method} as it doesn't seem to exist."
    TraceView.logger.warn "[traceview/error] #{__FILE__}:#{__LINE__}"
    return false
  end

  # Strip '!' or '?' from method if present
  safe_method_name = method.to_s.chop if method.to_s =~ /\?$|\!$/
  safe_method_name ||= method

  without_traceview = "#{safe_method_name}_without_traceview"
  with_traceview    = "#{safe_method_name}_with_traceview"

  # Check if already profiled
  if klass.instance_methods.include?(with_traceview.to_sym) ||
     klass.singleton_methods.include?(with_traceview.to_sym)
    TraceView.logger.warn "[traceview/error] profile_method: #{klass}::#{method} already profiled."
    TraceView.logger.warn "[traceview/error] profile_method: #{__FILE__}:#{__LINE__}"
    return false
  end

  source_location = []
  if instance_method
    ::TraceView::Util.send_include(klass, ::TraceView::MethodProfiling)
    source_location = klass.instance_method(method).source_location
  elsif class_method
    ::TraceView::Util.send_extend(klass, ::TraceView::MethodProfiling)
    source_location = klass.method(method).source_location
  end

  report_kvs = collect_profile_kvs(klass, method, opts, extra_kvs, source_location)
  report_kvs[:MethodName] = safe_method_name

  if instance_method
    klass.class_eval do
      define_method(with_traceview) do |*args, &block|
        profile_wrapper(without_traceview, report_kvs, opts, *args, &block)
      end

      alias_method without_traceview, method.to_s
      alias_method method.to_s, with_traceview
    end
  elsif class_method
    klass.define_singleton_method(with_traceview) do |*args, &block|
      profile_wrapper(without_traceview, report_kvs, opts, *args, &block)
    end

    klass.singleton_class.class_eval do
      alias_method without_traceview, method.to_s
      alias_method method.to_s, with_traceview
    end
  end
  true
end