ruby-debug 0.2-mswin32 → 0.3-mswin32
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.
- data/CHANGES +22 -5
- data/Rakefile +1 -1
- data/bin/rdebug +16 -27
- data/ext/ruby_debug.c +20 -40
- data/lib/ruby-debug.rb +123 -45
- data/lib/ruby-debug/command.rb +76 -0
- data/lib/ruby-debug/commands/breakpoints.rb +136 -0
- data/lib/ruby-debug/commands/catchpoint.rb +40 -0
- data/lib/ruby-debug/commands/control.rb +55 -0
- data/lib/ruby-debug/commands/display.rb +106 -0
- data/lib/ruby-debug/commands/eval.rb +54 -0
- data/lib/ruby-debug/commands/frame.rb +125 -0
- data/lib/ruby-debug/commands/help.rb +47 -0
- data/lib/ruby-debug/commands/list.rb +67 -0
- data/lib/ruby-debug/commands/method.rb +53 -0
- data/lib/ruby-debug/commands/stepping.rb +104 -0
- data/lib/ruby-debug/commands/threads.rb +164 -0
- data/lib/ruby-debug/commands/tmate.rb +25 -0
- data/lib/ruby-debug/commands/trace.rb +33 -0
- data/lib/ruby-debug/commands/variables.rb +113 -0
- data/lib/ruby-debug/lock.rb +41 -0
- data/lib/ruby-debug/processor.rb +118 -555
- data/lib/ruby_debug.so +0 -0
- metadata +19 -3
- data/bin/remote +0 -30
data/CHANGES
CHANGED
@@ -1,23 +1,40 @@
|
|
1
|
-
0.
|
1
|
+
0.3 (2006-08-07)
|
2
|
+
- Renamed Debugger.start_server to Debugger.start_remote.
|
3
|
+
- Debugger.start_remote activates debugger by calling Debugger.start.
|
4
|
+
- Debugger.start_remote starts a control thread which listen on port 8990 and accepts control
|
5
|
+
commands, such as adding/deleting breakpoints, assigning catchpoint, etc. (Useful for GUI integration)
|
6
|
+
- New Debugger.wait_connection option. When it's true, Debugger.start_remote waits until
|
7
|
+
a remote connection is made.
|
8
|
+
- New Debugger.stop_on_connect option. When a remote connection is established, debugger
|
9
|
+
stops the main thread (Thread.main).
|
10
|
+
- 'interrupt' command is available for the control thread.
|
11
|
+
|
12
|
+
0.2.1 (2006-07-29)
|
13
|
+
- 'f[rame] nn' command selects a numbered frame. Frame numbers can be obtained by running frame
|
14
|
+
command without parameters.
|
15
|
+
- 'l[ist] =' show code in the context of the current line.
|
16
|
+
- 'tm[ate]' opens the current file in TextMate. Available only on Mac OSX.
|
17
|
+
|
18
|
+
0.2 (2006-07-17)
|
2
19
|
- Added the remote debugging. It should be activated by calling Debugger#start_server method.
|
3
20
|
- CHANGED: In order to activate the debugger, it's not enough to require 'ruby-debug'.
|
4
21
|
Debugger#start method must be called explicitly.
|
5
22
|
- Debugger used to evaluate anything you enter as long as it's not a command. Starting from
|
6
23
|
this version the 'eval' command must be used to evaluate an expression.
|
7
24
|
|
8
|
-
0.1.5 (2006-
|
25
|
+
0.1.5 (2006-07-13)
|
9
26
|
- Now the check for a breakpoint uses base filename of the source file.
|
10
27
|
- Removed compilation warnings when compiling with -Wall
|
11
28
|
|
12
|
-
0.1.4 (2006-
|
29
|
+
0.1.4 (2006-07-12)
|
13
30
|
- Remembers the previous command. Invoke it by typing a carriage return
|
14
31
|
at the command prompt.
|
15
32
|
|
16
|
-
0.1.3 (2006-
|
33
|
+
0.1.3 (2006-07-11)
|
17
34
|
- Conditional breakpoints
|
18
35
|
- Bugfixes
|
19
36
|
|
20
|
-
0.1.2 (2006-
|
37
|
+
0.1.2 (2006-07-16)
|
21
38
|
========================
|
22
39
|
|
23
40
|
- Initial release.
|
data/Rakefile
CHANGED
data/bin/rdebug
CHANGED
@@ -9,7 +9,10 @@ options = OpenStruct.new(
|
|
9
9
|
'server' => false,
|
10
10
|
'client' => false,
|
11
11
|
'host' => nil,
|
12
|
-
'port' => Debugger::PORT
|
12
|
+
'port' => Debugger::PORT,
|
13
|
+
'cport' => Debugger::PORT + 1,
|
14
|
+
'wait' => false,
|
15
|
+
'nostop' => false
|
13
16
|
)
|
14
17
|
|
15
18
|
opts = OptionParser.new do |opts|
|
@@ -20,9 +23,12 @@ EOB
|
|
20
23
|
opts.separator ""
|
21
24
|
opts.separator "Options:"
|
22
25
|
opts.on("-s", "--server", "Listen for remote connections") {options.server = true}
|
26
|
+
opts.on("-w", "--wait", "Wait for a client connection, implies -s option") {options.wait = true}
|
27
|
+
opts.on("-n", "--nostop", "Do not stop when a client connects, implies -s option") {options.nostop = true}
|
23
28
|
opts.on("-c", "--client", "Connect to remote debugger") {options.client = true}
|
24
29
|
opts.on("-h", "--host HOST", "Host name used for remote debugging") {|options.host|}
|
25
30
|
opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|options.port|}
|
31
|
+
opts.on("--cport PORT", Integer, "Port used for contol commands, implies -s option") {|options.port|}
|
26
32
|
opts.separator ""
|
27
33
|
opts.separator "Common options:"
|
28
34
|
opts.on_tail("--help", "Show this message") do
|
@@ -45,29 +51,7 @@ rescue StandardError => e
|
|
45
51
|
end
|
46
52
|
|
47
53
|
if options.client
|
48
|
-
|
49
|
-
interface = Debugger::LocalInterface.new
|
50
|
-
socket = TCPSocket.new(options.host, options.port)
|
51
|
-
puts "Connected."
|
52
|
-
|
53
|
-
catch(:exit) do
|
54
|
-
while (line = socket.gets)
|
55
|
-
case line
|
56
|
-
when /^PROMPT (.*)$/
|
57
|
-
input = interface.read_command($1)
|
58
|
-
throw :exit unless input
|
59
|
-
socket.puts input
|
60
|
-
when /^CONFIRM (.*)$/
|
61
|
-
input = interface.confirm($1)
|
62
|
-
throw :exit unless input
|
63
|
-
socket.puts input
|
64
|
-
else
|
65
|
-
print line
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
socket.close
|
70
|
-
puts
|
54
|
+
Debugger.start_client(options.host, options.port)
|
71
55
|
else
|
72
56
|
if ARGV.empty?
|
73
57
|
puts opts
|
@@ -77,8 +61,13 @@ else
|
|
77
61
|
end
|
78
62
|
|
79
63
|
trap('INT') { Debugger.interrupt_last }
|
80
|
-
Debugger.
|
81
|
-
Debugger.
|
82
|
-
|
64
|
+
Debugger.stop_on_connect = !options.nostop
|
65
|
+
Debugger.wait_connection = options.wait
|
66
|
+
if options.server
|
67
|
+
Debugger.start_remote(options.host, [options.port, options.cport])
|
68
|
+
else
|
69
|
+
Debugger.start
|
70
|
+
debugger 2
|
71
|
+
end
|
83
72
|
load ARGV.shift
|
84
73
|
end
|
data/ext/ruby_debug.c
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
#include <rubysig.h>
|
5
5
|
#include <st.h>
|
6
6
|
|
7
|
-
#define DEBUG_VERSION "0.
|
7
|
+
#define DEBUG_VERSION "0.3"
|
8
8
|
|
9
9
|
typedef struct {
|
10
10
|
int thnum;
|
@@ -56,7 +56,7 @@ static ID idList;
|
|
56
56
|
static ID idClear;
|
57
57
|
|
58
58
|
static int thnum_max = 0;
|
59
|
-
static int last_debugged_thnum =
|
59
|
+
static int last_debugged_thnum = -1;
|
60
60
|
|
61
61
|
static VALUE debug_suspend(VALUE);
|
62
62
|
static VALUE create_binding(VALUE);
|
@@ -176,13 +176,25 @@ save_current_position(VALUE context)
|
|
176
176
|
debug_context->last_file == Qnil || \
|
177
177
|
rb_str_cmp(debug_context->last_file, file) != 0)
|
178
178
|
|
179
|
+
static VALUE
|
180
|
+
call_at_line_unprotected(VALUE args)
|
181
|
+
{
|
182
|
+
VALUE context;
|
183
|
+
context = *RARRAY(args)->ptr;
|
184
|
+
rb_funcall2(context, idAtLine, RARRAY(args)->len - 1, RARRAY(args)->ptr + 1);
|
185
|
+
}
|
186
|
+
|
179
187
|
static VALUE
|
180
188
|
call_at_line(VALUE context, int thnum, VALUE binding, VALUE file, VALUE line)
|
181
189
|
{
|
190
|
+
VALUE args;
|
191
|
+
|
182
192
|
last_debugged_thnum = thnum;
|
183
193
|
save_current_position(context);
|
184
194
|
debug_suspend(mDebugger);
|
185
|
-
|
195
|
+
|
196
|
+
args = rb_ary_new3(4, context, file, line, binding);
|
197
|
+
return rb_protect(call_at_line_unprotected, args, 0);
|
186
198
|
}
|
187
199
|
|
188
200
|
static void
|
@@ -288,7 +300,7 @@ get_breakpoint_at(int index)
|
|
288
300
|
static VALUE
|
289
301
|
eval_expression(VALUE args)
|
290
302
|
{
|
291
|
-
return
|
303
|
+
return rb_funcall2(rb_mKernel, idEval, 2, RARRAY(args)->ptr);
|
292
304
|
}
|
293
305
|
|
294
306
|
static int
|
@@ -558,22 +570,6 @@ debug_set_catchpoint(VALUE self, VALUE value)
|
|
558
570
|
return value;
|
559
571
|
}
|
560
572
|
|
561
|
-
static VALUE
|
562
|
-
debug_interrupt(VALUE self)
|
563
|
-
{
|
564
|
-
VALUE thread, context;
|
565
|
-
debug_context_t *debug_context;
|
566
|
-
|
567
|
-
debug_check_started();
|
568
|
-
|
569
|
-
thread = rb_thread_current();
|
570
|
-
context = thread_context_lookup(thread);
|
571
|
-
Data_Get_Struct(context, debug_context_t, debug_context);
|
572
|
-
debug_context->stop_next = 1;
|
573
|
-
|
574
|
-
return Qnil;
|
575
|
-
}
|
576
|
-
|
577
573
|
static int
|
578
574
|
find_last_context_func(VALUE key, VALUE value, VALUE *result)
|
579
575
|
{
|
@@ -588,29 +584,14 @@ find_last_context_func(VALUE key, VALUE value, VALUE *result)
|
|
588
584
|
}
|
589
585
|
|
590
586
|
static VALUE
|
591
|
-
|
587
|
+
debug_last_interrupted(VALUE self)
|
592
588
|
{
|
593
589
|
VALUE result = Qnil;
|
594
|
-
rb_hash_foreach(threads_tbl, find_last_context_func, (st_data_t)&result);
|
595
|
-
return result;
|
596
|
-
}
|
597
|
-
|
598
|
-
static VALUE
|
599
|
-
debug_interrupt_last(VALUE self)
|
600
|
-
{
|
601
|
-
VALUE context = Qnil;
|
602
|
-
debug_context_t *debug_context;
|
603
590
|
|
604
591
|
debug_check_started();
|
605
592
|
|
606
|
-
|
607
|
-
|
608
|
-
{
|
609
|
-
Data_Get_Struct(context, debug_context_t, debug_context);
|
610
|
-
debug_context->stop_next = 1;
|
611
|
-
}
|
612
|
-
|
613
|
-
return Qnil;
|
593
|
+
rb_hash_foreach(threads_tbl, find_last_context_func, (st_data_t)&result);
|
594
|
+
return result;
|
614
595
|
}
|
615
596
|
|
616
597
|
static VALUE
|
@@ -953,8 +934,7 @@ Init_ruby_debug()
|
|
953
934
|
rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
|
954
935
|
rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0);
|
955
936
|
rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1);
|
956
|
-
rb_define_module_function(mDebugger, "
|
957
|
-
rb_define_module_function(mDebugger, "interrupt_last", debug_interrupt_last, 0);
|
937
|
+
rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
|
958
938
|
rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
|
959
939
|
rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
|
960
940
|
rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
|
data/lib/ruby-debug.rb
CHANGED
@@ -2,56 +2,22 @@ require 'pp'
|
|
2
2
|
require 'stringio'
|
3
3
|
require 'thread'
|
4
4
|
require 'ruby_debug.so'
|
5
|
+
require 'ruby-debug/lock'
|
5
6
|
require 'ruby-debug/processor'
|
6
7
|
|
7
8
|
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
|
8
9
|
|
9
10
|
module Debugger
|
10
|
-
MUTEX =
|
11
|
-
def initialize
|
12
|
-
@locker = nil
|
13
|
-
@waiting = []
|
14
|
-
@locked = false;
|
15
|
-
end
|
16
|
-
|
17
|
-
def locked?
|
18
|
-
@locked
|
19
|
-
end
|
20
|
-
|
21
|
-
def lock
|
22
|
-
return if Thread.critical
|
23
|
-
return if @locker == Thread.current
|
24
|
-
while (Thread.critical = true; @locked)
|
25
|
-
@waiting.push Thread.current
|
26
|
-
Thread.stop
|
27
|
-
end
|
28
|
-
@locked = true
|
29
|
-
@locker = Thread.current
|
30
|
-
Thread.critical = false
|
31
|
-
self
|
32
|
-
end
|
33
|
-
|
34
|
-
def unlock
|
35
|
-
return if Thread.critical
|
36
|
-
return unless @locked
|
37
|
-
unless @locker == Thread.current
|
38
|
-
raise RuntimeError, "unlocked by other"
|
39
|
-
end
|
40
|
-
Thread.critical = true
|
41
|
-
t = @waiting.shift
|
42
|
-
@locked = false
|
43
|
-
@locker = nil
|
44
|
-
Thread.critical = false
|
45
|
-
t.run if t
|
46
|
-
self
|
47
|
-
end
|
48
|
-
end.new
|
49
|
-
|
11
|
+
MUTEX = Lock.new
|
50
12
|
PORT = 8989
|
51
13
|
|
52
14
|
@processor = CommandProcessor.new
|
53
15
|
|
54
16
|
class Context
|
17
|
+
def interrupt
|
18
|
+
self.stop_next = 1
|
19
|
+
end
|
20
|
+
|
55
21
|
private
|
56
22
|
|
57
23
|
def processor
|
@@ -80,23 +46,135 @@ module Debugger
|
|
80
46
|
|
81
47
|
class << self
|
82
48
|
attr_accessor :processor
|
83
|
-
|
49
|
+
|
50
|
+
# stop main thread when remote connection established
|
51
|
+
attr_accessor :stop_on_connect
|
52
|
+
|
53
|
+
# in remote mode, wait for the remote connection
|
54
|
+
attr_accessor :wait_connection
|
55
|
+
|
56
|
+
attr_reader :thread, :control_thread
|
57
|
+
|
58
|
+
# Interrupts the main thread
|
59
|
+
def interrupt
|
60
|
+
context = contexts.find{|c| c.thread == Thread.current }
|
61
|
+
context.interrupt
|
62
|
+
end
|
63
|
+
|
64
|
+
# Interrupts the last debugged thread
|
65
|
+
def interrupt_last
|
66
|
+
if context = last_context
|
67
|
+
context.interrupt
|
68
|
+
end
|
69
|
+
context
|
70
|
+
end
|
84
71
|
|
85
72
|
def interface=(value)
|
86
73
|
processor.interface = value
|
87
74
|
end
|
88
75
|
|
89
|
-
|
76
|
+
# Starts a remote debugger.
|
77
|
+
def start_remote(host = nil, port = PORT)
|
90
78
|
return if @thread
|
79
|
+
return if started?
|
80
|
+
|
81
|
+
self.interface = nil
|
82
|
+
start
|
83
|
+
|
91
84
|
require "socket"
|
92
85
|
|
93
|
-
|
94
|
-
|
86
|
+
if port.kind_of?(Array)
|
87
|
+
cmd_port, ctrl_port = port
|
88
|
+
else
|
89
|
+
cmd_port, ctrl_port = port, port + 1
|
90
|
+
end
|
91
|
+
|
92
|
+
@control_tread = Thread.start do
|
93
|
+
server = TCPServer.new(host, ctrl_port)
|
95
94
|
while (session = server.accept)
|
96
95
|
interface = RemoteInterface.new(session)
|
97
|
-
|
96
|
+
processor = ControlCommandProcessor.new(interface)
|
97
|
+
processor.process_commands
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
mutex = Mutex.new
|
102
|
+
proceed = ConditionVariable.new
|
103
|
+
|
104
|
+
@thread = Thread.start do
|
105
|
+
server = TCPServer.new(host, cmd_port)
|
106
|
+
while (session = server.accept)
|
107
|
+
self.interface = RemoteInterface.new(session)
|
108
|
+
if wait_connection
|
109
|
+
mutex.synchronize do
|
110
|
+
proceed.signal
|
111
|
+
end
|
112
|
+
else
|
113
|
+
stop_main_thread
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
if wait_connection
|
118
|
+
mutex.synchronize do
|
119
|
+
proceed.wait(mutex)
|
120
|
+
end
|
121
|
+
stop_main_thread
|
122
|
+
end
|
123
|
+
end
|
124
|
+
alias start_server start_remote
|
125
|
+
|
126
|
+
# Connects to the remote debugger
|
127
|
+
def start_client(host = 'localhost', port = PORT)
|
128
|
+
require "socket"
|
129
|
+
interface = Debugger::LocalInterface.new
|
130
|
+
socket = TCPSocket.new(host, port)
|
131
|
+
puts "Connected."
|
132
|
+
|
133
|
+
catch(:exit) do
|
134
|
+
while (line = socket.gets)
|
135
|
+
case line
|
136
|
+
when /^PROMPT (.*)$/
|
137
|
+
input = interface.read_command($1)
|
138
|
+
throw :exit unless input
|
139
|
+
socket.puts input
|
140
|
+
when /^CONFIRM (.*)$/
|
141
|
+
input = interface.confirm($1)
|
142
|
+
throw :exit unless input
|
143
|
+
socket.puts input
|
144
|
+
else
|
145
|
+
print line
|
146
|
+
end
|
98
147
|
end
|
99
148
|
end
|
149
|
+
socket.close
|
150
|
+
puts
|
151
|
+
end
|
152
|
+
|
153
|
+
def stop_main_thread
|
154
|
+
return unless stop_on_connect
|
155
|
+
|
156
|
+
context = contexts.find{ |c| c.thread == Thread.main }
|
157
|
+
context.stop_next = 2
|
158
|
+
end
|
159
|
+
private :stop_main_thread
|
160
|
+
|
161
|
+
def source_for(file)
|
162
|
+
if source = SCRIPT_LINES__[file]
|
163
|
+
return source unless source == true
|
164
|
+
end
|
165
|
+
if File.exists?(file)
|
166
|
+
SCRIPT_LINES__[file] = File.readlines(file)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def line_at(file, line)
|
171
|
+
lines = source_for(file)
|
172
|
+
if lines
|
173
|
+
line = lines[line-1]
|
174
|
+
return "\n" unless line
|
175
|
+
return "#{line.gsub(/^\s+/, '').chomp}\n"
|
176
|
+
end
|
177
|
+
return "\n"
|
100
178
|
end
|
101
179
|
end
|
102
180
|
end
|