debug 1.3.4 → 1.6.0

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/exe/rdbg CHANGED
@@ -3,7 +3,9 @@
3
3
  require_relative '../lib/debug/config'
4
4
  config = DEBUGGER__::Config::parse_argv(ARGV)
5
5
 
6
- case config[:mode]
6
+ # mode is not an actual configuration option
7
+ # it's only used to carry the result of parse_argv here
8
+ case config.delete(:mode)
7
9
  when :start
8
10
  require 'rbconfig'
9
11
 
@@ -12,13 +14,15 @@ when :start
12
14
  cmd = config[:command] ? ARGV.shift : (ENV['RUBY'] || RbConfig.ruby)
13
15
 
14
16
  env = ::DEBUGGER__::Config.config_to_env_hash(config)
15
- env['RUBYOPT'] = "-r #{libpath}/#{start_mode}"
17
+ rubyopt = env['RUBYOPT']
18
+ env['RUBY_DEBUG_ADDED_RUBYOPT'] = added = "-r #{libpath}/#{start_mode}"
19
+ env['RUBYOPT'] = "#{added} #{rubyopt}"
16
20
 
17
21
  exec(env, cmd, *ARGV)
18
22
 
19
23
  when :attach
20
24
  require_relative "../lib/debug/client"
21
- ::DEBUGGER__::CONFIG.update config
25
+ ::DEBUGGER__::CONFIG.set_config **config
22
26
 
23
27
  begin
24
28
  if ARGV.empty? && config[:port]
data/ext/debug/debug.c CHANGED
@@ -32,7 +32,10 @@ di_entry(VALUE loc, VALUE self, VALUE binding, VALUE iseq, VALUE klass, VALUE de
32
32
  // :show_line, :local_variables
33
33
  Qnil,
34
34
  // :_local_variables, :_callee # for recorder
35
- Qnil, Qnil);
35
+ Qnil, Qnil,
36
+ // :dupped_binding
37
+ Qnil
38
+ );
36
39
  }
37
40
 
38
41
  static int
@@ -94,32 +97,82 @@ frame_depth(VALUE self)
94
97
  return INT2FIX(RARRAY_LEN(bt));
95
98
  }
96
99
 
