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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +234 -9
- data/Gemfile +1 -0
- data/README.md +81 -31
- data/Rakefile +28 -10
- data/debug.gemspec +7 -5
- data/exe/rdbg +7 -3
- data/ext/debug/debug.c +80 -15
- data/ext/debug/extconf.rb +22 -0
- data/lib/debug/breakpoint.rb +141 -67
- data/lib/debug/client.rb +77 -20
- data/lib/debug/color.rb +29 -19
- data/lib/debug/config.rb +61 -27
- data/lib/debug/console.rb +59 -18
- data/lib/debug/frame_info.rb +41 -40
- data/lib/debug/local.rb +1 -1
- data/lib/debug/prelude.rb +2 -2
- data/lib/debug/server.rb +136 -103
- data/lib/debug/server_cdp.rb +880 -162
- data/lib/debug/server_dap.rb +445 -164
- data/lib/debug/session.rb +540 -269
- data/lib/debug/source_repository.rb +103 -52
- data/lib/debug/thread_client.rb +306 -138
- data/lib/debug/tracer.rb +8 -13
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +44 -16
- metadata +6 -15
- 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/workflows/ruby.yml +0 -34
- data/.gitignore +0 -12
- data/bin/console +0 -14
- data/bin/gentest +0 -22
- data/bin/setup +0 -8
- data/lib/debug/bp.vim +0 -68
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
|
-
|
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']
|
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.
|
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
|
-
|
98
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
151
|
+
iseq_first_line(VALUE iseqw)
|
114
152
|
{
|
115
|
-
|
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'
|
data/lib/debug/breakpoint.rb
CHANGED
@@ -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
|
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
|
94
|
-
|
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 = [
|
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
|
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
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
188
|
-
|
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
|
-
|
191
|
-
|
192
|
-
|
253
|
+
if !lines.empty? && lines.last >= line
|
254
|
+
nline = lines.bsearch{|l| line <= l}
|
255
|
+
events = line_events[nline]
|
193
256
|
|
194
|
-
|
257
|
+
next if events == [:RUBY_EVENT_B_CALL]
|
195
258
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
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
|
281
|
-
@
|
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
|
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
|
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")}
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 '
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
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}
|
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
|
-
|
138
|
-
|
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
|
-
|
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
|