15. Debugging
WTF? my_struct, :pp
WTF? my_var, :yaml
WTF? my_var, other_var, :pp, :file
WTF? *other_things, :json, :file, :time
But I want even more...
16. Debugging
def WTF?(*args)
allowed = [:pp, :yaml, :json]
options = {}
while allowed.include?(args.last)
options[args.pop] = true
end
case
when options[:yaml]
puts args.to_yaml
# ...
17. Debugging
data = ""
data << "[%s] " % Time.now if options[:time]
rx = %r{([^/]+?)(?:.rb)?:(d+):in `(.*)'$} # <- WTF is this?
data << "WTF (%s/%s:%s)" % caller[0].match(rx).values_at(1,3,2)
data << ": " <<
case
when options[:pp]
args.pretty_inspect.gsub(/^[|]$/,'') # removes array marks
when options[:yaml]
YAML.dump(args)
when options[:json]
JSON::pretty_generate(args)
when options[:text]
args.map(&:to_s).join("n")
20. Debugging
case
when options[:page]
(Thread.current[:wtf] ||= []) << data
when options[:file]
time = Time.now.strftime('%m%d_%H%M%S')
filename = "wtf_#{time}_#{rand(10000)}.txt"
File.write("#{Rails.root}/tmp/#{filename}", data)
when options[:raise]
raise data
when options[:redis]
REDIS.rpush 'wtf', data
REDIS.expire 'wtf', 30*60
else
Rails.logger.info data
end
27. Profiling
class Overview
# loading dependent data and calculations
def load!
load_departments
@budget_repo = Overviews::BudgetRepository.new(@period, @shops)
@data = {}
columns.each do |col|
calculate(col)
end
# … few more lines …
end
# … 50 more methods …
The problem lurks somewhere here
34. Profiling
module MethodTracker
class << self
def included(base)
methods = base.instance_methods(false) + base.private_instance_methods(false)
base.class_eval do
methods.each do |name|
original_method = instance_method(name)
define_method(name) do |*args, &block|
MethodTracker.on_start(base, name)
return_value = original_method.bind(self).call(*args, &block)
MethodTracker.on_end
return_value
end
end
end
end
37. Profiling
def prepare(base)
methods = base.instance_methods(false) +
base.private_instance_methods(false)
compiled = methods.map do |name|
override_method(base, name)
end
base.module_eval %{
module Tracking
#{compiled.join}
end
prepend Tracking
}
end
38. Public interface
module WTF
class << self
def track(*objects)
MethodTracker.setup(*objects)
MethodTracker.reset_state
end
def track_finish
MethodTracker.finish
end
end
end
class Overview
def load!
WTF.track(self, Overview::Helpers) # with additional classes
# ...
WTF.track_finish
end
end
39. Collecting data
require 'absolute_time'
def add_stats(at_start = nil)
stat = stats[stack.last]
this_time = AbsoluteTime.now
stat[:time] += this_time - last_time
self.last_time = this_time
this_heap = GC.stat[:heap_length]
stat[:heap] += this_heap - last_heap
self.last_heap = this_heap
stats[at_start][:freq] += 1 if at_start
end
45. Profiling
I want to do this
# loading dependent data and calculations
def load!
WTF.sql %(SELECT `weekly_balance_lines`.* FROM `weekly_balance_lines
load_shops
@budget_repo = Overviews::BudgetRepository.new(@period, @shops)
@salary_repo = Overviews::SalaryRepository.new(@period, @shops, @c
46. Profiling
… and get the answer
SQL: "SELECT `weekly_balance_lines`.`date`, `weekly_balance_lines`.`context_id`,
"(eval):4:in `log'"
"/home/vagrant/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1
"/home/vagrant/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1
"/home/vagrant/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1
"/home/vagrant/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1
"/home/vagrant/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1
"/vagrant/samesystem/app/models/overview.rb:350:in `weekly_balance_lines_for_p
"/vagrant/samesystem/app/models/overview.rb:342:in `weekly_balance_lines'"
"/vagrant/samesystem/app/models/overview.rb:870:in `sales'"
"/vagrant/samesystem/app/models/overview.rb:396:in `calculate'"
"/vagrant/samesystem/app/models/overview.rb:241:in `block in