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); }
static void call_event(VALUE trace_point, void *data) { VALUE breakpoint, klass, mid, binding, self, file, line; EVENT_SETUP dc->calced_stack_size++; EVENT_COMMON breakpoint = Qnil; klass = rb_tracearg_defined_class(trace_arg); mid = SYM2ID(rb_tracearg_method_id(trace_arg)); 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); }
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); }
/** * file_tracepoint_callback * Callback function for Tracer#file_tracepoint. It gets called on * RUBY_EVENT_CLASS, RUBY_EVENT_CALL, and RUBY_EVENT_B_CALL * events. It check if any breakpoints matches current file the VM program counter * is in, and turn on line event tracing for that thread. Otherwise turn off * line tracing if in wrong file. The first time it turns on line event tracing, * it also turns on Tracer#return_tracepoint to maintain line tracing * consistency when file execution interleaves. */ static void file_tracepoint_callback(VALUE tracepoint, void *data) { VALUE self = (VALUE) data; rb_trace_arg_t *tracepoint_arg = rb_tracearg_from_tracepoint(tracepoint); VALUE tracepoint_path = rb_tracearg_path(tracepoint_arg); int match_found; if (!RB_TYPE_P(tracepoint_path, T_STRING)) return; // Ensure tracepoint_path is absolute path tracepoint_path = rb_file_expand_path(tracepoint_path, Qnil); if (!RTEST(tracepoint_path)) { return; } match_found = match_breakpoints_files(self, tracepoint_path); if (match_found) { enable_line_trace_for_thread(self); enable_return_trace_for_thread(self); } else { disable_line_trace_for_thread(Qnil); } return; }
static VALUE tracepoint_inspect(VALUE self) { rb_tp_t *tp = tpptr(self); rb_trace_arg_t *trace_arg = GET_THREAD()->trace_arg; if (trace_arg) { switch (trace_arg->event) { case RUBY_EVENT_LINE: case RUBY_EVENT_SPECIFIED_LINE: { VALUE sym = rb_tracearg_method_id(trace_arg); if (NIL_P(sym)) goto default_inspect; return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d in `%"PRIsVALUE"'>", rb_tracearg_event(trace_arg), rb_tracearg_path(trace_arg), FIX2INT(rb_tracearg_lineno(trace_arg)), sym); } case RUBY_EVENT_CALL: case RUBY_EVENT_C_CALL: case RUBY_EVENT_RETURN: case RUBY_EVENT_C_RETURN: return rb_sprintf("#<TracePoint:%"PRIsVALUE" `%"PRIsVALUE"'@%"PRIsVALUE":%d>", rb_tracearg_event(trace_arg), rb_tracearg_method_id(trace_arg), rb_tracearg_path(trace_arg), FIX2INT(rb_tracearg_lineno(trace_arg))); case RUBY_EVENT_THREAD_BEGIN: case RUBY_EVENT_THREAD_END: return rb_sprintf("#<TracePoint:%"PRIsVALUE" %"PRIsVALUE">", rb_tracearg_event(trace_arg), rb_tracearg_self(trace_arg)); default: default_inspect: return rb_sprintf("#<TracePoint:%"PRIsVALUE"@%"PRIsVALUE":%d>", rb_tracearg_event(trace_arg), rb_tracearg_path(trace_arg), FIX2INT(rb_tracearg_lineno(trace_arg))); } } else { return rb_sprintf("#<TracePoint:%s>", tp->tracing ? "enabled" : "disabled"); } }
static void raise_event(VALUE trace_point, void *data) { VALUE expn_class, ancestors; VALUE path, lineno, binding, post_mortem_context; int i; debug_context_t *new_dc; EVENT_SETUP; path = rb_tracearg_path(trace_arg); lineno = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); raised_exception = rb_tracearg_raised_exception(trace_arg); if (post_mortem == Qtrue) { post_mortem_context = context_dup(dc); rb_ivar_set(raised_exception, rb_intern("@__bb_file"), path); rb_ivar_set(raised_exception, rb_intern("@__bb_line"), lineno); rb_ivar_set(raised_exception, rb_intern("@__bb_binding"), binding); rb_ivar_set(raised_exception, rb_intern("@__bb_context"), post_mortem_context); Data_Get_Struct(post_mortem_context, debug_context_t, new_dc); rb_debug_inspector_open(context_backtrace_set, (void *)new_dc); } if (catchpoints == Qnil || dc->calced_stack_size == 0 || RHASH_TBL(catchpoints)->num_entries == 0) { EVENT_TEARDOWN; return; } expn_class = rb_obj_class(raised_exception); ancestors = rb_mod_ancestors(expn_class); for (i = 0; i < RARRAY_LENINT(ancestors); i++) { VALUE ancestor_class, module_name, hit_count; ancestor_class = rb_ary_entry(ancestors, i); module_name = rb_mod_name(ancestor_class); hit_count = rb_hash_aref(catchpoints, module_name); /* increment exception */ if (hit_count != Qnil) { rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1)); call_at_catchpoint(context, dc, raised_exception); call_at_line(context, dc, path, lineno); break; } } EVENT_TEARDOWN; }
static void trace_print(rb_trace_arg_t * trace_arg, debug_context_t * dc, const char *file_filter, const char *debug_msg) { char *fullpath = NULL; const char *basename; int filtered = 0; const char *event = rb_id2name(SYM2ID(rb_tracearg_event(trace_arg))); VALUE rb_path = rb_tracearg_path(trace_arg); const char *path = NIL_P(rb_path) ? "" : RSTRING_PTR(rb_path); int line = NUM2INT(rb_tracearg_lineno(trace_arg)); VALUE rb_mid = rb_tracearg_method_id(trace_arg); const char *mid = NIL_P(rb_mid) ? "(top level)" : rb_id2name(SYM2ID(rb_mid)); VALUE rb_cl = rb_tracearg_defined_class(trace_arg); VALUE rb_cl_name = NIL_P(rb_cl) ? rb_cl : rb_mod_name(rb_cl); const char *defined_class = NIL_P(rb_cl_name) ? "" : RSTRING_PTR(rb_cl_name); if (!trace_arg) return; if (file_filter) { #ifndef _WIN32 fullpath = realpath(path, NULL); #endif basename = fullpath ? strrchr(fullpath, '/') : path; if (!basename || strncmp(basename + 1, file_filter, strlen(file_filter))) filtered = 1; #ifndef _WIN32 free(fullpath); #endif } if (!filtered) { if (debug_msg) rb_funcall(mByebug, idPuts, 1, rb_sprintf("[#%d] %s\n", dc->thnum, debug_msg)); else rb_funcall(mByebug, idPuts, 1, rb_sprintf("%*s [#%d] %s@%s:%d %s#%s\n", dc->calced_stack_size, "", dc->thnum, event, path, line, defined_class, mid)); } }
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 trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc) { if (trace_arg) { const char *event = rb_id2name(SYM2ID(rb_tracearg_event(trace_arg))); char *path = RSTRING_PTR(rb_tracearg_path(trace_arg)); int line = NUM2INT(rb_tracearg_lineno(trace_arg)); VALUE v_mid_sym = rb_tracearg_method_id(trace_arg); VALUE v_mid_id = NIL_P(v_mid_sym) ? Qnil : SYM2ID(v_mid_sym); const char *mid = NIL_P(v_mid_id) ? "" : rb_id2name(v_mid_id); printf("%*s (%d)->[#%d] %s@%s:%d %s\n", dc->calced_stack_size, "", dc->calced_stack_size, dc->thnum, event, path, line, mid); } }
static void trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc) { if (trace_arg) { int i = 0; VALUE path = rb_tracearg_path(trace_arg); VALUE line = rb_tracearg_lineno(trace_arg); VALUE event = rb_tracearg_event(trace_arg); VALUE mid = rb_tracearg_method_id(trace_arg); for (i=0; i<dc->calced_stack_size; i++) putc('|', stderr); fprintf(stderr, "[#%d] %s@%s:%d %s\n", dc->thnum, rb_id2name(SYM2ID(event)), RSTRING_PTR(path), NUM2INT(line), NIL_P(mid) ? "" : rb_id2name(SYM2ID(mid))); } }
static void call_event(VALUE trace_point, void *data) { VALUE brkpnt, klass, msym, mid, binding, self, file, line; EVENT_SETUP; if (dc->calced_stack_size <= dc->dest_frame) CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); dc->calced_stack_size++; dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out + 1; /* nil method_id means we are at top level so there can't be a method * breakpoint here. Just leave then. */ msym = rb_tracearg_method_id(trace_arg); if (NIL_P(msym)) { EVENT_TEARDOWN; return; } mid = SYM2ID(msym); klass = rb_tracearg_defined_class(trace_arg); binding = rb_tracearg_binding(trace_arg); self = rb_tracearg_self(trace_arg); file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); brkpnt = Qnil; if (!NIL_P(breakpoints)) brkpnt = find_breakpoint_by_method(breakpoints, klass, mid, binding, self); if (!NIL_P(brkpnt)) { call_at_breakpoint(context, dc, brkpnt); call_at_line(context, dc, file, line); } EVENT_TEARDOWN; }
static void trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc) { if (trace_arg) { const char *event = safe_sym_to_str(rb_tracearg_event(trace_arg)); VALUE path_sym = rb_tracearg_path(trace_arg); const char *path = NIL_P(path_sym) ? "" : RSTRING_PTR(path_sym); int line = NUM2INT(rb_tracearg_lineno(trace_arg)); const char *mid = safe_sym_to_str(rb_tracearg_method_id(trace_arg)); rb_funcall(mByebug, rb_intern("puts"), 1, rb_sprintf("%*s (%d)->[#%d] %s@%s:%d %s\n", dc->calced_stack_size, "", dc->calced_stack_size, dc->thnum, event, path, line, mid)); } }
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; }
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; }
/* * Path of the file being run */ static VALUE tracepoint_attr_path(VALUE tpval) { return rb_tracearg_path(get_trace_arg()); }
static void return_event(VALUE trace_point, void *data) { EVENT_SETUP if (dc->calced_stack_size > 0) dc->calced_stack_size--; EVENT_COMMON if (dc->calced_stack_size + 1 == dc->before_frame) { 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); } if (dc->calced_stack_size + 1 == dc->after_frame) { reset_stepping_stop_points(dc); dc->steps = 1; } cleanup(dc); } static void c_call_event(VALUE trace_point, void *data)