class Chronometer

Constants

VERSION

Attributes

trace_events[R]

Public Class Methods

from_file(path, contents: File.read(path)) click to toggle source
# File lib/chronometer.rb, line 11
def self.from_file(path, contents: File.read(path))
  new do
    instance_eval(contents, path)
  end
end
new(&blk) click to toggle source
# File lib/chronometer.rb, line 17
def initialize(&blk)
  dsl = DSL.new
  dsl.instance_exec(&blk)
  @events = dsl.events
  @tracepoints = dsl.tracepoints
  @trace_event_queue = Queue.new
  @trace_events = []
end
timestamp_us() click to toggle source
# File lib/chronometer.rb, line 67
def self.timestamp_us
  Time.now.utc.to_f.*(1_000_000).round
end

Public Instance Methods

associate_sub_slices!() click to toggle source
# File lib/chronometer.rb, line 37
def associate_sub_slices!
  parents = []
  @trace_events.each do |event|
    case event.event_type
    when :B
      parents << event
    when :E
      parent = parents.pop
      event.sub_slices.replace parent.sub_slices if parent
      parents.last.sub_slices << event unless parents.empty?
    end
  end
end
drain!() click to toggle source
# File lib/chronometer.rb, line 31
def drain!
  loop { @trace_events << @trace_event_queue.pop(true) }
rescue ThreadError
  nil
end
install!() click to toggle source
# File lib/chronometer.rb, line 26
def install!
  @events.each { |e| install_method_hook(e) }
  @tracepoints.each { |tp| install_tracepoint(tp) }
end
print_trace_event_report(dest, metadata: {}) click to toggle source
register_trace_event(event) click to toggle source
# File lib/chronometer.rb, line 71
def register_trace_event(event)
  @trace_event_queue << event
end

Private Instance Methods

install_method_hook(event) click to toggle source
# File lib/chronometer.rb, line 77
def install_method_hook(event)
  cls = event.cls
  method = event.method

  unbound_method = cls.instance_method(method)
  arg_labels = unbound_method.parameters.map(&:last)

  timer = self

  cls.send(:define_method, method) do |*args, &blk|
    context = event.context.call(self) if event.context
    args_dict = arg_labels.zip(args).to_h
    args_dict[:context] = context if context

    start_time = ::Chronometer.timestamp_us

    event_type = event.event_type
    if event_type == :X
      timer.register_trace_event TraceEvent.new(
        process_id: Process.pid,
        thread_id: Thread.current.object_id,
        start_time_usec: ::Chronometer.timestamp_us,
        event_type: :B,
        name: event.name
      )
      event_type = :E
    end

    r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    begin
      unbound_method.bind(self).call(*args, &blk)
    ensure
      duration = Process.clock_gettime(Process::CLOCK_MONOTONIC).-(r0).*(1_000_000).round

      timer.register_trace_event TraceEvent.new(
        process_id: Process.pid,
        thread_id: Thread.current.object_id,
        start_time_usec: event_type == :E ? ::Chronometer.timestamp_us : start_time,
        event_type: event_type,
        name: event.name,
        args: args_dict,
        category: event.category,
        duration: duration,
        cls: cls,
        method: method
      )
    end
  end
end
install_tracepoint(tracepoint) click to toggle source
# File lib/chronometer.rb, line 127
def install_tracepoint(tracepoint)
  event_name, blk = tracepoint
  TracePoint.trace(event_name) do |tp|
    next if tp.path == __FILE__
    args = {
      process_id: Process.pid,
      thread_id: Thread.current.object_id,
      start_time_usec: ::Chronometer.timestamp_us,
      event_type: :I,
      name: event_name
    }
    args.update blk.call(tp) if blk

    te = TraceEvent.new(**args)
    register_trace_event(te)
  end
end