ext/byebug/byebug.c in byebug-1.8.2 vs ext/byebug/byebug.c in byebug-2.0.0

- old
+ new

@@ -1,78 +1,116 @@ #include <byebug.h> static VALUE mByebug; /* Ruby Byebug Module object */ -static VALUE cContext; static VALUE tracing = Qfalse; static VALUE post_mortem = Qfalse; static VALUE debug = Qfalse; -static VALUE context = Qnil; static VALUE catchpoints = Qnil; static VALUE breakpoints = Qnil; +static VALUE tracepoints = Qnil; -static VALUE tpLine = Qnil; -static VALUE tpCall = Qnil; -static VALUE tpCCall = Qnil; -static VALUE tpReturn = Qnil; -static VALUE tpCReturn = Qnil; -static VALUE tpRaise = Qnil; +/* Implements thread syncronization, we must stop threads when debugging */ +VALUE locker = Qnil; -static VALUE -tp_inspect(rb_trace_arg_t *trace_arg) { - if (trace_arg) { - VALUE event = rb_tracearg_event(trace_arg); - if (ID2SYM(rb_intern("line")) == event || - ID2SYM(rb_intern("specified_line")) == event) - { - VALUE sym = rb_tracearg_method_id(trace_arg); - if (NIL_P(sym)) sym = rb_str_new_cstr("<main>"); - return rb_sprintf("%"PRIsVALUE"@%"PRIsVALUE":%d in `%"PRIsVALUE"'", - rb_tracearg_event(trace_arg), - rb_tracearg_path(trace_arg), - FIX2INT(rb_tracearg_lineno(trace_arg)), - sym); - } - if (ID2SYM(rb_intern("call")) == event || - ID2SYM(rb_intern("c_call")) == event || - ID2SYM(rb_intern("return")) == event || - ID2SYM(rb_intern("c_return")) == event) - return rb_sprintf("%"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))); - return rb_sprintf("%"PRIsVALUE"@%"PRIsVALUE":%d", - rb_tracearg_event(trace_arg), - rb_tracearg_path(trace_arg), - FIX2INT(rb_tracearg_lineno(trace_arg))); +/* Threads table */ +VALUE threads = Qnil; +VALUE cThreadsTable; + +#define IS_STARTED (catchpoints != Qnil) +static void +check_started() +{ + if (!IS_STARTED) + { + rb_raise(rb_eRuntimeError, "Byebug is not started yet."); } - return rb_sprintf("No info"); } -static VALUE -Byebug_context(VALUE self) +static void +trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc) { - return context; + 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->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 cleanup(debug_context_t *dc) { + VALUE thread; + dc->stop_reason = CTX_STOP_NONE; + + /* checks for dead threads */ + check_thread_contexts(); + + /* release a lock */ + locker = Qnil; + + /* let the next thread to run */ + thread = remove_from_locked(); + if (thread != Qnil) + rb_thread_run(thread); } +#define EVENT_SETUP \ + rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(trace_point); \ + debug_context_t *dc; \ + VALUE context; \ + thread_context_lookup(rb_thread_current(), &context); \ + Data_Get_Struct(context, debug_context_t, dc); \ + if (debug == Qtrue) trace_print(trace_arg, dc); \ + +#define EVENT_COMMON if (!trace_common(trace_arg, dc)) { return; } + +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; +} + static void save_current_position(debug_context_t *dc, VALUE file, VALUE line) { dc->last_file = file; dc->last_line = line; CTX_FL_UNSET(dc, CTX_FL_ENABLE_BKPT); CTX_FL_UNSET(dc, CTX_FL_FORCE_MOVE); } +/* Functions that return control to byebug after the different events */ + static VALUE call_at(VALUE context_obj, debug_context_t *dc, ID mid, int argc, VALUE a0, VALUE a1) { struct call_with_inspection_data cwi; @@ -136,56 +174,25 @@ reset_stepping_stop_points(dc); call_at_line(context_obj, dc, file, line); } -#define BYEBUG_STARTED (catchpoints != Qnil) -#define EVENT_SETUP \ - rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(trace_point); \ - debug_context_t *dc; \ - if (!BYEBUG_STARTED) \ - rb_raise(rb_eRuntimeError, "Byebug not started yet!"); \ - Data_Get_Struct(context, debug_context_t, dc); \ - if (debug == Qtrue) \ - printf("%s (stack_size: %d)\n", \ - RSTRING_PTR(tp_inspect(trace_arg)), dc->stack_size); \ +/* TracePoint API event handlers */ -#define EVENT_COMMON() \ - if (trace_common(trace_arg, dc) == 0) { return; } - -static int -trace_common(rb_trace_arg_t *trace_arg, debug_context_t *dc) -{ - /* ignore a skipped section of code */ - if (CTX_FL_TEST(dc, CTX_FL_SKIPPED)) - { - cleanup(dc); - return 0; - } - - /* 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; -} - static void -process_line_event(VALUE trace_point, void *data) +line_event(VALUE trace_point, void *data) { - EVENT_SETUP; + EVENT_SETUP + VALUE breakpoint = Qnil; VALUE file = rb_tracearg_path(trace_arg); VALUE line = rb_tracearg_lineno(trace_arg); VALUE binding = rb_tracearg_binding(trace_arg); int moved = 0; - EVENT_COMMON(); + EVENT_COMMON if (dc->stack_size == 0) dc->stack_size++; if (dc->last_line != rb_tracearg_lineno(trace_arg) || dc->last_file != rb_tracearg_path(trace_arg)) @@ -216,26 +223,45 @@ cleanup(dc); } static void -process_c_return_event(VALUE trace_point, void *data) +call_event(VALUE trace_point, void *data) { - EVENT_SETUP; - if (dc->stack_size > 0) dc->stack_size--; - EVENT_COMMON(); + EVENT_SETUP + dc->stack_size++; + + EVENT_COMMON + + VALUE breakpoint = Qnil; + VALUE klass = rb_tracearg_defined_class(trace_arg); + VALUE mid = SYM2ID(rb_tracearg_method_id(trace_arg)); + VALUE binding = rb_tracearg_binding(trace_arg); + VALUE self = rb_tracearg_self(trace_arg); + VALUE file = rb_tracearg_path(trace_arg); + VALUE 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 -process_return_event(VALUE trace_point, void *data) +return_event(VALUE trace_point, void *data) { - EVENT_SETUP; + EVENT_SETUP + if (dc->stack_size > 0) dc->stack_size--; - EVENT_COMMON(); + EVENT_COMMON + if (dc->stack_size + 1 == dc->before_frame) { reset_stepping_stop_points(dc); VALUE file = rb_tracearg_path(trace_arg); VALUE line = rb_tracearg_lineno(trace_arg); @@ -250,68 +276,57 @@ cleanup(dc); } static void -process_c_call_event(VALUE trace_point, void *data) +c_call_event(VALUE trace_point, void *data) { - EVENT_SETUP; + EVENT_SETUP + dc->stack_size++; - EVENT_COMMON(); + EVENT_COMMON + cleanup(dc); } static void -process_call_event(VALUE trace_point, void *data) +c_return_event(VALUE trace_point, void *data) { - EVENT_SETUP; - dc->stack_size++; - EVENT_COMMON(); + EVENT_SETUP - VALUE breakpoint = Qnil; - VALUE klass = rb_tracearg_defined_class(trace_arg); - VALUE mid = SYM2ID(rb_tracearg_method_id(trace_arg)); - VALUE binding = rb_tracearg_binding(trace_arg); - VALUE self = rb_tracearg_self(trace_arg); - VALUE file = rb_tracearg_path(trace_arg); - VALUE line = rb_tracearg_lineno(trace_arg); + if (dc->stack_size > 0) dc->stack_size--; - 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); - } + EVENT_COMMON cleanup(dc); } static void -process_raise_event(VALUE trace_point, void *data) +raise_event(VALUE trace_point, void *data) { EVENT_SETUP + VALUE expn_class, aclass; VALUE err = rb_errinfo(); VALUE ancestors; int i; debug_context_t *new_dc; - EVENT_COMMON(); + EVENT_COMMON VALUE binding = rb_tracearg_binding(trace_arg); VALUE path = rb_tracearg_path(trace_arg); VALUE lineno = rb_tracearg_lineno(trace_arg); if (post_mortem == Qtrue) { context = context_dup(dc); - rb_ivar_set(err, rb_intern("@__debug_file") , path); - rb_ivar_set(err, rb_intern("@__debug_line") , lineno); - rb_ivar_set(err, rb_intern("@__debug_binding"), binding); - rb_ivar_set(err, rb_intern("@__debug_context"), context); + rb_ivar_set(err, rb_intern("@__bb_file") , path); + rb_ivar_set(err, rb_intern("@__bb_line") , lineno); + rb_ivar_set(err, rb_intern("@__bb_binding"), binding); + rb_ivar_set(err, rb_intern("@__bb_context"), context); Data_Get_Struct(context, debug_context_t, new_dc); rb_debug_inspector_open(context_backtrace_set, (void *)new_dc); } @@ -346,138 +361,239 @@ } cleanup(dc); } + +/* Setup TracePoint functionality */ + +static void +register_tracepoints(VALUE self) +{ + int i; + VALUE traces = tracepoints; + + if (NIL_P(traces)) + { + traces = rb_ary_new(); + + int line_msk = RUBY_EVENT_LINE; + int call_msk = RUBY_EVENT_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS; + int return_msk = RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN | RUBY_EVENT_END; + int c_call_msk = RUBY_EVENT_C_CALL; + int c_return_msk = RUBY_EVENT_C_RETURN; + int raise_msk = RUBY_EVENT_RAISE; + + VALUE tpLine = rb_tracepoint_new(Qnil, line_msk , line_event , 0); + VALUE tpCall = rb_tracepoint_new(Qnil, call_msk , call_event , 0); + VALUE tpReturn = rb_tracepoint_new(Qnil, return_msk , return_event , 0); + VALUE tpCCall = rb_tracepoint_new(Qnil, c_call_msk , c_call_event , 0); + VALUE tpCReturn = rb_tracepoint_new(Qnil, c_return_msk, c_return_event, 0); + VALUE tpRaise = rb_tracepoint_new(Qnil, raise_msk , raise_event , 0); + + rb_ary_push(traces, tpLine); + rb_ary_push(traces, tpCall); + rb_ary_push(traces, tpReturn); + rb_ary_push(traces, tpCCall); + rb_ary_push(traces, tpCReturn); + rb_ary_push(traces, tpRaise); + + tracepoints = traces; + } + + for (i = 0; i < RARRAY_LEN(traces); i++) + rb_tracepoint_enable(rb_ary_entry(traces, i)); +} + +static void +clear_tracepoints(VALUE self) +{ + int i; + + for (i = RARRAY_LEN(tracepoints)-1; i >= 0; i--) + rb_tracepoint_disable(rb_ary_entry(tracepoints, i)); +} + + +/* Byebug's Public API */ + +/* + * call-seq: + * Byebug.contexts -> array + * + * Returns an array of all contexts. + */ static VALUE -Byebug_setup_tracepoints(VALUE self) +bb_contexts(VALUE self) { - if (catchpoints != Qnil) return Qnil; + volatile VALUE list; + volatile VALUE new_list; + VALUE context; + threads_table_t *t_tbl; + debug_context_t *dc; + int i; - breakpoints = rb_ary_new(); - catchpoints = rb_hash_new(); - context = context_create(); + check_started(); - tpLine = rb_tracepoint_new(Qnil, - RUBY_EVENT_LINE, - process_line_event, NULL); + new_list = rb_ary_new(); + list = rb_funcall(rb_cThread, rb_intern("list"), 0); - tpCall = rb_tracepoint_new(Qnil, - RUBY_EVENT_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS, - process_call_event, NULL); + for (i = 0; i < RARRAY_LEN(list); i++) + { + VALUE thread = rb_ary_entry(list, i); + thread_context_lookup(thread, &context); + rb_ary_push(new_list, context); + } - tpCCall = rb_tracepoint_new(Qnil, - RUBY_EVENT_C_CALL, - process_c_call_event, NULL); + threads_clear(threads); + Data_Get_Struct(threads, threads_table_t, t_tbl); - tpReturn = rb_tracepoint_new(Qnil, - RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN | RUBY_EVENT_END, - process_return_event, NULL); + for (i = 0; i < RARRAY_LEN(new_list); i++) + { + context = rb_ary_entry(new_list, i); + Data_Get_Struct(context, debug_context_t, dc); + st_insert(t_tbl->tbl, dc->thread, context); + } - tpCReturn = rb_tracepoint_new(Qnil, - RUBY_EVENT_C_RETURN, - process_c_return_event, NULL); + return new_list; +} - tpRaise = rb_tracepoint_new(Qnil, - RUBY_EVENT_RAISE, - process_raise_event, NULL); +/* + * call-seq: + * Byebug.thread_context(thread) -> context + * + * Returns context of the thread passed as an argument. + */ +static VALUE +bb_thread_context(VALUE self, VALUE thread) +{ + VALUE context; - rb_tracepoint_enable(tpLine); - rb_tracepoint_enable(tpCall); - rb_tracepoint_enable(tpCCall); - rb_tracepoint_enable(tpReturn); - rb_tracepoint_enable(tpCReturn); - rb_tracepoint_enable(tpRaise); + check_started(); - return Qnil; + thread_context_lookup(thread, &context); + + return context; } +/* + * call-seq: + * Byebug.current_context -> context + * + * Returns the current context. + * <i>Note:</i> Byebug.current_context.thread == Thread.current + */ static VALUE -Byebug_remove_tracepoints(VALUE self) +bb_current_context(VALUE self) { - rb_tracepoint_disable(tpRaise); - rb_tracepoint_disable(tpCReturn); - rb_tracepoint_disable(tpReturn); - rb_tracepoint_disable(tpCCall); - rb_tracepoint_disable(tpCall); - rb_tracepoint_disable(tpLine); + VALUE context; - context = Qnil; - breakpoints = Qnil; - catchpoints = Qnil; + check_started(); - return Qnil; + thread_context_lookup(rb_thread_current(), &context); + + return context; } +/* + * call-seq: + * Byebug.started? -> bool + * + * Returns +true+ byebug is started. + */ static VALUE -Byebug_started(VALUE self) +bb_started(VALUE self) { - return BYEBUG_STARTED; + return IS_STARTED; } +/* + * call-seq: + * Byebug.stop -> bool + * + * This method disables byebug. It returns +true+ if byebug was already + * disabled, otherwise it returns +false+. + */ static VALUE -Byebug_stop(VALUE self) +bb_stop(VALUE self) { - if (BYEBUG_STARTED) + if (IS_STARTED) { - Byebug_remove_tracepoints(self); + clear_tracepoints(self); + + breakpoints = Qnil; + catchpoints = Qnil; + threads = Qnil; + return Qfalse; } return Qtrue; } +/* + * call-seq: + * Byebug.start_ -> bool + * Byebug.start_ { ... } -> bool + * + * This method is internal and activates the debugger. Use Byebug.start (from + * <tt>lib/byebug.rb</tt>) instead. + * + * The return value is the value of !Byebug.started? <i>before</i> issuing the + * +start+; That is, +true+ is returned, unless byebug was previously started. + * + * If a block is given, it starts byebug and yields to block. When the block + * is finished executing it stops the debugger with Byebug.stop method. + */ static VALUE -Byebug_start(VALUE self) +bb_start(VALUE self) { VALUE result; - if (BYEBUG_STARTED) + if (IS_STARTED) result = Qfalse; else { - Byebug_setup_tracepoints(self); + locker = Qnil; + breakpoints = rb_ary_new(); + catchpoints = rb_hash_new(); + threads = threads_create(); + + register_tracepoints(self); result = Qtrue; } if (rb_block_given_p()) - rb_ensure(rb_yield, self, Byebug_stop, self); + rb_ensure(rb_yield, self, bb_stop, self); return result; } +/* + * call-seq: + * Byebug.debug_load(file, stop = false) -> nil + * + * Same as Kernel#load but resets current context's frames. + * +stop+ parameter forces byebug to stop at the first line of code in +file+ + */ static VALUE -set_current_skipped_status(VALUE status) +bb_load(int argc, VALUE *argv, VALUE self) { - VALUE context_obj; + VALUE file, stop, context; debug_context_t *dc; - - context_obj = Byebug_context(mByebug); - Data_Get_Struct(context_obj, debug_context_t, dc); - if (status) - CTX_FL_SET(dc, CTX_FL_SKIPPED); - else - CTX_FL_UNSET(dc, CTX_FL_SKIPPED); - return Qnil; -} - -static VALUE -Byebug_load(int argc, VALUE *argv, VALUE self) -{ - VALUE file, stop, context_obj; - debug_context_t *dc; VALUE status = Qnil; int state = 0; if (rb_scan_args(argc, argv, "11", &file, &stop) == 1) { stop = Qfalse; } - Byebug_start(self); + bb_start(self); - context_obj = Byebug_context(self); - Data_Get_Struct(context_obj, debug_context_t, dc); + context = bb_current_context(self); + Data_Get_Struct(context, debug_context_t, dc); + if (RTEST(stop)) dc->steps = 1; /* Reset stack size to ignore byebug's own frames */ dc->stack_size = 0; @@ -496,77 +612,144 @@ return status; } static VALUE +set_current_skipped_status(VALUE status) +{ + VALUE context; + debug_context_t *dc; + + context = bb_current_context(mByebug); + Data_Get_Struct(context, debug_context_t, dc); + + if (status) + CTX_FL_SET(dc, CTX_FL_SKIPPED); + else + CTX_FL_UNSET(dc, CTX_FL_SKIPPED); + + return Qnil; +} + +static VALUE debug_at_exit_c(VALUE proc) { return rb_funcall(proc, rb_intern("call"), 0); } static void debug_at_exit_i(VALUE proc) { - if (BYEBUG_STARTED) + if (IS_STARTED) { set_current_skipped_status(Qtrue); rb_ensure(debug_at_exit_c, proc, set_current_skipped_status, Qfalse); } else debug_at_exit_c(proc); } +/* + * call-seq: + * Byebug.debug_at_exit { block } -> proc + * + * Register <tt>at_exit</tt> hook which is escaped from byebug. + */ static VALUE -Byebug_at_exit(VALUE self) +bb_at_exit(VALUE self) { VALUE proc; + if (!rb_block_given_p()) rb_raise(rb_eArgError, "called without a block"); + proc = rb_block_proc(); rb_set_end_proc(debug_at_exit_i, proc); return proc; } +/* + * call-seq: + * Byebug.tracing -> bool + * + * Returns +true+ if global tracing is enabled. + */ static VALUE -Byebug_tracing(VALUE self) +bb_tracing(VALUE self) { return tracing; } +/* + * call-seq: + * Byebug.tracing = bool + * + * Sets the global tracing flag. + */ static VALUE -Byebug_set_tracing(VALUE self, VALUE value) +bb_set_tracing(VALUE self, VALUE value) { tracing = RTEST(value) ? Qtrue : Qfalse; return value; } +/* + * call-seq: + * Byebug.post_mortem? -> bool + * + * Returns +true+ if post-moterm debugging is enabled. + */ static VALUE -Byebug_post_mortem(VALUE self) +bb_post_mortem(VALUE self) { return post_mortem; } +/* + * call-seq: + * Byebug.post_mortem = bool + * + * Sets post-moterm flag. + */ static VALUE -Byebug_set_post_mortem(VALUE self, VALUE value) +bb_set_post_mortem(VALUE self, VALUE value) { post_mortem = RTEST(value) ? Qtrue : Qfalse; return value; } +/* + * call-seq: + * Byebug.breakpoints -> array + * + * Returns an array of breakpoints. + */ static VALUE -Byebug_breakpoints(VALUE self) +bb_breakpoints(VALUE self) { return breakpoints; } +/* + * call-seq: + * Byebug.catchpoints -> array + * + * Returns an array of catchpoints. + */ static VALUE -Byebug_catchpoints(VALUE self) +bb_catchpoints(VALUE self) { return catchpoints; } +/* + * call-seq: + * Byebug.add_catchpoint(exception) -> exception + * + * Adds a new exception to the catchpoints array. + */ static VALUE -Byebug_add_catchpoint(VALUE self, VALUE value) +bb_add_catchpoint(VALUE self, VALUE value) { if (TYPE(value) != T_STRING) rb_raise(rb_eTypeError, "value of a catchpoint must be String"); rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0)); @@ -583,36 +766,32 @@ */ void Init_byebug() { mByebug = rb_define_module("Byebug"); - rb_define_module_function(mByebug, "setup_tracepoints", - Byebug_setup_tracepoints, 0); - rb_define_module_function(mByebug, "remove_tracepoints", - Byebug_remove_tracepoints, 0); - rb_define_module_function(mByebug, "context", Byebug_context, 0); - rb_define_module_function(mByebug, "breakpoints", Byebug_breakpoints, 0); - rb_define_module_function(mByebug, "add_catchpoint", - Byebug_add_catchpoint, 1); - rb_define_module_function(mByebug, "catchpoints", Byebug_catchpoints, 0); - rb_define_module_function(mByebug, "_start", Byebug_start, 0); - rb_define_module_function(mByebug, "stop", Byebug_stop, 0); - rb_define_module_function(mByebug, "started?", Byebug_started, 0); - rb_define_module_function(mByebug, "tracing?", Byebug_tracing, 0); - rb_define_module_function(mByebug, "tracing=", Byebug_set_tracing, 1); - rb_define_module_function(mByebug, "debug_load", Byebug_load, -1); - rb_define_module_function(mByebug, "debug_at_exit", Byebug_at_exit, 0); - rb_define_module_function(mByebug, "post_mortem?", Byebug_post_mortem, 0); - rb_define_module_function(mByebug, "post_mortem=", Byebug_set_post_mortem, 1); - cContext = Init_context(mByebug); + rb_define_module_function(mByebug, "add_catchpoint" , bb_add_catchpoint , 1); + rb_define_module_function(mByebug, "breakpoints" , bb_breakpoints , 0); + rb_define_module_function(mByebug, "catchpoints" , bb_catchpoints , 0); + rb_define_module_function(mByebug, "contexts" , bb_contexts , 0); + rb_define_module_function(mByebug, "current_context", bb_current_context, 0); + rb_define_module_function(mByebug, "debug_at_exit" , bb_at_exit , 0); + rb_define_module_function(mByebug, "debug_load" , bb_load , -1); + rb_define_module_function(mByebug, "post_mortem?" , bb_post_mortem , 0); + rb_define_module_function(mByebug, "post_mortem=" , bb_set_post_mortem, 1); + rb_define_module_function(mByebug, "_start" , bb_start , 0); + rb_define_module_function(mByebug, "started?" , bb_started , 0); + rb_define_module_function(mByebug, "stop" , bb_stop , 0); + rb_define_module_function(mByebug, "thread_context" , bb_thread_context , 1); + rb_define_module_function(mByebug, "tracing?" , bb_tracing , 0); + rb_define_module_function(mByebug, "tracing=" , bb_set_tracing , 1); + cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject); + + Init_context(mByebug); Init_breakpoint(mByebug); - context = Qnil; - catchpoints = Qnil; - breakpoints = Qnil; - rb_global_variable(&breakpoints); rb_global_variable(&catchpoints); - rb_global_variable(&context); + rb_global_variable(&tracepoints); + rb_global_variable(&threads); }