debug 1.4.0 → 1.5.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 +124 -0
- data/Gemfile +1 -0
- data/README.md +16 -16
- data/Rakefile +5 -5
- data/debug.gemspec +6 -4
- data/ext/debug/debug.c +84 -0
- data/ext/debug/extconf.rb +11 -0
- data/lib/debug/breakpoint.rb +24 -35
- data/lib/debug/client.rb +9 -3
- data/lib/debug/console.rb +34 -13
- data/lib/debug/frame_info.rb +4 -5
- data/lib/debug/local.rb +1 -1
- data/lib/debug/server.rb +65 -27
- data/lib/debug/server_cdp.rb +369 -148
- data/lib/debug/server_dap.rb +72 -75
- data/lib/debug/session.rb +225 -114
- data/lib/debug/source_repository.rb +91 -51
- data/lib/debug/thread_client.rb +94 -42
- data/lib/debug/tracer.rb +3 -9
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +6 -6
- metadata +4 -13
- 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/session.rb
CHANGED
@@ -7,7 +7,7 @@ return if ENV['RUBY_DEBUG_ENABLE'] == '0'
|
|
7
7
|
if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
8
8
|
trace_var(:$0) do |file|
|
9
9
|
trace_var(:$0, nil)
|
10
|
-
if /-r (#{__dir__}\S+)/ =~ ENV['RUBYOPT']
|
10
|
+
if /-r (#{Regexp.escape(__dir__)}\S+)/ =~ ENV['RUBYOPT']
|
11
11
|
lib = $1
|
12
12
|
$LOADED_FEATURES.delete_if{|path| path.start_with?(__dir__)}
|
13
13
|
ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH'] = file
|
@@ -31,7 +31,7 @@ $LOADED_FEATURES << 'debug.rb'
|
|
31
31
|
$LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
|
32
32
|
require 'debug' # invalidate the $LOADED_FEATURE cache
|
33
33
|
|
34
|
-
require 'json' if ENV['
|
34
|
+
require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
35
35
|
|
36
36
|
class RubyVM::InstructionSequence
|
37
37
|
def traceable_lines_norec lines
|
@@ -56,23 +56,22 @@ class RubyVM::InstructionSequence
|
|
56
56
|
|
57
57
|
def type
|
58
58
|
self.to_a[9]
|
59
|
-
end
|
60
|
-
|
61
|
-
def argc
|
62
|
-
self.to_a[4][:arg_size]
|
63
|
-
end
|
59
|
+
end unless method_defined?(:type)
|
64
60
|
|
65
|
-
def
|
66
|
-
self.to_a
|
67
|
-
|
61
|
+
def parameters_symbols
|
62
|
+
ary = self.to_a
|
63
|
+
argc = ary[4][:arg_size]
|
64
|
+
locals = ary.to_a[10]
|
65
|
+
locals[0...argc]
|
66
|
+
end unless method_defined?(:parameters_symbols)
|
68
67
|
|
69
68
|
def last_line
|
70
69
|
self.to_a[4][:code_location][2]
|
71
|
-
end
|
70
|
+
end unless method_defined?(:last_line)
|
72
71
|
|
73
72
|
def first_line
|
74
73
|
self.to_a[4][:code_location][0]
|
75
|
-
end
|
74
|
+
end unless method_defined?(:first_line)
|
76
75
|
end
|
77
76
|
|
78
77
|
module DEBUGGER__
|
@@ -82,8 +81,10 @@ module DEBUGGER__
|
|
82
81
|
class Session
|
83
82
|
attr_reader :intercepted_sigint_cmd, :process_group
|
84
83
|
|
85
|
-
|
86
|
-
|
84
|
+
include Color
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
@ui = nil
|
87
88
|
@sr = SourceRepository.new
|
88
89
|
@bps = {} # bp.key => bp
|
89
90
|
# [file, line] => LineBreakpoint
|
@@ -104,13 +105,13 @@ module DEBUGGER__
|
|
104
105
|
@intercept_trap_sigint = false
|
105
106
|
@intercepted_sigint_cmd = 'DEFAULT'
|
106
107
|
@process_group = ProcessGroup.new
|
107
|
-
@
|
108
|
+
@subsession_stack = []
|
108
109
|
|
109
110
|
@frame_map = {} # for DAP: {id => [threadId, frame_depth]} and CDP: {id => frame_depth}
|
110
111
|
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
111
112
|
@src_map = {} # {id => src}
|
112
113
|
|
113
|
-
@
|
114
|
+
@scr_id_map = {} # for CDP
|
114
115
|
@obj_map = {} # { object_id => ... } for CDP
|
115
116
|
|
116
117
|
@tp_thread_begin = nil
|
@@ -120,9 +121,6 @@ module DEBUGGER__
|
|
120
121
|
@tp_load_script.enable
|
121
122
|
|
122
123
|
@thread_stopper = thread_stopper
|
123
|
-
|
124
|
-
activate
|
125
|
-
|
126
124
|
self.postmortem = CONFIG[:postmortem]
|
127
125
|
end
|
128
126
|
|
@@ -134,15 +132,13 @@ module DEBUGGER__
|
|
134
132
|
@bps.has_key? [file, line]
|
135
133
|
end
|
136
134
|
|
137
|
-
def activate on_fork: false
|
135
|
+
def activate ui = nil, on_fork: false
|
136
|
+
@ui = ui if ui
|
137
|
+
|
138
138
|
@tp_thread_begin&.disable
|
139
139
|
@tp_thread_begin = nil
|
140
140
|
|
141
|
-
|
142
|
-
@ui.activate self, on_fork: true
|
143
|
-
else
|
144
|
-
@ui.activate self, on_fork: false
|
145
|
-
end
|
141
|
+
@ui.activate self, on_fork: on_fork
|
146
142
|
|
147
143
|
q = Queue.new
|
148
144
|
@session_server = Thread.new do
|
@@ -215,6 +211,7 @@ module DEBUGGER__
|
|
215
211
|
q << true
|
216
212
|
|
217
213
|
when :init
|
214
|
+
enter_subsession
|
218
215
|
wait_command_loop tc
|
219
216
|
|
220
217
|
when :load
|
@@ -257,7 +254,7 @@ module DEBUGGER__
|
|
257
254
|
end
|
258
255
|
|
259
256
|
when :result
|
260
|
-
raise "[BUG] not in subsession"
|
257
|
+
raise "[BUG] not in subsession" if @subsession_stack.empty?
|
261
258
|
|
262
259
|
case ev_args.first
|
263
260
|
when :try_display
|
@@ -369,7 +366,7 @@ module DEBUGGER__
|
|
369
366
|
@ui.puts "(rdbg:#{@preset_command.source}) #{line}"
|
370
367
|
end
|
371
368
|
else
|
372
|
-
@ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['
|
369
|
+
@ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
373
370
|
line = @ui.readline prompt
|
374
371
|
end
|
375
372
|
|
@@ -510,8 +507,8 @@ module DEBUGGER__
|
|
510
507
|
# * break and run `<command>` before stopping.
|
511
508
|
# * `b[reak] ... do: <command>`
|
512
509
|
# * break and run `<command>`, and continue.
|
513
|
-
# * `b[reak] ... path: <
|
514
|
-
# * break if the
|
510
|
+
# * `b[reak] ... path: <path>`
|
511
|
+
# * break if the path matches to `<path>`. `<path>` can be a regexp with `/regexp/`.
|
515
512
|
# * `b[reak] if: <expr>`
|
516
513
|
# * break if: `<expr>` is true at any lines.
|
517
514
|
# * Note that this feature is super slow.
|
@@ -566,8 +563,8 @@ module DEBUGGER__
|
|
566
563
|
# * runs `<command>` before stopping.
|
567
564
|
# * `catch ... do: <command>`
|
568
565
|
# * stops and run `<command>`, and continue.
|
569
|
-
# * `catch ... path: <
|
570
|
-
# * stops if the exception is raised from a path
|
566
|
+
# * `catch ... path: <path>`
|
567
|
+
# * stops if the exception is raised from a `<path>`. `<path>` can be a regexp with `/regexp/`.
|
571
568
|
when 'catch'
|
572
569
|
check_postmortem
|
573
570
|
|
@@ -588,8 +585,8 @@ module DEBUGGER__
|
|
588
585
|
# * runs `<command>` before stopping.
|
589
586
|
# * `watch ... do: <command>`
|
590
587
|
# * stops and run `<command>`, and continue.
|
591
|
-
# * `watch ... path: <
|
592
|
-
# * stops if the
|
588
|
+
# * `watch ... path: <path>`
|
589
|
+
# * stops if the path matches `<path>`. `<path>` can be a regexp with `/regexp/`.
|
593
590
|
when 'wat', 'watch'
|
594
591
|
check_postmortem
|
595
592
|
|
@@ -699,8 +696,8 @@ module DEBUGGER__
|
|
699
696
|
# * Show information about accessible constants except toplevel constants.
|
700
697
|
# * `i[nfo] g[lobal[s]]`
|
701
698
|
# * Show information about global variables
|
702
|
-
# * `i[nfo] ...
|
703
|
-
# * Filter the output with
|
699
|
+
# * `i[nfo] ... /regexp/`
|
700
|
+
# * Filter the output with `/regexp/`.
|
704
701
|
# * `i[nfo] th[read[s]]`
|
705
702
|
# * Show all threads (same as `th[read]`).
|
706
703
|
when 'i', 'info'
|
@@ -834,8 +831,8 @@ module DEBUGGER__
|
|
834
831
|
# * Add an exception tracer. It indicates raising exceptions.
|
835
832
|
# * `trace object <expr>`
|
836
833
|
# * Add an object tracer. It indicates that an object by `<expr>` is passed as a parameter or a receiver on method call.
|
837
|
-
# * `trace ...
|
838
|
-
# * Indicates only matched events to
|
834
|
+
# * `trace ... /regexp/`
|
835
|
+
# * Indicates only matched events to `/regexp/`.
|
839
836
|
# * `trace ... into: <file>`
|
840
837
|
# * Save trace information into: `<file>`.
|
841
838
|
# * `trace off <num>`
|
@@ -978,7 +975,7 @@ module DEBUGGER__
|
|
978
975
|
when 'open'
|
979
976
|
case arg&.downcase
|
980
977
|
when '', nil
|
981
|
-
|
978
|
+
repl_open
|
982
979
|
when 'vscode'
|
983
980
|
repl_open_vscode
|
984
981
|
when /\A(.+):(\d+)\z/
|
@@ -1003,11 +1000,7 @@ module DEBUGGER__
|
|
1003
1000
|
# * `h[elp] <command>`
|
1004
1001
|
# * Show help for the given command.
|
1005
1002
|
when 'h', 'help', '?'
|
1006
|
-
|
1007
|
-
show_help arg
|
1008
|
-
else
|
1009
|
-
@ui.puts DEBUGGER__.help
|
1010
|
-
end
|
1003
|
+
show_help arg
|
1011
1004
|
return :retry
|
1012
1005
|
|
1013
1006
|
### END
|
@@ -1055,14 +1048,14 @@ module DEBUGGER__
|
|
1055
1048
|
repl_open_setup
|
1056
1049
|
end
|
1057
1050
|
|
1058
|
-
def
|
1059
|
-
DEBUGGER__.
|
1051
|
+
def repl_open
|
1052
|
+
DEBUGGER__.open nonstop: true
|
1060
1053
|
repl_open_setup
|
1061
1054
|
end
|
1062
1055
|
|
1063
1056
|
def repl_open_vscode
|
1064
1057
|
CONFIG[:open_frontend] = 'vscode'
|
1065
|
-
|
1058
|
+
repl_open
|
1066
1059
|
end
|
1067
1060
|
|
1068
1061
|
def step_command type, arg
|
@@ -1159,16 +1152,50 @@ module DEBUGGER__
|
|
1159
1152
|
end
|
1160
1153
|
end
|
1161
1154
|
|
1162
|
-
def show_help arg
|
1163
|
-
DEBUGGER__.
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1155
|
+
def show_help arg = nil
|
1156
|
+
instructions = (DEBUGGER__.commands.keys + DEBUGGER__.commands.values).uniq
|
1157
|
+
print_instructions = proc do |desc|
|
1158
|
+
desc.split("\n").each do |line|
|
1159
|
+
next if line.start_with?(" ") # workaround for step back
|
1160
|
+
formatted_line = line.gsub(/[\[\]\*]/, "").strip
|
1161
|
+
instructions.each do |inst|
|
1162
|
+
if formatted_line.start_with?("`#{inst}")
|
1163
|
+
desc.sub!(line, colorize(line, [:CYAN, :BOLD]))
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
@ui.puts desc
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
print_category = proc do |cat|
|
1171
|
+
@ui.puts "\n"
|
1172
|
+
@ui.puts colorize("### #{cat}", [:GREEN, :BOLD])
|
1173
|
+
@ui.puts "\n"
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
DEBUGGER__.helps.each { |cat, cs|
|
1177
|
+
# categories
|
1178
|
+
if arg.nil?
|
1179
|
+
print_category.call(cat)
|
1180
|
+
else
|
1181
|
+
cs.each { |ws, _|
|
1182
|
+
if ws.include?(arg)
|
1183
|
+
print_category.call(cat)
|
1184
|
+
break
|
1185
|
+
end
|
1186
|
+
}
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
# instructions
|
1190
|
+
cs.each { |ws, desc|
|
1191
|
+
if arg.nil? || ws.include?(arg)
|
1192
|
+
print_instructions.call(desc.dup)
|
1193
|
+
return if arg
|
1168
1194
|
end
|
1169
1195
|
}
|
1170
1196
|
}
|
1171
|
-
|
1197
|
+
|
1198
|
+
@ui.puts "not found: #{arg}" if arg
|
1172
1199
|
end
|
1173
1200
|
|
1174
1201
|
def ask msg, default = 'Y'
|
@@ -1272,14 +1299,20 @@ module DEBUGGER__
|
|
1272
1299
|
end
|
1273
1300
|
}
|
1274
1301
|
expr.default_proc = nil
|
1275
|
-
expr.transform_values{|v| v.join(' ')}
|
1302
|
+
expr = expr.transform_values{|v| v.join(' ')}
|
1303
|
+
|
1304
|
+
if (path = expr[:path]) && path =~ /\A\/(.*)\/\z/
|
1305
|
+
expr[:path] = Regexp.compile($1)
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
expr
|
1276
1309
|
end
|
1277
1310
|
|
1278
1311
|
def repl_add_breakpoint arg
|
1279
1312
|
expr = parse_break arg.strip
|
1280
1313
|
cond = expr[:if]
|
1281
1314
|
cmd = ['break', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1282
|
-
path =
|
1315
|
+
path = expr[:path]
|
1283
1316
|
|
1284
1317
|
case expr[:sig]
|
1285
1318
|
when /\A(\d+)\z/
|
@@ -1290,7 +1323,7 @@ module DEBUGGER__
|
|
1290
1323
|
@tc << [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
|
1291
1324
|
return :noretry
|
1292
1325
|
when nil
|
1293
|
-
add_check_breakpoint cond, path
|
1326
|
+
add_check_breakpoint cond, path, cmd
|
1294
1327
|
else
|
1295
1328
|
@ui.puts "Unknown breakpoint format: #{arg}"
|
1296
1329
|
@ui.puts
|
@@ -1302,7 +1335,7 @@ module DEBUGGER__
|
|
1302
1335
|
expr = parse_break arg.strip
|
1303
1336
|
cond = expr[:if]
|
1304
1337
|
cmd = ['catch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1305
|
-
path =
|
1338
|
+
path = expr[:path]
|
1306
1339
|
|
1307
1340
|
bp = CatchBreakpoint.new(expr[:sig], cond: cond, command: cmd, path: path)
|
1308
1341
|
add_bp bp
|
@@ -1322,8 +1355,8 @@ module DEBUGGER__
|
|
1322
1355
|
add_bp bp
|
1323
1356
|
end
|
1324
1357
|
|
1325
|
-
def add_check_breakpoint
|
1326
|
-
bp = CheckBreakpoint.new(
|
1358
|
+
def add_check_breakpoint cond, path, command
|
1359
|
+
bp = CheckBreakpoint.new(cond: cond, path: path, command: command)
|
1327
1360
|
add_bp bp
|
1328
1361
|
end
|
1329
1362
|
|
@@ -1336,6 +1369,15 @@ module DEBUGGER__
|
|
1336
1369
|
@ui.puts e.message
|
1337
1370
|
end
|
1338
1371
|
|
1372
|
+
def clear_line_breakpoints path
|
1373
|
+
path = resolve_path(path)
|
1374
|
+
@bps.delete_if do |k, bp|
|
1375
|
+
if (Array === k) && DEBUGGER__.compare_path(k.first, path)
|
1376
|
+
bp.delete
|
1377
|
+
end
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
|
1339
1381
|
def add_iseq_breakpoint iseq, **kw
|
1340
1382
|
bp = ISeqBreakpoint.new(iseq, [:line], **kw)
|
1341
1383
|
add_bp bp
|
@@ -1503,27 +1545,38 @@ module DEBUGGER__
|
|
1503
1545
|
end
|
1504
1546
|
|
1505
1547
|
private def enter_subsession
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1548
|
+
if !@subsession_stack.empty?
|
1549
|
+
DEBUGGER__.info "Enter subsession (nested #{@subsession_stack.size})"
|
1550
|
+
else
|
1551
|
+
DEBUGGER__.info "Enter subsession"
|
1552
|
+
stop_all_threads
|
1553
|
+
@process_group.lock
|
1554
|
+
end
|
1555
|
+
|
1556
|
+
@subsession_stack << true
|
1511
1557
|
end
|
1512
1558
|
|
1513
1559
|
private def leave_subsession type
|
1514
|
-
|
1515
|
-
@
|
1516
|
-
|
1560
|
+
raise '[BUG] leave_subsession: not entered' if @subsession_stack.empty?
|
1561
|
+
@subsession_stack.pop
|
1562
|
+
|
1563
|
+
if @subsession_stack.empty?
|
1564
|
+
DEBUGGER__.info "Leave subsession"
|
1565
|
+
@process_group.unlock
|
1566
|
+
restart_all_threads
|
1567
|
+
else
|
1568
|
+
DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
|
1569
|
+
end
|
1570
|
+
|
1517
1571
|
@tc << type if type
|
1518
1572
|
@tc = nil
|
1519
|
-
@subsession = false
|
1520
1573
|
rescue Exception => e
|
1521
|
-
STDERR.puts [e, e.backtrace].
|
1574
|
+
STDERR.puts PP.pp([e, e.backtrace], ''.dup)
|
1522
1575
|
raise
|
1523
1576
|
end
|
1524
1577
|
|
1525
1578
|
def in_subsession?
|
1526
|
-
|
1579
|
+
!@subsession_stack.empty?
|
1527
1580
|
end
|
1528
1581
|
|
1529
1582
|
## event
|
@@ -1537,7 +1590,7 @@ module DEBUGGER__
|
|
1537
1590
|
end
|
1538
1591
|
|
1539
1592
|
pending_line_breakpoints.each do |_key, bp|
|
1540
|
-
if bp.path
|
1593
|
+
if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
|
1541
1594
|
bp.try_activate
|
1542
1595
|
end
|
1543
1596
|
end
|
@@ -1860,6 +1913,9 @@ module DEBUGGER__
|
|
1860
1913
|
puts "\nStop by #{args.first}"
|
1861
1914
|
end
|
1862
1915
|
end
|
1916
|
+
|
1917
|
+
def flush
|
1918
|
+
end
|
1863
1919
|
end
|
1864
1920
|
|
1865
1921
|
# manual configuration methods
|
@@ -1876,7 +1932,7 @@ module DEBUGGER__
|
|
1876
1932
|
# nil for -r
|
1877
1933
|
def self.require_location
|
1878
1934
|
locs = caller_locations
|
1879
|
-
dir_prefix = /#{__dir__}/
|
1935
|
+
dir_prefix = /#{Regexp.escape(__dir__)}/
|
1880
1936
|
|
1881
1937
|
locs.each do |loc|
|
1882
1938
|
case loc.absolute_path
|
@@ -1896,7 +1952,7 @@ module DEBUGGER__
|
|
1896
1952
|
|
1897
1953
|
unless defined? SESSION
|
1898
1954
|
require_relative 'local'
|
1899
|
-
initialize_session UI_LocalConsole.new
|
1955
|
+
initialize_session{ UI_LocalConsole.new }
|
1900
1956
|
end
|
1901
1957
|
|
1902
1958
|
setup_initial_suspend unless nonstop
|
@@ -1904,8 +1960,9 @@ module DEBUGGER__
|
|
1904
1960
|
|
1905
1961
|
def self.open host: nil, port: CONFIG[:port], sock_path: nil, sock_dir: nil, nonstop: false, **kw
|
1906
1962
|
CONFIG.set_config(**kw)
|
1963
|
+
require_relative 'server'
|
1907
1964
|
|
1908
|
-
if port || CONFIG[:open_frontend] == 'chrome'
|
1965
|
+
if port || CONFIG[:open_frontend] == 'chrome' || (!::Addrinfo.respond_to?(:unix))
|
1909
1966
|
open_tcp host: host, port: (port || 0), nonstop: nonstop
|
1910
1967
|
else
|
1911
1968
|
open_unix sock_path: sock_path, sock_dir: sock_dir, nonstop: nonstop
|
@@ -1919,7 +1976,7 @@ module DEBUGGER__
|
|
1919
1976
|
if defined? SESSION
|
1920
1977
|
SESSION.reset_ui UI_TcpServer.new(host: host, port: port)
|
1921
1978
|
else
|
1922
|
-
initialize_session UI_TcpServer.new(host: host, port: port)
|
1979
|
+
initialize_session{ UI_TcpServer.new(host: host, port: port) }
|
1923
1980
|
end
|
1924
1981
|
|
1925
1982
|
setup_initial_suspend unless nonstop
|
@@ -1932,7 +1989,7 @@ module DEBUGGER__
|
|
1932
1989
|
if defined? SESSION
|
1933
1990
|
SESSION.reset_ui UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
|
1934
1991
|
else
|
1935
|
-
initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
|
1992
|
+
initialize_session{ UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path) }
|
1936
1993
|
end
|
1937
1994
|
|
1938
1995
|
setup_initial_suspend unless nonstop
|
@@ -1959,9 +2016,10 @@ module DEBUGGER__
|
|
1959
2016
|
end
|
1960
2017
|
|
1961
2018
|
class << self
|
1962
|
-
define_method :initialize_session do |
|
2019
|
+
define_method :initialize_session do |&init_ui|
|
1963
2020
|
DEBUGGER__.info "Session start (pid: #{Process.pid})"
|
1964
|
-
::DEBUGGER__.const_set(:SESSION, Session.new
|
2021
|
+
::DEBUGGER__.const_set(:SESSION, Session.new)
|
2022
|
+
SESSION.activate init_ui.call
|
1965
2023
|
load_rc
|
1966
2024
|
end
|
1967
2025
|
end
|
@@ -2062,10 +2120,64 @@ module DEBUGGER__
|
|
2062
2120
|
yield
|
2063
2121
|
end
|
2064
2122
|
|
2123
|
+
if File.identical?(__FILE__.upcase, __FILE__.downcase)
|
2124
|
+
# For case insensitive file system (like Windows)
|
2125
|
+
# Note that this check is not enough because case sensitive/insensitive is
|
2126
|
+
# depend on the file system. So this check is only roughly estimation.
|
2127
|
+
|
2128
|
+
def self.compare_path(a, b)
|
2129
|
+
a.downcase == b.downcase
|
2130
|
+
end
|
2131
|
+
else
|
2132
|
+
def self.compare_path(a, b)
|
2133
|
+
a == b
|
2134
|
+
end
|
2135
|
+
end
|
2136
|
+
|
2065
2137
|
module ForkInterceptor
|
2066
|
-
|
2067
|
-
|
2138
|
+
if Process.respond_to? :_fork
|
2139
|
+
def _fork
|
2140
|
+
return yield unless defined?(SESSION) && SESSION.active?
|
2141
|
+
|
2142
|
+
parent_hook, child_hook = __fork_setup_for_debugger
|
2143
|
+
|
2144
|
+
super.tap do |pid|
|
2145
|
+
if pid != 0
|
2146
|
+
# after fork: parent
|
2147
|
+
parent_hook.call pid
|
2148
|
+
else
|
2149
|
+
# after fork: child
|
2150
|
+
child_hook.call
|
2151
|
+
end
|
2152
|
+
end
|
2153
|
+
end
|
2154
|
+
else
|
2155
|
+
def fork(&given_block)
|
2156
|
+
return yield unless defined?(SESSION) && SESSION.active?
|
2157
|
+
parent_hook, child_hook = __fork_setup_for_debugger
|
2158
|
+
|
2159
|
+
if given_block
|
2160
|
+
new_block = proc {
|
2161
|
+
# after fork: child
|
2162
|
+
child_hook.call
|
2163
|
+
given_block.call
|
2164
|
+
}
|
2165
|
+
super(&new_block).tap{|pid| parent_hook.call(pid)}
|
2166
|
+
else
|
2167
|
+
super.tap do |pid|
|
2168
|
+
if pid
|
2169
|
+
# after fork: parent
|
2170
|
+
parent_hook.call pid
|
2171
|
+
else
|
2172
|
+
# after fork: child
|
2173
|
+
child_hook.call
|
2174
|
+
end
|
2175
|
+
end
|
2176
|
+
end
|
2177
|
+
end
|
2178
|
+
end
|
2068
2179
|
|
2180
|
+
private def __fork_setup_for_debugger
|
2069
2181
|
unless fork_mode = CONFIG[:fork_mode]
|
2070
2182
|
if CONFIG[:parent_on_fork]
|
2071
2183
|
fork_mode = :parent
|
@@ -2112,26 +2224,7 @@ module DEBUGGER__
|
|
2112
2224
|
}
|
2113
2225
|
end
|
2114
2226
|
|
2115
|
-
|
2116
|
-
new_block = proc {
|
2117
|
-
# after fork: child
|
2118
|
-
child_hook.call
|
2119
|
-
given_block.call
|
2120
|
-
}
|
2121
|
-
pid = super(&new_block)
|
2122
|
-
parent_hook.call(pid)
|
2123
|
-
pid
|
2124
|
-
else
|
2125
|
-
if pid = super
|
2126
|
-
# after fork: parent
|
2127
|
-
parent_hook.call pid
|
2128
|
-
else
|
2129
|
-
# after fork: child
|
2130
|
-
child_hook.call
|
2131
|
-
end
|
2132
|
-
|
2133
|
-
pid
|
2134
|
-
end
|
2227
|
+
return parent_hook, child_hook
|
2135
2228
|
end
|
2136
2229
|
end
|
2137
2230
|
|
@@ -2148,28 +2241,46 @@ module DEBUGGER__
|
|
2148
2241
|
end
|
2149
2242
|
end
|
2150
2243
|
|
2151
|
-
if
|
2244
|
+
if Process.respond_to? :_fork
|
2245
|
+
module ::Process
|
2246
|
+
class << self
|
2247
|
+
prepend ForkInterceptor
|
2248
|
+
end
|
2249
|
+
end
|
2250
|
+
|
2251
|
+
# trap
|
2152
2252
|
module ::Kernel
|
2153
|
-
prepend ForkInterceptor
|
2154
2253
|
prepend TrapInterceptor
|
2155
2254
|
end
|
2255
|
+
module ::Signal
|
2256
|
+
class << self
|
2257
|
+
prepend TrapInterceptor
|
2258
|
+
end
|
2259
|
+
end
|
2156
2260
|
else
|
2157
|
-
|
2158
|
-
|
2159
|
-
|
2261
|
+
if RUBY_VERSION >= '3.0.0'
|
2262
|
+
module ::Kernel
|
2263
|
+
prepend ForkInterceptor
|
2264
|
+
prepend TrapInterceptor
|
2265
|
+
end
|
2266
|
+
else
|
2267
|
+
class ::Object
|
2268
|
+
include ForkInterceptor
|
2269
|
+
include TrapInterceptor
|
2270
|
+
end
|
2160
2271
|
end
|
2161
|
-
end
|
2162
2272
|
|
2163
|
-
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2273
|
+
module ::Kernel
|
2274
|
+
class << self
|
2275
|
+
prepend ForkInterceptor
|
2276
|
+
prepend TrapInterceptor
|
2277
|
+
end
|
2167
2278
|
end
|
2168
|
-
end
|
2169
2279
|
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
2280
|
+
module ::Process
|
2281
|
+
class << self
|
2282
|
+
prepend ForkInterceptor
|
2283
|
+
end
|
2173
2284
|
end
|
2174
2285
|
end
|
2175
2286
|
|