Class: Tracing::Tracer

Inherits:
Object show all
Defined in:
lib/tracing.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTracer

Returns a new instance of Tracer.



54
55
56
# File 'lib/tracing.rb', line 54

def initialize
  reinitialize
end

Instance Attribute Details

#delayedObject

Returns the value of attribute delayed.



52
53
54
# File 'lib/tracing.rb', line 52

def delayed
  @delayed
end

#indentObject

Returns the value of attribute indent.



52
53
54
# File 'lib/tracing.rb', line 52

def indent
  @indent
end

#nestedObject

Returns the value of attribute nested.



52
53
54
# File 'lib/tracing.rb', line 52

def nested
  @nested
end

Instance Method Details

#available_keysObject



80
81
82
# File 'lib/tracing.rb', line 80

def available_keys
  @available.keys
end

#disable(key) ⇒ Object



102
103
104
# File 'lib/tracing.rb', line 102

def disable key
  !key.empty? and @keys.delete(key.to_sym)
end

#display(key, msg) ⇒ Object



213
214
215
# File 'lib/tracing.rb', line 213

def display key, msg
  puts msg
end

#enable(key) ⇒ Object



92
93
94
95
96
97
98
99
100
# 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

#enabledObject



88
89
90
# File 'lib/tracing.rb', line 88

def enabled
  @keys.keys
end

#enabled?(key) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/tracing.rb', line 84

def enabled? key
  !key.empty? && @keys[key.to_sym]
end

#reinitializeObject



58
59
60
61
62
63
64
65
66
67
68
# 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_debuggerObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# 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 +
    [
      'debug',
      '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_firstaidObject



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/tracing.rb', line 186

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_flameObject



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# 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_helpObject



118
119
120
121
122
# 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) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/tracing.rb', line 217

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) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
# 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, &block) ⇒ Object



70
71
72
73
74
75
76
77
78
# 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