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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +195 -3
- data/Gemfile +1 -0
- data/README.md +55 -32
- data/Rakefile +28 -10
- data/debug.gemspec +7 -5
- data/exe/rdbg +7 -3
- data/ext/debug/debug.c +76 -14
- data/ext/debug/extconf.rb +22 -0
- data/lib/debug/breakpoint.rb +90 -66
- data/lib/debug/client.rb +21 -5
- data/lib/debug/config.rb +55 -24
- data/lib/debug/console.rb +43 -16
- data/lib/debug/frame_info.rb +32 -26
- data/lib/debug/local.rb +1 -1
- data/lib/debug/server.rb +105 -40
- data/lib/debug/server_cdp.rb +373 -152
- data/lib/debug/server_dap.rb +273 -170
- data/lib/debug/session.rb +450 -236
- data/lib/debug/source_repository.rb +104 -51
- data/lib/debug/thread_client.rb +252 -103
- data/lib/debug/tracer.rb +7 -12
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +34 -14
- metadata +6 -16
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -24
- data/.github/ISSUE_TEMPLATE/custom.md +0 -10
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
- data/.github/pull_request_template.md +0 -9
- data/.github/workflows/ruby.yml +0 -34
- data/.gitignore +0 -12
- data/bin/console +0 -14
- data/bin/gentest +0 -30
- data/bin/setup +0 -8
- data/lib/debug/bp.vim +0 -68
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
|
-
|
101
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
160
|
+
iseq_last_line(VALUE iseqw)
|
117
161
|
{
|
118
|
-
|
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'
|
data/lib/debug/breakpoint.rb
CHANGED
@@ -8,9 +8,13 @@ module DEBUGGER__
|
|
8
8
|
|
9
9
|
attr_reader :key
|
10
10
|
|
11
|
-
def initialize do_enable
|
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
|
-
|
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
|
130
|
-
|
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 = [
|
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
|
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
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
-
|
224
|
-
|
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
|
-
|
227
|
-
|
228
|
-
|
253
|
+
if !lines.empty? && lines.last >= line
|
254
|
+
nline = lines.bsearch{|l| line <= l}
|
255
|
+
events = line_events[nline]
|
229
256
|
|
230
|
-
|
257
|
+
next if events == [:RUBY_EVENT_B_CALL]
|
231
258
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
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
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
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
|
-
|
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
|
318
|
-
@
|
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
|
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
|
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")}
|
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
|
-
|
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
|
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}
|
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
|
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
|
-
|
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
|
15
|
-
show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint
|
16
|
-
show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint
|
17
|
-
use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)",
|
18
|
-
no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize
|
19
|
-
no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT
|
20
|
-
no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library
|
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
|
24
|
-
skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines
|
25
|
-
keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it
|
26
|
-
postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug
|
27
|
-
fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child)
|
28
|
-
sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal
|
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
|
37
|
-
save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines
|
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
|
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
|
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
|
-
|
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 |
|
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
|
-
|
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'] || '
|
462
|
+
user = ENV['USER'] || 'UnknownUser'
|
432
463
|
File.join(base_dir, "ruby-debug-#{user}")
|
433
464
|
end
|
434
465
|
|