debug 1.4.0 → 1.6.1

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/ext/debug/debug.c CHANGED
@@ -97,32 +97,82 @@ frame_depth(VALUE self)
97
97
  return INT2FIX(RARRAY_LEN(bt));
98
98
  }
99
99
 
100
- static void
101
- 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)
102
109
  {
103
- rb_trace_arg_t *arg = rb_tracearg_from_tracepoint(tpval);
104
- VALUE mid = rb_tracearg_callee_id(arg);
105
-
106
- if (RB_UNLIKELY(mid == ID2SYM(rb_intern("method_added")) ||
107
- mid == ID2SYM(rb_intern("singleton_method_added")))) {
108
- VALUE args[] = {
109
- tpval,
110
- };
111
- 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("&"));
112
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
+ }
141
+ }
142
+
143
+ return ary;
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);
149
+
150
+ static VALUE
151
+ iseq_first_line(VALUE iseqw)
152
+ {
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);
113
157
  }
114
158
 
115
159
  static VALUE
116
- create_method_added_tracker(VALUE self)
160
+ iseq_last_line(VALUE iseqw)
117
161
  {
118
- return rb_tracepoint_new(0, RUBY_EVENT_CALL, method_added_tracker, NULL);
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);
119
166
  }
167
+ #endif
120
168
 
121
169
  void Init_iseq_collector(void);
122
170
 
123
171
  void
124
172
  Init_debug(void)
125
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"));
126
176
  rb_mDebugger = rb_const_get(rb_cObject, rb_intern("DEBUGGER__"));
127
177
  rb_cFrameInfo = rb_const_get(rb_mDebugger, rb_intern("FrameInfo"));
128
178
 
@@ -132,7 +182,19 @@ Init_debug(void)
132
182
  rb_gc_register_mark_object(rb_cFrameInfo);
133
183
  rb_define_singleton_method(rb_mDebugger, "capture_frames", capture_frames, 1);
134
184
  rb_define_singleton_method(rb_mDebugger, "frame_depth", frame_depth, 0);
135
- rb_define_singleton_method(rb_mDebugger, "create_method_added_tracker", create_method_added_tracker, 0);
136
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
+
137
199
  Init_iseq_collector();
138
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
+ $distcleanfiles << "debug_version.h"
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'
@@ -8,9 +8,13 @@ module DEBUGGER__
8
8
 
9
9
  attr_reader :key
10
10
 
11
- def initialize do_enable = true
11
+ def initialize cond, command, path, do_enable: true
12
12
  @deleted = false
13
13
 
14
+ @cond = cond
15
+ @command = command
16
+ @path = path
17
+
14
18
  setup
15
19
  enable if do_enable
16
20
  end
@@ -82,8 +86,11 @@ module DEBUGGER__
82
86
  end
83
87
 
84
88
  def skip_path?(path)
85
- if @path
89
+ case @path
90
+ when Regexp
86
91
  !path.match?(@path)
92
+ when String
93
+ !path.include?(@path)
87
94
  else
88
95
  super
89
96
  end
@@ -108,7 +115,7 @@ module DEBUGGER__
108
115
  @oneshot = oneshot
109
116
  @key = [:iseq, @iseq.path, @iseq.first_lineno].freeze
110
117
 
111
- super()
118
+ super(nil, nil, nil)
112
119
  end
113
120
 
114
121
  def setup
@@ -124,25 +131,30 @@ module DEBUGGER__
124
131
  end
125
132
 
126
133
  class LineBreakpoint < Breakpoint
127
- attr_reader :path, :line, :iseq
134
+ attr_reader :path, :line, :iseq, :cond, :oneshot, :hook_call, :command
128
135
 
129
- def initialize path, line, cond: nil, oneshot: false, hook_call: true, command: nil
130
- @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
131
145
  @line = line
132
- @cond = cond
133
146
  @oneshot = oneshot
134
147
  @hook_call = hook_call
135
- @command = command
136
148
  @pending = false
137
149
 
138
150
  @iseq = nil
139
151
  @type = nil
140
152
 
141
- @key = [@path, @line].freeze
153
+ @key = [path, @line].freeze
142
154
 
143
- super()
155
+ super(cond, command, path)
144
156
 
145
- try_activate
157
+ try_activate unless skip_activate
146
158
  @pending = !@iseq
147
159
  end
148
160
 
@@ -202,7 +214,7 @@ module DEBUGGER__
202
214
  when events.include?(:RUBY_EVENT_END)
203
215
  activate(iseq, :end, line)
204
216
  else
