debug 1.0.0.rc2 → 1.2.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/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- data/.github/ISSUE_TEMPLATE/custom.md +10 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
- data/README.md +26 -14
- data/Rakefile +1 -1
- data/TODO.md +4 -8
- data/debug.gemspec +1 -1
- data/exe/rdbg +1 -1
- data/ext/debug/debug.c +5 -0
- data/lib/debug/breakpoint.rb +14 -5
- data/lib/debug/color.rb +25 -3
- data/lib/debug/config.rb +18 -3
- data/lib/debug/console.rb +10 -2
- data/lib/debug/frame_info.rb +2 -2
- data/lib/debug/local.rb +21 -24
- data/lib/debug/server.rb +8 -7
- data/lib/debug/server_dap.rb +30 -19
- data/lib/debug/session.rb +260 -101
- data/lib/debug/thread_client.rb +23 -14
- data/lib/debug/tracer.rb +8 -3
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +18 -11
- metadata +9 -6
data/lib/debug/session.rb
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# skip to load debugger for bundle exec
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
|
6
|
+
trace_var(:$0) do |file|
|
|
7
|
+
trace_var(:$0, nil)
|
|
8
|
+
if /-r (#{__dir__}\S+)/ =~ ENV['RUBYOPT']
|
|
9
|
+
lib = $1
|
|
10
|
+
$LOADED_FEATURES.delete_if{|path| path.start_with?(__dir__)}
|
|
11
|
+
ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH'] = file
|
|
12
|
+
require lib
|
|
13
|
+
ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH'] = nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
return
|
|
18
|
+
end
|
|
5
19
|
|
|
6
20
|
require_relative 'config'
|
|
7
21
|
require_relative 'thread_client'
|
|
@@ -9,6 +23,10 @@ require_relative 'source_repository'
|
|
|
9
23
|
require_relative 'breakpoint'
|
|
10
24
|
require_relative 'tracer'
|
|
11
25
|
|
|
26
|
+
# To prevent loading old lib/debug.rb in Ruby 2.6 to 3.0
|
|
27
|
+
$LOADED_FEATURES << 'debug.rb'
|
|
28
|
+
require 'debug' # invalidate the $LOADED_FEATURE cache
|
|
29
|
+
|
|
12
30
|
require 'json' if ENV['RUBY_DEBUG_TEST_MODE']
|
|
13
31
|
|
|
14
32
|
class RubyVM::InstructionSequence
|
|
@@ -68,7 +86,7 @@ module DEBUGGER__
|
|
|
68
86
|
# [:watch, ivar] => WatchIVarBreakpoint
|
|
69
87
|
# [:check, expr] => CheckBreakpoint
|
|
70
88
|
#
|
|
71
|
-
@tracers =
|
|
89
|
+
@tracers = {}
|
|
72
90
|
@th_clients = nil # {Thread => ThreadClient}
|
|
73
91
|
@q_evt = Queue.new
|
|
74
92
|
@displays = []
|
|
@@ -78,17 +96,22 @@ module DEBUGGER__
|
|
|
78
96
|
@postmortem_hook = nil
|
|
79
97
|
@postmortem = false
|
|
80
98
|
@thread_stopper = nil
|
|
99
|
+
@intercept_trap_sigint = false
|
|
100
|
+
@intercepted_sigint_cmd = 'DEFAULT'
|
|
81
101
|
|
|
82
102
|
@frame_map = {} # {id => [threadId, frame_depth]} for DAP
|
|
83
103
|
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
|
84
104
|
@src_map = {} # {id => src}
|
|
85
105
|
|
|
106
|
+
@tp_thread_begin = nil
|
|
86
107
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
|
87
108
|
ThreadClient.current.on_load tp.instruction_sequence, tp.eval_script
|
|
88
109
|
}
|
|
89
110
|
@tp_load_script.enable
|
|
90
111
|
|
|
91
112
|
activate
|
|
113
|
+
|
|
114
|
+
self.postmortem = CONFIG[:postmortem]
|
|
92
115
|
end
|
|
93
116
|
|
|
94
117
|
def active?
|
|
@@ -99,40 +122,41 @@ module DEBUGGER__
|
|
|
99
122
|
@bps.has_key? [file, line]
|
|
100
123
|
end
|
|
101
124
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
125
|
+
def activate on_fork: false
|
|
126
|
+
@tp_thread_begin&.disable
|
|
127
|
+
@tp_thread_begin = nil
|
|
128
|
+
|
|
129
|
+
if on_fork
|
|
130
|
+
@ui.activate self, on_fork: true
|
|
131
|
+
else
|
|
132
|
+
@ui.activate self, on_fork: false
|
|
106
133
|
end
|
|
107
|
-
end
|
|
108
134
|
|
|
109
|
-
|
|
135
|
+
q = Queue.new
|
|
110
136
|
@session_server = Thread.new do
|
|
111
137
|
Thread.current.name = 'DEBUGGER__::SESSION@server'
|
|
112
138
|
Thread.current.abort_on_exception = true
|
|
113
|
-
session_server_main
|
|
114
|
-
end
|
|
115
139
|
|
|
116
|
-
|
|
140
|
+
# Thread management
|
|
141
|
+
setup_threads
|
|
142
|
+
thc = thread_client Thread.current
|
|
143
|
+
thc.is_management
|
|
117
144
|
|
|
118
|
-
|
|
119
|
-
|
|
145
|
+
if @ui.respond_to?(:reader_thread) && thc = thread_client(@ui.reader_thread)
|
|
146
|
+
thc.is_management
|
|
147
|
+
end
|
|
120
148
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
@
|
|
125
|
-
end
|
|
149
|
+
@tp_thread_begin = TracePoint.new(:thread_begin) do |tp|
|
|
150
|
+
thread_client
|
|
151
|
+
end
|
|
152
|
+
@tp_thread_begin.enable
|
|
126
153
|
|
|
127
|
-
|
|
128
|
-
|
|
154
|
+
# session start
|
|
155
|
+
q << true
|
|
156
|
+
session_server_main
|
|
129
157
|
end
|
|
130
158
|
|
|
131
|
-
|
|
132
|
-
th = Thread.current
|
|
133
|
-
ThreadClient.current.on_thread_begin th
|
|
134
|
-
}
|
|
135
|
-
@tp_thread_begin.enable
|
|
159
|
+
q.pop
|
|
136
160
|
end
|
|
137
161
|
|
|
138
162
|
def deactivate
|
|
@@ -142,14 +166,14 @@ module DEBUGGER__
|
|
|
142
166
|
@tp_thread_begin.disable
|
|
143
167
|
@bps.each{|k, bp| bp.disable}
|
|
144
168
|
@th_clients.each{|th, thc| thc.close}
|
|
145
|
-
@tracers.each{|t| t.disable}
|
|
169
|
+
@tracers.values.each{|t| t.disable}
|
|
146
170
|
@q_evt.close
|
|
147
171
|
@ui&.deactivate
|
|
148
172
|
@ui = nil
|
|
149
173
|
end
|
|
150
174
|
|
|
151
175
|
def reset_ui ui
|
|
152
|
-
@ui.
|
|
176
|
+
@ui.deactivate
|
|
153
177
|
@ui = ui
|
|
154
178
|
end
|
|
155
179
|
|
|
@@ -159,11 +183,18 @@ module DEBUGGER__
|
|
|
159
183
|
|
|
160
184
|
def session_server_main
|
|
161
185
|
while evt = pop_event
|
|
162
|
-
#
|
|
186
|
+
# variable `@internal_info` is only used for test
|
|
163
187
|
tc, output, ev, @internal_info, *ev_args = evt
|
|
164
188
|
output.each{|str| @ui.puts str}
|
|
165
189
|
|
|
166
190
|
case ev
|
|
191
|
+
|
|
192
|
+
when :thread_begin # special event, tc is nil
|
|
193
|
+
th = ev_args.shift
|
|
194
|
+
q = ev_args.shift
|
|
195
|
+
on_thread_begin th
|
|
196
|
+
q << true
|
|
197
|
+
|
|
167
198
|
when :init
|
|
168
199
|
wait_command_loop tc
|
|
169
200
|
|
|
@@ -175,26 +206,25 @@ module DEBUGGER__
|
|
|
175
206
|
|
|
176
207
|
when :trace
|
|
177
208
|
trace_id, msg = ev_args
|
|
178
|
-
if t = @tracers.find{|t| t.object_id == trace_id}
|
|
209
|
+
if t = @tracers.values.find{|t| t.object_id == trace_id}
|
|
179
210
|
t.puts msg
|
|
180
211
|
end
|
|
181
212
|
tc << :continue
|
|
182
213
|
|
|
183
|
-
when :thread_begin
|
|
184
|
-
th = ev_args.shift
|
|
185
|
-
on_thread_begin th
|
|
186
|
-
@ui.event :thread_begin, th
|
|
187
|
-
tc << :continue
|
|
188
|
-
|
|
189
214
|
when :suspend
|
|
190
215
|
case ev_args.first
|
|
191
216
|
when :breakpoint
|
|
192
217
|
bp, i = bp_index ev_args[1]
|
|
193
|
-
@ui.event :suspend_bp, i, bp
|
|
218
|
+
@ui.event :suspend_bp, i, bp, tc.id
|
|
194
219
|
when :trap
|
|
195
|
-
@ui.event :suspend_trap, ev_args[1]
|
|
220
|
+
@ui.event :suspend_trap, sig = ev_args[1], tc.id
|
|
221
|
+
|
|
222
|
+
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
|
223
|
+
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registerred as SIGINT handler."
|
|
224
|
+
@ui.puts "`sigint` command execute it."
|
|
225
|
+
end
|
|
196
226
|
else
|
|
197
|
-
@ui.event :suspended
|
|
227
|
+
@ui.event :suspended, tc.id
|
|
198
228
|
end
|
|
199
229
|
|
|
200
230
|
if @displays.empty?
|
|
@@ -228,8 +258,7 @@ module DEBUGGER__
|
|
|
228
258
|
obj_id = ev_args[1]
|
|
229
259
|
obj_inspect = ev_args[2]
|
|
230
260
|
opt = ev_args[3]
|
|
231
|
-
|
|
232
|
-
@ui.puts "Enable #{t.to_s}"
|
|
261
|
+
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
|
|
233
262
|
else
|
|
234
263
|
# ignore
|
|
235
264
|
end
|
|
@@ -303,7 +332,9 @@ module DEBUGGER__
|
|
|
303
332
|
if @preset_command.commands.empty?
|
|
304
333
|
if @preset_command.auto_continue
|
|
305
334
|
@preset_command = nil
|
|
335
|
+
|
|
306
336
|
@tc << :continue
|
|
337
|
+
restart_all_threads
|
|
307
338
|
return
|
|
308
339
|
else
|
|
309
340
|
@preset_command = nil
|
|
@@ -412,6 +443,29 @@ module DEBUGGER__
|
|
|
412
443
|
when 'kill!'
|
|
413
444
|
exit! (arg || 1).to_i
|
|
414
445
|
|
|
446
|
+
# * `sigint`
|
|
447
|
+
# * Execute SIGINT handler registerred by the debuggee.
|
|
448
|
+
# * Note that this command should be used just after stop by `SIGINT`.
|
|
449
|
+
when 'sigint'
|
|
450
|
+
begin
|
|
451
|
+
case cmd = @intercepted_sigint_cmd
|
|
452
|
+
when nil, 'IGNORE', :IGNORE, 'DEFAULT', :DEFAULT
|
|
453
|
+
# ignore
|
|
454
|
+
when String
|
|
455
|
+
eval(cmd)
|
|
456
|
+
when Proc
|
|
457
|
+
cmd.call
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
@tc << :continue
|
|
461
|
+
restart_all_threads
|
|
462
|
+
|
|
463
|
+
rescue Exception => e
|
|
464
|
+
@ui.puts "Exception: #{e}"
|
|
465
|
+
@ui.puts e.backtrace.map{|line| " #{e}"}
|
|
466
|
+
return :retry
|
|
467
|
+
end
|
|
468
|
+
|
|
415
469
|
### Breakpoint
|
|
416
470
|
|
|
417
471
|
# * `b[reak]`
|
|
@@ -596,7 +650,7 @@ module DEBUGGER__
|
|
|
596
650
|
# * Show information about the current frame (local variables)
|
|
597
651
|
# * It includes `self` as `%self` and a return value as `%return`.
|
|
598
652
|
# * `i[nfo] i[var[s]]` or `i[nfo] instance`
|
|
599
|
-
# * Show information about
|
|
653
|
+
# * Show information about instance variables about `self`.
|
|
600
654
|
# * `i[nfo] c[onst[s]]` or `i[nfo] constant[s]`
|
|
601
655
|
# * Show information about accessible constants except toplevel constants.
|
|
602
656
|
# * `i[nfo] g[lobal[s]]`
|
|
@@ -702,10 +756,16 @@ module DEBUGGER__
|
|
|
702
756
|
when 'pp'
|
|
703
757
|
@tc << [:eval, :pp, arg.to_s]
|
|
704
758
|
|
|
705
|
-
# * `
|
|
759
|
+
# * `eval <expr>`
|
|
706
760
|
# * Evaluate `<expr>` on the current frame.
|
|
707
|
-
when '
|
|
708
|
-
|
|
761
|
+
when 'eval', 'call'
|
|
762
|
+
if arg == nil || arg.empty?
|
|
763
|
+
show_help 'eval'
|
|
764
|
+
@ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
|
|
765
|
+
return :retry
|
|
766
|
+
else
|
|
767
|
+
@tc << [:eval, :call, arg]
|
|
768
|
+
end
|
|
709
769
|
|
|
710
770
|
# * `irb`
|
|
711
771
|
# * Invoke `irb` on the current frame.
|
|
@@ -714,7 +774,7 @@ module DEBUGGER__
|
|
|
714
774
|
@ui.puts "not supported on the remote console."
|
|
715
775
|
return :retry
|
|
716
776
|
end
|
|
717
|
-
@tc << [:eval, :
|
|
777
|
+
@tc << [:eval, :irb]
|
|
718
778
|
|
|
719
779
|
# don't repeat irb command
|
|
720
780
|
@repl_prev_line = nil
|
|
@@ -752,32 +812,29 @@ module DEBUGGER__
|
|
|
752
812
|
case arg
|
|
753
813
|
when nil
|
|
754
814
|
@ui.puts 'Tracers:'
|
|
755
|
-
@tracers.each_with_index{|t, i|
|
|
815
|
+
@tracers.values.each_with_index{|t, i|
|
|
756
816
|
@ui.puts "* \##{i} #{t}"
|
|
757
817
|
}
|
|
758
818
|
@ui.puts
|
|
759
819
|
return :retry
|
|
760
820
|
|
|
761
821
|
when /\Aline\z/
|
|
762
|
-
|
|
763
|
-
@ui.puts "Enable #{t.to_s}"
|
|
822
|
+
add_tracer LineTracer.new(@ui, pattern: pattern, into: into)
|
|
764
823
|
return :retry
|
|
765
824
|
|
|
766
825
|
when /\Acall\z/
|
|
767
|
-
|
|
768
|
-
@ui.puts "Enable #{t.to_s}"
|
|
826
|
+
add_tracer CallTracer.new(@ui, pattern: pattern, into: into)
|
|
769
827
|
return :retry
|
|
770
828
|
|
|
771
829
|
when /\Aexception\z/
|
|
772
|
-
|
|
773
|
-
@ui.puts "Enable #{t.to_s}"
|
|
830
|
+
add_tracer ExceptionTracer.new(@ui, pattern: pattern, into: into)
|
|
774
831
|
return :retry
|
|
775
832
|
|
|
776
833
|
when /\Aobject\s+(.+)/
|
|
777
834
|
@tc << [:trace, :object, $1.strip, {pattern: pattern, into: into}]
|
|
778
835
|
|
|
779
836
|
when /\Aoff\s+(\d+)\z/
|
|
780
|
-
if t = @tracers[$1.to_i]
|
|
837
|
+
if t = @tracers.values[$1.to_i]
|
|
781
838
|
t.disable
|
|
782
839
|
@ui.puts "Disable #{t.to_s}"
|
|
783
840
|
else
|
|
@@ -786,7 +843,7 @@ module DEBUGGER__
|
|
|
786
843
|
return :retry
|
|
787
844
|
|
|
788
845
|
when /\Aoff(\s+(line|call|exception|object))?\z/
|
|
789
|
-
@tracers.each{|t|
|
|
846
|
+
@tracers.values.each{|t|
|
|
790
847
|
if $2.nil? || t.type == $2
|
|
791
848
|
t.disable
|
|
792
849
|
@ui.puts "Disable #{t.to_s}"
|
|
@@ -1145,6 +1202,11 @@ module DEBUGGER__
|
|
|
1145
1202
|
add_bp bp
|
|
1146
1203
|
end
|
|
1147
1204
|
|
|
1205
|
+
def add_catch_breakpoint pat
|
|
1206
|
+
bp = CatchBreakpoint.new(pat)
|
|
1207
|
+
add_bp bp
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1148
1210
|
def add_check_breakpoint expr
|
|
1149
1211
|
bp = CheckBreakpoint.new(expr)
|
|
1150
1212
|
add_bp bp
|
|
@@ -1159,6 +1221,20 @@ module DEBUGGER__
|
|
|
1159
1221
|
@ui.puts e.message
|
|
1160
1222
|
end
|
|
1161
1223
|
|
|
1224
|
+
# tracers
|
|
1225
|
+
|
|
1226
|
+
def add_tracer tracer
|
|
1227
|
+
# don't repeat commands that add tracers
|
|
1228
|
+
@repl_prev_line = nil
|
|
1229
|
+
if @tracers.has_key? tracer.key
|
|
1230
|
+
tracer.disable
|
|
1231
|
+
@ui.puts "Duplicated tracer: #{tracer}"
|
|
1232
|
+
else
|
|
1233
|
+
@tracers[tracer.key] = tracer
|
|
1234
|
+
@ui.puts "Enable #{tracer}"
|
|
1235
|
+
end
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1162
1238
|
# threads
|
|
1163
1239
|
|
|
1164
1240
|
def update_thread_list
|
|
@@ -1211,10 +1287,6 @@ module DEBUGGER__
|
|
|
1211
1287
|
thread_list
|
|
1212
1288
|
end
|
|
1213
1289
|
|
|
1214
|
-
def thread_client_create th
|
|
1215
|
-
@th_clients[th] = ThreadClient.new((@tc_id += 1), @q_evt, Queue.new, th)
|
|
1216
|
-
end
|
|
1217
|
-
|
|
1218
1290
|
def setup_threads
|
|
1219
1291
|
@th_clients = {}
|
|
1220
1292
|
|
|
@@ -1225,18 +1297,38 @@ module DEBUGGER__
|
|
|
1225
1297
|
|
|
1226
1298
|
def on_thread_begin th
|
|
1227
1299
|
if @th_clients.has_key? th
|
|
1228
|
-
# OK
|
|
1229
|
-
else
|
|
1230
1300
|
# TODO: NG?
|
|
1301
|
+
else
|
|
1231
1302
|
thread_client_create th
|
|
1232
1303
|
end
|
|
1233
1304
|
end
|
|
1234
1305
|
|
|
1235
|
-
def
|
|
1236
|
-
|
|
1237
|
-
|
|
1306
|
+
private def thread_client_create th
|
|
1307
|
+
# TODO: Ractor support
|
|
1308
|
+
raise "Only session_server can create thread_client" unless Thread.current == @session_server
|
|
1309
|
+
@th_clients[th] = ThreadClient.new((@tc_id += 1), @q_evt, Queue.new, th)
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
private def ask_thread_client th = Thread.current
|
|
1313
|
+
# TODO: Ractor support
|
|
1314
|
+
q2 = Queue.new
|
|
1315
|
+
# tc, output, ev, @internal_info, *ev_args = evt
|
|
1316
|
+
@q_evt << [nil, [], :thread_begin, nil, th, q2]
|
|
1317
|
+
q2.pop
|
|
1318
|
+
|
|
1319
|
+
@th_clients[th] or raise "unexpected error"
|
|
1320
|
+
end
|
|
1321
|
+
|
|
1322
|
+
# can be called by other threads
|
|
1323
|
+
def thread_client th = Thread.current
|
|
1324
|
+
if @th_clients.has_key? th
|
|
1325
|
+
@th_clients[th]
|
|
1238
1326
|
else
|
|
1239
|
-
|
|
1327
|
+
if Thread.current == @session_server
|
|
1328
|
+
thread_client_create th
|
|
1329
|
+
else
|
|
1330
|
+
ask_thread_client th
|
|
1331
|
+
end
|
|
1240
1332
|
end
|
|
1241
1333
|
end
|
|
1242
1334
|
|
|
@@ -1355,7 +1447,10 @@ module DEBUGGER__
|
|
|
1355
1447
|
end
|
|
1356
1448
|
end
|
|
1357
1449
|
|
|
1358
|
-
def enter_postmortem_session
|
|
1450
|
+
def enter_postmortem_session exc
|
|
1451
|
+
return unless exc.instance_variable_defined? :@__debugger_postmortem_frames
|
|
1452
|
+
|
|
1453
|
+
frames = exc.instance_variable_get(:@__debugger_postmortem_frames)
|
|
1359
1454
|
@postmortem = true
|
|
1360
1455
|
ThreadClient.current.suspend :postmortem, postmortem_frames: frames
|
|
1361
1456
|
ensure
|
|
@@ -1368,7 +1463,7 @@ module DEBUGGER__
|
|
|
1368
1463
|
@postmortem_hook = TracePoint.new(:raise){|tp|
|
|
1369
1464
|
exc = tp.raised_exception
|
|
1370
1465
|
frames = DEBUGGER__.capture_frames(__dir__)
|
|
1371
|
-
exc.instance_variable_set(:@
|
|
1466
|
+
exc.instance_variable_set(:@__debugger_postmortem_frames, frames)
|
|
1372
1467
|
}
|
|
1373
1468
|
at_exit{
|
|
1374
1469
|
@postmortem_hook.disable
|
|
@@ -1380,7 +1475,7 @@ module DEBUGGER__
|
|
|
1380
1475
|
@ui.puts exc.backtrace.map{|e| ' ' + e}
|
|
1381
1476
|
@ui.puts "\n"
|
|
1382
1477
|
|
|
1383
|
-
enter_postmortem_session exc
|
|
1478
|
+
enter_postmortem_session exc
|
|
1384
1479
|
rescue SystemExit
|
|
1385
1480
|
exit!
|
|
1386
1481
|
rescue Exception => e
|
|
@@ -1400,6 +1495,30 @@ module DEBUGGER__
|
|
|
1400
1495
|
end
|
|
1401
1496
|
end
|
|
1402
1497
|
end
|
|
1498
|
+
|
|
1499
|
+
def save_int_trap cmd
|
|
1500
|
+
prev, @intercepted_sigint_cmd = @intercepted_sigint_cmd, cmd
|
|
1501
|
+
prev
|
|
1502
|
+
end
|
|
1503
|
+
|
|
1504
|
+
attr_reader :intercepted_sigint_cmd
|
|
1505
|
+
|
|
1506
|
+
def intercept_trap_sigint?
|
|
1507
|
+
@intercept_trap_sigint
|
|
1508
|
+
end
|
|
1509
|
+
|
|
1510
|
+
def intercept_trap_sigint flag, &b
|
|
1511
|
+
prev = @intercept_trap_sigint
|
|
1512
|
+
@intercept_trap_sigint = flag
|
|
1513
|
+
yield
|
|
1514
|
+
ensure
|
|
1515
|
+
@intercept_trap_sigint = prev
|
|
1516
|
+
end
|
|
1517
|
+
|
|
1518
|
+
def intercept_trap_sigint_start prev
|
|
1519
|
+
@intercept_trap_sigint = true
|
|
1520
|
+
@intercepted_sigint_cmd = prev
|
|
1521
|
+
end
|
|
1403
1522
|
end
|
|
1404
1523
|
|
|
1405
1524
|
class UI_Base
|
|
@@ -1494,7 +1613,13 @@ module DEBUGGER__
|
|
|
1494
1613
|
|
|
1495
1614
|
def self.setup_initial_suspend
|
|
1496
1615
|
if !CONFIG[:nonstop]
|
|
1497
|
-
|
|
1616
|
+
case
|
|
1617
|
+
when CONFIG[:stop_at_load]
|
|
1618
|
+
add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true, hook_call: false
|
|
1619
|
+
nil # stop here
|
|
1620
|
+
when path = ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH']
|
|
1621
|
+
add_line_breakpoint path, 0, oneshot: true, hook_call: false
|
|
1622
|
+
when loc = ::DEBUGGER__.require_location
|
|
1498
1623
|
# require 'debug/start' or 'debug'
|
|
1499
1624
|
add_line_breakpoint loc.absolute_path, loc.lineno + 1, oneshot: true, hook_call: false
|
|
1500
1625
|
else
|
|
@@ -1506,29 +1631,8 @@ module DEBUGGER__
|
|
|
1506
1631
|
|
|
1507
1632
|
class << self
|
|
1508
1633
|
define_method :initialize_session do |ui|
|
|
1509
|
-
DEBUGGER__.
|
|
1510
|
-
|
|
1634
|
+
DEBUGGER__.info "Session start (pid: #{Process.pid})"
|
|
1511
1635
|
::DEBUGGER__.const_set(:SESSION, Session.new(ui))
|
|
1512
|
-
|
|
1513
|
-
# default breakpoints
|
|
1514
|
-
|
|
1515
|
-
# ::DEBUGGER__.add_catch_breakpoint 'RuntimeError'
|
|
1516
|
-
|
|
1517
|
-
Binding.module_eval do
|
|
1518
|
-
def break pre: nil, do: nil
|
|
1519
|
-
return unless SESSION.active?
|
|
1520
|
-
|
|
1521
|
-
if pre || (do_expr = binding.local_variable_get(:do))
|
|
1522
|
-
cmds = ['binding.break', pre, do_expr]
|
|
1523
|
-
end
|
|
1524
|
-
|
|
1525
|
-
::DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true, command: cmds
|
|
1526
|
-
true
|
|
1527
|
-
end
|
|
1528
|
-
alias b break
|
|
1529
|
-
# alias bp break
|
|
1530
|
-
end
|
|
1531
|
-
|
|
1532
1636
|
load_rc
|
|
1533
1637
|
end
|
|
1534
1638
|
end
|
|
@@ -1586,14 +1690,6 @@ module DEBUGGER__
|
|
|
1586
1690
|
end
|
|
1587
1691
|
end
|
|
1588
1692
|
|
|
1589
|
-
LOG_LEVELS = {
|
|
1590
|
-
UNKNOWN: 0,
|
|
1591
|
-
FATAL: 1,
|
|
1592
|
-
ERROR: 2,
|
|
1593
|
-
WARN: 3,
|
|
1594
|
-
INFO: 4,
|
|
1595
|
-
}.freeze
|
|
1596
|
-
|
|
1597
1693
|
def self.warn msg
|
|
1598
1694
|
log :WARN, msg
|
|
1599
1695
|
end
|
|
@@ -1638,7 +1734,18 @@ module DEBUGGER__
|
|
|
1638
1734
|
|
|
1639
1735
|
at_exit{
|
|
1640
1736
|
trap(:SIGINT, :IGNORE)
|
|
1641
|
-
|
|
1737
|
+
|
|
1738
|
+
# only check child process from its parent
|
|
1739
|
+
if Process.pid == parent_pid
|
|
1740
|
+
begin
|
|
1741
|
+
# sending a null signal to see if the child is still alive
|
|
1742
|
+
Process.kill(0, child_pid)
|
|
1743
|
+
# if the child is still alive, wait for it
|
|
1744
|
+
Process.waitpid(child_pid)
|
|
1745
|
+
rescue Errno::ESRCH
|
|
1746
|
+
# if the child process has died, do nothing
|
|
1747
|
+
end
|
|
1748
|
+
end
|
|
1642
1749
|
}
|
|
1643
1750
|
}
|
|
1644
1751
|
child_hook = -> {
|
|
@@ -1670,8 +1777,36 @@ module DEBUGGER__
|
|
|
1670
1777
|
end
|
|
1671
1778
|
end
|
|
1672
1779
|
|
|
1673
|
-
|
|
1674
|
-
|
|
1780
|
+
module TrapInterceptor
|
|
1781
|
+
def trap sig, *command, &command_proc
|
|
1782
|
+
case sig&.to_sym
|
|
1783
|
+
when :INT, :SIGINT
|
|
1784
|
+
if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
|
|
1785
|
+
return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
|
|
1786
|
+
end
|
|
1787
|
+
end
|
|
1788
|
+
|
|
1789
|
+
super
|
|
1790
|
+
end
|
|
1791
|
+
end
|
|
1792
|
+
|
|
1793
|
+
if RUBY_VERSION >= '3.0.0'
|
|
1794
|
+
module ::Kernel
|
|
1795
|
+
prepend ForkInterceptor
|
|
1796
|
+
prepend TrapInterceptor
|
|
1797
|
+
end
|
|
1798
|
+
else
|
|
1799
|
+
class ::Object
|
|
1800
|
+
include ForkInterceptor
|
|
1801
|
+
include TrapInterceptor
|
|
1802
|
+
end
|
|
1803
|
+
end
|
|
1804
|
+
|
|
1805
|
+
module ::Kernel
|
|
1806
|
+
class << self
|
|
1807
|
+
prepend ForkInterceptor
|
|
1808
|
+
prepend TrapInterceptor
|
|
1809
|
+
end
|
|
1675
1810
|
end
|
|
1676
1811
|
|
|
1677
1812
|
module ::Process
|
|
@@ -1679,5 +1814,29 @@ module DEBUGGER__
|
|
|
1679
1814
|
prepend ForkInterceptor
|
|
1680
1815
|
end
|
|
1681
1816
|
end
|
|
1817
|
+
|
|
1818
|
+
module ::Signal
|
|
1819
|
+
class << self
|
|
1820
|
+
prepend TrapInterceptor
|
|
1821
|
+
end
|
|
1822
|
+
end
|
|
1823
|
+
end
|
|
1824
|
+
|
|
1825
|
+
module Kernel
|
|
1826
|
+
def debugger pre: nil, do: nil
|
|
1827
|
+
return if !defined?(::DEBUGGER__::SESSION) || !::DEBUGGER__::SESSION.active?
|
|
1828
|
+
|
|
1829
|
+
if pre || (do_expr = binding.local_variable_get(:do))
|
|
1830
|
+
cmds = ['binding.break', pre, do_expr]
|
|
1831
|
+
end
|
|
1832
|
+
|
|
1833
|
+
::DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true, command: cmds
|
|
1834
|
+
self
|
|
1835
|
+
end
|
|
1836
|
+
end
|
|
1837
|
+
|
|
1838
|
+
class Binding
|
|
1839
|
+
alias break debugger
|
|
1840
|
+
alias b debugger
|
|
1682
1841
|
end
|
|
1683
1842
|
|