class Trip

Constants

END_STATE

@private

Error
InProgressError
InternalError
PAUSE_WHEN

@private

PauseError
RESCUABLE_EXCEPTIONS

@private

RUN_STATE

@private

SLEEP_STATE

@private

VERSION

Public Class Methods

analyze(io: $stdout, color: true, page: false, precision: Trip::Analyzer::DEFAULT_PRECISION, &blk) click to toggle source

Analyzes a block of Ruby code by printing a detailed trace to $stdout.

@example

# Prints trace to $stdout (no paging)
Trip.analyze { ERB.new("").result }

# Prints trace with a pager (less)
Trip.analyze(page: true) { ERB.new("").result }

# Prints a trace to a StringIO with colors disabled
# and then returns the io.
io = Trip.analyze(color: false, io: StringIO.new)
io.string

@param (see Trip::Analyzer#analyze)

@return [void]

# File lib/trip/analyzer.rb, line 129
def Trip.analyze io: $stdout,
                 color: true,
                 page: false,
                 precision: Trip::Analyzer::DEFAULT_PRECISION,
                 &blk
  Trip::Analyzer.new(&blk).analyze(
    io: io,
    color: color,
    page: page,
    precision: precision
  )
end
new(&block) click to toggle source

@param [Proc] block

The block to be traced.

@return [Trip]

Returns an instance of Trip.
# File lib/trip.rb, line 37
def initialize(&block)
  raise ArgumentError, "Expected a block to trace" unless block
  @thread = nil
  @block = block
  @queue = nil
  @pause_when = PAUSE_WHEN
  @caller = Thread.current
end

Public Instance Methods

finished?() click to toggle source

@return [Boolean]

Returns true when the tracer thread has finished.
# File lib/trip.rb, line 88
def finished?
  @thread and END_STATE.include?(@thread.status)
end
pause_when(callable = nil, &block) click to toggle source

Stores a callable that decides when to pause the tracer.

@param [Proc] callable

A block or an object that responds to "#call".

@raise [ArgumentError]

Raised when the "callable"  argument is not provided.

@return [nil]

Returns nil.

@example

trip = Trip.new { Kernel.puts 1+1 }
trip.pause_when {|event| event.c_call? || event.c_return? }
event = trip.start
# File lib/trip.rb, line 61
def pause_when(callable = nil, &block)
  pauser = callable || block
  raise ArgumentError, "Expected a block or an object implementing #call" unless pauser
  @pause_when = pauser
  nil
end
resume() click to toggle source

Resumes a trace or starts one if {#start} hasn't been called.

@raise [Trip::PauseError] (see start)

@raise [Trip::InternalError] (see start)

@return [Trip::Event, nil]

Returns an event or nil.
# File lib/trip.rb, line 130
def resume
  return start unless started?
  if sleeping?
    @thread.wakeup
    @queue.deq
  end
end
running?() click to toggle source

@return [Boolean]

Returns true when the tracer thread is running.
# File lib/trip.rb, line 76
def running?
  @thread and @thread.status == RUN_STATE
end
sleeping?() click to toggle source

@return [Boolean]

Returns true when the tracer thread is sleeping.
# File lib/trip.rb, line 82
def sleeping?
  @thread and @thread.status == SLEEP_STATE
end
start() click to toggle source

Starts the tracer.

@raise [Trip::InProgessError]

Raised when there is already a trace in progress.

@raise [Trip::PauseError]

Raised when an exception is raised by the callable
given to {#pause_when}.

@raise [Trip::InternalError]

Raised when an exception internal to Trip is raised.

@return [Trip::Event, nil]

Returns an event, or nil
# File lib/trip.rb, line 106
def start
  if started? && !finished?
    raise InProgressError, "A trace is already in progress." \
                           "Call #resume instead ?"
  end
  @queue = Queue.new
  @thread = Thread.new do
    Thread.current.set_trace_func method(:on_event).to_proc
    @block.call
    Thread.current.set_trace_func(nil)
    @queue.enq(nil)
  end
  @queue.deq
end
started?() click to toggle source

@return [Boolean]

Returns true when a trace has been started
# File lib/trip.rb, line 70
def started?
  @thread != nil
end
stop() click to toggle source

Stops the tracer.

@return [nil]

# File lib/trip.rb, line 141
def stop
  if @thread
    @thread.set_trace_func(nil)
    @thread.exit
    @thread.join
    nil
  end
end

Private Instance Methods

on_event(name, path, lineno, method_name, binding, mod) click to toggle source
# File lib/trip.rb, line 152
def on_event(name, path, lineno, method_name, binding, mod)
  internal_error = Trip::InternalError.new("The tracer encountered an internal error and crashed")
  pause_error = Trip::PauseError.new("The pause Proc encountered an error and crashed")
  rescued_run(internal_error) do
    event = Event.new name, {
      path: path,
      lineno: lineno,
      module: mod,
      method_name: method_name,
      binding: binding
    }
    return if event.path == __FILE__
    if rescued_run(pause_error) { @pause_when.call(event) }
      @queue.enq(event)
      Thread.stop
    end
  end
end
rescued_run(e) { || ... } click to toggle source
# File lib/trip.rb, line 171
def rescued_run(e)
  yield
rescue *RESCUABLE_EXCEPTIONS => cause
  e.define_singleton_method(:cause) { cause }
  Thread.current.set_trace_func(nil)
  @caller.raise(e)
  false
end