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 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