Debugger is the module name space for ruby-debug.
RUBY_DEBUG_DIR | = | File.expand_path(File.dirname(__FILE__)) unless defined?(RUBY_DEBUG_DIR) | ||
DEFAULT_START_SETTINGS | = | { :init => true, # Set $0 and save ARGV? :post_mortem => false, # post-mortem debugging on uncaught exception? :tracing => nil | Default options to Debugger.start | |
VERSION | = | rb_str_new2(DEBUG_VERSION) |
handler | [RW] | interface modules provide handler object |
last_exception | [RW] | |
reload_source_on_change | [RW] | if true, checks the modification time of source files and reloads if it was modified |
Adds a new breakpoint. source is a name of a file or a class. pos is a line number or a method name if source is a class name. condition is a string which is evaluated to true when this breakpoint is activated.
/* * call-seq: * Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint * * Adds a new breakpoint. * <i>source</i> is a name of a file or a class. * <i>pos</i> is a line number or a method name if <i>source</i> is a class name. * <i>condition</i> is a string which is evaluated to +true+ when this breakpoint * is activated. */ static VALUE debug_add_breakpoint(int argc, VALUE *argv, VALUE self) { VALUE result; debug_check_started(); result = create_breakpoint_from_args(argc, argv, ++bkp_count); rb_ary_push(rdebug_breakpoints, result); return result; }
Sets catchpoint. Returns the string passed.
/* * call-seq: * Debugger.catchpoint(string) -> string * * Sets catchpoint. Returns the string passed. */ VALUE rdebug_add_catchpoint(VALUE self, VALUE value) { debug_check_started(); if (TYPE(value) != T_STRING) { rb_raise(rb_eTypeError, "value of a catchpoint must be String"); } rb_hash_aset(rdebug_catchpoints, rb_str_dup(value), INT2FIX(0)); return value; }
Returns an Array of Breakpoint objects; all the breakpoints that have been created.
/* * call-seq: * Debugger.breakpoints -> Array * * Returns an Array of Breakpoint objects; all the breakpoints that * have been created. */ static VALUE debug_breakpoints(VALUE self) { debug_check_started(); return rdebug_breakpoints; }
Returns a current catchpoints, which is a hash exception names that will trigger a debugger when raised. The values are the number of times taht catchpoint was hit, initially 0.
/* * call-seq: * Debugger.catchpoints -> hash * * Returns a current catchpoints, which is a hash exception names that will * trigger a debugger when raised. The values are the number of times taht * catchpoint was hit, initially 0. */ VALUE debug_catchpoints(VALUE self) { debug_check_started(); return rdebug_catchpoints; }
Returns an array of all contexts.
/* * call-seq: * Debugger.contexts -> array * * Returns an array of all contexts. */ static VALUE debug_contexts(VALUE self) { volatile VALUE list; volatile VALUE new_list; VALUE thread, context; threads_table_t *threads_table; debug_context_t *debug_context; int i; debug_check_started(); new_list = rb_ary_new(); list = rb_funcall(rb_cThread, idList, 0); for(i = 0; i < RARRAY(list)->len; i++) { thread = rb_ary_entry(list, i); thread_context_lookup(thread, &context, NULL); rb_ary_push(new_list, context); } threads_table_clear(rdebug_threads_tbl); Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table); for(i = 0; i < RARRAY(new_list)->len; i++) { context = rb_ary_entry(new_list, i); Data_Get_Struct(context, debug_context_t, debug_context); st_insert(threads_table->tbl, debug_context->thread_id, context); } return new_list; }
Returns current context. Note: Debugger.current_context.thread == Thread.current
/* * call-seq: * Debugger.current_context -> context * * Returns current context. * <i>Note:</i> Debugger.current_context.thread == Thread.current */ static VALUE debug_current_context(VALUE self) { VALUE thread, context; debug_check_started(); thread = rb_thread_current(); thread_context_lookup(thread, &context, NULL); return context; }
Register at_exit hook which is escaped from the debugger. FOR INTERNAL USE ONLY.
/* * call-seq: * Debugger.debug_at_exit { block } -> proc * * Register <tt>at_exit</tt> hook which is escaped from the debugger. * FOR INTERNAL USE ONLY. */ static VALUE debug_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; }
Same as Kernel#load but resets current context‘s frames. stop parameter forces the debugger to stop at the first line of code in the file increment_start determines if start_count should be incremented. When
control threads are used, they have to be set up before loading the debugger; so here +increment_start+ will be false.
FOR INTERNAL USE ONLY.
/* * call-seq: * Debugger.debug_load(file, stop = false, increment_start = false) -> nil * * Same as Kernel#load but resets current context's frames. * +stop+ parameter forces the debugger to stop at the first line of code in the +file+ * +increment_start+ determines if start_count should be incremented. When * control threads are used, they have to be set up before loading the * debugger; so here +increment_start+ will be false. * FOR INTERNAL USE ONLY. */ static VALUE debug_debug_load(int argc, VALUE *argv, VALUE self) { VALUE file, stop, context, increment_start; debug_context_t *debug_context; int state = 0; if(rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1) { stop = Qfalse; increment_start = Qtrue; } debug_start(self); if (Qfalse == increment_start) start_count--; context = debug_current_context(self); Data_Get_Struct(context, debug_context_t, debug_context); debug_context->stack_size = 0; if(RTEST(stop)) debug_context->stop_next = 1; /* Initializing $0 to the script's path */ ruby_script(RSTRING(file)->ptr); rb_load_protect(file, 0, &state); if (0 != state) { VALUE errinfo = ruby_errinfo; debug_suspend(self); reset_stepping_stop_points(debug_context); ruby_errinfo = Qnil; return errinfo; } /* We should run all at_exit handler's in order to provide, * for instance, a chance to run all defined test cases */ rb_exec_end_proc(); /* We could have issued a Debugger.stop inside the debug session. */ if (start_count > 0) { debug_stop(self); } return Qnil; }
# File lib/ruby-debug-base.rb, line 147 147: def handle_post_mortem(exp) 148: return if !exp || !exp.__debug_context || 149: exp.__debug_context.stack_size == 0 150: Debugger.suspend 151: orig_tracing = Debugger.tracing, Debugger.current_context.tracing 152: Debugger.tracing = Debugger.current_context.tracing = false 153: Debugger.last_exception = exp 154: handler.at_line(exp.__debug_context, exp.__debug_file, exp.__debug_line) 155: ensure 156: Debugger.tracing, Debugger.current_context.tracing = orig_tracing 157: Debugger.resume 158: end
# File cli/ruby-debug/commands/show.rb, line 222 222: def help(args) 223: if args[1] 224: s = args[1] 225: subcmd = Subcommands.find do |try_subcmd| 226: (s.size >= try_subcmd.min) and 227: (try_subcmd.name[0..s.size-1] == s) 228: end 229: if subcmd 230: str = subcmd.short_help + '.' 231: str += "\n" + subcmd.long_help if subcmd.long_help 232: return str 233: else 234: return "Invalid 'show' subcommand '#{args[1]}'." 235: end 236: end 237: s = " 238: Generic command for showing things about the debugger. 239: 240: -- 241: List of show subcommands: 242: -- 243: " 244: for subcmd in Subcommands do 245: s += "show #{subcmd.name} -- #{subcmd.short_help}\n" 246: end 247: return s 248: end
# File cli/ruby-debug/commands/set.rb, line 206 206: def help(args) 207: if args[1] 208: s = args[1] 209: subcmd = Subcommands.find do |try_subcmd| 210: (s.size >= try_subcmd.min) and 211: (try_subcmd.name[0..s.size-1] == s) 212: end 213: if subcmd 214: str = subcmd.short_help + '.' 215: str += "\n" + subcmd.long_help if subcmd.long_help 216: return str 217: else 218: return "Invalid 'set' subcommand '#{args[1]}'." 219: end 220: end 221: s = %{ 222: Modifies parts of the ruby-debug environment. Boolean values take 223: on, off, 1 or 0. 224: You can see these environment settings with the \"show\" command. 225: 226: -- 227: List of set subcommands: 228: -- 229: } 230: for subcmd in Subcommands do 231: s += "set #{subcmd.name} -- #{subcmd.short_help}\n" 232: end 233: return s 234: end
# File cli/ruby-debug/commands/info.rb, line 431 431: def help(args) 432: if args[1] 433: s = args[1] 434: subcmd = Subcommands.find do |try_subcmd| 435: (s.size >= try_subcmd.min) and 436: (try_subcmd.name[0..s.size-1] == s) 437: end 438: if subcmd 439: str = subcmd.short_help + '.' 440: if 'file' == subcmd.name and args[2] 441: s = args[2] 442: subsubcmd = InfoFileSubcommands.find do |try_subcmd| 443: (s.size >= try_subcmd.min) and 444: (try_subcmd.name[0..s.size-1] == s) 445: end 446: if subsubcmd 447: str += "\n" + subsubcmd.short_help + '.' 448: else 449: str += "\nInvalid file attribute #{args[2]}." 450: end 451: else 452: str += "\n" + subcmd.long_help if subcmd.long_help 453: end 454: return str 455: else 456: return "Invalid 'info' subcommand '#{args[1]}'." 457: end 458: end 459: s = %{ 460: Generic command for showing things about the program being debugged. 461: -- 462: List of info subcommands: 463: -- 464: } 465: for subcmd in Subcommands do 466: s += "info #{subcmd.name} -- #{subcmd.short_help}\n" 467: end 468: return s 469: end
Interrupts the current thread
# File lib/ruby-debug-base.rb, line 77 77: def interrupt 78: current_context.interrupt 79: end
Interrupts the last debugged thread
# File lib/ruby-debug-base.rb, line 84 84: def interrupt_last 85: if context = last_context 86: return nil unless context.thread.alive? 87: context.interrupt 88: end 89: context 90: end
Setting to true will make the debugger create frame bindings.
/* * call-seq: * Debugger.keep_frame_binding = bool * * Setting to +true+ will make the debugger create frame bindings. */ static VALUE debug_set_keep_frame_binding(VALUE self, VALUE value) { keep_frame_binding = RTEST(value) ? Qtrue : Qfalse; return value; }
Returns true if the debugger will collect frame bindings.
/* * call-seq: * Debugger.keep_frame_binding? -> bool * * Returns +true+ if the debugger will collect frame bindings. */ static VALUE debug_keep_frame_binding(VALUE self) { return keep_frame_binding; }
Returns last debugged context.
/* * call-seq: * Debugger.last_interrupted -> context * * Returns last debugged context. */ static VALUE debug_last_interrupted(VALUE self) { VALUE result = Qnil; threads_table_t *threads_table; debug_check_started(); Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table); st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result); return result; }
Activates the post-mortem mode. There are two ways of using it:
By calling Debugger.post_mortem method without a block, you install at_exit hook that intercepts any unhandled by your script exceptions and enables post-mortem mode.
If you know that a particular block of code raises an exception you can enable post-mortem mode by wrapping this block with Debugger.post_mortem, e.g.
def offender raise 'error' end Debugger.post_mortem do ... offender ... end
# File lib/ruby-debug-base.rb, line 126 126: def post_mortem 127: if block_given? 128: old_post_mortem = self.post_mortem? 129: begin 130: self.post_mortem = true 131: yield 132: rescue Exception => exp 133: handle_post_mortem(exp) 134: raise 135: ensure 136: self.post_mortem = old_post_mortem 137: end 138: else 139: return if post_mortem? 140: self.post_mortem = true 141: debug_at_exit do 142: handle_post_mortem($!) if $! && post_mortem? 143: end 144: end 145: end
Sets post-moterm flag. FOR INTERNAL USE ONLY.
/* * call-seq: * Debugger.post_mortem = bool * * Sets post-moterm flag. * FOR INTERNAL USE ONLY. */ static VALUE debug_set_post_mortem(VALUE self, VALUE value) { debug_check_started(); post_mortem = RTEST(value) ? Qtrue : Qfalse; return value; }
Returns true if post-moterm debugging is enabled.
/* * call-seq: * Debugger.post_mortem? -> bool * * Returns +true+ if post-moterm debugging is enabled. */ static VALUE debug_post_mortem(VALUE self) { return post_mortem; }
Removes breakpoint by its id. id is an identificator of a breakpoint.
/* * call-seq: * Debugger.remove_breakpoint(id) -> breakpoint * * Removes breakpoint by its id. * <i>id</i> is an identificator of a breakpoint. */ VALUE rdebug_remove_breakpoint(VALUE self, VALUE id_value) { int i; int id; VALUE breakpoint; debug_breakpoint_t *debug_breakpoint; id = FIX2INT(id_value); for( i = 0; i < RARRAY(rdebug_breakpoints)->len; i += 1 ) { breakpoint = rb_ary_entry(rdebug_breakpoints, i); Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint); if(debug_breakpoint->id == id) { rb_ary_delete_at(rdebug_breakpoints, i); return breakpoint; } } return Qnil; }
Resumes all contexts.
/* * call-seq: * Debugger.resume -> Debugger * * Resumes all contexts. */ static VALUE debug_resume(VALUE self) { VALUE current, context; VALUE saved_crit; VALUE context_list; debug_context_t *debug_context; int i; debug_check_started(); saved_crit = rb_thread_critical; rb_thread_critical = Qtrue; context_list = debug_contexts(self); thread_context_lookup(rb_thread_current(), ¤t, NULL); for(i = 0; i < RARRAY(context_list)->len; i++) { context = rb_ary_entry(context_list, i); if(current == context) continue; Data_Get_Struct(context, debug_context_t, debug_context); context_resume_0(debug_context); } rb_thread_critical = saved_crit; rb_thread_schedule(); return self; }
Returns setting object. Use Debugger.settings[] and Debugger.settings[]= methods to query and set debugger settings. These settings are available:
# File cli/ruby-debug/command.rb, line 264 264: def self.settings 265: Command.settings 266: end
The code inside of the block is escaped from the debugger.
/* * call-seq: * Debugger.skip { block } -> obj or nil * * The code inside of the block is escaped from the debugger. */ static VALUE debug_skip(VALUE self) { if (!rb_block_given_p()) { rb_raise(rb_eArgError, "called without a block"); } if(!IS_STARTED) return rb_yield(Qnil); set_current_skipped_status(Qtrue); return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse); }
# File lib/ruby-debug-base.rb, line 92 92: def source_reload 93: LineCache::clear_file_cache 94: end
This method is internal and activates the debugger. Use Debugger.start (from lib/ruby-debug-base.rb) instead.
The return value is the value of !Debugger.started? before issuing the start; That is, true is returned, unless debugger was previously started.
If a block is given, it starts debugger and yields to block. When the block is finished executing it stops the debugger with Debugger.stop method. Inside the block you will probably want to have a call to Debugger.debugger. For example:
Debugger.start{debugger; foo} # Stop inside of foo
Also, ruby-debug only allows one invocation of debugger at a time; nested Debugger.start‘s have no effect and you can‘t use this inside the debugger itself.
Note that if you want to completely remove the debugger hook, you must call Debugger.stop as many times as you called Debugger.start method.
/* * call-seq: * Debugger.start_ -> bool * Debugger.start_ { ... } -> bool * * This method is internal and activates the debugger. Use * Debugger.start (from <tt>lib/ruby-debug-base.rb</tt>) instead. * * The return value is the value of !Debugger.started? <i>before</i> * issuing the +start+; That is, +true+ is returned, unless debugger * was previously started. * If a block is given, it starts debugger and yields to block. When * the block is finished executing it stops the debugger with * Debugger.stop method. Inside the block you will probably want to * have a call to Debugger.debugger. For example: * Debugger.start{debugger; foo} # Stop inside of foo * * Also, ruby-debug only allows * one invocation of debugger at a time; nested Debugger.start's * have no effect and you can't use this inside the debugger itself. * * <i>Note that if you want to completely remove the debugger hook, * you must call Debugger.stop as many times as you called * Debugger.start method.</i> */ static VALUE debug_start(VALUE self) { VALUE result; start_count++; if(IS_STARTED) result = Qfalse; else { locker = Qnil; rdebug_breakpoints = rb_ary_new(); rdebug_catchpoints = rb_hash_new(); rdebug_threads_tbl = threads_table_create(); rb_add_event_hook(debug_event_hook, RUBY_EVENT_ALL); result = Qtrue; } if(rb_block_given_p()) rb_ensure(rb_yield, self, debug_stop_i, self); return result; }
Returns true the debugger is started.
/* * call-seq: * Debugger.started? -> bool * * Returns +true+ the debugger is started. */ static VALUE debug_is_started(VALUE self) { return IS_STARTED ? Qtrue : Qfalse; }
This method disables the debugger. It returns true if the debugger is disabled, otherwise it returns false.
Note that if you want to complete remove the debugger hook, you must call Debugger.stop as many times as you called Debugger.start method.
/* * call-seq: * Debugger.stop -> bool * * This method disables the debugger. It returns +true+ if the debugger is disabled, * otherwise it returns +false+. * * <i>Note that if you want to complete remove the debugger hook, * you must call Debugger.stop as many times as you called * Debugger.start method.</i> */ static VALUE debug_stop(VALUE self) { debug_check_started(); start_count--; if(start_count) return Qfalse; rb_remove_event_hook(debug_event_hook); locker = Qnil; rdebug_breakpoints = Qnil; rdebug_threads_tbl = Qnil; return Qtrue; }
Suspends all contexts.
/* * call-seq: * Debugger.suspend -> Debugger * * Suspends all contexts. */ static VALUE debug_suspend(VALUE self) { VALUE current, context; VALUE saved_crit; VALUE context_list; debug_context_t *debug_context; int i; debug_check_started(); saved_crit = rb_thread_critical; rb_thread_critical = Qtrue; context_list = debug_contexts(self); thread_context_lookup(rb_thread_current(), ¤t, NULL); for(i = 0; i < RARRAY(context_list)->len; i++) { context = rb_ary_entry(context_list, i); if(current == context) continue; Data_Get_Struct(context, debug_context_t, debug_context); context_suspend_0(debug_context); } rb_thread_critical = saved_crit; if(rb_thread_critical == Qfalse) rb_thread_schedule(); return self; }
Returns context of the thread passed as an argument.
/* * call-seq: * Debugger.thread_context(thread) -> context * * Returns context of the thread passed as an argument. */ static VALUE debug_thread_context(VALUE self, VALUE thread) { VALUE context; debug_check_started(); thread_context_lookup(thread, &context, NULL); return context; }
Setting to true will make the debugger save argument info on calls.
/* * call-seq: * Debugger.track_frame_args = bool * * Setting to +true+ will make the debugger save argument info on calls. */ static VALUE debug_set_track_frame_args(VALUE self, VALUE value) { track_frame_args = RTEST(value) ? Qtrue : Qfalse; return value; }
Returns true if the debugger track frame argument values on calls.
/* * call-seq: * Debugger.track_fame_args? -> bool * * Returns +true+ if the debugger track frame argument values on calls. */ static VALUE debug_track_frame_args(VALUE self) { return track_frame_args; }
# File cli/ruby-debug/commands/info.rb, line 93 93: def execute 94: if !@match[1] || @match[1].empty? 95: errmsg "\"info\" must be followed by the name of an info command:\n" 96: print "List of info subcommands:\n\n" 97: for subcmd in Subcommands do 98: print "info #{subcmd.name} -- #{subcmd.short_help}\n" 99: end 100: else 101: args = @match[1].split(/[ \t]+/) 102: param = args.shift 103: subcmd = find(Subcommands, param) 104: if subcmd 105: send("info_#{subcmd.name}", *args) 106: else 107: errmsg "Unknown info command #{param}\n" 108: end 109: end 110: end
# File cli/ruby-debug/commands/set.rb, line 63 63: def execute 64: if not @match[1] 65: print "\"set\" must be followed by the name of an set command:\n" 66: print "List of set subcommands:\n\n" 67: for subcmd in Subcommands do 68: print "set #{subcmd.name} -- #{subcmd.short_help}\n" 69: end 70: else 71: args = @match[1].split(/[ \t]+/) 72: subcmd = args.shift 73: subcmd.downcase! 74: if subcmd =~ /^no/i 75: set_on = false 76: subcmd = subcmd[2..-1] 77: else 78: set_on = true 79: end 80: for try_subcmd in Subcommands do 81: if (subcmd.size >= try_subcmd.min) and 82: (try_subcmd.name[0..subcmd.size-1] == subcmd) 83: begin 84: if try_subcmd.is_bool 85: if args.size > 0 86: set_on = get_onoff(args[0]) 87: end 88: end 89: case try_subcmd.name 90: when /^annotate$/ 91: level = get_int(args[0], "Set annotate", 0, 3, 0) 92: if level 93: Debugger.annotate = level 94: else 95: return 96: end 97: if defined?(Debugger::RDEBUG_SCRIPT) 98: # rdebug was called initially. 1st arg is script name. 99: Command.settings[:argv][1..-1] = args 100: else 101: # rdebug wasn't called initially. 1st arg is not script name. 102: Command.settings[:argv] = args 103: end 104: when /^args$/ 105: Command.settings[:argv][1..-1] = args 106: when /^autolist$/ 107: Command.settings[:autolist] = (set_on ? 1 : 0) 108: when /^autoeval$/ 109: Command.settings[:autoeval] = set_on 110: when /^basename$/ 111: Command.settings[:basename] = set_on 112: when /^callstyle$/ 113: if args[0] 114: arg = args[0].downcase.to_sym 115: case arg 116: when :short, :last, :tracked 117: Command.settings[:callstyle] = arg 118: Debugger.track_frame_args = arg == :tracked ? true : false 119: print "%s\n" % show_setting(try_subcmd.name) 120: return 121: end 122: end 123: print "Invalid call style #{arg}. Should be one of: " + 124: "'short', 'last', or 'tracked'.\n" 125: when /^trace$/ 126: Command.settings[:stack_trace_on_error] = set_on 127: when /^fullpath$/ 128: Command.settings[:full_path] = set_on 129: when /^autoreload$/ 130: Command.settings[:reload_source_on_change] = set_on 131: when /^autoirb$/ 132: Command.settings[:autoirb] = (set_on ? 1 : 0) 133: when /^debuggertesting$/ 134: Command.settings[:debuggertesting] = set_on 135: if set_on 136: Command.settings[:basename] = true 137: end 138: when /^forcestep$/ 139: self.class.settings[:force_stepping] = set_on 140: when /^history$/ 141: if 2 == args.size 142: interface = @state.interface 143: case args[0] 144: when /^save$/ 145: interface.history_save = get_onoff(args[1]) 146: when /^size$/ 147: interface.history_length = get_int(args[1], 148: "Set history size") 149: else 150: print "Invalid history parameter #{args[0]}. Should be 'save' or 'size'.\n" 151: end 152: else 153: print "Need two parameters for 'set history'; got #{args.size}.\n" 154: return 155: end 156: when /^keep-frame-bindings$/ 157: Debugger.keep_frame_binding = set_on 158: when /^linetrace\+$/ 159: self.class.settings[:tracing_plus] = set_on 160: when /^linetrace$/ 161: Debugger.tracing = set_on 162: when /^listsize$/ 163: listsize = get_int(args[0], "Set listsize", 1, nil, 10) 164: if listsize 165: self.class.settings[:listsize] = listsize 166: else 167: return 168: end 169: # when /^post-mortem$/ 170: # unless Debugger.post_mortem? == set_on 171: # if set_on 172: # Debugger.post_mortem 173: # else 174: # errmsg "Can't turn off post-mortem once it is on.\n" 175: # return 176: # end 177: # end 178: when /^width$/ 179: width = get_int(args[0], "Set width", 10, nil, 80) 180: if width 181: self.class.settings[:width] = width 182: ENV['COLUMNS'] = width.to_s 183: else 184: return 185: end 186: else 187: print "Unknown setting #{@match[1]}.\n" 188: return 189: end 190: print "%s\n" % show_setting(try_subcmd.name) 191: return 192: rescue RuntimeError 193: return 194: end 195: end 196: end 197: print "Unknown set command #{subcmd}\n" 198: end 199: end
# File cli/ruby-debug/commands/show.rb, line 198 198: def execute 199: if not @match[1] 200: print "\"show\" must be followed by the name of an show command:\n" 201: print "List of show subcommands:\n\n" 202: for subcmd in Subcommands do 203: print "show #{subcmd.name} -- #{subcmd.short_help}\n" 204: end 205: else 206: args = @match[1].split(/[ \t]+/) 207: param = args.shift 208: subcmd = find(Subcommands, param) 209: if subcmd 210: print "%s\n" % show_setting(subcmd.name) 211: else 212: print "Unknown show command #{param}\n" 213: end 214: end 215: end
# File cli/ruby-debug/commands/info.rb, line 112 112: def info_args(*args) 113: unless @state.context 114: print "No frame selected.\n" 115: return 116: end 117: locals = @state.context.frame_locals(@state.frame_pos) 118: args = @state.context.frame_args(@state.frame_pos) 119: args.each do |name| 120: s = "#{name} = #{locals[name].inspect}" 121: if s.size > self.class.settings[:width] 122: s[self.class.settings[:width]-3 .. -1] = "..." 123: end 124: print "#{s}\n" 125: end 126: end
# File cli/ruby-debug/commands/info.rb, line 128 128: def info_breakpoints(*args) 129: unless @state.context 130: print "info breakpoints not available here.\n" 131: return 132: end 133: unless Debugger.breakpoints.empty? 134: brkpts = Debugger.breakpoints.sort_by{|b| b.id} 135: unless args.empty? 136: a = args.map{|a| a.to_i} 137: brkpts = brkpts.select{|b| a.member?(b.id)} 138: if brkpts.empty? 139: errmsg "No breakpoints found among list given.\n" 140: return 141: end 142: end 143: print "Num Enb What\n" 144: brkpts.each do |b| 145: fname = Command.settings[:basename] ? 146: File.basename(b.source) : b.source 147: 148: if b.expr.nil? 149: print "%3d %s at %s:%s\n", 150: b.id, (b.enabled? ? 'y' : 'n'), fname, b.pos 151: else 152: print "%3d %s at %s:%s if %s\n", 153: b.id, (b.enabled? ? 'y' : 'n'), fname, b.pos, b.expr 154: end 155: hits = b.hit_count 156: if hits > 0 157: s = (hits > 1) ? 's' : '' 158: print "\tbreakpoint already hit #{hits} time#{s}\n" 159: end 160: end 161: else 162: print "No breakpoints.\n" 163: end 164: end
# File cli/ruby-debug/commands/info.rb, line 166 166: def info_display(*args) 167: unless @state.context 168: print "info display not available here.\n" 169: return 170: end 171: if @state.display.find{|d| d[0]} 172: print "Auto-display expressions now in effect:\n" 173: print "Num Enb Expression\n" 174: n = 1 175: for d in @state.display 176: print "%3d: %s %s\n", n, (d[0] ? 'y' : 'n'), d[1] if 177: d[0] != nil 178: n += 1 179: end 180: else 181: print "There are no auto-display expressions now.\n" 182: end 183: end
# File cli/ruby-debug/commands/info.rb, line 185 185: def info_file(*args) 186: unless args[0] 187: info_files 188: return 189: end 190: file = args[0] 191: param = args[1] 192: 193: param = 'basic' unless param 194: subcmd = find(InfoFileSubcommands, param) 195: unless subcmd 196: errmsg "Invalid parameter #{param}\n" 197: return 198: end 199: 200: unless LineCache::cached?(file) 201: unless LineCache::cached_script?(file) 202: print "File #{file} is not cached\n" 203: return 204: end 205: LineCache::cache(file, Command.settings[:reload_source_on_change]) 206: end 207: 208: print "File %s", file 209: path = LineCache.path(file) 210: if %w(all basic path).member?(subcmd.name) and path != file 211: print " - %s\n", path 212: else 213: print "\n" 214: end 215: 216: if %w(all basic lines).member?(subcmd.name) 217: lines = LineCache.size(file) 218: print "\t %d lines\n", lines if lines 219: end 220: 221: if %w(all breakpoints).member?(subcmd.name) 222: breakpoints = LineCache.trace_line_numbers(file) 223: if breakpoints 224: print "\tbreakpoint line numbers:\n" 225: print columnize(breakpoints.to_a.sort, self.class.settings[:width]) 226: end 227: end 228: 229: if %w(all mtime).member?(subcmd.name) 230: stat = LineCache.stat(file) 231: print "\t%s\n", stat.mtime if stat 232: end 233: if %w(all sha1).member?(subcmd.name) 234: print "\t%s\n", LineCache.sha1(file) 235: end 236: end
# File cli/ruby-debug/commands/info.rb, line 238 238: def info_files(*args) 239: files = LineCache::cached_files 240: files += SCRIPT_LINES__.keys unless 'stat' == args[0] 241: files.uniq.sort.each do |file| 242: stat = LineCache::stat(file) 243: path = LineCache::path(file) 244: print "File %s", file 245: if path and path != file 246: print " - %s\n", path 247: else 248: print "\n" 249: end 250: print "\t%s\n", stat.mtime if stat 251: end 252: end
# File cli/ruby-debug/commands/info.rb, line 388 388: def info_global_variables(*args) 389: unless @state.context 390: errmsg "info global_variables not available here.\n" 391: return 392: end 393: var_list(global_variables) 394: end
# File cli/ruby-debug/commands/info.rb, line 254 254: def info_instance_variables(*args) 255: unless @state.context 256: print "info instance_variables not available here.\n" 257: return 258: end 259: obj = debug_eval('self') 260: var_list(obj.instance_variables) 261: end
# File cli/ruby-debug/commands/info.rb, line 263 263: def info_line(*args) 264: unless @state.context 265: errmsg "info line not available here.\n" 266: return 267: end 268: print "Line %d of \"%s\"\n", @state.line, @state.file 269: end
# File cli/ruby-debug/commands/info.rb, line 271 271: def info_locals(*args) 272: unless @state.context 273: errmsg "info line not available here.\n" 274: return 275: end 276: locals = @state.context.frame_locals(@state.frame_pos) 277: locals.keys.sort.each do |name| 278: ### FIXME: make a common routine 279: begin 280: s = "#{name} = #{locals[name].inspect}" 281: rescue 282: begin 283: s = "#{name} = #{locals[name].to_s}" 284: rescue 285: s = "*Error in evaluation*" 286: end 287: end 288: if s.size > self.class.settings[:width] 289: s[self.class.settings[:width]-3 .. -1] = "..." 290: end 291: print "#{s}\n" 292: end 293: end
# File cli/ruby-debug/commands/info.rb, line 295 295: def info_program(*args) 296: if not @state.context 297: print "The program being debugged is not being run.\n" 298: return 299: elsif @state.context.dead? 300: print "The program crashed.\n" 301: if Debugger.last_exception 302: print("Exception: #{Debugger.last_exception.inspect}\n") 303: end 304: return 305: end 306: 307: print "Program stopped. " 308: case @state.context.stop_reason 309: when :step 310: print "It stopped after stepping, next'ing or initial start.\n" 311: when :breakpoint 312: print("It stopped at a breakpoint.\n") 313: when :catchpoint 314: print("It stopped at a catchpoint.\n") 315: when :catchpoint 316: print("It stopped at a catchpoint.\n") 317: else 318: print "unknown reason: %s\n" % @state.context.stop_reason.to_s 319: end 320: end
# File cli/ruby-debug/commands/info.rb, line 322 322: def info_stack(*args) 323: if not @state.context 324: errmsg "info stack not available here.\n" 325: return 326: end 327: (0...@state.context.stack_size).each do |idx| 328: if idx == @state.frame_pos 329: print "--> " 330: else 331: print " " 332: end 333: print_frame(idx) 334: end 335: end
# File cli/ruby-debug/commands/info.rb, line 370 370: def info_thread(*args) 371: unless args[0] 372: info_threads(args[0]) 373: return 374: end 375: ok, verbose = info_thread_preamble(args[1]) 376: return unless ok 377: c = parse_thread_num("info thread" , args[0]) 378: return unless c 379: display_context(c, !verbose) 380: if verbose and not c.ignored? 381: (0...c.stack_size).each do |idx| 382: print "\t" 383: print_frame(idx, false, c) 384: end 385: end 386: end
# File cli/ruby-debug/commands/info.rb, line 356 356: def info_threads(*args) 357: ok, verbose = info_thread_preamble(args[0]) 358: return unless ok 359: threads = Debugger.contexts.sort_by{|c| c.thnum}.each do |c| 360: display_context(c, !verbose) 361: if verbose and not c.ignored? 362: (0...c.stack_size).each do |idx| 363: print "\t" 364: print_frame(idx, false, c) 365: end 366: end 367: end 368: end
# File cli/ruby-debug/commands/info.rb, line 396 396: def info_variables(*args) 397: if not @state.context 398: errmsg "info variables not available here.\n" 399: return 400: end 401: obj = debug_eval('self') 402: locals = @state.context.frame_locals(@state.frame_pos) 403: locals['self'] = @state.context.frame_self(@state.frame_pos) 404: locals.keys.sort.each do |name| 405: next if name =~ /^__dbg_/ # skip debugger pollution 406: ### FIXME: make a common routine 407: begin 408: s = "#{name} = #{locals[name].inspect}" 409: rescue 410: begin 411: s = "#{name} = #{locals[name].to_s}" 412: rescue 413: s = "#{name} = *Error in evaluation*" 414: end 415: end 416: if s.size > self.class.settings[:width] 417: s[self.class.settings[:width]-3 .. -1] = "..." 418: end 419: s.gsub!('%', '%%') # protect against printf format strings 420: print "#{s}\n" 421: end 422: var_list(obj.instance_variables, obj.instance_eval{binding()}) 423: var_class_self 424: end
# File cli/ruby-debug/helper.rb, line 3 3: def inside_emacs? 4: ENV['EMACS'] || ENV['INSIDE_EMACS'] 5: end
# File cli/ruby-debug/commands/info.rb, line 89 89: def regexp 90: /^\s* i(?:nfo)? (?:\s+(.*))?$/ix 91: end
# File cli/ruby-debug/commands/show.rb, line 194 194: def regexp 195: /^show (?: \s+ (.+) )?$/xi 196: end
Debugger.start(options) -> bool Debugger.start(options) { … } -> obj
If it‘s called without a block it returns true, unless debugger was already started. If a block is given, it starts debugger and yields to block. When the block is finished executing it stops the debugger with Debugger.stop method.
If a block is given, it starts debugger and yields to block. When the block is finished executing it stops the debugger with Debugger.stop method. Inside the block you will probably want to have a call to Debugger.debugger. For example:
Debugger.start{debugger; foo} # Stop inside of foo
Also, ruby-debug only allows one invocation of debugger at a time; nested Debugger.start‘s have no effect and you can‘t use this inside the debugger itself.
Note that if you want to stop debugger, you must call Debugger.stop as many time as you called Debugger.start method.
options is a hash used to set various debugging options. Set :init true if you want to save ARGV and some variables which make a debugger restart possible. Only the first time :init is set true will values get set. Since ARGV is saved, you should make sure it hasn‘t been changed before the (first) call. Set :post_mortem true if you want to enter post-mortem debugging on an uncaught exception. Once post-mortem debugging is set, it can‘t be unset.
# File lib/ruby-debug-base.rb, line 199 199: def start(options={}, &block) 200: options = Debugger::DEFAULT_START_SETTINGS.merge(options) 201: if options[:init] 202: Debugger.const_set('ARGV', ARGV.clone) unless 203: defined? Debugger::ARGV 204: Debugger.const_set('PROG_SCRIPT', $0) unless 205: defined? Debugger::PROG_SCRIPT 206: Debugger.const_set('INITIAL_DIR', Dir.pwd) unless 207: defined? Debugger::INITIAL_DIR 208: end 209: Debugger.tracing = options[:tracing] unless options[:tracing].nil? 210: retval = Debugger.started? ? block && block.call(self) : Debugger.start_(&block) 211: if options[:post_mortem] 212: post_mortem 213: end 214: return retval 215: end