Class: AppMap::Trace::TracePointHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/appmap/trace/tracer.rb

Overview

Processes a series of calls into recorded events. Each call to the handle should provide a TracePoint (or duck-typed object) as the argument. On each call, a MethodEvent is constructed according to the nature of the TracePoint, and then stored using the record_event method.

Constant Summary collapse

DEFAULT_HANDLER_CLASSES =
{
  call: MethodCall,
  return: MethodReturn
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tracer) ⇒ TracePointHandler

Returns a new instance of TracePointHandler.



218
219
220
221
222
223
# File 'lib/appmap/trace/tracer.rb', line 218

def initialize(tracer)
  @pwd = Dir.pwd
  @tracer = tracer
  @call_stack = Hash.new { |h, k| h[k] = [] }
  @handler_classes = {}
end

Instance Attribute Details

#call_constructorObject

Returns the value of attribute call_constructor.



210
211
212
# File 'lib/appmap/trace/tracer.rb', line 210

def call_constructor
  @call_constructor
end

#return_constructorObject

Returns the value of attribute return_constructor.



210
211
212
# File 'lib/appmap/trace/tracer.rb', line 210

def return_constructor
  @return_constructor
end

Instance Method Details

#handle(tp) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/appmap/trace/tracer.rb', line 226

def handle(tp)
  # Absoute paths which are within the current working directory are normalized
  # to be relative paths.
  path = tp.path
  if path.index(@pwd) == 0
    path = path[@pwd.length+1..-1]
  end

  method_event = \
    if tp.event == :call && (function = @tracer.lookup_function(path, tp.lineno))
      call_constructor = handler_class(function, tp.event)
      call_constructor.build_from_tracepoint(tp, path).tap do |c|
        @call_stack[Thread.current.object_id] << [ tp.defined_class, tp.method_id, c.id, Time.now, function ]
      end
    elsif (c = @call_stack[Thread.current.object_id].last) &&
          c[0] == tp.defined_class &&
          c[1] == tp.method_id
      function = c[4]
      @call_stack[Thread.current.object_id].pop
      return_constructor = handler_class(function, tp.event)
      return_constructor.build_from_tracepoint(tp, path, c[2], Time.now - c[3])
    end

  @tracer.record_event method_event if method_event

  method_event
rescue
  puts $!.message
  puts $!.backtrace.join("\n")
  # XXX If this exception doesn't get reraised, internal errors
  # (e.g. a missing method on TracePoint) get silently
  # ignored. This allows tests to pass that should fail, which
  # is bad, but is it desirable otherwise?
  raise
end