How do I get ruby to print a full backtrace that includes arguments passed to functions?

Viewed 1876

Sometimes backtrace is enough to diagnose problem. But sometimes reason of crash is not obvious without knowledge what was passed to function.

Getting information what was passed to function that caused crash would be quite useful, especially in cases where reproducing is not obvious because it was caused by for example exception in network connection, weird user input or because program is depends on randomisation or processes data from external sensor.

Lets say that there is following program

def handle_changed_input(changed_input)
    raise 'ops' if changed_input =~ /magic/
end

def do_something_with_user_input(input)
    input = "#{input.strip}c"
    handle_changed_input(input)
end

input = gets
do_something_with_user_input(input)

where user typed "magic" as input. Normally one has

test.rb:2:in `handle_changed_input': ops (RuntimeError)
    from test.rb:7:in `do_something_with_user_input'
    from test.rb:11:in `<main>'

as output. What one may do to show also what was passed to function? Something like

test.rb:2:in `handle_changed_input("magic")': ops (RuntimeError)
    from test.rb:7:in `do_something_with_user_input("magi\n")'
    from test.rb:11:in `<main>'

It would be useful in many situations (and not truly useful where parameters are not representable as strings of reasonable legth, there is a good reason why it is not enabled by default).

How one may add this functionality? It is necessary that program works as usually during normal operation and preferably there is no additional output before crash.

I tried for example

def do_something_with_user_input(input)
    method(__method__).parameters.map do |_, name|
        puts "#{name}=#{binding.local_variable_get(name)}"
    end
    raise 'ops' if input =~ /magic/
end

input = gets

found in Is there a way to access method arguments in Ruby? but it would print on every single entrance to function what both would flood output and make program significantly slower.

4 Answers
MAX_STACK_SIZE = 200
tracer = proc do |event|
  if event == 'call' && caller_locations.length > MAX_STACK_SIZE
    fail "Probable Stack Overflow"
  end
end
set_trace_func(tracer)
Related