97
- static void
98
- method_added_tracker(VALUE tpval, void *ptr)
100
+ // iseq
101
+
102
+ const struct rb_iseq *rb_iseqw_to_iseq(VALUE iseqw);
103
+
104
+ #ifdef HAVE_RB_ISEQ_TYPE
105
+ VALUE rb_iseq_type(const struct rb_iseq *);
106
+
107
+ static VALUE
108
+ iseq_type(VALUE iseqw)
99
109
  {
100
- rb_trace_arg_t *arg = rb_tracearg_from_tracepoint(tpval);
101
- VALUE mid = rb_tracearg_callee_id(arg);
102
-
103
- if (RB_UNLIKELY(mid == ID2SYM(rb_intern("method_added")) ||
104
- mid == ID2SYM(rb_intern("singleton_method_added")))) {
105
- VALUE args[] = {
106
- tpval,
107
- };
108
- rb_funcallv(rb_mDebugger, rb_intern("method_added"), 1, args);
110
+ const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
111
+ return rb_iseq_type(iseq);
112
+ }
113
+ #endif
114
+
115
+ #ifdef HAVE_RB_ISEQ_PARAMETERS
116
+ VALUE rb_iseq_parameters(const struct rb_iseq *, int is_proc);
117
+
118
+ static VALUE
119
+ iseq_parameters_symbols(VALUE iseqw)
120
+ {
121
+ const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
122
+ VALUE params = rb_iseq_parameters(iseq, 0);
123
+ VALUE ary = rb_ary_new();
124
+
125
+ static VALUE sym_ast, sym_astast, sym_amp;
126
+
127
+ if (sym_ast == 0) {
128
+ sym_ast = ID2SYM(rb_intern("*"));
129
+ sym_astast = ID2SYM(rb_intern("**"));
130
+ sym_amp = ID2SYM(rb_intern("&"));
131
+ }
132
+
133
+ for (long i=0; i<RARRAY_LEN(params); i++) {
134
+ VALUE e = RARRAY_AREF(params, i);
135
+ if (RARRAY_LEN(e) == 2) {
136
+ VALUE sym = RARRAY_AREF(e, 1);
137
+ if (sym != sym_ast &&
138
+ sym != sym_astast &&
139
+ sym != sym_amp) rb_ary_push(ary, RARRAY_AREF(e, 1));
140
+ }
109
141
  }
142
+
143
+ return ary;
110
144
  }
145
+ #endif
146
+
147
+ #ifdef HAVE_RB_ISEQ_CODE_LOCATION
148
+ void rb_iseq_code_location(const struct rb_iseq *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);
111
149
 
112
150
  static VALUE
113
- create_method_added_tracker(VALUE self)
151
+ iseq_first_line(VALUE iseqw)
114
152
  {
115
- return rb_tracepoint_new(0, RUBY_EVENT_CALL, method_added_tracker, NULL);
153
+ const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
154
+ int line;
155
+ rb_iseq_code_location(iseq, &line, NULL, NULL, NULL);
156
+ return INT2NUM(line);
116
157
  }
117
158
 
159
+ static VALUE
160
+ iseq_last_line(VALUE iseqw)
161
+ {
162
+ const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
163
+ int line;
164
+ rb_iseq_code_location(iseq, NULL, NULL, &line, NULL);
165
+ return INT2NUM(line);
166
+ }
167
+ #endif
168
+
118
169
  void Init_iseq_collector(void);
119
170
 
120
171
  void
121
172
  Init_debug(void)
122
173
  {
174
+ VALUE rb_mRubyVM = rb_const_get(rb_cObject, rb_intern("RubyVM"));
175
+ VALUE rb_cISeq = rb_const_get(rb_mRubyVM, rb_intern("InstructionSequence"));
123
176
  rb_mDebugger = rb_const_get(rb_cObject, rb_intern("DEBUGGER__"));
124
177
  rb_cFrameInfo = rb_const_get(rb_mDebugger, rb_intern("FrameInfo"));
125
178
 
@@ -129,7 +182,19 @@ Init_debug(void)
129
182
  rb_gc_register_mark_object(rb_cFrameInfo);
130
183
  rb_define_singleton_method(rb_mDebugger, "capture_frames", capture_frames, 1);
131
184
  rb_define_singleton_method(rb_mDebugger, "frame_depth", frame_depth, 0);
132
- rb_define_singleton_method(rb_mDebugger, "create_method_added_tracker", create_method_added_tracker, 0);
133
185
  rb_define_const(rb_mDebugger, "SO_VERSION", rb_str_new2(RUBY_DEBUG_VERSION));
186
+
187
+ // iseq
188
+ #ifdef HAVE_RB_ISEQ_TYPE
189
+ rb_define_method(rb_cISeq, "type", iseq_type, 0);
190
+ #endif
191
+ #ifdef HAVE_RB_ISEQ_PARAMETERS
192
+ rb_define_method(rb_cISeq, "parameters_symbols", iseq_parameters_symbols, 0);
193
+ #endif
194
+ #ifdef HAVE_RB_ISEQ_CODE_LOCATION
195
+ rb_define_method(rb_cISeq, "first_line", iseq_first_line, 0);
196
+ rb_define_method(rb_cISeq, "last_line", iseq_last_line, 0);
197
+ #endif
198
+
134
199
  Init_iseq_collector();
135
200
  }
data/ext/debug/extconf.rb CHANGED
@@ -1,4 +1,26 @@
1
1
  require 'mkmf'
2
2
  require_relative '../../lib/debug/version'
3
3
  File.write("debug_version.h", "#define RUBY_DEBUG_VERSION \"#{DEBUGGER__::VERSION}\"\n")
4
+
5
+
6
+ if defined? RubyVM
7
+ $defs << '-DHAVE_RB_ISEQ_PARAMETERS'
8
+ $defs << '-DHAVE_RB_ISEQ_CODE_LOCATION'
9
+
10
+ if RUBY_VERSION >= '3.1.0'
11
+ $defs << '-DHAVE_RB_ISEQ_TYPE'
12
+ end
13
+ else
14
+ # not on MRI
15
+
16
+ have_func "rb_iseq_parameters(NULL, 0)",
17
+ [["VALUE rb_iseq_parameters(void *, int is_proc);"]]
18
+
19
+ have_func "rb_iseq_code_location(NULL, NULL, NULL, NULL, NULL)",
20
+ [["void rb_iseq_code_location(void *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);"]]
21
+ # from Ruby 3.1
22
+ have_func "rb_iseq_type(NULL)",
23
+ [["VALUE rb_iseq_type(void *);"]]
24
+ end
25
+
4
26
  create_makefile 'debug/debug'
@@ -4,11 +4,17 @@ require_relative 'color'
4
4
 
5
5
  module DEBUGGER__
6
6
  class Breakpoint
7
+ include SkipPathHelper
8
+
7
9
  attr_reader :key
8
10
 
9
- def initialize do_enable = true
11
+ def initialize cond, command, path, do_enable: true
10
12
  @deleted = false
11
13
 
14
+ @cond = cond
15
+ @command = command
16
+ @path = path
17
+
12
18
  setup
13
19
  enable if do_enable
14
20
  end
@@ -23,6 +29,10 @@ module DEBUGGER__
23
29
  nil
24
30
  end
25
31
 
32
+ def oneshot?
33
+ defined?(@oneshot) && @oneshot
34
+ end
35
+
26
36
  def setup
27
37
  raise "not implemented..."
28
38
  end
@@ -75,6 +85,17 @@ module DEBUGGER__
75
85
  false
76
86
  end
77
87
 
88
+ def skip_path?(path)
89
+ case @path
90
+ when Regexp
91
+ !path.match?(@path)
92
+ when String
93
+ !path.include?(@path)
94
+ else
95
+ super
96
+ end
97
+ end
98
+
78
99
  include Color
79
100
 
80
101
  def generate_label(name)
@@ -87,26 +108,53 @@ module DEBUGGER__
87
108
  TracePoint.new(:line){}.enable{}
88
109
  end
89
110
 
111
+ class ISeqBreakpoint < Breakpoint
112
+ def initialize iseq, events, oneshot: false
113
+ @events = events
114
+ @iseq = iseq
115
+ @oneshot = oneshot
116
+ @key = [:iseq, @iseq.path, @iseq.first_lineno].freeze
117
+
118
+ super(nil, nil, nil)
119
+ end
120
+
121
+ def setup
122
+ @tp = TracePoint.new(*@events) do |tp|
123
+ delete if @oneshot
124
+ suspend
125
+ end
126
+ end
127
+
128
+ def enable
129
+ @tp.enable(target: @iseq)
130
+ end
131
+ end
132
+
90
133
  class LineBreakpoint < Breakpoint
91
- attr_reader :path, :line, :iseq
134
+ attr_reader :path, :line, :iseq, :cond, :oneshot, :hook_call, :command
92
135
 
93
- def initialize path, line, cond: nil, oneshot: false, hook_call: true, command: nil
94
- @path = path
136
+ def self.copy bp, root_iseq
137
+ nbp = LineBreakpoint.new bp.path, bp.line,
138
+ cond: bp.cond, oneshot: bp.oneshot, hook_call: bp.hook_call,
139
+ command: bp.command, skip_activate: true
140
+ nbp.try_activate root_iseq
141
+ nbp
142
+ end
143
+
144
+ def initialize path, line, cond: nil, oneshot: false, hook_call: true, command: nil, skip_activate: false
95
145
  @line = line
96
- @cond = cond
97
146
  @oneshot = oneshot
98
147
  @hook_call = hook_call
99
- @command = command
100
148
  @pending = false
101
149
 
102
150
  @iseq = nil
103
151
  @type = nil
104
152
 
105
- @key = [@path, @line].freeze
153
+ @key = [path, @line].freeze
106
154
 
107
- super()
155
+ super(cond, command, path)
108
156
 
109
- try_activate
157
+ try_activate unless skip_activate
110
158
  @pending = !@iseq
111
159
  end
112
160
 
@@ -166,7 +214,7 @@ module DEBUGGER__
166
214
  when events.include?(:RUBY_EVENT_END)
167
215
  activate(iseq, :end, line)
168
216
  else
169
- # not actiavated
217
+ # not activated
170
218
  end
171
219
  end
172
220
 
@@ -176,42 +224,56 @@ module DEBUGGER__
176
224
 
177
225
  NearestISeq = Struct.new(:iseq, :line, :events)
178
226
 
179
- def try_activate
180
- nearest = nil # NearestISeq
181
-
182
- ObjectSpace.each_iseq{|iseq|
183
- if (iseq.absolute_path || iseq.path) == self.path &&
184
- iseq.first_lineno <= self.line &&
185
- iseq.type != :ensure # ensure iseq is copied (duplicated)
227
+ def iterate_iseq root_iseq
228
+ if root_iseq
229
+ is = [root_iseq]
230
+ while iseq = is.pop
231
+ yield iseq
232
+ iseq.each_child do |child_iseq|
233
+ is << child_iseq
234
+ end
235
+ end
236
+ else
237
+ ObjectSpace.each_iseq do |iseq|
238
+ if DEBUGGER__.compare_path((iseq.absolute_path || iseq.path), self.path) &&
239
+ iseq.first_lineno <= self.line &&
240
+ iseq.type != :ensure # ensure iseq is copied (duplicated)
241
+ yield iseq
242
+ end
243
+ end
244
+ end
245
+ end
186
246
 
187
- iseq.traceable_lines_norec(line_events = {})
188
- lines = line_events.keys.sort
247
+ def try_activate root_iseq = nil
248
+ nearest = nil # NearestISeq
249
+ iterate_iseq root_iseq do |iseq|
250
+ iseq.traceable_lines_norec(line_events = {})
251
+ lines = line_events.keys.sort
189
252
 
190
- if !lines.empty? && lines.last >= line
191
- nline = lines.bsearch{|l| line <= l}
192
- events = line_events[nline]
253
+ if !lines.empty? && lines.last >= line
254
+ nline = lines.bsearch{|l| line <= l}
255
+ events = line_events[nline]
193
256
 
194
- next if events == [:RUBY_EVENT_B_CALL]
257
+ next if events == [:RUBY_EVENT_B_CALL]
195
258
 
196
- if @hook_call &&
197
- events.include?(:RUBY_EVENT_CALL) &&
198
- self.line == iseq.first_lineno
199
- nline = iseq.first_lineno
200
- end
259
+ if @hook_call &&
260
+ events.include?(:RUBY_EVENT_CALL) &&
261
+ self.line == iseq.first_lineno
262
+ nline = iseq.first_lineno
263
+ end
201
264
 
202
- if !nearest || ((line - nline).abs < (line - nearest.line).abs)
203
- nearest = NearestISeq.new(iseq, nline, events)
204
- else
205
- if @hook_call && nearest.iseq.first_lineno <= iseq.first_lineno
206
- if (nearest.line > line && !nearest.events.include?(:RUBY_EVENT_CALL)) ||
207
- (events.include?(:RUBY_EVENT_CALL))
208
- nearest = NearestISeq.new(iseq, nline, events)
209
- end
265
+ if !nearest || ((line - nline).abs < (line - nearest.line).abs)
266
+ nearest = NearestISeq.new(iseq, nline, events)
267
+ else
268
+ if @hook_call && nearest.iseq.first_lineno <= iseq.first_lineno
269
+ if (nearest.line > line && !nearest.events.include?(:RUBY_EVENT_CALL)) ||
270
+ (events.include?(:RUBY_EVENT_CALL))
271
+ nearest = NearestISeq.new(iseq, nline, events)
210
272
  end
211
273
  end
212
274
  end
213
275
  end
214
- }
276
+ end
215
277
 
216
278
  if nearest
217
279
  activate_exact nearest.iseq, nearest.events, nearest.line
@@ -235,24 +297,21 @@ module DEBUGGER__
235
297
 
236
298
  class CatchBreakpoint < Breakpoint
237
299
  attr_reader :last_exc
238
- include SkipPathHelper
239
300
 
240
- def initialize pat, cond: nil, command: nil
301
+ def initialize pat, cond: nil, command: nil, path: nil
241
302
  @pat = pat.freeze
242
303
  @key = [:catch, @pat].freeze
243
304
  @last_exc = nil
244
305
 
245
- @cond = cond
246
- @command = command
247
-
248
- super()
306
+ super(cond, command, path)
249
307
  end
250
308
 
251
309
  def setup
252
310
  @tp = TracePoint.new(:raise){|tp|
253
- next if skip_path?(tp.path)
254
311
  exc = tp.raised_exception
255
312
  next if SystemExit === exc
313
+ next if skip_path?(tp.path)
314
+
256
315
  next if !safe_eval(tp.binding, @cond) if @cond
257
316
  should_suspend = false
258
317
 
@@ -277,47 +336,65 @@ module DEBUGGER__
277
336
  end
278
337
 
279
338
  class CheckBreakpoint < Breakpoint
280
- def initialize expr
281
- @expr = expr.freeze
282
- @key = [:check, @expr].freeze
339
+ def initialize cond:, command: nil, path: nil
340
+ @key = [:check, cond].freeze
283
341
 
284
- super()
342
+ super(cond, command, path)
285
343
  end
286
344
 
287
345
  def setup
288
346
  @tp = TracePoint.new(:line){|tp|
289
- next if tp.path.start_with? __dir__
290
- next if tp.path.start_with? '<internal:'
347
+ next if SESSION.in_subsession? # TODO: Ractor support
291
348
  next if ThreadClient.current.management?
349
+ next if skip_path?(tp.path)
292
350
 
293
- if safe_eval tp.binding, @expr
351
+ if need_suspend? safe_eval(tp.binding, @cond)
294
352
  suspend
295
353
  end
296
354
  }
297
355
  end
298
356
 
357
+ private def need_suspend? cond_result
358
+ map = ThreadClient.current.check_bp_fulfillment_map
359
+ if cond_result
360
+ if map[self]
361
+ false
362
+ else
363
+ map[self] = true
364
+ end
365
+ else
366
+ map[self] = false
367
+ end
368
+ end
369
+
299
370
  def to_s
300
- "#{generate_label("Check")} #{@expr}"
371
+ s = "#{generate_label("Check")}"
372
+ s += super
373
+ s
301
374
  end
302
375
  end
303
376
 
304
377
  class WatchIVarBreakpoint < Breakpoint
305
- def initialize ivar, object, current
378
+ def initialize ivar, object, current, cond: nil, command: nil, path: nil
306
379
  @ivar = ivar.to_sym
307
380
  @object = object
308
- @key = [:watch, @ivar].freeze
381
+ @key = [:watch, object.object_id, @ivar].freeze
309
382
 
310
383
  @current = current
311
- super()
384
+
385
+ super(cond, command, path)
312
386
  end
313
387
 
314
- def watch_eval
388
+ def watch_eval(tp)
315
389
  result = @object.instance_variable_get(@ivar)
316
390
  if result != @current
317
391
  begin
318
392
  @prev = @current
319
393
  @current = result
320
- suspend
394
+
395
+ if (@cond.nil? || @object.instance_eval(@cond)) && !skip_path?(tp.path)
396
+ suspend
397
+ end
321
398
  ensure
322
399
  remove_instance_variable(:@prev)
323
400
  end
@@ -328,10 +405,7 @@ module DEBUGGER__
328
405
 
329
406
  def setup
330
407
  @tp = TracePoint.new(:line, :return, :b_return){|tp|
331
- next if tp.path.start_with? __dir__
332
- next if tp.path.start_with? '<internal:'
333
-
334
- watch_eval
408
+ watch_eval(tp)
335
409
  }
336
410
  end
337
411
 
@@ -347,9 +421,9 @@ module DEBUGGER__
347
421
  end
348
422
 
349
423
  class MethodBreakpoint < Breakpoint
350
- attr_reader :sig_method_name, :method
424
+ attr_reader :sig_method_name, :method, :klass
351
425
 
352
- def initialize b, klass_name, op, method_name, cond: nil, command: nil
426
+ def initialize b, klass_name, op, method_name, cond: nil, command: nil, path: nil
353
427
  @sig_klass_name = klass_name
354
428
  @sig_op = op
355
429
  @sig_method_name = method_name
@@ -358,19 +432,19 @@ module DEBUGGER__
358
432
 
359
433
  @klass = nil
360
434
  @method = nil
361
- @cond = cond
362
435
  @cond_class = nil
363
- @command = command
364
436
  @key = "#{klass_name}#{op}#{method_name}".freeze
365
437
 
366
- super(false)
438
+ super(cond, command, path, do_enable: false)
367
439
  end
368
440
 
369
441
  def setup
370
442
  @tp = TracePoint.new(:call){|tp|
371
443
  next if !safe_eval(tp.binding, @cond) if @cond
372
444
  next if @cond_class && !tp.self.kind_of?(@cond_class)
373
- next if @override_method ? (caller_locations(2, 1).first.to_s.start_with?(__dir__)) : tp.path.start_with?(__dir__)
445
+
446
+ caller_location = caller_locations(2, 1).first.to_s
447
+ next if skip_path?(caller_location)
374
448
 
375
449
  suspend
376
450
  }
data/lib/debug/client.rb CHANGED
@@ -18,29 +18,69 @@ module DEBUGGER__
18
18
  case name
19
19
  when 'gen-sockpath'
20
20
  puts DEBUGGER__.create_unix_domain_socket_name
21
+ when 'gen-portpath'
22
+ port_path = File.join(DEBUGGER__.unix_domain_socket_dir, 'tcp_port')
23
+ File.unlink port_path if File.exist?(port_path)
24
+ puts port_path
21
25
  when 'list-socks'
22
26
  cleanup_unix_domain_sockets
23
27
  puts list_connections
24
- when 'init'
25
- if ARGV.shift == '-'
26
- puts <<~EOS
27
- export RUBYOPT="-r #{__dir__}/prelude $(RUBYOPT)"
28
- EOS
29
- else
30
- puts <<~EOS
31
- # add the following lines in your .bash_profile
32
-
33
- eval "$(rdbg init -)"
34
- EOS
35
- end
28
+ when 'setup-autoload'
29
+ setup_autoload
30
+ else
31
+ abort "Unknown utility: #{name}"
32
+ end
33
+ end
34
+
35
+ def working_shell_type
36
+ shell = `ps -p #{Process.ppid} -o 'args='`
37
+ case shell
38
+ when /bash/
39
+ :bash
40
+ when /fish/
41
+ :fish
42
+ when /csh/
43
+ :csh
44
+ when /zsh/
45
+ :szh
46
+ when /dash/
47
+ :dash
48
+ else
49
+ :unknown
50
+ end
51
+ end
52
+
53
+ def setup_autoload
54
+ prelude_path = File.join(__dir__, 'prelude.rb')
55
+
56
+ case shell = working_shell_type
57
+ when :bash, :zsh
58
+ puts <<~EOS
59
+ # add the following lines in your ~/.#{shell}_profile
60
+
61
+ if test -s #{prelude_path} ; then
62
+ export RUBYOPT='-r #{prelude_path}'
63
+ fi
64
+
65
+ # Add `Kernel#bb` method which is alias of `Kernel#debugger`
66
+ # export RUBY_DEBUG_BB=1
67
+ EOS
68
+
69
+ when :fish
70
+ puts <<~EOS
71
+ # add the following lines in your ~/.config/fish/config.fish
72
+ set -x RUBYOPT "-r #{__dir__}/prelude" $RUBYOPT
73
+ EOS
74
+
36
75
  else
37
- raise "Unknown utility: #{name}"
76
+ puts "# Sorry that your shell is not supported yet.",
77
+ "# Please use the content in #{prelude_path} as a reference and modify your login script accordingly."
38
78
  end
39
79
  end
40
80
 
41
81
  def cleanup_unix_domain_sockets
42
82
  Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*') do |file|
43
- if /(\d+)$/ =~ file
83
+ if File.socket?(file) && (/-(\d+)-\d+$/ =~ file || /-(\d+)$/ =~ file)
44
84
  begin
45
85
  Process.kill(0, $1.to_i)
46
86
  rescue Errno::EPERM
@@ -52,7 +92,9 @@ module DEBUGGER__
52
92
  end
53
93
 
54
94
  def list_connections
55
- Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*')
95
+ Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*').find_all do |path|
96
+ File.socket?(path)
97
+ end
56
98
  end
57
99
  end
58
100
 
@@ -79,7 +121,10 @@ module DEBUGGER__
79
121
  @width = IO.console_size[1]
80
122
  @width = 80 if @width == 0
81
123
 
82
- send "version: #{VERSION} width: #{@width} cookie: #{CONFIG[:cookie]}"
124
+ send "version: #{VERSION} " +
125
+ "width: #{@width} " +
126
+ "cookie: #{CONFIG[:cookie] || '-'} " +
127
+ "nonstop: #{CONFIG[:nonstop] ? 'true' : 'false'}"
83
128
  end
84
129
 
85
130
  def deactivate
@@ -131,12 +176,19 @@ module DEBUGGER__
131
176
  end
132
177
 
133
178
  def connect
179
+ pre_commands = (CONFIG[:commands] || '').split(';;')
180
+
134
181
  trap(:SIGINT){
135
182
  send "pause"
136
183
  }
137
- trap(:SIGWINCH){
138
- @width = IO.console_size[1]
139
- }
184
+
185
+ begin
186
+ trap(:SIGWINCH){
187
+ @width = IO.console_size[1]
188
+ }
189
+ rescue ArgumentError
190
+ @width = 80
191
+ end
140
192
 
141
193
  while line = @s.gets
142
194
  p recv: line if $VERBOSE
@@ -152,7 +204,12 @@ module DEBUGGER__
152
204
  prev_trap = trap(:SIGINT, 'DEFAULT')
153
205
 
154
206
  begin
155
- line = readline
207
+ if pre_commands.empty?
208
+ line = readline
209
+ else
210
+ line = pre_commands.shift
211
+ puts "(rdbg:remote:command) #{line}"
212
+ end
156
213
  rescue Interrupt
157
214
  retry
158
215
  ensure