debug 1.2.2 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +116 -5
- data/ext/debug/debug.c +2 -1
- data/ext/debug/extconf.rb +2 -0
- data/lib/debug/breakpoint.rb +2 -2
- data/lib/debug/client.rb +69 -46
- data/lib/debug/config.rb +74 -21
- data/lib/debug/console.rb +92 -25
- data/lib/debug/local.rb +22 -1
- data/lib/debug/prelude.rb +49 -0
- data/lib/debug/server.rb +186 -25
- data/lib/debug/server_cdp.rb +412 -0
- data/lib/debug/server_dap.rb +53 -23
- data/lib/debug/session.rb +387 -120
- data/lib/debug/thread_client.rb +4 -2
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +95 -1
- metadata +8 -6
data/lib/debug/session.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# skip to load debugger for bundle exec
|
4
4
|
|
@@ -17,6 +17,7 @@ if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
|
17
17
|
return
|
18
18
|
end
|
19
19
|
|
20
|
+
require_relative 'frame_info'
|
20
21
|
require_relative 'config'
|
21
22
|
require_relative 'thread_client'
|
22
23
|
require_relative 'source_repository'
|
@@ -25,6 +26,7 @@ require_relative 'tracer'
|
|
25
26
|
|
26
27
|
# To prevent loading old lib/debug.rb in Ruby 2.6 to 3.0
|
27
28
|
$LOADED_FEATURES << 'debug.rb'
|
29
|
+
$LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
|
28
30
|
require 'debug' # invalidate the $LOADED_FEATURE cache
|
29
31
|
|
30
32
|
require 'json' if ENV['RUBY_DEBUG_TEST_MODE']
|
@@ -76,6 +78,8 @@ module DEBUGGER__
|
|
76
78
|
class PostmortemError < RuntimeError; end
|
77
79
|
|
78
80
|
class Session
|
81
|
+
attr_reader :intercepted_sigint_cmd, :process_group
|
82
|
+
|
79
83
|
def initialize ui
|
80
84
|
@ui = ui
|
81
85
|
@sr = SourceRepository.new
|
@@ -98,11 +102,15 @@ module DEBUGGER__
|
|
98
102
|
@thread_stopper = nil
|
99
103
|
@intercept_trap_sigint = false
|
100
104
|
@intercepted_sigint_cmd = 'DEFAULT'
|
105
|
+
@process_group = ProcessGroup.new
|
106
|
+
@subsession = nil
|
101
107
|
|
102
108
|
@frame_map = {} # {id => [threadId, frame_depth]} for DAP
|
103
109
|
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
104
110
|
@src_map = {} # {id => src}
|
105
111
|
|
112
|
+
@script_paths = [File.absolute_path($0)] # for CDP
|
113
|
+
|
106
114
|
@tp_thread_begin = nil
|
107
115
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
108
116
|
ThreadClient.current.on_load tp.instruction_sequence, tp.eval_script
|
@@ -183,95 +191,105 @@ module DEBUGGER__
|
|
183
191
|
|
184
192
|
def session_server_main
|
185
193
|
while evt = pop_event
|
186
|
-
|
187
|
-
|
194
|
+
process_event evt
|
195
|
+
end
|
196
|
+
ensure
|
197
|
+
deactivate
|
198
|
+
end
|
199
|
+
|
200
|
+
def process_event evt
|
201
|
+
# variable `@internal_info` is only used for test
|
202
|
+
tc, output, ev, @internal_info, *ev_args = evt
|
203
|
+
output.each{|str| @ui.puts str} if ev != :suspend
|
204
|
+
|
205
|
+
case ev
|
206
|
+
|
207
|
+
when :thread_begin # special event, tc is nil
|
208
|
+
th = ev_args.shift
|
209
|
+
q = ev_args.shift
|
210
|
+
on_thread_begin th
|
211
|
+
q << true
|
212
|
+
|
213
|
+
when :init
|
214
|
+
wait_command_loop tc
|
215
|
+
|
216
|
+
when :load
|
217
|
+
iseq, src = ev_args
|
218
|
+
on_load iseq, src
|
219
|
+
@ui.event :load
|
220
|
+
tc << :continue
|
221
|
+
|
222
|
+
when :trace
|
223
|
+
trace_id, msg = ev_args
|
224
|
+
if t = @tracers.values.find{|t| t.object_id == trace_id}
|
225
|
+
t.puts msg
|
226
|
+
end
|
227
|
+
tc << :continue
|
228
|
+
|
229
|
+
when :suspend
|
230
|
+
enter_subsession if ev_args.first != :replay
|
188
231
|
output.each{|str| @ui.puts str}
|
189
232
|
|
190
|
-
case
|
233
|
+
case ev_args.first
|
234
|
+
when :breakpoint
|
235
|
+
bp, i = bp_index ev_args[1]
|
236
|
+
@ui.event :suspend_bp, i, bp, tc.id
|
237
|
+
when :trap
|
238
|
+
@ui.event :suspend_trap, sig = ev_args[1], tc.id
|
191
239
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
240
|
+
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
241
|
+
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registerred as SIGINT handler."
|
242
|
+
@ui.puts "`sigint` command execute it."
|
243
|
+
end
|
244
|
+
else
|
245
|
+
@ui.event :suspended, tc.id
|
246
|
+
end
|
197
247
|
|
198
|
-
|
248
|
+
if @displays.empty?
|
199
249
|
wait_command_loop tc
|
250
|
+
else
|
251
|
+
tc << [:eval, :display, @displays]
|
252
|
+
end
|
200
253
|
|
201
|
-
|
202
|
-
|
203
|
-
on_load iseq, src
|
204
|
-
@ui.event :load
|
205
|
-
tc << :continue
|
254
|
+
when :result
|
255
|
+
raise "[BUG] not in subsession" unless @subsession
|
206
256
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
when :suspend
|
215
|
-
case ev_args.first
|
216
|
-
when :breakpoint
|
217
|
-
bp, i = bp_index ev_args[1]
|
218
|
-
@ui.event :suspend_bp, i, bp, tc.id
|
219
|
-
when :trap
|
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."
|
257
|
+
case ev_args.first
|
258
|
+
when :try_display
|
259
|
+
failed_results = ev_args[1]
|
260
|
+
if failed_results.size > 0
|
261
|
+
i, _msg = failed_results.last
|
262
|
+
if i+1 == @displays.size
|
263
|
+
@ui.puts "canceled: #{@displays.pop}"
|
225
264
|
end
|
226
|
-
else
|
227
|
-
@ui.event :suspended, tc.id
|
228
|
-
end
|
229
|
-
|
230
|
-
if @displays.empty?
|
231
|
-
stop_all_threads
|
232
|
-
wait_command_loop tc
|
233
|
-
else
|
234
|
-
tc << [:eval, :display, @displays]
|
235
265
|
end
|
236
266
|
|
237
|
-
when :
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
i, _msg = failed_results.last
|
243
|
-
if i+1 == @displays.size
|
244
|
-
@ui.puts "canceled: #{@displays.pop}"
|
245
|
-
end
|
246
|
-
end
|
247
|
-
stop_all_threads
|
248
|
-
|
249
|
-
when :method_breakpoint, :watch_breakpoint
|
250
|
-
bp = ev_args[1]
|
251
|
-
if bp
|
252
|
-
add_bp(bp)
|
253
|
-
show_bps bp
|
254
|
-
else
|
255
|
-
# can't make a bp
|
256
|
-
end
|
257
|
-
when :trace_pass
|
258
|
-
obj_id = ev_args[1]
|
259
|
-
obj_inspect = ev_args[2]
|
260
|
-
opt = ev_args[3]
|
261
|
-
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
|
267
|
+
when :method_breakpoint, :watch_breakpoint
|
268
|
+
bp = ev_args[1]
|
269
|
+
if bp
|
270
|
+
add_bp(bp)
|
271
|
+
show_bps bp
|
262
272
|
else
|
263
|
-
#
|
273
|
+
# can't make a bp
|
264
274
|
end
|
275
|
+
when :trace_pass
|
276
|
+
obj_id = ev_args[1]
|
277
|
+
obj_inspect = ev_args[2]
|
278
|
+
opt = ev_args[3]
|
279
|
+
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
|
280
|
+
else
|
281
|
+
# ignore
|
282
|
+
end
|
265
283
|
|
266
|
-
|
284
|
+
wait_command_loop tc
|
267
285
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
286
|
+
when :dap_result
|
287
|
+
dap_event ev_args # server.rb
|
288
|
+
wait_command_loop tc
|
289
|
+
when :cdp_result
|
290
|
+
cdp_event ev_args
|
291
|
+
wait_command_loop tc
|
272
292
|
end
|
273
|
-
ensure
|
274
|
-
deactivate
|
275
293
|
end
|
276
294
|
|
277
295
|
def add_preset_commands name, cmds, kick: true, continue: true
|
@@ -322,6 +340,8 @@ module DEBUGGER__
|
|
322
340
|
def prompt
|
323
341
|
if @postmortem
|
324
342
|
'(rdbg:postmortem) '
|
343
|
+
elsif @process_group.multi?
|
344
|
+
"(rdbg@#{process_info}) "
|
325
345
|
else
|
326
346
|
'(rdbg) '
|
327
347
|
end
|
@@ -333,8 +353,7 @@ module DEBUGGER__
|
|
333
353
|
if @preset_command.auto_continue
|
334
354
|
@preset_command = nil
|
335
355
|
|
336
|
-
|
337
|
-
restart_all_threads
|
356
|
+
leave_subsession :continue
|
338
357
|
return
|
339
358
|
else
|
340
359
|
@preset_command = nil
|
@@ -353,7 +372,7 @@ module DEBUGGER__
|
|
353
372
|
when String
|
354
373
|
process_command line
|
355
374
|
when Hash
|
356
|
-
|
375
|
+
process_protocol_request line # defined in server.rb
|
357
376
|
else
|
358
377
|
raise "unexpected input: #{line.inspect}"
|
359
378
|
end
|
@@ -409,16 +428,14 @@ module DEBUGGER__
|
|
409
428
|
# * Resume the program.
|
410
429
|
when 'c', 'continue'
|
411
430
|
cancel_auto_continue
|
412
|
-
|
413
|
-
restart_all_threads
|
431
|
+
leave_subsession :continue
|
414
432
|
|
415
433
|
# * `q[uit]` or `Ctrl-D`
|
416
434
|
# * Finish debugger (with the debuggee process on non-remote debugging).
|
417
435
|
when 'q', 'quit'
|
418
436
|
if ask 'Really quit?'
|
419
437
|
@ui.quit arg.to_i
|
420
|
-
|
421
|
-
restart_all_threads
|
438
|
+
leave_subsession :continue
|
422
439
|
else
|
423
440
|
return :retry
|
424
441
|
end
|
@@ -427,7 +444,7 @@ module DEBUGGER__
|
|
427
444
|
# * Same as q[uit] but without the confirmation prompt.
|
428
445
|
when 'q!', 'quit!'
|
429
446
|
@ui.quit arg.to_i
|
430
|
-
|
447
|
+
leave_subsession nil
|
431
448
|
|
432
449
|
# * `kill`
|
433
450
|
# * Stop the debuggee process with `Kernal#exit!`.
|
@@ -457,8 +474,7 @@ module DEBUGGER__
|
|
457
474
|
cmd.call
|
458
475
|
end
|
459
476
|
|
460
|
-
|
461
|
-
restart_all_threads
|
477
|
+
leave_subsession :continue
|
462
478
|
|
463
479
|
rescue Exception => e
|
464
480
|
@ui.puts "Exception: #{e}"
|
@@ -722,8 +738,8 @@ module DEBUGGER__
|
|
722
738
|
if ask "clear all?", 'N'
|
723
739
|
@displays.clear
|
724
740
|
end
|
741
|
+
return :retry
|
725
742
|
end
|
726
|
-
return :retry
|
727
743
|
|
728
744
|
### Frame control
|
729
745
|
|
@@ -922,6 +938,36 @@ module DEBUGGER__
|
|
922
938
|
end
|
923
939
|
return :retry
|
924
940
|
|
941
|
+
# * `open`
|
942
|
+
# * open debuggee port on UNIX domain socket and wait for attaching.
|
943
|
+
# * Note that `open` command is EXPERIMENTAL.
|
944
|
+
# * `open [<host>:]<port>`
|
945
|
+
# * open debuggee port on TCP/IP with given `[<host>:]<port>` and wait for attaching.
|
946
|
+
# * `open vscode`
|
947
|
+
# * open debuggee port for VSCode and launch VSCode if available.
|
948
|
+
# * `open chrome`
|
949
|
+
# * open debuggee port for Chrome and wait for attaching.
|
950
|
+
when 'open'
|
951
|
+
case arg&.downcase
|
952
|
+
when '', nil
|
953
|
+
repl_open_unix
|
954
|
+
when 'vscode'
|
955
|
+
repl_open_vscode
|
956
|
+
when /\A(.+):(\d+)\z/
|
957
|
+
repl_open_tcp $1, $2.to_i
|
958
|
+
when /\A(\d+)z/
|
959
|
+
repl_open_tcp nil, $1.to_i
|
960
|
+
when 'tcp'
|
961
|
+
repl_open_tcp CONFIG[:host], (CONFIG[:port] || 0)
|
962
|
+
when 'chrome', 'cdp'
|
963
|
+
CONFIG[:open_frontend] = 'chrome'
|
964
|
+
repl_open_tcp CONFIG[:host], (CONFIG[:port] || 0)
|
965
|
+
else
|
966
|
+
raise "Unknown arg: #{arg}"
|
967
|
+
end
|
968
|
+
|
969
|
+
return :retry
|
970
|
+
|
925
971
|
### Help
|
926
972
|
|
927
973
|
# * `h[elp]`
|
@@ -967,14 +1013,38 @@ module DEBUGGER__
|
|
967
1013
|
return :retry
|
968
1014
|
end
|
969
1015
|
|
1016
|
+
def repl_open_setup
|
1017
|
+
@tp_thread_begin.disable
|
1018
|
+
@ui.activate self
|
1019
|
+
if @ui.respond_to?(:reader_thread) && thc = thread_client(@ui.reader_thread)
|
1020
|
+
thc.is_management
|
1021
|
+
end
|
1022
|
+
@tp_thread_begin.enable
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def repl_open_tcp host, port, **kw
|
1026
|
+
DEBUGGER__.open_tcp host: host, port: port, nonstop: true, **kw
|
1027
|
+
repl_open_setup
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def repl_open_unix
|
1031
|
+
DEBUGGER__.open_unix nonstop: true
|
1032
|
+
repl_open_setup
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
def repl_open_vscode
|
1036
|
+
CONFIG[:open_frontend] = 'vscode'
|
1037
|
+
repl_open_unix
|
1038
|
+
end
|
1039
|
+
|
970
1040
|
def step_command type, arg
|
971
1041
|
case arg
|
972
|
-
when nil
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
1042
|
+
when nil, /\A\d+\z/
|
1043
|
+
if type == :in && @tc.recorder&.replaying?
|
1044
|
+
@tc << [:step, type, arg&.to_i]
|
1045
|
+
else
|
1046
|
+
leave_subsession [:step, type, arg&.to_i]
|
1047
|
+
end
|
978
1048
|
when /\Aback\z/, /\Areset\z/
|
979
1049
|
if type != :in
|
980
1050
|
@ui.puts "only `step #{arg}` is supported."
|
@@ -1288,10 +1358,15 @@ module DEBUGGER__
|
|
1288
1358
|
end
|
1289
1359
|
|
1290
1360
|
def setup_threads
|
1361
|
+
prev_clients = @th_clients || {}
|
1291
1362
|
@th_clients = {}
|
1292
1363
|
|
1293
1364
|
Thread.list.each{|th|
|
1294
|
-
|
1365
|
+
if tc = prev_clients[th]
|
1366
|
+
@th_clients[th] = tc
|
1367
|
+
else
|
1368
|
+
thread_client_create(th)
|
1369
|
+
end
|
1295
1370
|
}
|
1296
1371
|
end
|
1297
1372
|
|
@@ -1375,7 +1450,30 @@ module DEBUGGER__
|
|
1375
1450
|
next if @tc == tc
|
1376
1451
|
tc << :continue
|
1377
1452
|
}
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
private def enter_subsession
|
1456
|
+
raise "already in subsession" if @subsession
|
1457
|
+
@subsession = true
|
1458
|
+
stop_all_threads
|
1459
|
+
@process_group.lock
|
1460
|
+
DEBUGGER__.info "enter_subsession"
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
private def leave_subsession type
|
1464
|
+
DEBUGGER__.info "leave_subsession"
|
1465
|
+
@process_group.unlock
|
1466
|
+
restart_all_threads
|
1467
|
+
@tc << type if type
|
1378
1468
|
@tc = nil
|
1469
|
+
@subsession = false
|
1470
|
+
rescue Exception => e
|
1471
|
+
STDERR.puts [e, e.backtrace].inspect
|
1472
|
+
raise
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
def in_subsession?
|
1476
|
+
@subsession
|
1379
1477
|
end
|
1380
1478
|
|
1381
1479
|
## event
|
@@ -1501,8 +1599,6 @@ module DEBUGGER__
|
|
1501
1599
|
prev
|
1502
1600
|
end
|
1503
1601
|
|
1504
|
-
attr_reader :intercepted_sigint_cmd
|
1505
|
-
|
1506
1602
|
def intercept_trap_sigint?
|
1507
1603
|
@intercept_trap_sigint
|
1508
1604
|
end
|
@@ -1519,6 +1615,159 @@ module DEBUGGER__
|
|
1519
1615
|
@intercept_trap_sigint = true
|
1520
1616
|
@intercepted_sigint_cmd = prev
|
1521
1617
|
end
|
1618
|
+
|
1619
|
+
def intercept_trap_sigint_end
|
1620
|
+
@intercept_trap_sigint = false
|
1621
|
+
prev, @intercepted_sigint_cmd = @intercepted_sigint_cmd, nil
|
1622
|
+
prev
|
1623
|
+
end
|
1624
|
+
|
1625
|
+
def process_info
|
1626
|
+
if @process_group.multi?
|
1627
|
+
"#{$0}\##{Process.pid}"
|
1628
|
+
end
|
1629
|
+
end
|
1630
|
+
|
1631
|
+
def before_fork need_lock = true
|
1632
|
+
if need_lock
|
1633
|
+
@process_group.multi_process!
|
1634
|
+
end
|
1635
|
+
end
|
1636
|
+
|
1637
|
+
def after_fork_parent
|
1638
|
+
@ui.after_fork_parent
|
1639
|
+
end
|
1640
|
+
end
|
1641
|
+
|
1642
|
+
class ProcessGroup
|
1643
|
+
def initialize
|
1644
|
+
@lock_file = nil
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
def locked?
|
1648
|
+
true
|
1649
|
+
end
|
1650
|
+
|
1651
|
+
def trylock
|
1652
|
+
true
|
1653
|
+
end
|
1654
|
+
|
1655
|
+
def lock
|
1656
|
+
true
|
1657
|
+
end
|
1658
|
+
|
1659
|
+
def unlock
|
1660
|
+
true
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
def sync
|
1664
|
+
yield
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
def after_fork
|
1668
|
+
end
|
1669
|
+
|
1670
|
+
def multi?
|
1671
|
+
@lock_file
|
1672
|
+
end
|
1673
|
+
|
1674
|
+
def multi_process!
|
1675
|
+
require 'tempfile'
|
1676
|
+
@lock_tempfile = Tempfile.open("ruby-debug-lock-")
|
1677
|
+
@lock_tempfile.close
|
1678
|
+
extend MultiProcessGroup
|
1679
|
+
end
|
1680
|
+
end
|
1681
|
+
|
1682
|
+
module MultiProcessGroup
|
1683
|
+
def multi_process!
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
def after_fork child: true
|
1687
|
+
if child || !@lock_file
|
1688
|
+
@m = Mutex.new
|
1689
|
+
@lock_level = 0
|
1690
|
+
@lock_file = open(@lock_tempfile.path, 'w')
|
1691
|
+
end
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
def info msg
|
1695
|
+
DEBUGGER__.info "#{msg} (#{@lock_level})" # #{caller.first(1).map{|bt| bt.sub(__dir__, '')}}"
|
1696
|
+
end
|
1697
|
+
|
1698
|
+
def locked?
|
1699
|
+
# DEBUGGER__.info "locked? #{@lock_level}"
|
1700
|
+
@lock_level > 0
|
1701
|
+
end
|
1702
|
+
|
1703
|
+
private def lock_level_up
|
1704
|
+
raise unless @m.owned?
|
1705
|
+
@lock_level += 1
|
1706
|
+
end
|
1707
|
+
|
1708
|
+
private def lock_level_down
|
1709
|
+
raise unless @m.owned?
|
1710
|
+
raise "@lock_level underflow: #{@lock_level}" if @lock_level < 1
|
1711
|
+
@lock_level -= 1
|
1712
|
+
end
|
1713
|
+
|
1714
|
+
private def trylock
|
1715
|
+
@m.synchronize do
|
1716
|
+
if locked?
|
1717
|
+
lock_level_up
|
1718
|
+
info "Try lock, already locked"
|
1719
|
+
true
|
1720
|
+
else
|
1721
|
+
case r = @lock_file.flock(File::LOCK_EX | File::LOCK_NB)
|
1722
|
+
when 0
|
1723
|
+
lock_level_up
|
1724
|
+
info "Try lock with file: success"
|
1725
|
+
true
|
1726
|
+
when false
|
1727
|
+
info "Try lock with file: failed"
|
1728
|
+
false
|
1729
|
+
else
|
1730
|
+
raise "unknown flock result: #{r.inspect}"
|
1731
|
+
end
|
1732
|
+
end
|
1733
|
+
end
|
1734
|
+
end
|
1735
|
+
|
1736
|
+
def lock
|
1737
|
+
unless trylock
|
1738
|
+
@m.synchronize do
|
1739
|
+
if locked?
|
1740
|
+
lock_level_up
|
1741
|
+
else
|
1742
|
+
info "Lock: block"
|
1743
|
+
@lock_file.flock(File::LOCK_EX)
|
1744
|
+
lock_level_up
|
1745
|
+
end
|
1746
|
+
end
|
1747
|
+
|
1748
|
+
info "Lock: success"
|
1749
|
+
end
|
1750
|
+
end
|
1751
|
+
|
1752
|
+
def unlock
|
1753
|
+
@m.synchronize do
|
1754
|
+
raise "lock file is not opened (#{@lock_file.inspect})" if @lock_file.closed?
|
1755
|
+
lock_level_down
|
1756
|
+
@lock_file.flock(File::LOCK_UN) unless locked?
|
1757
|
+
info "Unlocked"
|
1758
|
+
end
|
1759
|
+
end
|
1760
|
+
|
1761
|
+
def sync &b
|
1762
|
+
info "sync"
|
1763
|
+
|
1764
|
+
lock
|
1765
|
+
begin
|
1766
|
+
b.call if b
|
1767
|
+
ensure
|
1768
|
+
unlock
|
1769
|
+
end
|
1770
|
+
end
|
1522
1771
|
end
|
1523
1772
|
|
1524
1773
|
class UI_Base
|
@@ -1576,8 +1825,8 @@ module DEBUGGER__
|
|
1576
1825
|
def self.open host: nil, port: CONFIG[:port], sock_path: nil, sock_dir: nil, nonstop: false, **kw
|
1577
1826
|
CONFIG.set_config(**kw)
|
1578
1827
|
|
1579
|
-
if port
|
1580
|
-
open_tcp host: host, port: port, nonstop: nonstop
|
1828
|
+
if port || CONFIG[:open_frontend] == 'chrome'
|
1829
|
+
open_tcp host: host, port: (port || 0), nonstop: nonstop
|
1581
1830
|
else
|
1582
1831
|
open_unix sock_path: sock_path, sock_dir: sock_dir, nonstop: nonstop
|
1583
1832
|
end
|
@@ -1699,15 +1948,24 @@ module DEBUGGER__
|
|
1699
1948
|
end
|
1700
1949
|
|
1701
1950
|
def self.log level, msg
|
1951
|
+
@logfile = STDERR unless defined? @logfile
|
1952
|
+
|
1702
1953
|
lv = LOG_LEVELS[level]
|
1703
1954
|
config_lv = LOG_LEVELS[CONFIG[:log_level] || :WARN]
|
1704
1955
|
|
1956
|
+
if defined? SESSION
|
1957
|
+
pi = SESSION.process_info
|
1958
|
+
process_info = pi ? "[#{pi}]" : nil
|
1959
|
+
end
|
1960
|
+
|
1705
1961
|
if lv <= config_lv
|
1706
1962
|
if level == :WARN
|
1707
1963
|
# :WARN on debugger is general information
|
1708
|
-
|
1964
|
+
@logfile.puts "DEBUGGER#{process_info}: #{msg}"
|
1965
|
+
@logfile.flush
|
1709
1966
|
else
|
1710
|
-
|
1967
|
+
@logfile.puts "DEBUGGER#{process_info} (#{level}): #{msg}"
|
1968
|
+
@logfile.flush
|
1711
1969
|
end
|
1712
1970
|
end
|
1713
1971
|
end
|
@@ -1716,8 +1974,19 @@ module DEBUGGER__
|
|
1716
1974
|
def fork(&given_block)
|
1717
1975
|
return super unless defined?(SESSION) && SESSION.active?
|
1718
1976
|
|
1977
|
+
unless fork_mode = CONFIG[:fork_mode]
|
1978
|
+
if CONFIG[:parent_on_fork]
|
1979
|
+
fork_mode = :parent
|
1980
|
+
else
|
1981
|
+
fork_mode = :both
|
1982
|
+
end
|
1983
|
+
end
|
1984
|
+
|
1985
|
+
parent_pid = Process.pid
|
1986
|
+
|
1719
1987
|
# before fork
|
1720
|
-
|
1988
|
+
case fork_mode
|
1989
|
+
when :parent
|
1721
1990
|
parent_hook = -> child_pid {
|
1722
1991
|
# Do nothing
|
1723
1992
|
}
|
@@ -1725,31 +1994,28 @@ module DEBUGGER__
|
|
1725
1994
|
DEBUGGER__.warn "Detaching after fork from child process #{Process.pid}"
|
1726
1995
|
SESSION.deactivate
|
1727
1996
|
}
|
1728
|
-
|
1729
|
-
|
1997
|
+
when :child
|
1998
|
+
SESSION.before_fork false
|
1730
1999
|
|
1731
2000
|
parent_hook = -> child_pid {
|
1732
2001
|
DEBUGGER__.warn "Detaching after fork from parent process #{Process.pid}"
|
2002
|
+
SESSION.after_fork_parent
|
1733
2003
|
SESSION.deactivate
|
2004
|
+
}
|
2005
|
+
child_hook = -> {
|
2006
|
+
DEBUGGER__.warn "Attaching after process #{parent_pid} fork to child process #{Process.pid}"
|
2007
|
+
SESSION.activate on_fork: true
|
2008
|
+
}
|
2009
|
+
when :both
|
2010
|
+
SESSION.before_fork
|
1734
2011
|
|
1735
|
-
|
1736
|
-
|
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
|
1749
|
-
}
|
2012
|
+
parent_hook = -> child_pid {
|
2013
|
+
SESSION.process_group.after_fork
|
2014
|
+
SESSION.after_fork_parent
|
1750
2015
|
}
|
1751
2016
|
child_hook = -> {
|
1752
2017
|
DEBUGGER__.warn "Attaching after process #{parent_pid} fork to child process #{Process.pid}"
|
2018
|
+
SESSION.process_group.after_fork child: true
|
1753
2019
|
SESSION.activate on_fork: true
|
1754
2020
|
}
|
1755
2021
|
end
|
@@ -1823,20 +2089,21 @@ module DEBUGGER__
|
|
1823
2089
|
end
|
1824
2090
|
|
1825
2091
|
module Kernel
|
1826
|
-
def debugger pre: nil, do: nil
|
2092
|
+
def debugger pre: nil, do: nil, up_level: 0
|
1827
2093
|
return if !defined?(::DEBUGGER__::SESSION) || !::DEBUGGER__::SESSION.active?
|
1828
2094
|
|
1829
2095
|
if pre || (do_expr = binding.local_variable_get(:do))
|
1830
2096
|
cmds = ['binding.break', pre, do_expr]
|
1831
2097
|
end
|
1832
2098
|
|
1833
|
-
::DEBUGGER__.add_line_breakpoint
|
2099
|
+
loc = caller_locations(up_level, 1).first; ::DEBUGGER__.add_line_breakpoint loc.path, loc.lineno + 1, oneshot: true, command: cmds
|
1834
2100
|
self
|
1835
2101
|
end
|
2102
|
+
|
2103
|
+
alias bb debugger if ENV['RUBY_DEBUG_BB']
|
1836
2104
|
end
|
1837
2105
|
|
1838
2106
|
class Binding
|
1839
2107
|
alias break debugger
|
1840
2108
|
alias b debugger
|
1841
2109
|
end
|
1842
|
-
|