ruby-debug 0.2-mswin32 → 0.3-mswin32

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,23 +1,40 @@
1
- 0.2 (2006-06-17)
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-06-13)
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-06-12)
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-06-11)
33
+ 0.1.3 (2006-07-11)
17
34
  - Conditional breakpoints
18
35
  - Bugfixes
19
36
 
20
- 0.1.2 (2006-06-16)
37
+ 0.1.2 (2006-07-16)
21
38
  ========================
22
39
 
23
40
  - Initial release.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/rdoctask'
5
5
  SO_NAME = "ruby_debug.so"
6
6
 
7
7
  # ------- Default Package ----------
8
- RUBY_DEBUG_VERSION = "0.2"
8
+ RUBY_DEBUG_VERSION = "0.3"
9
9
 
10
10
  FILES = FileList[
11
11
  'Rakefile',
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
- require "socket"
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.start_server(options.host, options.port) if options.server
81
- Debugger.start
82
- debugger 2
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.2"
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 = 0;
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
- return rb_funcall(context, idAtLine, 3, file, line, binding);
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 rb_funcall(rb_mKernel, idEval, 2, RARRAY(args)->ptr[0], RARRAY(args)->ptr[1]);
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
- find_last_context()
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
- context = find_last_context();
607
- if(context != Qnil)
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, "interrupt", debug_interrupt, 0);
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 = Class.new do
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
- attr_reader :thread
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
- def start_server(host = nil, port = PORT)
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
- server = TCPServer.new(host, port)
94
- @thread = Thread.start do
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
- self.interface = interface
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