205
- # not actiavated
217
+ # not activated
206
218
  end
207
219
  end
208
220
 
@@ -212,42 +224,56 @@ module DEBUGGER__
212
224
 
213
225
  NearestISeq = Struct.new(:iseq, :line, :events)
214
226
 
215
- def try_activate
216
- nearest = nil # NearestISeq
217
-
218
- ObjectSpace.each_iseq{|iseq|
219
- if (iseq.absolute_path || iseq.path) == self.path &&
220
- iseq.first_lineno <= self.line &&
221
- 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
222
246
 
223
- iseq.traceable_lines_norec(line_events = {})
224
- 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
225
252
 
226
- if !lines.empty? && lines.last >= line
227
- nline = lines.bsearch{|l| line <= l}
228
- events = line_events[nline]
253
+ if !lines.empty? && lines.last >= line
254
+ nline = lines.bsearch{|l| line <= l}
255
+ events = line_events[nline]
229
256
 
230
- next if events == [:RUBY_EVENT_B_CALL]
257
+ next if events == [:RUBY_EVENT_B_CALL]
231
258
 
232
- if @hook_call &&
233
- events.include?(:RUBY_EVENT_CALL) &&
234
- self.line == iseq.first_lineno
235
- nline = iseq.first_lineno
236
- end
259
+ if @hook_call &&
260
+ events.include?(:RUBY_EVENT_CALL) &&
261
+ self.line == iseq.first_lineno
262
+ nline = iseq.first_lineno
263
+ end
237
264
 
238
- if !nearest || ((line - nline).abs < (line - nearest.line).abs)
239
- nearest = NearestISeq.new(iseq, nline, events)
240
- else
241
- if @hook_call && nearest.iseq.first_lineno <= iseq.first_lineno
242
- if (nearest.line > line && !nearest.events.include?(:RUBY_EVENT_CALL)) ||
243
- (events.include?(:RUBY_EVENT_CALL))
244
- nearest = NearestISeq.new(iseq, nline, events)
245
- 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)
246
272
  end
247
273
  end
248
274
  end
249
275
  end
250
- }
276
+ end
251
277
 
252
278
  if nearest
253
279
  activate_exact nearest.iseq, nearest.events, nearest.line
@@ -277,11 +303,7 @@ module DEBUGGER__
277
303
  @key = [:catch, @pat].freeze
278
304
  @last_exc = nil
279
305
 
280
- @cond = cond
281
- @command = command
282
- @path = path
283
-
284
- super()
306
+ super(cond, command, path)
285
307
  end
286
308
 
287
309
  def setup
@@ -314,29 +336,41 @@ module DEBUGGER__
314
336
  end
315
337
 
316
338
  class CheckBreakpoint < Breakpoint
317
- def initialize expr, path
318
- @expr = expr.freeze
319
- @key = [:check, @expr].freeze
320
- @path = path
339
+ def initialize cond:, command: nil, path: nil
340
+ @key = [:check, cond].freeze
321
341
 
322
- super()
342
+ super(cond, command, path)
323
343
  end
324
344
 
325
345
  def setup
326
346
  @tp = TracePoint.new(:line){|tp|
327
- next if tp.path.start_with? __dir__
328
- next if tp.path.start_with? '<internal:'
347
+ next if SESSION.in_subsession? # TODO: Ractor support
329
348
  next if ThreadClient.current.management?
330
349
  next if skip_path?(tp.path)
331
350
 
332
- if safe_eval tp.binding, @expr
351
+ if need_suspend? safe_eval(tp.binding, @cond)
333
352
  suspend
334
353
  end
335
354
  }
336
355
  end
337
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
+
338
370
  def to_s
339
- "#{generate_label("Check")} #{@expr}"
371
+ s = "#{generate_label("Check")}"
372
+ s += super
373
+ s
340
374
  end
341
375
  end
342
376
 
@@ -348,10 +382,7 @@ module DEBUGGER__
348
382
 
349
383
  @current = current
350
384
 
351
- @cond = cond
352
- @command = command
353
- @path = path
354
- super()
385
+ super(cond, command, path)
355
386
  end
356
387
 
357
388
  def watch_eval(tp)
@@ -374,9 +405,6 @@ module DEBUGGER__
374
405
 
375
406
  def setup
376
407
  @tp = TracePoint.new(:line, :return, :b_return){|tp|
377
- next if tp.path.start_with? __dir__
378
- next if tp.path.start_with? '<internal:'
379
-
380
408
  watch_eval(tp)
381
409
  }
382
410
  end
