static VALUE Context_step_over(int argc, VALUE *argv, VALUE self) { VALUE lines, frame, force; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if(context->stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); rb_scan_args(argc, argv, "12", &lines, &frame, &force); context->stop_line = FIX2INT(lines); CTX_FL_UNSET(context, CTX_FL_STEPPED); if (frame == Qnil) { context->dest_frame = context->calced_stack_size; } else { if (FIX2INT(frame) < 0 && FIX2INT(frame) >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame is out of range."); context->dest_frame = context->calced_stack_size - FIX2INT(frame); } if(RTEST(force)) CTX_FL_SET(context, CTX_FL_FORCE_MOVE); else CTX_FL_UNSET(context, CTX_FL_FORCE_MOVE); return Qnil; }
/* * call-seq: * context.step_out(n_frames = 1, force = false) * * 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; }
/* * call-seq: * context.step_into(steps, frame = 0) * * 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; }
extern VALUE context_dup(debug_context_t * context) { debug_context_t *new_context = ALLOC(debug_context_t); memcpy(new_context, context, sizeof(debug_context_t)); reset_stepping_stop_points(new_context); new_context->backtrace = context->backtrace; CTX_FL_SET(new_context, CTX_FL_DEAD); return Data_Wrap_Struct(cContext, context_mark, 0, new_context); }
/* * call-seq: * context.suspend -> nil * * 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; }
/* * call-seq: * context.tracing = bool * * 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; }
/* * call-seq: * context.switch -> nil * * 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; }
/* * Holds thread execution while another thread is active. * * Thanks to this, all threads are "frozen" while the user is typing commands. */ void acquire_lock(debug_context_t * dc) { while ((!NIL_P(locker) && locker != rb_thread_current()) || CTX_FL_TEST(dc, CTX_FL_SUSPEND)) { add_to_locked(rb_thread_current()); rb_thread_stop(); if (CTX_FL_TEST(dc, CTX_FL_SUSPEND)) CTX_FL_SET(dc, CTX_FL_WAS_RUNNING); } locker = rb_thread_current(); }
static VALUE Context_stop_next(int argc, VALUE *argv, VALUE self) { VALUE steps; VALUE force; debug_context_t *context; rb_scan_args(argc, argv, "11", &steps, &force); if(FIX2INT(steps) < 0) rb_raise(rb_eRuntimeError, "Steps argument can't be negative."); Data_Get_Struct(self, debug_context_t, context); context->stop_next = FIX2INT(steps); if(RTEST(force)) CTX_FL_SET(context, CTX_FL_FORCE_MOVE); else CTX_FL_UNSET(context, CTX_FL_FORCE_MOVE); return steps; }
extern VALUE context_create(VALUE thread) { debug_context_t *context = ALLOC(debug_context_t); context->flags = 0; context->thnum = ++thnum_max; context->thread = thread; reset_stepping_stop_points(context); context->stop_reason = CTX_STOP_NONE; rb_debug_inspector_open(context_backtrace_set, (void *)context); context->calced_stack_size = dc_stack_size(context) + 1; if (rb_obj_class(thread) == cDebugThread) CTX_FL_SET(context, CTX_FL_IGNORE); return Data_Wrap_Struct(cContext, context_mark, 0, context); }
extern VALUE context_create(VALUE thread) { debug_context_t *context = ALLOC(debug_context_t); context->last_file = Qnil; context->last_line = Qnil; context->flags = 0; context->calced_stack_size = real_stack_size(); context->thnum = ++thnum_max; context->thread = thread; reset_stepping_stop_points(context); context->stop_reason = CTX_STOP_NONE; context->backtrace = Qnil; if (rb_obj_class(thread) == cDebugThread) CTX_FL_SET(context, CTX_FL_IGNORE); return Data_Wrap_Struct(cContext, context_mark, 0, context); }
extern VALUE context_create(VALUE thread, VALUE cDebugThread) { debug_context_t *context; VALUE locations; context = ALLOC(debug_context_t); context->stack_size = 0; locations = rb_funcall(thread, rb_intern("backtrace_locations"), 1, INT2FIX(1)); context->calced_stack_size = locations != Qnil ? (int)RARRAY_LEN(locations) : 0; context->stack = NULL; context->thnum = ++thnum_current; context->thread = thread; context->flags = 0; context->last_file = NULL; context->last_line = -1; context->stop_frame = -1; context->thread_pause = 0; reset_stepping_stop_points(context); if(rb_obj_class(thread) == cDebugThread) CTX_FL_SET(context, CTX_FL_IGNORE); return Data_Wrap_Struct(cContext, Context_mark, Context_free, context); }
static int trace_common(rb_trace_arg_t *trace_arg, debug_context_t *dc) { /* return if thread marked as 'ignored', like byebug's control thread */ if (CTX_FL_TEST(dc, CTX_FL_IGNORE)) { cleanup(dc); return 0; } halt_while_other_thread_is_active(dc); /* Get the lock! */ locker = rb_thread_current(); /* Many events per line, but only *one* breakpoint */ if (dc->last_line != rb_tracearg_lineno(trace_arg) || dc->last_file != rb_tracearg_path(trace_arg)) { CTX_FL_SET(dc, CTX_FL_ENABLE_BKPT); } return 1; }