debugger2 1.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +3 -0
- data/AUTHORS +10 -0
- data/CHANGELOG.md +65 -0
- data/CONTRIBUTING.md +1 -0
- data/Gemfile +3 -0
- data/LICENSE +23 -0
- data/OLDER_CHANGELOG +334 -0
- data/OLD_CHANGELOG +5655 -0
- data/OLD_README +122 -0
- data/README.md +108 -0
- data/Rakefile +78 -0
- data/bin/rdebug +397 -0
- data/debugger2.gemspec +29 -0
- data/doc/.cvsignore +42 -0
- data/doc/Makefile.am +63 -0
- data/doc/emacs-notes.txt +38 -0
- data/doc/hanoi.rb +35 -0
- data/doc/primes.rb +28 -0
- data/doc/rdebug-emacs.texi +1030 -0
- data/doc/ruby-debug.texi +3791 -0
- data/doc/test-tri2.rb +18 -0
- data/doc/tri3.rb +8 -0
- data/doc/triangle.rb +12 -0
- data/emacs/Makefile.am +130 -0
- data/emacs/rdebug-annotate.el +385 -0
- data/emacs/rdebug-breaks.el +407 -0
- data/emacs/rdebug-cmd.el +92 -0
- data/emacs/rdebug-core.el +502 -0
- data/emacs/rdebug-dbg.el +62 -0
- data/emacs/rdebug-error.el +79 -0
- data/emacs/rdebug-fns.el +111 -0
- data/emacs/rdebug-frames.el +230 -0
- data/emacs/rdebug-gud.el +242 -0
- data/emacs/rdebug-help.el +104 -0
- data/emacs/rdebug-info.el +83 -0
- data/emacs/rdebug-layouts.el +180 -0
- data/emacs/rdebug-locring.el +118 -0
- data/emacs/rdebug-output.el +106 -0
- data/emacs/rdebug-regexp.el +118 -0
- data/emacs/rdebug-secondary.el +260 -0
- data/emacs/rdebug-shortkey.el +175 -0
- data/emacs/rdebug-source.el +568 -0
- data/emacs/rdebug-track.el +392 -0
- data/emacs/rdebug-varbuf.el +150 -0
- data/emacs/rdebug-vars.el +125 -0
- data/emacs/rdebug-watch.el +132 -0
- data/emacs/rdebug.el +326 -0
- data/emacs/test/elk-test.el +242 -0
- data/emacs/test/test-annotate.el +103 -0
- data/emacs/test/test-cmd.el +116 -0
- data/emacs/test/test-core.el +104 -0
- data/emacs/test/test-fns.el +65 -0
- data/emacs/test/test-frames.el +62 -0
- data/emacs/test/test-gud.el +35 -0
- data/emacs/test/test-indent.el +58 -0
- data/emacs/test/test-regexp.el +144 -0
- data/emacs/test/test-shortkey.el +61 -0
- data/ext/ruby_debug/breakpoint.c +630 -0
- data/ext/ruby_debug/extconf.rb +11 -0
- data/ext/ruby_debug/ruby_debug.c +2203 -0
- data/ext/ruby_debug/ruby_debug.h +151 -0
- data/lib/debugger.rb +5 -0
- data/lib/debugger/version.rb +5 -0
- data/lib/debugger2.rb +6 -0
- data/lib/ruby-debug-base.rb +307 -0
- data/lib/ruby-debug.rb +176 -0
- data/lib/ruby-debug/command.rb +227 -0
- data/lib/ruby-debug/commands/breakpoints.rb +153 -0
- data/lib/ruby-debug/commands/catchpoint.rb +55 -0
- data/lib/ruby-debug/commands/condition.rb +49 -0
- data/lib/ruby-debug/commands/continue.rb +38 -0
- data/lib/ruby-debug/commands/control.rb +107 -0
- data/lib/ruby-debug/commands/display.rb +120 -0
- data/lib/ruby-debug/commands/edit.rb +48 -0
- data/lib/ruby-debug/commands/enable.rb +202 -0
- data/lib/ruby-debug/commands/eval.rb +176 -0
- data/lib/ruby-debug/commands/finish.rb +42 -0
- data/lib/ruby-debug/commands/frame.rb +301 -0
- data/lib/ruby-debug/commands/help.rb +56 -0
- data/lib/ruby-debug/commands/info.rb +467 -0
- data/lib/ruby-debug/commands/irb.rb +123 -0
- data/lib/ruby-debug/commands/jump.rb +66 -0
- data/lib/ruby-debug/commands/kill.rb +51 -0
- data/lib/ruby-debug/commands/list.rb +94 -0
- data/lib/ruby-debug/commands/method.rb +84 -0
- data/lib/ruby-debug/commands/quit.rb +39 -0
- data/lib/ruby-debug/commands/reload.rb +40 -0
- data/lib/ruby-debug/commands/save.rb +90 -0
- data/lib/ruby-debug/commands/set.rb +223 -0
- data/lib/ruby-debug/commands/show.rb +247 -0
- data/lib/ruby-debug/commands/skip.rb +35 -0
- data/lib/ruby-debug/commands/source.rb +36 -0
- data/lib/ruby-debug/commands/stepping.rb +81 -0
- data/lib/ruby-debug/commands/threads.rb +189 -0
- data/lib/ruby-debug/commands/tmate.rb +36 -0
- data/lib/ruby-debug/commands/trace.rb +57 -0
- data/lib/ruby-debug/commands/variables.rb +199 -0
- data/lib/ruby-debug/debugger.rb +5 -0
- data/lib/ruby-debug/helper.rb +69 -0
- data/lib/ruby-debug/interface.rb +232 -0
- data/lib/ruby-debug/processor.rb +474 -0
- data/man/rdebug.1 +241 -0
- data/old_scripts/Makefile.am +14 -0
- data/old_scripts/README.md +2 -0
- data/old_scripts/autogen.sh +4 -0
- data/old_scripts/configure.ac +12 -0
- data/old_scripts/rdbg.rb +33 -0
- data/old_scripts/runner.sh +7 -0
- data/old_scripts/svn2cl_usermap +3 -0
- data/test/.cvsignore +1 -0
- data/test/breakpoints_test.rb +366 -0
- data/test/conditions_test.rb +77 -0
- data/test/continue_test.rb +28 -0
- data/test/display_test.rb +143 -0
- data/test/edit_test.rb +55 -0
- data/test/eval_test.rb +94 -0
- data/test/examples/breakpoint1.rb +15 -0
- data/test/examples/breakpoint2.rb +7 -0
- data/test/examples/conditions.rb +4 -0
- data/test/examples/continue.rb +4 -0
- data/test/examples/display.rb +5 -0
- data/test/examples/edit.rb +3 -0
- data/test/examples/edit2.rb +3 -0
- data/test/examples/eval.rb +4 -0
- data/test/examples/finish.rb +20 -0
- data/test/examples/frame.rb +31 -0
- data/test/examples/help.rb +2 -0
- data/test/examples/info.rb +48 -0
- data/test/examples/info2.rb +3 -0
- data/test/examples/irb.rb +6 -0
- data/test/examples/jump.rb +14 -0
- data/test/examples/kill.rb +2 -0
- data/test/examples/list.rb +12 -0
- data/test/examples/method.rb +15 -0
- data/test/examples/post_mortem.rb +19 -0
- data/test/examples/quit.rb +2 -0
- data/test/examples/reload.rb +6 -0
- data/test/examples/restart.rb +6 -0
- data/test/examples/save.rb +3 -0
- data/test/examples/set.rb +3 -0
- data/test/examples/set_annotate.rb +12 -0
- data/test/examples/settings.rb +1 -0
- data/test/examples/show.rb +2 -0
- data/test/examples/source.rb +3 -0
- data/test/examples/stepping.rb +21 -0
- data/test/examples/thread.rb +32 -0
- data/test/examples/tmate.rb +10 -0
- data/test/examples/trace.rb +7 -0
- data/test/examples/trace_threads.rb +20 -0
- data/test/examples/variables.rb +26 -0
- data/test/finish_test.rb +49 -0
- data/test/frame_test.rb +140 -0
- data/test/help_test.rb +51 -0
- data/test/info_test.rb +326 -0
- data/test/irb_test.rb +82 -0
- data/test/jump_test.rb +70 -0
- data/test/kill_test.rb +49 -0
- data/test/list_test.rb +147 -0
- data/test/method_test.rb +72 -0
- data/test/post_mortem_test.rb +25 -0
- data/test/quit_test.rb +56 -0
- data/test/reload_test.rb +47 -0
- data/test/restart_test.rb +145 -0
- data/test/save_test.rb +94 -0
- data/test/set_test.rb +183 -0
- data/test/show_test.rb +294 -0
- data/test/source_test.rb +46 -0
- data/test/stepping_test.rb +122 -0
- data/test/support/breakpoint.rb +12 -0
- data/test/support/context.rb +14 -0
- data/test/support/matchers.rb +67 -0
- data/test/support/mocha_extensions.rb +71 -0
- data/test/support/processor.rb +7 -0
- data/test/support/test_dsl.rb +206 -0
- data/test/support/test_interface.rb +66 -0
- data/test/test_helper.rb +9 -0
- data/test/thread_test.rb +124 -0
- data/test/tmate_test.rb +45 -0
- data/test/trace_test.rb +156 -0
- data/test/variables_test.rb +116 -0
- metadata +319 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include "ruby/debug.h"
|
|
3
|
+
|
|
4
|
+
/* Context info */
|
|
5
|
+
enum ctx_stop_reason {
|
|
6
|
+
CTX_STOP_NONE,
|
|
7
|
+
CTX_STOP_STEP,
|
|
8
|
+
CTX_STOP_BREAKPOINT,
|
|
9
|
+
CTX_STOP_CATCHPOINT
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/* Context flags */
|
|
13
|
+
#define CTX_FL_SUSPEND (1<<1) /* suspend because another thread stopping */
|
|
14
|
+
#define CTX_FL_TRACING (1<<2) /* call at_tracing method */
|
|
15
|
+
#define CTX_FL_SKIPPED (1<<3) /* skip all debugger events */
|
|
16
|
+
#define CTX_FL_IGNORE (1<<4) /* this context belongs to ignored thread */
|
|
17
|
+
#define CTX_FL_DEAD (1<<5) /* this context belonged to died thraed */
|
|
18
|
+
#define CTX_FL_WAS_RUNNING (1<<6) /* previous thread state */
|
|
19
|
+
#define CTX_FL_ENABLE_BKPT (1<<7) /* can check breakpoint */
|
|
20
|
+
#define CTX_FL_STEPPED (1<<8)
|
|
21
|
+
#define CTX_FL_FORCE_MOVE (1<<9)
|
|
22
|
+
#define CTX_FL_CATCHING (1<<10)
|
|
23
|
+
|
|
24
|
+
#if 0
|
|
25
|
+
static inline const char *
|
|
26
|
+
flag_name(int flag)
|
|
27
|
+
{
|
|
28
|
+
switch (flag) {
|
|
29
|
+
#define C(x) case x: return #x
|
|
30
|
+
C(CTX_FL_SUSPEND);
|
|
31
|
+
C(CTX_FL_TRACING);
|
|
32
|
+
C(CTX_FL_SKIPPED);
|
|
33
|
+
C(CTX_FL_IGNORE);
|
|
34
|
+
C(CTX_FL_DEAD);
|
|
35
|
+
C(CTX_FL_WAS_RUNNING);
|
|
36
|
+
C(CTX_FL_ENABLE_BKPT);
|
|
37
|
+
C(CTX_FL_STEPPED);
|
|
38
|
+
C(CTX_FL_FORCE_MOVE);
|
|
39
|
+
C(CTX_FL_CATCHING);
|
|
40
|
+
default:
|
|
41
|
+
return "unknown";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
#define CTX_DUMP(set, f) fprintf(stderr, "[CTX %s:%d] %s %s\n", __FILE__, __LINE__, (set) ? "set" : "unset", flag_name(f))
|
|
45
|
+
#else
|
|
46
|
+
#define CTX_DUMP(set, f)
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
#define CTX_FL_TEST(c,f) ((c)->flags & (f))
|
|
50
|
+
#define CTX_FL_SET(c,f) do { CTX_DUMP(1, f); (c)->flags |= (f); } while (0)
|
|
51
|
+
#define CTX_FL_UNSET(c,f) do { CTX_DUMP(0, f); (c)->flags &= ~(f); } while (0)
|
|
52
|
+
|
|
53
|
+
typedef struct {
|
|
54
|
+
VALUE thread_id;
|
|
55
|
+
int thnum;
|
|
56
|
+
int flags;
|
|
57
|
+
int calced_stack_size;
|
|
58
|
+
enum ctx_stop_reason stop_reason;
|
|
59
|
+
|
|
60
|
+
/* control flow */
|
|
61
|
+
int stop_next;
|
|
62
|
+
int dest_frame;
|
|
63
|
+
int stop_line;
|
|
64
|
+
int stop_frame;
|
|
65
|
+
|
|
66
|
+
int thread_pause;
|
|
67
|
+
|
|
68
|
+
VALUE last_file;
|
|
69
|
+
VALUE last_line;
|
|
70
|
+
|
|
71
|
+
VALUE breakpoint;
|
|
72
|
+
|
|
73
|
+
/* [[loc, self, klass, binding, iseq], ...] */
|
|
74
|
+
VALUE inspected_frame;
|
|
75
|
+
} debug_context_t;
|
|
76
|
+
|
|
77
|
+
enum inspected_frame_type {
|
|
78
|
+
INSPECTED_FRAME_LOCATION,
|
|
79
|
+
INSPECTED_FRAME_SELF,
|
|
80
|
+
INSPECTED_FRAME_CLASS,
|
|
81
|
+
INSPECTED_FRAME_BIDING,
|
|
82
|
+
INSPECTED_FRAME_ISEQ,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/* variables in ruby_debug.c */
|
|
86
|
+
extern VALUE mDebugger;
|
|
87
|
+
extern VALUE rdebug_breakpoints;
|
|
88
|
+
extern VALUE rdebug_catchpoints;
|
|
89
|
+
extern VALUE rdebug_threads_tbl;
|
|
90
|
+
|
|
91
|
+
#define IS_STARTED (rdebug_threads_tbl != Qnil)
|
|
92
|
+
static inline void
|
|
93
|
+
debug_check_started()
|
|
94
|
+
{
|
|
95
|
+
if (!IS_STARTED) {
|
|
96
|
+
rb_raise(rb_eRuntimeError, "Debugger.start is not called yet.");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static inline int
|
|
101
|
+
classname_cmp(VALUE name, VALUE klass)
|
|
102
|
+
{
|
|
103
|
+
VALUE mod_name;
|
|
104
|
+
VALUE class_name = (Qnil == name) ? rb_str_new2("main") : name;
|
|
105
|
+
if (klass == Qnil) return(0);
|
|
106
|
+
mod_name = rb_mod_name(klass);
|
|
107
|
+
return (mod_name != Qnil
|
|
108
|
+
&& rb_str_cmp(class_name, mod_name) == 0);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Breakpoint information */
|
|
112
|
+
enum bp_type {
|
|
113
|
+
BP_POS_TYPE,
|
|
114
|
+
BP_METHOD_TYPE
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
enum hit_condition {
|
|
118
|
+
HIT_COND_NONE,
|
|
119
|
+
HIT_COND_GE,
|
|
120
|
+
HIT_COND_EQ,
|
|
121
|
+
HIT_COND_MOD
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
typedef struct {
|
|
125
|
+
int id;
|
|
126
|
+
enum bp_type type;
|
|
127
|
+
VALUE source;
|
|
128
|
+
union {
|
|
129
|
+
int line;
|
|
130
|
+
ID mid;
|
|
131
|
+
} pos;
|
|
132
|
+
VALUE expr;
|
|
133
|
+
VALUE enabled;
|
|
134
|
+
int hit_count;
|
|
135
|
+
int hit_value;
|
|
136
|
+
enum hit_condition hit_condition;
|
|
137
|
+
} debug_breakpoint_t;
|
|
138
|
+
|
|
139
|
+
/* routines in breakpoint.c */
|
|
140
|
+
extern int check_breakpoint_expression(VALUE breakpoint, VALUE binding);
|
|
141
|
+
extern int check_breakpoint_hit_condition(VALUE breakpoint);
|
|
142
|
+
extern VALUE check_breakpoints_by_method(debug_context_t *debug_context, VALUE klass, ID mid, VALUE self);
|
|
143
|
+
extern VALUE check_breakpoints_by_pos(debug_context_t *debug_context, VALUE file, VALUE line);
|
|
144
|
+
extern VALUE create_breakpoint_from_args(int argc, VALUE *argv, int id);
|
|
145
|
+
extern VALUE context_breakpoint(VALUE self);
|
|
146
|
+
extern VALUE context_set_breakpoint(int argc, VALUE *argv, VALUE self);
|
|
147
|
+
extern VALUE rdebug_add_catchpoint(VALUE self, VALUE value);
|
|
148
|
+
extern VALUE debug_catchpoints(VALUE self);
|
|
149
|
+
extern VALUE rdebug_remove_breakpoint(VALUE self, VALUE id_value);
|
|
150
|
+
|
|
151
|
+
extern void Init_breakpoint();
|
data/lib/debugger.rb
ADDED
data/lib/debugger2.rb
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
require 'ruby_debug.so'
|
|
2
|
+
require 'linecache19'
|
|
3
|
+
|
|
4
|
+
module Debugger
|
|
5
|
+
|
|
6
|
+
# Default options to Debugger.start
|
|
7
|
+
DEFAULT_START_SETTINGS = {
|
|
8
|
+
:init => true, # Set $0 and save ARGV?
|
|
9
|
+
:post_mortem => false, # post-mortem debugging on uncaught exception?
|
|
10
|
+
:tracing => nil # Debugger.tracing value. true/false resets,
|
|
11
|
+
# nil keeps the prior value
|
|
12
|
+
} unless defined?(DEFAULT_START_SETTINGS)
|
|
13
|
+
|
|
14
|
+
class Context
|
|
15
|
+
def interrupt
|
|
16
|
+
self.stop_next = 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
alias __c_frame_binding frame_binding
|
|
20
|
+
def frame_binding(frame)
|
|
21
|
+
b = __c_frame_binding(frame)
|
|
22
|
+
return b if b
|
|
23
|
+
hbinding(frame)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def hbinding(frame)
|
|
29
|
+
hash = frame_locals(frame)
|
|
30
|
+
code = hash.keys.map{|k| "#{k} = hash['#{k}']" unless k=='self' }.compact.join(';') + ';binding'
|
|
31
|
+
if obj = frame_self(frame)
|
|
32
|
+
obj.instance_eval code
|
|
33
|
+
else
|
|
34
|
+
eval code, TOPLEVEL_BINDING
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def handler
|
|
39
|
+
Debugger.handler or raise 'No interface loaded'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def at_breakpoint(breakpoint)
|
|
43
|
+
handler.at_breakpoint(self, breakpoint)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def at_catchpoint(excpt)
|
|
47
|
+
handler.at_catchpoint(self, excpt)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def at_tracing(file, line)
|
|
51
|
+
@tracing_started = File.identical?(file, Debugger::PROG_SCRIPT)
|
|
52
|
+
handler.at_tracing(self, file, line) if @tracing_started
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def at_line(file, line)
|
|
56
|
+
handler.at_line(self, file, line)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def at_return(file, line)
|
|
60
|
+
handler.at_return(self, file, line)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@reload_source_on_change = false
|
|
65
|
+
@tracing_started = false
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
# interface modules provide +handler+ object
|
|
69
|
+
attr_accessor :handler
|
|
70
|
+
|
|
71
|
+
# if <tt>true</tt>, checks the modification time of source files and reloads if it was modified
|
|
72
|
+
attr_accessor :reload_source_on_change
|
|
73
|
+
|
|
74
|
+
attr_accessor :last_exception
|
|
75
|
+
Debugger.last_exception = nil
|
|
76
|
+
|
|
77
|
+
#
|
|
78
|
+
# Interrupts the current thread
|
|
79
|
+
#
|
|
80
|
+
def interrupt
|
|
81
|
+
current_context.interrupt
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#
|
|
85
|
+
# Interrupts the last debugged thread
|
|
86
|
+
#
|
|
87
|
+
def interrupt_last
|
|
88
|
+
if context = last_context
|
|
89
|
+
return nil unless context.thread.alive?
|
|
90
|
+
context.interrupt
|
|
91
|
+
end
|
|
92
|
+
context
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def source_reload
|
|
96
|
+
Object.send(:remove_const, "SCRIPT_LINES__") if Object.const_defined?("SCRIPT_LINES__")
|
|
97
|
+
Object.const_set("SCRIPT_LINES__", {})
|
|
98
|
+
LineCache::clear_file_cache
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Get line +line_number+ from file named +filename+. Return "\n"
|
|
102
|
+
# there was a problem. Leaking blanks are stripped off.
|
|
103
|
+
def line_at(filename, line_number) # :nodoc:
|
|
104
|
+
@reload_on_change=nil unless defined?(@reload_on_change)
|
|
105
|
+
line = LineCache::getline(filename, line_number, @reload_on_change)
|
|
106
|
+
return "\n" unless line
|
|
107
|
+
return "#{line.gsub(/^\s+/, '').chomp}\n"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Activates the post-mortem mode. There are two ways of using it:
|
|
112
|
+
#
|
|
113
|
+
# == Global post-mortem mode
|
|
114
|
+
# By calling Debugger.post_mortem method without a block, you install
|
|
115
|
+
# at_exit hook that intercepts any unhandled by your script exceptions
|
|
116
|
+
# and enables post-mortem mode.
|
|
117
|
+
#
|
|
118
|
+
# == Local post-mortem mode
|
|
119
|
+
#
|
|
120
|
+
# If you know that a particular block of code raises an exception you can
|
|
121
|
+
# enable post-mortem mode by wrapping this block with Debugger.post_mortem, e.g.
|
|
122
|
+
#
|
|
123
|
+
# def offender
|
|
124
|
+
# raise 'error'
|
|
125
|
+
# end
|
|
126
|
+
# Debugger.post_mortem do
|
|
127
|
+
# ...
|
|
128
|
+
# offender
|
|
129
|
+
# ...
|
|
130
|
+
# end
|
|
131
|
+
def post_mortem
|
|
132
|
+
if block_given?
|
|
133
|
+
old_post_mortem = self.post_mortem?
|
|
134
|
+
begin
|
|
135
|
+
self.post_mortem = true
|
|
136
|
+
yield
|
|
137
|
+
rescue Exception => exp
|
|
138
|
+
handle_post_mortem(exp)
|
|
139
|
+
raise
|
|
140
|
+
ensure
|
|
141
|
+
self.post_mortem = old_post_mortem
|
|
142
|
+
end
|
|
143
|
+
else
|
|
144
|
+
return if post_mortem?
|
|
145
|
+
self.post_mortem = true
|
|
146
|
+
debug_at_exit do
|
|
147
|
+
handle_post_mortem($!) if $! && post_mortem?
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def handle_post_mortem(exp)
|
|
153
|
+
return if !exp || !exp.__debug_context ||
|
|
154
|
+
exp.__debug_context.stack_size == 0
|
|
155
|
+
Debugger.suspend
|
|
156
|
+
orig_tracing = Debugger.tracing, Debugger.current_context.tracing
|
|
157
|
+
Debugger.tracing = Debugger.current_context.tracing = false
|
|
158
|
+
Debugger.last_exception = exp
|
|
159
|
+
handler.at_line(exp.__debug_context, exp.__debug_file, exp.__debug_line)
|
|
160
|
+
ensure
|
|
161
|
+
Debugger.tracing, Debugger.current_context.tracing = orig_tracing
|
|
162
|
+
Debugger.resume
|
|
163
|
+
end
|
|
164
|
+
# private :handle_post_mortem
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class DebugThread # :nodoc:
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class ThreadsTable # :nodoc:
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Debugger.start(options) -> bool
|
|
174
|
+
# Debugger.start(options) { ... } -> obj
|
|
175
|
+
#
|
|
176
|
+
# If it's called without a block it returns +true+, unless debugger
|
|
177
|
+
# was already started. If a block is given, it starts debugger and
|
|
178
|
+
# yields to block. When the block is finished executing it stops
|
|
179
|
+
# the debugger with Debugger.stop method.
|
|
180
|
+
#
|
|
181
|
+
# If a block is given, it starts debugger and yields to block. When
|
|
182
|
+
# the block is finished executing it stops the debugger with
|
|
183
|
+
# Debugger.stop method. Inside the block you will probably want to
|
|
184
|
+
# have a call to Debugger.debugger. For example:
|
|
185
|
+
#
|
|
186
|
+
# Debugger.start{debugger; foo} # Stop inside of foo
|
|
187
|
+
#
|
|
188
|
+
# Also, ruby-debug only allows
|
|
189
|
+
# one invocation of debugger at a time; nested Debugger.start's
|
|
190
|
+
# have no effect and you can't use this inside the debugger itself.
|
|
191
|
+
#
|
|
192
|
+
# <i>Note that if you want to stop debugger, you must call
|
|
193
|
+
# Debugger.stop as many time as you called Debugger.start
|
|
194
|
+
# method.</i>
|
|
195
|
+
#
|
|
196
|
+
# +options+ is a hash used to set various debugging options.
|
|
197
|
+
# Set :init true if you want to save ARGV and some variables which
|
|
198
|
+
# make a debugger restart possible. Only the first time :init is set true
|
|
199
|
+
# will values get set. Since ARGV is saved, you should make sure
|
|
200
|
+
# it hasn't been changed before the (first) call.
|
|
201
|
+
# Set :post_mortem true if you want to enter post-mortem debugging
|
|
202
|
+
# on an uncaught exception. Once post-mortem debugging is set, it can't
|
|
203
|
+
# be unset.
|
|
204
|
+
def start(options={}, &block)
|
|
205
|
+
options = Debugger::DEFAULT_START_SETTINGS.merge(options)
|
|
206
|
+
if options[:init]
|
|
207
|
+
Debugger.const_set('ARGV', ARGV.clone) unless
|
|
208
|
+
defined? Debugger::ARGV
|
|
209
|
+
Debugger.const_set('PROG_SCRIPT', $0) unless
|
|
210
|
+
defined? Debugger::PROG_SCRIPT
|
|
211
|
+
Debugger.const_set('INITIAL_DIR', Dir.pwd) unless
|
|
212
|
+
defined? Debugger::INITIAL_DIR
|
|
213
|
+
end
|
|
214
|
+
Debugger.tracing = options[:tracing] unless options[:tracing].nil?
|
|
215
|
+
retval = Debugger.started? ? block && block.call(self) : Debugger.start_(&block)
|
|
216
|
+
if options[:post_mortem]
|
|
217
|
+
post_mortem
|
|
218
|
+
end
|
|
219
|
+
return retval
|
|
220
|
+
end
|
|
221
|
+
module_function :start
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
module Kernel
|
|
225
|
+
|
|
226
|
+
# Enters the debugger in the current thread after _steps_ line events occur.
|
|
227
|
+
# Before entering the debugger startup script is read.
|
|
228
|
+
#
|
|
229
|
+
# Setting _steps_ to 0 will cause a break in the debugger subroutine
|
|
230
|
+
# and not wait for a line event to occur. You will have to go "up 1"
|
|
231
|
+
# in order to be back in your debugged program rather than the
|
|
232
|
+
# debugger. Settings _steps_ to 0 could be useful you want to stop
|
|
233
|
+
# right after the last statement in some scope, because the next
|
|
234
|
+
# step will take you out of some scope.
|
|
235
|
+
|
|
236
|
+
# If a block is given (and the debugger hasn't been started, we run the
|
|
237
|
+
# block under the debugger. Alas, when a block is given, we can't support
|
|
238
|
+
# running the startup script or support the steps option. FIXME.
|
|
239
|
+
def debugger(steps = 1, &block)
|
|
240
|
+
if block
|
|
241
|
+
Debugger.start({}, &block)
|
|
242
|
+
else
|
|
243
|
+
Debugger.start unless Debugger.started?
|
|
244
|
+
Debugger.run_init_script(StringIO.new)
|
|
245
|
+
if 0 == steps
|
|
246
|
+
Debugger.current_context.stop_frame = 0
|
|
247
|
+
else
|
|
248
|
+
Debugger.current_context.stop_next = steps
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
alias breakpoint debugger unless respond_to?(:breakpoint)
|
|
253
|
+
|
|
254
|
+
#
|
|
255
|
+
# Returns a binding of n-th call frame
|
|
256
|
+
#
|
|
257
|
+
def binding_n(n = 0)
|
|
258
|
+
Debugger.skip do
|
|
259
|
+
if RUBY_VERSION < "1.9"
|
|
260
|
+
Debugger.current_context.frame_binding(n+2)
|
|
261
|
+
else
|
|
262
|
+
Debugger.current_context.frame_binding(n+1)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
class Exception # :nodoc:
|
|
269
|
+
attr_reader :__debug_file, :__debug_line, :__debug_binding, :__debug_context
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
class Module
|
|
273
|
+
#
|
|
274
|
+
# Wraps the +meth+ method with Debugger.start {...} block.
|
|
275
|
+
#
|
|
276
|
+
def debug_method(meth)
|
|
277
|
+
old_meth = "__debugee_#{meth}"
|
|
278
|
+
old_meth = "#{$1}_set" if old_meth =~ /^(.+)=$/
|
|
279
|
+
alias_method old_meth.to_sym, meth
|
|
280
|
+
class_eval <<-EOD
|
|
281
|
+
def #{meth}(*args, &block)
|
|
282
|
+
Debugger.start do
|
|
283
|
+
debugger 2
|
|
284
|
+
#{old_meth}(*args, &block)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
EOD
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
#
|
|
291
|
+
# Wraps the +meth+ method with Debugger.post_mortem {...} block.
|
|
292
|
+
#
|
|
293
|
+
def post_mortem_method(meth)
|
|
294
|
+
old_meth = "__postmortem_#{meth}"
|
|
295
|
+
old_meth = "#{$1}_set" if old_meth =~ /^(.+)=$/
|
|
296
|
+
alias_method old_meth.to_sym, meth
|
|
297
|
+
class_eval <<-EOD
|
|
298
|
+
def #{meth}(*args, &block)
|
|
299
|
+
Debugger.start do |dbg|
|
|
300
|
+
dbg.post_mortem do
|
|
301
|
+
#{old_meth}(*args, &block)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
EOD
|
|
306
|
+
end
|
|
307
|
+
end
|