@@ -393,7 +421,7 @@ module DEBUGGER__
393
421
  end
394
422
 
395
423
  class MethodBreakpoint < Breakpoint
396
- attr_reader :sig_method_name, :method
424
+ attr_reader :sig_method_name, :method, :klass
397
425
 
398
426
  def initialize b, klass_name, op, method_name, cond: nil, command: nil, path: nil
399
427
  @sig_klass_name = klass_name
@@ -404,13 +432,10 @@ module DEBUGGER__
404
432
 
405
433
  @klass = nil
406
434
  @method = nil
407
- @cond = cond
408
435
  @cond_class = nil
409
- @command = command
410
- @path = path
411
436
  @key = "#{klass_name}#{op}#{method_name}".freeze
412
437
 
413
- super(false)
438
+ super(cond, command, path, do_enable: false)
414
439
  end
415
440
 
416
441
  def setup
@@ -419,7 +444,6 @@ module DEBUGGER__
419
444
  next if @cond_class && !tp.self.kind_of?(@cond_class)
420
445
 
421
446
  caller_location = caller_locations(2, 1).first.to_s
422
- next if caller_location.start_with?(__dir__)
423
447
  next if skip_path?(caller_location)
424
448
 
425
449
  suspend
data/lib/debug/client.rb CHANGED
@@ -18,6 +18,10 @@ 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
@@ -76,7 +80,7 @@ module DEBUGGER__
76
80
 
77
81
  def cleanup_unix_domain_sockets
78
82
  Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*') do |file|
79
- if /(\d+)$/ =~ file
83
+ if File.socket?(file) && (/-(\d+)-\d+$/ =~ file || /-(\d+)$/ =~ file)
80
84
  begin
81
85
  Process.kill(0, $1.to_i)
82
86
  rescue Errno::EPERM
@@ -88,7 +92,9 @@ module DEBUGGER__
88
92
  end
89
93
 
90
94
  def list_connections
91
- 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
92
98
  end
93
99
  end
94
100
 
@@ -115,7 +121,10 @@ module DEBUGGER__
115
121
  @width = IO.console_size[1]
116
122
  @width = 80 if @width == 0
117
123
 
118
- 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'}"
119
128
  end
120
129
 
121
130
  def deactivate
@@ -167,6 +176,8 @@ module DEBUGGER__
167
176
  end
168
177
 
169
178
  def connect
179
+ pre_commands = (CONFIG[:commands] || '').split(';;')
180
+
170
181
  trap(:SIGINT){
171
182
  send "pause"
172
183
  }
@@ -175,7 +186,7 @@ module DEBUGGER__
175
186
  trap(:SIGWINCH){
176
187
  @width = IO.console_size[1]
177
188
  }
178
- rescue ArgumentError => e
189
+ rescue ArgumentError
179
190
  @width = 80
180
191
  end
181
192
 
@@ -193,7 +204,12 @@ module DEBUGGER__
193
204
  prev_trap = trap(:SIGINT, 'DEFAULT')
194
205
 
195
206
  begin
196
- 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
197
213
  rescue Interrupt
198
214
  retry
199
215
  ensure
data/lib/debug/config.rb CHANGED
@@ -7,46 +7,50 @@ module DEBUGGER__
7
7
  ERROR: 2,
8
8
  WARN: 3,
9
9
  INFO: 4,
10
+ DEBUG: 5
10
11
  }.freeze
11
12
 
12
13
  CONFIG_SET = {
13
14
  # UI setting
14
- log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger (default: WARN)", :loglevel],
15
- show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint (default: 10 lines)", :int],
16
- show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint (default: 2 frames)", :int],
17
- use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool],
18
- no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize (default: false)", :bool],
19
- no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT (default: false)", :bool],
20
- no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library (default: false)", :bool],
15
+ log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger", :loglevel, "WARN"],
16
+ show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
17
+ show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint", :int, "2"],
18
+ use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
19
+ no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize", :bool, "false"],
20
+ no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT", :bool, "false"],
21
+ no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"],
22
+ no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"],
21
23
 
22
24
  # control setting
23
- skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths (default: [])", :path],
24
- skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines (default: false)", :bool],
25
- keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it (default: false)", :bool],
26
- postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug (default: false)", :bool],
27
- fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child) (default: both)", :forkmode],
28
- sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal (default: disabled)"],
25
+ skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path],
26
+ skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines", :bool, "false"],
27
+ keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it", :bool, "false"],
28
+ postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug", :bool, "false"],
29
+ fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
30
+ sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal", :bool, "false"],
29
31
 
