static void line_event(VALUE trace_point, void *data) { VALUE breakpoint, file, line, binding, self; int moved = 0; EVENT_SETUP breakpoint = Qnil; file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); self = rb_tracearg_self(trace_arg); EVENT_COMMON if (dc->calced_stack_size == 0) dc->calced_stack_size++; if (dc->last_line != rb_tracearg_lineno(trace_arg) || dc->last_file != rb_tracearg_path(trace_arg)) { moved = 1; } if (RTEST(tracing)) call_at_tracing(context, dc, file, line); if (moved || !CTX_FL_TEST(dc, CTX_FL_FORCE_MOVE)) { dc->steps = dc->steps <= 0 ? -1 : dc->steps - 1; if (dc->calced_stack_size <= dc->dest_frame) { dc->lines = dc->lines <= 0 ? -1 : dc->lines - 1; if (dc->calced_stack_size < dc->dest_frame) { dc->dest_frame = dc->calced_stack_size; rb_funcall(mByebug, rb_intern("puts"), 1, rb_str_new2("Next went up a frame because previous frame finished\n")); } } } if (dc->steps == 0 || dc->lines == 0 || (CTX_FL_TEST(dc, CTX_FL_ENABLE_BKPT) && (!NIL_P( breakpoint = find_breakpoint_by_pos(bb_breakpoints(self), file, line, binding))))) { call_at_line_check(context, dc, breakpoint, file, line); } cleanup(dc); }
/* * 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(); }
/* * call-seq: * context.dead? -> bool * * Returns +true+ if context doesn't represent a live context and is created * during post-mortem exception handling. */ static 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; }
static VALUE Context_stop_reason(VALUE self) { debug_context_t *context; const char *symbol; Data_Get_Struct(self, debug_context_t, context); 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"; } if(CTX_FL_TEST(context, CTX_FL_DEAD)) symbol = "post-mortem"; return ID2SYM(rb_intern(symbol)); }
static void call_event(VALUE trace_point, void *data) { VALUE breakpoint, klass, msym, mid, binding, self, file, line; EVENT_SETUP dc->calced_stack_size++; if (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET)) dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out + 1; EVENT_COMMON breakpoint = Qnil; klass = rb_tracearg_defined_class(trace_arg); msym = rb_tracearg_method_id(trace_arg); mid = NIL_P(msym) ? Qnil : SYM2ID(msym); binding = rb_tracearg_binding(trace_arg); self = rb_tracearg_self(trace_arg); file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); breakpoint = find_breakpoint_by_method(breakpoints, klass, mid, binding, self); if (breakpoint != Qnil) { call_at_breakpoint(context, dc, breakpoint); call_at_line(context, dc, file, line); } cleanup(dc); }
/* * call-seq: * context.tracing -> bool * * 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; }
/* * call-seq: * context.resume -> nil * * 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; }
/* * call-seq: * context.ignored? -> bool * * Returns the ignore flag for the context, which marks whether the associated * thread is ignored while debugging. */ static 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; }
extern VALUE Context_ignored(VALUE self) { debug_context_t *context; if (self == Qnil) return Qtrue; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_IGNORE) ? Qtrue : Qfalse; }
/* * call-seq: * context.suspended? -> bool * * 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; }
static void return_event(VALUE trace_point, void *data) { VALUE brkpnt, file, line, binding; EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { reset_stepping_stop_points(dc); call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } else if (!NIL_P(breakpoints)) { file = rb_tracearg_path(trace_arg); /* * @todo Sometimes the TracePoint API gives some return events without * file:line information, so we need to guard for nil until we know what's * going on. This happens, for example, with active_support core extensions: * * [#7] call@.../core_ext/numeric/conversions.rb:124 Fixnum#to_s * [#7] b_call@.../core_ext/numeric/conversions.rb:124 BigDecimal#to_s * [#7] line@.../core_ext/numeric/conversions.rb:125 BigDecimal#to_s * [#7] c_call@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? * [#7] c_return@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? * [#7] line@.../core_ext/numeric/conversions.rb:131 BigDecimal#to_s * [#7] c_call@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s * [#7] c_return@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s * [#7] b_return@/hort/core_ext/numeric/conversions.rb:133 BigDecimal#to_s * [#7] return@:0 Fixnum#to_s # => This guy... */ if (!NIL_P(file)) { line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); if (!NIL_P(brkpnt)) call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; }
static void return_event(VALUE trace_point, void *data) { EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { reset_stepping_stop_points(dc); call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; }
static void end_event(VALUE trace_point, void *data) { EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { reset_stepping_stop_points(dc); call_at_end(context, dc); } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; }
static void line_event(VALUE trace_point, void *data) { VALUE brkpnt, file, line, binding; EVENT_SETUP; file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); if (RTEST(tracing)) call_at_tracing(context, dc); if (!CTX_FL_TEST(dc, CTX_FL_IGNORE_STEPS)) dc->steps = dc->steps <= 0 ? -1 : dc->steps - 1; if (dc->calced_stack_size <= dc->dest_frame) { dc->dest_frame = dc->calced_stack_size; CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); dc->lines = dc->lines <= 0 ? -1 : dc->lines - 1; } if (dc->steps == 0 || dc->lines == 0) call_at_line_check(context, dc, Qnil); else { brkpnt = Qnil; if (!NIL_P(breakpoints)) brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); if (!NIL_P(brkpnt)) call_at_line_check(context, dc, brkpnt); } EVENT_TEARDOWN; }
VALUE check_breakpoints_by_method(debug_context_t *debug_context, VALUE klass, ID mid, VALUE self) { VALUE breakpoint; int i; if(!CTX_FL_TEST(debug_context, CTX_FL_ENABLE_BKPT)) return Qnil; if(check_breakpoint_by_method(debug_context->breakpoint, klass, mid, self)) return debug_context->breakpoint; if(RARRAY_LEN(rdebug_breakpoints) == 0) return Qnil; for(i = 0; i < RARRAY_LEN(rdebug_breakpoints); i++) { breakpoint = rb_ary_entry(rdebug_breakpoints, i); if(check_breakpoint_by_method(breakpoint, klass, mid, self)) return breakpoint; } return Qnil; }
VALUE check_breakpoints_by_pos(debug_context_t *debug_context, const char *file, int line) { VALUE breakpoint; int i; if(!CTX_FL_TEST(debug_context, CTX_FL_ENABLE_BKPT)) return Qnil; if(check_breakpoint_by_pos(debug_context->breakpoint, file, line)) return debug_context->breakpoint; if(RARRAY_LEN(rdebug_breakpoints) == 0) return Qnil; for(i = 0; i < RARRAY_LEN(rdebug_breakpoints); i++) { breakpoint = rb_ary_entry(rdebug_breakpoints, i); if(check_breakpoint_by_pos(breakpoint, file, line)) return breakpoint; } return Qnil; }
static void return_event(VALUE trace_point, void *data) { EVENT_SETUP; dc->calced_stack_size--; if (dc->steps_out == 1) dc->steps = 1; else if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { VALUE file, line; reset_stepping_stop_points(dc); file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); call_at_return(context, dc, file, line); } dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out - 1; EVENT_TEARDOWN; }
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; }