class ABProf::ABHarnessProcess

Attributes

last_iters[R]
last_run[R]

Public Class Methods

new(command_line, opts = {}) click to toggle source
# File lib/abprof.rb, line 183
def initialize command_line, opts = {}
  debug "Controller of nobody yet: SPAWN"
  @in_reader, @in_writer = IO.pipe
  @out_reader, @out_writer = IO.pipe
  @in_writer.sync = true
  @out_writer.sync = true
  @debug = opts[:debug]

  @pid = fork do
    STDOUT.reopen(@out_writer)
    STDIN.reopen(@in_reader)
    @out_reader.close
    @in_writer.close

    ENV['ABDEBUG'] = @debug.inspect

    if command_line.respond_to?(:call)
      STDERR.puts "Caution! An ABProf Harness process (non-bare) is being used with a block. This is almost never what you want!"
      command_line.call
    elsif command_line.respond_to?(:to_s)
      exec command_line.to_s
    else
      raise "Don't know how to execute benchmark code: #{command_line.inspect}!"
    end
    exit! 0
  end
  @out_writer.close
  @in_reader.close

  debug "Controller spawned #{@pid} (debug: #{@debug.inspect})"
end

Public Instance Methods

debug(string) click to toggle source
# File lib/abprof.rb, line 179
def debug string
  STDERR.puts(string) if @debug
end
kill() click to toggle source
# File lib/abprof.rb, line 220
def kill
  debug "Controller of #{@pid}: DIE"
  ::Process.detach @pid
  ::Process.kill "TERM", @pid
end
quit() click to toggle source
# File lib/abprof.rb, line 215
def quit
  debug "Controller of #{@pid}: QUIT"
  @in_writer.write "QUIT\n"
end
run_iters(n) click to toggle source
# File lib/abprof.rb, line 226
def run_iters(n)
  debug "Controller of #{@pid}: #{n} ITERS"
  @in_writer.write "ITERS #{n.to_i}\n"

  ignored_out = 0
  state = :failed
  t_start = Time.now
  loop do
    # Read and block
    output = @out_reader.gets
    ignored_out += output.length
    debug "Controller of #{@pid} out: #{output.inspect}"
    if output =~ /^VALUES/ # These anchors match newlines, too
      state = :succeeded
      vals = MultiJson.load output[7..-1]
      raise "Must return an array value from iterations!" unless vals.is_a?(Array)
      raise "Must return an array of numbers from iterations!" unless vals[0].is_a?(Numeric)
      @last_run = vals
      break
    elsif output =~ /^VALUE/ # These anchors match newlines, too
      state = :succeeded
      val = output[6..-1].to_f
      raise "Must return a number from iterations!" unless val.is_a?(Numeric)
      @last_run = [ val ]
      break
    elsif output =~ /^OK$/   # These anchors match newlines, too
      state = :succeeded_get_time
      break
    elsif output =~ /^NOT OK$/ # These anchors match newlines, too
      # Failed, break
      state = :explicit_not_ok
      break
    elsif ignored_out > 10_000
      # 10k of output and no OK? Bail with failed state.
      state = :too_much_output_without_status
      break
    end
    # None of these? Loop again.
  end
  t_end = Time.now
  unless [:succeeded, :succeeded_get_time].include?(state)
    self.kill
    STDERR.puts "Killing process #{@pid} after failed iterations, error code #{state.inspect}"
  end

  @last_run = [ (t_end - t_start).to_f ] if state == :succeeded_get_time
  @last_iters = n

  @last_run
end