30
32
  # boot setting
31
- nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool],
32
- stop_at_load: ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location", :bool],
33
+ nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool, "false"],
34
+ stop_at_load: ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location", :bool, "false"],
33
35
  init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
34
36
  commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. commands should be separated by ';;'"],
35
- no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool],
36
- history_file: ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file (default: ~/.rdbg_history)"],
37
- save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines (default: 10,000)"],
37
+ no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool, "false"],
38
+ history_file: ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file", :string, "~/.rdbg_history"],
39
+ save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
38
40
 
39
41
  # remote setting
40
42
  port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
41
- host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host (localhost if not given)"],
43
+ host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
42
44
  sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
43
45
  sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
46
+ local_fs_map: ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
47
+ skip_bp: ['RUBY_DEBUG_SKIP_BP', "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
44
48
  cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
45
49
  open_frontend: ['RUBY_DEBUG_OPEN_FRONTEND',"REMOTE: frontend used by open command (vscode, chrome, default: rdbg)."],
46
50
  chrome_path: ['RUBY_DEBUG_CHROME_PATH', "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
47
51
 
48
52
  # obsolete
49
- parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork (default: false)", :bool],
53
+ parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork", :bool, "false"],
50
54
  }.freeze
51
55
 
52
56
  CONFIG_MAP = CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
@@ -59,11 +63,23 @@ module DEBUGGER__
59
63
  end
60
64
 
61
65
  def initialize argv
66
+ @skip_all = false
67
+
62
68
  if self.class.config
63
69
  raise 'Can not make multiple configurations in one process'
64
70
  end
65
71
 
66
- update self.class.parse_argv(argv)
72
+ config = self.class.parse_argv(argv)
73
+
74
+ # apply defaults
75
+ CONFIG_SET.each do |k, config_detail|
76
+ unless config.key?(k)
77
+ default_value = config_detail[3]
78
+ config[k] = parse_config_value(k, default_value)
79
+ end
80
+ end
81
+
82
+ update config
67
83
  end
68
84
 
69
85
  def inspect
@@ -78,6 +94,14 @@ module DEBUGGER__
78
94
  set_config(key => val)
79
95
  end
80
96
 
97
+ def skip_all
98
+ @skip_all = true
99
+ end
100
+
101
+ def skip?
102
+ @skip_all
103
+ end
104
+
81
105
  def set_config(**kw)
82
106
  conf = config.dup
83
107
  kw.each{|k, v|
@@ -114,11 +138,13 @@ module DEBUGGER__
114
138
  self.class.instance_variable_set(:@config, conf.freeze)
115
139
 
116
140
  # Post process
117
- if_updated old_conf, conf, :keep_alloc_site do |_, new|
141
+ if_updated old_conf, conf, :keep_alloc_site do |old, new|
118
142
  if new
119
143
  require 'objspace'
120
144
  ObjectSpace.trace_object_allocations_start
121
- else
145
+ end
146
+
147
+ if old && !new
122
148
  ObjectSpace.trace_object_allocations_stop
123
149
  end
124
150
  end
@@ -215,6 +241,8 @@ module DEBUGGER__
215
241
  e
216
242
  end
217
243
  }
244
+ when :path_map
245
+ valstr.split(',').map{|e| e.split(':')}
218
246
  else
219
247
  valstr
220
248
  end
@@ -223,6 +251,7 @@ module DEBUGGER__
223
251
  def self.parse_argv argv
224
252
  config = {
225
253
  mode: :start,
254
+ no_color: (nc = ENV['NO_COLOR']) && !nc.empty?,
226
255
  }
227
256
  CONFIG_MAP.each{|key, evname|
228
257
  if val = ENV[evname]
@@ -361,6 +390,8 @@ module DEBUGGER__
361
390
  case CONFIG_SET[key][2]
362
391
  when :path
363
392
  valstr = config[key].map{|e| e.kind_of?(Regexp) ? e.inspect : e}.join(':')
393
+ when :path_map
394
+ valstr = config[key].map{|e| e.join(':')}.join(',')
364
395
  else
365
396
  valstr = config[key].to_s
366
397
  end
@@ -428,7 +459,7 @@ module DEBUGGER__
428
459
  end
429
460
 
430
461
  def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
431
- user = ENV['USER'] || 'ruby-debug'
462
+ user = ENV['USER'] || 'UnknownUser'
432
463
  File.join(base_dir, "ruby-debug-#{user}")
433
464
  end
434
465