Ruby defines a number of features for tracing the execution of
a program. These are mainly useful for debugging code and printing
informative error messages. Two of the simplest features are actual
language keywords: __FILE__
and __LINE__
. These keyword expressions always evaluate to the name of
the file and the line number within that file on which they appear, and
they allow an error message to specify the exact location at which it
was generated:
STDERR.puts "#{__FILE__}:#{__LINE__): invalid data"
As an aside, note that the methods Kernel.eval
,
Object.instance_eval
, and Module.class_eval
all accept a filename (or other string) and a line number
as their final two arguments. If you are evaluating code that you have
extracted from a file of some sort, you can use these arguments to
specify the values of __FILE__
and
__LINE__
for the evaluation.
You have undoubtedly noticed that when an exception is raised and
not handled, the error message printed to the console contains filename
and line number information. This information is based on __FILE__
and __LINE__
, of course. Every Exception
object has a backtrace associated with it that
shows exactly where it was raised, where the method that raised the
exception was invoked, where that method was invoked, and so on. The
Exception.backtrace
method returns an
array of strings containing this information. The first element of this
array is the location at which the exception occurred, and each subsequent element is
one stack frame higher.
You needn’t raise an exception to obtain a current stack trace,
however. The Kernel.caller
method returns the current state of the call stack in the same
form as Exception.backtrace
. With no argument,
caller
returns a stack trace whose
first element is the method that invoked the method that calls caller
. That is, caller[0]
specifies the location from which
the current method was invoked. You can also invoke caller
with an argument that specifies how
many stack frames to drop from the start of the backtrace. The default
is 1, and caller(0)[0]
specifies the
location at which the caller
method
is invoked. This means, for example, that caller[0]
is the same thing as caller(0)[1]
and that caller(2)
is the same as caller[1..-1]
.
Stack traces returned by Exception.backtrace
and Kernel.caller
also include method names. Prior
to Ruby 1.9, you must parse the stack trace strings to extract method
names. In Ruby 1.9, however, you can obtain the name (as a symbol)
of the currently executing method with Kernel.__method__
or its synonym, Kernel.__callee__
. __method__
is useful in conjunction with
__FILE__
and __LINE__
:
raise "Assertion failed in #{__method__} at #{__FILE__}:#{__LINE__}"
Note that __method__
returns
the name by which a method was originally defined, even if the method
was invoked through an alias.
Instead of simply printing the filename and number at which an
error occurs, you can take it one step further and display the actual
line of code. If your program defines a global constant named SCRIPT_LINES__
and sets it equal to a hash,
then the require
and load
methods add an entry to this hash for
each file they load. The hash keys are filenames and the values
associated with those keys are arrays that contain the lines of those
files. If you want to include the main file (rather than just the files
it requires) in the hash, initialize it like this:
SCRIPT_LINES__ = {__FILE__ => File.readlines(__FILE__)}
If you do this, then you can obtain the current line of source code anywhere in your program with this expression:
SCRIPT_LINES__[__FILE__][__LINE__-1]
Ruby allows you to trace assignments to global variables with
Kernel.trace_var
. Pass this method a symbol that names a global variable and a
string or block of code. When the value of the named variable changes,
the string will be evaluated or the block will be invoked. When a block
is specified, the new value of the variable is passed as an argument. To
stop tracing the variable, call Kernel.untrace_var
. In the following example, note the use of caller[1]
to determine the program location at
which the variable tracing block was invoked:
# Print a message every time $SAFE changes trace_var(:$SAFE) {|v| puts "$SAFE set to #{v} at #{caller[1]}" }
The final tracing method is Kernel.set_trace_func
, which registers a Proc
to be invoked after every line of a Ruby program. set_trace_func
is useful if you want to write
a debugger module that allows line-by-line stepping through a program,
but we won’t cover it in any detail here.
3.15.34.39