class Tracing::Tracer

Attributes

delayed[RW]
indent[RW]
nested[RW]

Public Class Methods

new() click to toggle source
# File lib/tracing.rb, line 54
def initialize
  reinitialize
end

Public Instance Methods

available_keys() click to toggle source
# File lib/tracing.rb, line 80
def available_keys
  @available.keys
end
disable(key) click to toggle source
# File lib/tracing.rb, line 102
def disable key
  !key.empty? and @keys.delete(key.to_sym)
end
display(key, msg) click to toggle source
# File lib/tracing.rb, line 212
def display key, msg
  puts msg
end
enable(key) click to toggle source
# File lib/tracing.rb, line 92
def enable key
  if !key.empty? && !@keys[s = key.to_sym]
    @keys[s] = true
    setup_help if s == :help
    setup_flame if s == :flame
  else
    true
  end
end
enabled() click to toggle source
# File lib/tracing.rb, line 88
def enabled
  @keys.keys
end
enabled?(key) click to toggle source
# File lib/tracing.rb, line 84
def enabled? key
  !key.empty? && @keys[key.to_sym]
end
reinitialize() click to toggle source
# File lib/tracing.rb, line 58
def reinitialize
  @indent = 0       # Current nesting level of enabled trace blocks
  @nested = false   # Set when a block enables all enclosed tracing
  @available = {}   # Hash of available trace keys, accumulated during the run
  @delayed = nil    # A delayed message, emitted only if the enclosed block emits tracing

  @keys = {}
  if (e = ENV["TRACE"])
    e.split(/[^_a-zA-Z0-9]/).each{|k| enable(k) }
  end
end
setup_debugger() click to toggle source
# File lib/tracing.rb, line 141
def setup_debugger
  begin
    require 'ruby-trace '
    Debugger.start # (:post_mortem => true)  # Some Ruby versions crash on post-mortem debugging
  rescue LoadError
    # Ok, no debugger, tough luck.
  end

  if trace :trap
    trap('SIGINT') do
      puts "Stopped at:\n\t"+caller*"\n\t"
      debugger
      true  # Stopped on SIGINT
    end
  end

  errors = []
  (
    [ENV["DEBUG_PREFERENCE"]].compact +
    [
      'byebug',
      'pry',
      'debugger',
      'ruby-trace '
    ]
  ).each do |debugger|
    begin
      require debugger
      if debugger == 'byebug'
        Kernel.class_eval do
          alias_method :debugger, :byebug
        end
      end
      ::Debugger.start if (const_get(::Debugger) rescue nil)
      return
    rescue LoadError => e
      errors << e
    end
  end

  # Report when we couldn't load any debugger
  $stderr.puts(errors.inspect)
end
setup_firstaid() click to toggle source
# File lib/tracing.rb, line 185
def setup_firstaid
  if trace :firstaid
    puts "Preparing first aid kit"
    ::Exception.class_eval do
      alias_method :firstaid_initialize, :initialize

      def initialize *args, &b
        send(:firstaid_initialize, *args, &b)
        # Array#flatten calls to_ary, ignore it when it comes from Gem Specifications:
        return if NoMethodError === self && message =~ /^undefined method `to_ary' for \#<Gem::Specification/

        # LoadErrors are not hard to diagnose, and polyglot uses them
        return if LoadError === self
        return if self.message =~ /uninitialized constant Mini[Tt]est/  # From RSpec usually

        # The Array() method calls to_ary and/or to_a before making a new array, ignore that:
        clr = caller
        return if NoMethodError === self && clr.detect{|frame| frame =~ /in `Array'/}

        puts "Stopped due to #{self.class}: #{message} at "+clr*"\n\t"
        debugger
        true # Stopped in Exception constructor
      end
    end
  end
end
setup_flame() click to toggle source
# File lib/tracing.rb, line 124
def setup_flame
  require 'ruby-prof'
  require 'ruby-prof-flamegraph'
  profile_result = RubyProf.start
  at_exit {
    profile_result2 = RubyProf.stop
    printer = RubyProf::FlameGraphPrinter.new(profile_result2)
    data_file = "/tmp/flamedata_#{Process.pid}.txt"
    svg_file = "/tmp/flamedata_#{Process.pid}.svg"
    flamegraph = File.dirname(__FILE__)+"/flamegraph.pl"
    File.popen("tee #{data_file} | perl #{flamegraph} --countname=ms --width=4800 > #{svg_file}", "w") { |f|
      printer.print(f, {})
    }
    STDERR.puts("Flame graph dumped to file:///#{svg_file}")
  }
end
setup_help() click to toggle source
# File lib/tracing.rb, line 118
def setup_help
  at_exit {
    $stderr.puts "---\nTracing keys available: #{@available.keys.map{|s| s.to_s}.sort*", "}"
  }
end
show(*args) click to toggle source
# File lib/tracing.rb, line 216
def show(*args)
  key, enabled_prefix = *selected?(args)

  # Emit the message if enabled or a parent is:
  if enabled_prefix && args.size > 0
    message =
      "\##{enabled_prefix} " +
      '  '*@indent +
      args.
        map{|a| a.respond_to?(:call) ? a.call : a}.
        join(' ')

    if @delay
      @delayed = [@delayed, message].compact*"\n"   # Arrange to display this message later, if necessary
      @delay = false
    else
      if @delayed
        display key, @delayed               # Display a delayed message, then the current one
        @delayed = nil
        @delay = false
      end
      display key, message
    end
  end
  @indent += (enabled_prefix ? 1 : 0)
  !!enabled_prefix
end
toggle(key) click to toggle source
# File lib/tracing.rb, line 106
def toggle key
  if !key.empty?
    if enabled?(key)
      disable(key)
      false
    else
      enable(key)
      true
    end
  end
end
trace(*args) { |: (size == 0 ? self : enabled)| ... } click to toggle source
# File lib/tracing.rb, line 70
def trace(*args, &block)
  begin
    old_indent, old_nested, old_delayed, enabled = @indent, @nested, @delayed, show(*args)
    # This monstrosity reduces the steps when single-stepping:
    block ? yield : (args.size == 0 ? self : enabled)
  ensure
    @indent, @nested, @delayed = old_indent, old_nested, old_delayed
  end
end

Private Instance Methods

selected?(args) click to toggle source
# File lib/tracing.rb, line 245
def selected?(args)
  # Figure out whether this trace is enabled (itself or by :all), if it nests, and if we should print the key:
  @delay = false
  key =
    if Symbol === args[0]
      control = args.shift
      case s = control.to_s
      when /!\Z/            # Enable all nested trace calls
        nested = true
        s.sub(/!\Z/, '').to_sym
      when /\?\Z/           # Delay this message until a nested active trace
        @delay = true
        s.sub(/\?\Z/, '').to_sym
      else
        control
      end
    else
      :all
    end

  @available[key] ||= key           # Remember that this trace was requested, for help
  if @nested ||                     # This trace is enabled because it's in a nested block
      @keys[key] ||                 # This trace is enabled in its own right
      @keys[:all]                   # This trace is enabled because all are
    if @keys[:keys] || @keys[:all]  # Use a formatting prefix?
      enabled_prefix = " %-15s"%key
      @keys[key] = enabled_prefix if @keys[key] == true # Save the formatting prefix
    else
      enabled_prefix = ''
    end
    @nested ||= nested              # Activate nesting, if requested
  end

  [key, enabled_prefix]
end