Note: we describe the Frame object for used in 2.1.5; this is a little different form object in 1.9.3.
A big change I've made, is access to the call stack, or call frame. This is done via the object RubyVM::Frame.
Information here overlaps with Thread::Backtrace::Location, but frame objects can access dynamic information. Thread::Backtrace::Location object is confined to static information around a backtrace at a given point of time.
For example, one method on the frame object is binding() which returns a Binding object. Over the course of execution, values available to the frame may change and new objects may get added; the binding() method will track those changes, and can all you to set variables in the context of the frame as well.
Since a frame tracks dynamic call-stack information, it is meaningful only while that frame is active. I have put in checks in methods using frames to test that the frame is still valid. However these checks aren't foolprool. You shouldn't save frames in variables which have a lifetime longer than the lifetime of the frame it refers to. If you do, either the check will raise an error that the frame is invalid, or you will get garbage information back, or possibly a system crash.
Get the frame stack size for the current thread.
RubyVM::Frame::get(thread) -> RubyVM::Frame object RubyVM::Frame::get(thread, n) -> RubyVM::Frame object RubyVM::Frame::get -> RubyVM::Frame object RubyVM::Frame::get(n) -> RubyVM::Frame object
In the first form, get(thread), we return the current RubyVM::Frame
for the Thread object
passed. This is the current frame, Thread::current.
In the second form, get(thread, n) we try to go back that Fixnum n frames.
In the the third form, get(), the current thread is assumed, and like the
first form, but we use get the current frame.
The fourth form, get(n), like the third form, we assume the current
thread. And like the first form we go back we try to back a
Fixnum n, number of frame.
When count n is given, 1 is synonymous with the previous frame. If n is negative, we count from the bottom of the frame stack.
In all cases we return a RubyVM::Frame or nil if we can't go back (or forward for a negative n) that many frames.
FrameError can be raised if the frame object is no longer valid.
All of the following are equivalent
RubyVM::Frame::get(Thread::current) # -> RubyVM::Frame object RubyVM::Frame::get(Thread::current, 0)a RubyVM::Frame::get RubyVM::Frame::get(0)
Returns the number of arguments that were actually passed in the call to this frame. This often the same value as arity(), but differs when arity can take optional or "splat"ted parameters.
FrameError can be raised if the frame object is no longer valid.
irb$ proc { || RubyVM::Frame::get.argc }.call => 0 irb$ proc { |a| RubyVM::Frame::get.argc }.call => 1 irb$ proc { |a,*b| RubyVM::Frame::get.argc }.call => 1 irb$ proc { |a,b| RubyVM::Frame::get.argc }.call => 2
Returns the number of arguments that would not be ignored. See Ruby 2.1 proc#arity
FrameError can be raised if the frame object is no longer valid.
irb$ proc { || RubyVM::Frame::get.arity }.call => 0 irb$ proc { |a| RubyVM::Frame::get.arity }.call => 1 irb$ proc { |a,*b| RubyVM::Frame::get.arity }.call => -2 irb$ proc { |a,b| RubyVM::Frame::get.argc }.call => 2
Returns a Binding object for
the frame.
FrameError can be raised if the frame object is no longer valid.
irb$ proc { | a=1 | eval("a+2", RubyVM::Frame::get.binding ) }.call => 3
Returns the local variable at the given stack index. The values of local variables in the Ruby MRI indexed by number. This method retrieves the value of a local variable by number.
FrameError can be raised if the frame object is no longer valid.
irb$ proc { || a = 'ab'; RubyVM::Frame::get.getlocal(2) }.call => "ab"
Returns an Ruby VM instruction sequence object create from the Frame object or nil if there is none. Note that frames associated with calls to C functions do not Ruby VM instructions sequences.
FrameError can be raised if the frame object is no longer valid.
irb$ puts proc { || a = 'ab'; RubyVM::Frame::get.iseq.disassemble }.call <{ || a = 'ab'; RubyVM::Frame::get.iseq.disassemble }.call == disasm: <RubyVM::InstructionSequence:block in irb_binding@(irb)>===== == catch table | catch type: redo st: 0002 ed: 0025 sp: 0000 cont: 0002 | catch type: next st: 0002 ed: 0025 sp: 0000 cont: 0025 |------------------------------------------------------------------------ local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1) [ 2] a 0000 trace 4096 ( 1) 0002 trace 1 0004 putstring "ab" 0006 setlocal_OP__WC__0 2 0008 trace 1 0010 getinlinecache 19, <is:0> 0013 getconstant :RubyVM 0015 getconstant :Frame 0017 setinlinecache <is:0> 0019 opt_send_simple <callinfo!mid:get, argc:0, ARGS_SKIP> 0021 opt_send_simple <callinfo!mid:iseq, argc:0, ARGS_SKIP> 0023 opt_send_simple <callinfo!mid:disassemble, argc:0, ARGS_SKIP> 0025 trace 512 0027 leave
Returns the class (if any) associated with the frame.
FrameError can be raised if the frame object is no longer valid.
Returns the name of the frame.
FrameError can be raised if the frame object is no longer valid.
irb$ proc { || a = 'ab'; RubyVM::Frame::get.label }.call() => "block in irb_binding" irb(main):005:0> def foo; RubyVM::Frame::get.label ; end; foo => "foo"
Returns the method associated with the frame or nil of none.
FrameError can be raised if the frame object is no longer valid.
Returns the offset inside the instruction sequence or -1 if invalid. Note that C frames do not have VM instruction sequences associated with them.
FrameError can be raised if the frame object is no longer valid.
proc { || f = RubyVM::Frame::get; [f.pc_offset, f.pc_offset] }.call => [23, 27]
Sets Ruby VM PC to the offset given. This is pretty dangerous.
It is the way a debugger can implement skipping over statements.
You need to make sure set this to a valid instruction offset since
little checking is done.
Note that C frames do not have VM instruction sequences
associated with them.
FrameError can be raised if the frame object is no longer valid.
Returns a RubyVM::Frame object for the frame prior to the Frame object or +nil+ if there is none. Setting n to 0 just returns the object passed. A negative number starts from the end. So prev(-1) is the top frame. Counts outside of the range of [-stack_size* .. stack_size-1]
exceed the the range of the stack and return nil See also get()
FrameError can be raised if the frame object is no longer valid.
Sets the local variable at the given stack index. The values of local variables in the Ruby MRI indexed by number.
FrameError can be raised if the frame object is no longer valid.
Returns a tuple representing
A container represents where where the code that is getting run comes from. Most Ruby programs of course come from reading in a Ruby source-code file. In that case, the container would be "file", and the second entry in the tuple is the name of a file. The 3rd and last parameter here is the absolute name of the file. Note that even though the type may be "file" and the second parameter is a file name, sometimes it is not Ruby source code. The trepanning debugger performs and additional test to make sure a filename given pack is a Ruby file.
However somtimes, code comes from eval'ing a string. In that container is "string". If that's the case, the value of the string is probably somewhere on the call stack. The trepanning debugger picks this value out from the stack. What is stored as the filename may be somewhat arbitrary.
Finally, it could be the case that the code running comes from some Dynamically linked code. Here, the container type is "binary". Here the second parameter is the machine address, and one can use this inside say gdb to get the function name.
FrameError can be raised if the frame object is no longer valid.
Returns an array of source location positions that match
tf.instruction_offset. A source location here is a line number, a Fixnum. In the future we may want to change that to a pair of line and column numbers or byte offset in the file, or interval of start end locations.
But why do we return an array? Underneath you are stopped at an instruction. In the presence of compiler optimization and common-subexpression elimination, an instruction may map to several different positions in the Ruby source code. In the MRI VM specifically, I don't believe this can happen. But it doesn't hurt to handle the general case.
FrameError can be raised if the frame object is no longer valid.
proc { || f = RubyVM::Frame::get.source_location }.call => [1]
RubyVM::Frame#sp(n) -> Object
The MRI VM stores interediate values on a stack; sp() will retrieve the object given position n; 0 is the top object, 1 the next-to-top object and so on.
FrameError can be raised if the frame object is no longer valid.
irb $ proc { || 10 + RubyVM::Frame::get.sp(1) }.call() => 20
Sets VM stack position nto new_value. The top object is position 0. 1 is the next object.
FrameError can be raised if the frame object is no longer valid.
Returns the number of stack or sp entries in the current frame. This is the number values that have been pushed onto the stack since the current call. This is different thanRubyVM::Frame#stack_size() which counts the number of frames in the call stack. nil is returned if there is an error.
FrameError can be raised if the frame object is no longer valid.
Assists in a debugger implementing a "step out" command by turning off any tracing for this call frame and any call frames that are called from this frame.
FrameError can be raised if the frame object is no longer valid.
Assists in a debugger implementing a "step oover" command by turning off any tracing for ny call frames that are called from this frame.
FrameError can be raised if the frame object is no longer valid.
Returns the Thread object for the call frame.
FrameError can be raised if the frame object is no longer valid.
Returns true if the frame is no longer valid. On the other hand, since the test we use is weak, returning false might not mean the frame is valid, just that we can't disprove that it is not invalid.
You shouldn't save frames in variables which have a lifetime longer than the lifetime of the frame it refers to. If you do, either the check will raise an error that the frame is invalid, or you will get garbage information back, or possibly a system crash.