class Byebug::Context
Mantains context information for the debugger and it's the main communication point between the library and the C-extension through the #at_breakpoint, #at_catchpoint, #at_tracing, #at_line and #at_return callbacks
Public Class Methods
# File lib/byebug/context.rb, line 15 def self.bin_file @bin_file ||= Gem.bin_path('byebug', 'byebug') end
List of files byebug will ignore while debugging
# File lib/byebug/context.rb, line 11 def self.ignored_files Byebug.mode == :standalone ? lib_files + [bin_file] : lib_files end
# File lib/byebug/context.rb, line 19 def self.lib_files @lib_files ||= Dir.glob(File.expand_path('../../**/*.rb', __FILE__)) end
Public Instance Methods
# File lib/byebug/context.rb, line 77 def at_breakpoint(brkpnt) handler.at_breakpoint(self, brkpnt) end
# File lib/byebug/context.rb, line 81 def at_catchpoint(excpt) handler.at_catchpoint(self, excpt) end
# File lib/byebug/context.rb, line 89 def at_line(file, line) handler.at_line(self, file, line) unless ignored_file?(file) end
# File lib/byebug/context.rb, line 93 def at_return(file, line) handler.at_return(self, file, line) unless ignored_file?(file) end
# File lib/byebug/context.rb, line 85 def at_tracing(file, line) handler.at_tracing(self, file, line) unless ignored_file?(file) end
inline VALUE Context_backtrace(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return dc_backtrace(context); }
inline VALUE Context_dead(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_DEAD) ? Qtrue : Qfalse; }
Gets current method arguments for a frame.
@param frame_no Frame index in the backtrace. Defaults to 0.
# File lib/byebug/context.rb, line 66 def frame_args(frame_no = 0) bind = frame_binding(frame_no) return c_frame_args(frame_no) unless bind ruby_frame_args(bind) end
Returns frame's binding.
static VALUE Context_frame_binding(int argc, VALUE * argv, VALUE self) { FRAME_SETUP; return dc_frame_binding(context, frame_n); }
Returns frame's defined class.
static VALUE Context_frame_class(int argc, VALUE * argv, VALUE self) { FRAME_SETUP; return dc_frame_class(context, frame_n); }
Returns the name of the file in the frame.
static VALUE Context_frame_file(int argc, VALUE * argv, VALUE self) { VALUE loc, absolute_path; FRAME_SETUP; loc = dc_frame_location(context, frame_n); absolute_path = rb_funcall(loc, rb_intern("absolute_path"), 0); if (!NIL_P(absolute_path)) return absolute_path; return rb_funcall(loc, rb_intern("path"), 0); }
Returns the line number in the file.
static VALUE Context_frame_line(int argc, VALUE * argv, VALUE self) { VALUE loc; FRAME_SETUP; loc = dc_frame_location(context, frame_n); return rb_funcall(loc, rb_intern("lineno"), 0); }
Gets local variables for a frame.
@param frame_no Frame index in the backtrace. Defaults to 0.
TODO: Use brand new local_variable_{get,set,defined?} for rubies >= 2.1
# File lib/byebug/context.rb, line 54 def frame_locals(frame_no = 0) bind = frame_binding(frame_no) return [] unless bind bind.eval('local_variables.inject({}){|h, v| h[v] = eval(v.to_s); h}') end
Returns the sym of the called method.
static VALUE Context_frame_method(int argc, VALUE * argv, VALUE self) { VALUE loc; FRAME_SETUP; loc = dc_frame_location(context, frame_n); return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0)); }
Returns self object of the frame.
static VALUE Context_frame_self(int argc, VALUE * argv, VALUE self) { FRAME_SETUP; return dc_frame_self(context, frame_n); }
# File lib/byebug/context.rb, line 73 def handler Byebug.handler || fail('No interface loaded') end
inline VALUE Context_ignored(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_IGNORE) ? Qtrue : Qfalse; }
Tells whether a file is ignored by the debugger.
@param path [String] filename to be checked.
# File lib/byebug/context.rb, line 28 def ignored_file?(path) self.class.ignored_files.include?(path) end
# File lib/byebug/context.rb, line 43 def interrupt step_into 1 end
Resumes thread from the suspended mode.
static VALUE Context_resume(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (!CTX_FL_TEST(context, CTX_FL_SUSPEND)) return Qnil; CTX_FL_UNSET(context, CTX_FL_SUSPEND); if (CTX_FL_TEST(context, CTX_FL_WAS_RUNNING)) rb_thread_wakeup(context->thread); return Qnil; }
Context's stack size
# File lib/byebug/context.rb, line 35 def stack_size return 0 unless backtrace backtrace.drop_while { |l| ignored_file?(l.first.path) } .take_while { |l| !ignored_file?(l.first.path) } .size end
Stops the current context after a number of steps
are made
from frame frame
(by default the newest one).
static VALUE Context_step_into(int argc, VALUE * argv, VALUE self) { VALUE steps, v_frame; int n_args, from_frame; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->calced_stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); n_args = rb_scan_args(argc, argv, "11", &steps, &v_frame); if (FIX2INT(steps) <= 0) rb_raise(rb_eRuntimeError, "Steps argument can't be negative."); from_frame = n_args == 1 ? 0 : FIX2INT(v_frame); if (from_frame < 0 || from_frame >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", from_frame, context->calced_stack_size); else if (from_frame > 0) CTX_FL_SET(context, CTX_FL_IGNORE_STEPS); context->steps = FIX2INT(steps); context->dest_frame = context->calced_stack_size - from_frame; return steps; }
Stops after n_frames
frames are finished. force
parameter (if true) ensures that the execution will stop in the specified
frame even when there are no more instructions to run. In that case, it
will stop when the return event for that frame is triggered.
static VALUE Context_step_out(int argc, VALUE * argv, VALUE self) { int n_args, n_frames; VALUE v_frames, force; debug_context_t *context; n_args = rb_scan_args(argc, argv, "02", &v_frames, &force); n_frames = n_args == 0 ? 1 : FIX2INT(v_frames); Data_Get_Struct(self, debug_context_t, context); if (n_frames < 0 || n_frames > context->calced_stack_size) rb_raise(rb_eRuntimeError, "You want to finish %d frames, but stack size is only %d", n_frames, context->calced_stack_size); context->steps_out = n_frames; if (n_args == 2 && RTEST(force)) CTX_FL_SET(context, CTX_FL_STOP_ON_RET); else CTX_FL_UNSET(context, CTX_FL_STOP_ON_RET); return Qnil; }
Steps over lines
lines in frame frame
(by default
the newest one) or higher (if frame frame
finishes).
static VALUE Context_step_over(int argc, VALUE * argv, VALUE self) { int n_args, frame; VALUE lines, v_frame; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->calced_stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); n_args = rb_scan_args(argc, argv, "11", &lines, &v_frame); frame = n_args == 1 ? 0 : FIX2INT(v_frame); if (frame < 0 || frame >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", frame, context->calced_stack_size); context->lines = FIX2INT(lines); context->dest_frame = context->calced_stack_size - frame; return Qnil; }
static VALUE Context_stop_reason(VALUE self) { debug_context_t *context; const char *symbol; Data_Get_Struct(self, debug_context_t, context); if (CTX_FL_TEST(context, CTX_FL_DEAD)) symbol = "post-mortem"; else switch (context->stop_reason) { case CTX_STOP_STEP: symbol = "step"; break; case CTX_STOP_BREAKPOINT: symbol = "breakpoint"; break; case CTX_STOP_CATCHPOINT: symbol = "catchpoint"; break; case CTX_STOP_NONE: default: symbol = "none"; } return ID2SYM(rb_intern(symbol)); }
Suspends the thread when it is running.
static VALUE Context_suspend(VALUE self) { VALUE status; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); status = rb_funcall(context->thread, rb_intern("status"), 0); if (rb_str_cmp(status, rb_str_new2("run")) == 0) CTX_FL_SET(context, CTX_FL_WAS_RUNNING); else if (rb_str_cmp(status, rb_str_new2("sleep")) == 0) CTX_FL_UNSET(context, CTX_FL_WAS_RUNNING); else return Qnil; CTX_FL_SET(context, CTX_FL_SUSPEND); return Qnil; }
Returns true
if the thread is suspended by debugger.
static VALUE Context_is_suspended(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_SUSPEND) ? Qtrue : Qfalse; }
Switches execution to this context.
static VALUE Context_switch(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); next_thread = context->thread; context->steps = 1; context->steps_out = 0; CTX_FL_SET(context, CTX_FL_STOP_ON_RET); return Qnil; }
inline VALUE Context_thnum(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return INT2FIX(context->thnum); }
inline VALUE Context_thread(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return context->thread; }
Returns the tracing flag for the current context.
static VALUE Context_tracing(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_TRACING) ? Qtrue : Qfalse; }
Controls the tracing for this context.
static VALUE Context_set_tracing(VALUE self, VALUE value) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (RTEST(value)) CTX_FL_SET(context, CTX_FL_TRACING); else CTX_FL_UNSET(context, CTX_FL_TRACING); return value; }
Private Instance Methods
Gets method arguments for a c-frame.
@param frame_no Frame index in the backtrace.
# File lib/byebug/context.rb, line 104 def c_frame_args(frame_no) myself = frame_self(frame_no) return [] unless myself.to_s != 'main' myself.method(frame_method(frame_no)).parameters end
Gets method arguments for a ruby-frame.
@param bind Binding for the ruby-frame.
# File lib/byebug/context.rb, line 116 def ruby_frame_args(bind) return [] unless bind.eval('__method__') bind.eval('method(__method__).parameters') rescue NameError => e Byebug.errmsg "Exception #{e.class} (#{e.message}) while retreving frame params" [] end