debug 1.3.4 → 1.4.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/.github/pull_request_template.md +9 -0
- data/CONTRIBUTING.md +39 -6
- data/README.md +34 -7
- data/bin/gentest +12 -4
- data/ext/debug/debug.c +4 -1
- data/lib/debug/breakpoint.rb +61 -11
- data/lib/debug/client.rb +57 -16
- data/lib/debug/color.rb +29 -19
- data/lib/debug/config.rb +6 -3
- data/lib/debug/console.rb +17 -3
- data/lib/debug/frame_info.rb +11 -16
- data/lib/debug/prelude.rb +2 -2
- data/lib/debug/server.rb +45 -77
- data/lib/debug/server_cdp.rb +590 -93
- data/lib/debug/server_dap.rb +231 -53
- data/lib/debug/session.rb +114 -52
- data/lib/debug/source_repository.rb +4 -6
- data/lib/debug/thread_client.rb +67 -48
- data/lib/debug/tracer.rb +1 -1
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +12 -4
- metadata +3 -2
data/lib/debug/session.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
return if ENV['RUBY_DEBUG_ENABLE'] == '0'
|
4
|
+
|
3
5
|
# skip to load debugger for bundle exec
|
4
6
|
|
5
7
|
if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
@@ -99,17 +101,17 @@ module DEBUGGER__
|
|
99
101
|
@preset_command = nil
|
100
102
|
@postmortem_hook = nil
|
101
103
|
@postmortem = false
|
102
|
-
@thread_stopper = nil
|
103
104
|
@intercept_trap_sigint = false
|
104
105
|
@intercepted_sigint_cmd = 'DEFAULT'
|
105
106
|
@process_group = ProcessGroup.new
|
106
107
|
@subsession = nil
|
107
108
|
|
108
|
-
@frame_map = {} # {id => [threadId, frame_depth]}
|
109
|
+
@frame_map = {} # for DAP: {id => [threadId, frame_depth]} and CDP: {id => frame_depth}
|
109
110
|
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
110
111
|
@src_map = {} # {id => src}
|
111
112
|
|
112
113
|
@script_paths = [File.absolute_path($0)] # for CDP
|
114
|
+
@obj_map = {} # { object_id => ... } for CDP
|
113
115
|
|
114
116
|
@tp_thread_begin = nil
|
115
117
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
@@ -117,6 +119,8 @@ module DEBUGGER__
|
|
117
119
|
}
|
118
120
|
@tp_load_script.enable
|
119
121
|
|
122
|
+
@thread_stopper = thread_stopper
|
123
|
+
|
120
124
|
activate
|
121
125
|
|
122
126
|
self.postmortem = CONFIG[:postmortem]
|
@@ -147,15 +151,15 @@ module DEBUGGER__
|
|
147
151
|
|
148
152
|
# Thread management
|
149
153
|
setup_threads
|
150
|
-
thc =
|
151
|
-
thc.
|
154
|
+
thc = get_thread_client Thread.current
|
155
|
+
thc.mark_as_management
|
152
156
|
|
153
|
-
if @ui.respond_to?(:reader_thread) && thc =
|
154
|
-
thc.
|
157
|
+
if @ui.respond_to?(:reader_thread) && thc = get_thread_client(@ui.reader_thread)
|
158
|
+
thc.mark_as_management
|
155
159
|
end
|
156
160
|
|
157
161
|
@tp_thread_begin = TracePoint.new(:thread_begin) do |tp|
|
158
|
-
|
162
|
+
get_thread_client
|
159
163
|
end
|
160
164
|
@tp_thread_begin.enable
|
161
165
|
|
@@ -168,12 +172,12 @@ module DEBUGGER__
|
|
168
172
|
end
|
169
173
|
|
170
174
|
def deactivate
|
171
|
-
|
172
|
-
@thread_stopper.disable
|
175
|
+
get_thread_client.deactivate
|
176
|
+
@thread_stopper.disable
|
173
177
|
@tp_load_script.disable
|
174
178
|
@tp_thread_begin.disable
|
175
|
-
@bps.
|
176
|
-
@th_clients.
|
179
|
+
@bps.each_value{|bp| bp.disable}
|
180
|
+
@th_clients.each_value{|thc| thc.close}
|
177
181
|
@tracers.values.each{|t| t.disable}
|
178
182
|
@q_evt.close
|
179
183
|
@ui&.deactivate
|
@@ -233,12 +237,13 @@ module DEBUGGER__
|
|
233
237
|
case ev_args.first
|
234
238
|
when :breakpoint
|
235
239
|
bp, i = bp_index ev_args[1]
|
240
|
+
clean_bps unless bp
|
236
241
|
@ui.event :suspend_bp, i, bp, tc.id
|
237
242
|
when :trap
|
238
243
|
@ui.event :suspend_trap, sig = ev_args[1], tc.id
|
239
244
|
|
240
245
|
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
241
|
-
@ui.puts "#{@intercepted_sigint_cmd.inspect} is
|
246
|
+
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
|
242
247
|
@ui.puts "`sigint` command execute it."
|
243
248
|
end
|
244
249
|
else
|
@@ -418,10 +423,15 @@ module DEBUGGER__
|
|
418
423
|
# * `fin[ish]`
|
419
424
|
# * Finish this frame. Resume the program until the current frame is finished.
|
420
425
|
# * `fin[ish] <n>`
|
421
|
-
# * Finish frames
|
426
|
+
# * Finish `<n>`th frames.
|
422
427
|
when 'fin', 'finish'
|
423
428
|
cancel_auto_continue
|
424
429
|
check_postmortem
|
430
|
+
|
431
|
+
if arg&.to_i == 0
|
432
|
+
raise 'finish command with 0 does not make sense.'
|
433
|
+
end
|
434
|
+
|
425
435
|
step_command :finish, arg
|
426
436
|
|
427
437
|
# * `c[ontinue]`
|
@@ -447,7 +457,7 @@ module DEBUGGER__
|
|
447
457
|
leave_subsession nil
|
448
458
|
|
449
459
|
# * `kill`
|
450
|
-
# * Stop the debuggee process with `
|
460
|
+
# * Stop the debuggee process with `Kernel#exit!`.
|
451
461
|
when 'kill'
|
452
462
|
if ask 'Really kill?'
|
453
463
|
exit! (arg || 1).to_i
|
@@ -461,7 +471,7 @@ module DEBUGGER__
|
|
461
471
|
exit! (arg || 1).to_i
|
462
472
|
|
463
473
|
# * `sigint`
|
464
|
-
# * Execute SIGINT handler
|
474
|
+
# * Execute SIGINT handler registered by the debuggee.
|
465
475
|
# * Note that this command should be used just after stop by `SIGINT`.
|
466
476
|
when 'sigint'
|
467
477
|
begin
|
@@ -500,6 +510,8 @@ module DEBUGGER__
|
|
500
510
|
# * break and run `<command>` before stopping.
|
501
511
|
# * `b[reak] ... do: <command>`
|
502
512
|
# * break and run `<command>`, and continue.
|
513
|
+
# * `b[reak] ... path: <path_regexp>`
|
514
|
+
# * break if the triggering event's path matches <path_regexp>.
|
503
515
|
# * `b[reak] if: <expr>`
|
504
516
|
# * break if: `<expr>` is true at any lines.
|
505
517
|
# * Note that this feature is super slow.
|
@@ -526,7 +538,7 @@ module DEBUGGER__
|
|
526
538
|
require 'json'
|
527
539
|
|
528
540
|
h = Hash.new{|h, k| h[k] = []}
|
529
|
-
@bps.
|
541
|
+
@bps.each_value{|bp|
|
530
542
|
if LineBreakpoint === bp
|
531
543
|
h[bp.path] << {lnum: bp.line}
|
532
544
|
end
|
@@ -548,6 +560,14 @@ module DEBUGGER__
|
|
548
560
|
|
549
561
|
# * `catch <Error>`
|
550
562
|
# * Set breakpoint on raising `<Error>`.
|
563
|
+
# * `catch ... if: <expr>`
|
564
|
+
# * stops only if `<expr>` is true as well.
|
565
|
+
# * `catch ... pre: <command>`
|
566
|
+
# * runs `<command>` before stopping.
|
567
|
+
# * `catch ... do: <command>`
|
568
|
+
# * stops and run `<command>`, and continue.
|
569
|
+
# * `catch ... path: <path_regexp>`
|
570
|
+
# * stops if the exception is raised from a path that matches <path_regexp>.
|
551
571
|
when 'catch'
|
552
572
|
check_postmortem
|
553
573
|
|
@@ -562,11 +582,19 @@ module DEBUGGER__
|
|
562
582
|
# * `watch @ivar`
|
563
583
|
# * Stop the execution when the result of current scope's `@ivar` is changed.
|
564
584
|
# * Note that this feature is super slow.
|
585
|
+
# * `watch ... if: <expr>`
|
586
|
+
# * stops only if `<expr>` is true as well.
|
587
|
+
# * `watch ... pre: <command>`
|
588
|
+
# * runs `<command>` before stopping.
|
589
|
+
# * `watch ... do: <command>`
|
590
|
+
# * stops and run `<command>`, and continue.
|
591
|
+
# * `watch ... path: <path_regexp>`
|
592
|
+
# * stops if the triggering event's path matches <path_regexp>.
|
565
593
|
when 'wat', 'watch'
|
566
594
|
check_postmortem
|
567
595
|
|
568
596
|
if arg && arg.match?(/\A@\w+/)
|
569
|
-
|
597
|
+
repl_add_watch_breakpoint(arg)
|
570
598
|
else
|
571
599
|
show_bps
|
572
600
|
return :retry
|
@@ -902,7 +930,7 @@ module DEBUGGER__
|
|
902
930
|
when nil, 'list', 'l'
|
903
931
|
thread_list
|
904
932
|
when /(\d+)/
|
905
|
-
|
933
|
+
switch_thread $1.to_i
|
906
934
|
else
|
907
935
|
@ui.puts "unknown thread command: #{arg}"
|
908
936
|
end
|
@@ -1016,8 +1044,8 @@ module DEBUGGER__
|
|
1016
1044
|
def repl_open_setup
|
1017
1045
|
@tp_thread_begin.disable
|
1018
1046
|
@ui.activate self
|
1019
|
-
if @ui.respond_to?(:reader_thread) && thc =
|
1020
|
-
thc.
|
1047
|
+
if @ui.respond_to?(:reader_thread) && thc = get_thread_client(@ui.reader_thread)
|
1048
|
+
thc.mark_as_management
|
1021
1049
|
end
|
1022
1050
|
@tp_thread_begin.enable
|
1023
1051
|
end
|
@@ -1195,6 +1223,12 @@ module DEBUGGER__
|
|
1195
1223
|
}
|
1196
1224
|
end
|
1197
1225
|
|
1226
|
+
def clean_bps
|
1227
|
+
@bps.delete_if{|_k, bp|
|
1228
|
+
bp.deleted?
|
1229
|
+
}
|
1230
|
+
end
|
1231
|
+
|
1198
1232
|
def add_bp bp
|
1199
1233
|
# don't repeat commands that add breakpoints
|
1200
1234
|
@repl_prev_line = nil
|
@@ -1225,7 +1259,7 @@ module DEBUGGER__
|
|
1225
1259
|
end
|
1226
1260
|
end
|
1227
1261
|
|
1228
|
-
BREAK_KEYWORDS = %w(if: do: pre:).freeze
|
1262
|
+
BREAK_KEYWORDS = %w(if: do: pre: path:).freeze
|
1229
1263
|
|
1230
1264
|
def parse_break arg
|
1231
1265
|
mode = :sig
|
@@ -1245,6 +1279,7 @@ module DEBUGGER__
|
|
1245
1279
|
expr = parse_break arg.strip
|
1246
1280
|
cond = expr[:if]
|
1247
1281
|
cmd = ['break', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1282
|
+
path = Regexp.compile(expr[:path]) if expr[:path]
|
1248
1283
|
|
1249
1284
|
case expr[:sig]
|
1250
1285
|
when /\A(\d+)\z/
|
@@ -1252,10 +1287,10 @@ module DEBUGGER__
|
|
1252
1287
|
when /\A(.+)[:\s+](\d+)\z/
|
1253
1288
|
add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
|
1254
1289
|
when /\A(.+)([\.\#])(.+)\z/
|
1255
|
-
@tc << [:breakpoint, :method, $1, $2, $3, cond, cmd]
|
1290
|
+
@tc << [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
|
1256
1291
|
return :noretry
|
1257
1292
|
when nil
|
1258
|
-
add_check_breakpoint cond
|
1293
|
+
add_check_breakpoint cond, path
|
1259
1294
|
else
|
1260
1295
|
@ui.puts "Unknown breakpoint format: #{arg}"
|
1261
1296
|
@ui.puts
|
@@ -1267,18 +1302,28 @@ module DEBUGGER__
|
|
1267
1302
|
expr = parse_break arg.strip
|
1268
1303
|
cond = expr[:if]
|
1269
1304
|
cmd = ['catch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1305
|
+
path = Regexp.compile(expr[:path]) if expr[:path]
|
1270
1306
|
|
1271
|
-
bp = CatchBreakpoint.new(expr[:sig], cond: cond, command: cmd)
|
1307
|
+
bp = CatchBreakpoint.new(expr[:sig], cond: cond, command: cmd, path: path)
|
1272
1308
|
add_bp bp
|
1273
1309
|
end
|
1274
1310
|
|
1311
|
+
def repl_add_watch_breakpoint arg
|
1312
|
+
expr = parse_break arg.strip
|
1313
|
+
cond = expr[:if]
|
1314
|
+
cmd = ['watch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1315
|
+
path = Regexp.compile(expr[:path]) if expr[:path]
|
1316
|
+
|
1317
|
+
@tc << [:breakpoint, :watch, expr[:sig], cond, cmd, path]
|
1318
|
+
end
|
1319
|
+
|
1275
1320
|
def add_catch_breakpoint pat
|
1276
1321
|
bp = CatchBreakpoint.new(pat)
|
1277
1322
|
add_bp bp
|
1278
1323
|
end
|
1279
1324
|
|
1280
|
-
def add_check_breakpoint expr
|
1281
|
-
bp = CheckBreakpoint.new(expr)
|
1325
|
+
def add_check_breakpoint expr, path
|
1326
|
+
bp = CheckBreakpoint.new(expr, path)
|
1282
1327
|
add_bp bp
|
1283
1328
|
end
|
1284
1329
|
|
@@ -1291,6 +1336,11 @@ module DEBUGGER__
|
|
1291
1336
|
@ui.puts e.message
|
1292
1337
|
end
|
1293
1338
|
|
1339
|
+
def add_iseq_breakpoint iseq, **kw
|
1340
|
+
bp = ISeqBreakpoint.new(iseq, [:line], **kw)
|
1341
|
+
add_bp bp
|
1342
|
+
end
|
1343
|
+
|
1294
1344
|
# tracers
|
1295
1345
|
|
1296
1346
|
def add_tracer tracer
|
@@ -1344,7 +1394,7 @@ module DEBUGGER__
|
|
1344
1394
|
thcs
|
1345
1395
|
end
|
1346
1396
|
|
1347
|
-
def
|
1397
|
+
def switch_thread n
|
1348
1398
|
thcs, _unmanaged_ths = update_thread_list
|
1349
1399
|
|
1350
1400
|
if tc = thcs[n]
|
@@ -1365,7 +1415,7 @@ module DEBUGGER__
|
|
1365
1415
|
if tc = prev_clients[th]
|
1366
1416
|
@th_clients[th] = tc
|
1367
1417
|
else
|
1368
|
-
|
1418
|
+
create_thread_client(th)
|
1369
1419
|
end
|
1370
1420
|
}
|
1371
1421
|
end
|
@@ -1374,17 +1424,17 @@ module DEBUGGER__
|
|
1374
1424
|
if @th_clients.has_key? th
|
1375
1425
|
# TODO: NG?
|
1376
1426
|
else
|
1377
|
-
|
1427
|
+
create_thread_client th
|
1378
1428
|
end
|
1379
1429
|
end
|
1380
1430
|
|
1381
|
-
private def
|
1431
|
+
private def create_thread_client th
|
1382
1432
|
# TODO: Ractor support
|
1383
1433
|
raise "Only session_server can create thread_client" unless Thread.current == @session_server
|
1384
1434
|
@th_clients[th] = ThreadClient.new((@tc_id += 1), @q_evt, Queue.new, th)
|
1385
1435
|
end
|
1386
1436
|
|
1387
|
-
private def ask_thread_client th
|
1437
|
+
private def ask_thread_client th
|
1388
1438
|
# TODO: Ractor support
|
1389
1439
|
q2 = Queue.new
|
1390
1440
|
# tc, output, ev, @internal_info, *ev_args = evt
|
@@ -1395,30 +1445,18 @@ module DEBUGGER__
|
|
1395
1445
|
end
|
1396
1446
|
|
1397
1447
|
# can be called by other threads
|
1398
|
-
def
|
1448
|
+
def get_thread_client th = Thread.current
|
1399
1449
|
if @th_clients.has_key? th
|
1400
1450
|
@th_clients[th]
|
1401
1451
|
else
|
1402
1452
|
if Thread.current == @session_server
|
1403
|
-
|
1453
|
+
create_thread_client th
|
1404
1454
|
else
|
1405
1455
|
ask_thread_client th
|
1406
1456
|
end
|
1407
1457
|
end
|
1408
1458
|
end
|
1409
1459
|
|
1410
|
-
private def thread_stopper
|
1411
|
-
@thread_stopper ||= TracePoint.new(:line) do
|
1412
|
-
# run on each thread
|
1413
|
-
tc = ThreadClient.current
|
1414
|
-
next if tc.management?
|
1415
|
-
next unless tc.running?
|
1416
|
-
next if tc == @tc
|
1417
|
-
|
1418
|
-
tc.on_pause
|
1419
|
-
end
|
1420
|
-
end
|
1421
|
-
|
1422
1460
|
private def running_thread_clients_count
|
1423
1461
|
@th_clients.count{|th, tc|
|
1424
1462
|
next if tc.management?
|
@@ -1435,15 +1473,27 @@ module DEBUGGER__
|
|
1435
1473
|
}.compact
|
1436
1474
|
end
|
1437
1475
|
|
1476
|
+
private def thread_stopper
|
1477
|
+
TracePoint.new(:line) do
|
1478
|
+
# run on each thread
|
1479
|
+
tc = ThreadClient.current
|
1480
|
+
next if tc.management?
|
1481
|
+
next unless tc.running?
|
1482
|
+
next if tc == @tc
|
1483
|
+
|
1484
|
+
tc.on_pause
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
|
1438
1488
|
private def stop_all_threads
|
1439
1489
|
return if running_thread_clients_count == 0
|
1440
1490
|
|
1441
|
-
stopper = thread_stopper
|
1491
|
+
stopper = @thread_stopper
|
1442
1492
|
stopper.enable unless stopper.enabled?
|
1443
1493
|
end
|
1444
1494
|
|
1445
1495
|
private def restart_all_threads
|
1446
|
-
stopper = thread_stopper
|
1496
|
+
stopper = @thread_stopper
|
1447
1497
|
stopper.disable if stopper.enabled?
|
1448
1498
|
|
1449
1499
|
waiting_thread_clients.each{|tc|
|
@@ -1550,7 +1600,7 @@ module DEBUGGER__
|
|
1550
1600
|
|
1551
1601
|
frames = exc.instance_variable_get(:@__debugger_postmortem_frames)
|
1552
1602
|
@postmortem = true
|
1553
|
-
ThreadClient.current.suspend :postmortem, postmortem_frames: frames
|
1603
|
+
ThreadClient.current.suspend :postmortem, postmortem_frames: frames, postmortem_exc: exc
|
1554
1604
|
ensure
|
1555
1605
|
@postmortem = false
|
1556
1606
|
end
|
@@ -1822,7 +1872,7 @@ module DEBUGGER__
|
|
1822
1872
|
::DEBUGGER__::SESSION.add_catch_breakpoint pat
|
1823
1873
|
end
|
1824
1874
|
|
1825
|
-
# String for
|
1875
|
+
# String for requiring location
|
1826
1876
|
# nil for -r
|
1827
1877
|
def self.require_location
|
1828
1878
|
locs = caller_locations
|
@@ -1960,13 +2010,17 @@ module DEBUGGER__
|
|
1960
2010
|
METHOD_ADDED_TRACKER = self.create_method_added_tracker
|
1961
2011
|
|
1962
2012
|
SHORT_INSPECT_LENGTH = 40
|
1963
|
-
|
2013
|
+
|
2014
|
+
def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
|
1964
2015
|
str = obj.inspect
|
1965
|
-
|
1966
|
-
|
2016
|
+
|
2017
|
+
if short && str.length > max_length
|
2018
|
+
str[0...max_length] + '...'
|
1967
2019
|
else
|
1968
2020
|
str
|
1969
2021
|
end
|
2022
|
+
rescue Exception => e
|
2023
|
+
str = "<#inspect raises #{e.inspect}>"
|
1970
2024
|
end
|
1971
2025
|
|
1972
2026
|
def self.warn msg
|
@@ -2000,6 +2054,14 @@ module DEBUGGER__
|
|
2000
2054
|
end
|
2001
2055
|
end
|
2002
2056
|
|
2057
|
+
def self.step_in &b
|
2058
|
+
if defined?(SESSION) && SESSION.active?
|
2059
|
+
SESSION.add_iseq_breakpoint RubyVM::InstructionSequence.of(b), oneshot: true
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
yield
|
2063
|
+
end
|
2064
|
+
|
2003
2065
|
module ForkInterceptor
|
2004
2066
|
def fork(&given_block)
|
2005
2067
|
return super unless defined?(SESSION) && SESSION.active?
|
@@ -40,12 +40,10 @@ module DEBUGGER__
|
|
40
40
|
end
|
41
41
|
|
42
42
|
private def add_path path
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
rescue SystemCallError
|
48
|
-
end
|
43
|
+
src = File.read(path)
|
44
|
+
src = src.gsub("\r\n", "\n") # CRLF -> LF
|
45
|
+
@files[path] = SrcInfo.new(src.lines)
|
46
|
+
rescue SystemCallError
|
49
47
|
end
|
50
48
|
|
51
49
|
private def get_si iseq
|
data/lib/debug/thread_client.rb
CHANGED
@@ -22,7 +22,7 @@ module DEBUGGER__
|
|
22
22
|
if thc = Thread.current[:DEBUGGER__ThreadClient]
|
23
23
|
thc
|
24
24
|
else
|
25
|
-
thc = SESSION.
|
25
|
+
thc = SESSION.get_thread_client
|
26
26
|
Thread.current[:DEBUGGER__ThreadClient] = thc
|
27
27
|
end
|
28
28
|
end
|
@@ -67,7 +67,7 @@ module DEBUGGER__
|
|
67
67
|
result = "#{call_identifier_str} at #{location_str}"
|
68
68
|
|
69
69
|
if return_str = frame.return_str
|
70
|
-
result += " #=> #{colorize_magenta(
|
70
|
+
result += " #=> #{colorize_magenta(return_str)}"
|
71
71
|
end
|
72
72
|
|
73
73
|
result
|
@@ -84,6 +84,7 @@ module DEBUGGER__
|
|
84
84
|
@output = []
|
85
85
|
@frame_formatter = method(:default_frame_formatter)
|
86
86
|
@var_map = {} # { thread_local_var_id => obj } for DAP
|
87
|
+
@obj_map = {} # { object_id => obj } for CDP
|
87
88
|
@recorder = nil
|
88
89
|
@mode = :waiting
|
89
90
|
set_mode :running
|
@@ -100,13 +101,13 @@ module DEBUGGER__
|
|
100
101
|
@is_management
|
101
102
|
end
|
102
103
|
|
103
|
-
def
|
104
|
+
def mark_as_management
|
104
105
|
@is_management = true
|
105
106
|
end
|
106
107
|
|
107
108
|
def set_mode mode
|
108
109
|
# STDERR.puts "#{@mode} => #{mode} @ #{caller.inspect}"
|
109
|
-
#pp caller
|
110
|
+
# pp caller
|
110
111
|
|
111
112
|
# mode transition check
|
112
113
|
case mode
|
@@ -228,7 +229,7 @@ module DEBUGGER__
|
|
228
229
|
suspend :pause
|
229
230
|
end
|
230
231
|
|
231
|
-
def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil
|
232
|
+
def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil, postmortem_exc: nil
|
232
233
|
return if management?
|
233
234
|
|
234
235
|
@current_frame_index = 0
|
@@ -256,10 +257,15 @@ module DEBUGGER__
|
|
256
257
|
cf.has_raised_exception = true
|
257
258
|
cf.raised_exception = bp.last_exc
|
258
259
|
end
|
260
|
+
|
261
|
+
if postmortem_exc
|
262
|
+
cf.has_raised_exception = true
|
263
|
+
cf.raised_exception = postmortem_exc
|
264
|
+
end
|
259
265
|
end
|
260
266
|
|
261
267
|
if event != :pause
|
262
|
-
show_src
|
268
|
+
show_src
|
263
269
|
show_frames CONFIG[:show_frames] || 2
|
264
270
|
|
265
271
|
set_mode :waiting
|
@@ -292,15 +298,15 @@ module DEBUGGER__
|
|
292
298
|
SUPPORT_TARGET_THREAD = false
|
293
299
|
end
|
294
300
|
|
295
|
-
def step_tp iter
|
301
|
+
def step_tp iter, events = [:line, :b_return, :return]
|
296
302
|
@step_tp.disable if @step_tp
|
297
303
|
|
298
304
|
thread = Thread.current
|
299
305
|
|
300
306
|
if SUPPORT_TARGET_THREAD
|
301
|
-
@step_tp = TracePoint.new(
|
307
|
+
@step_tp = TracePoint.new(*events){|tp|
|
302
308
|
next if SESSION.break_at? tp.path, tp.lineno
|
303
|
-
next if !yield
|
309
|
+
next if !yield(tp.event)
|
304
310
|
next if tp.path.start_with?(__dir__)
|
305
311
|
next if tp.path.start_with?('<internal:trace_point>')
|
306
312
|
next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
|
@@ -313,10 +319,10 @@ module DEBUGGER__
|
|
313
319
|
}
|
314
320
|
@step_tp.enable(target_thread: thread)
|
315
321
|
else
|
316
|
-
@step_tp = TracePoint.new(
|
322
|
+
@step_tp = TracePoint.new(*events){|tp|
|
317
323
|
next if thread != Thread.current
|
318
324
|
next if SESSION.break_at? tp.path, tp.lineno
|
319
|
-
next if !yield
|
325
|
+
next if !yield(tp.event)
|
320
326
|
next if tp.path.start_with?(__dir__)
|
321
327
|
next if tp.path.start_with?('<internal:trace_point>')
|
322
328
|
next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
|
@@ -338,38 +344,45 @@ module DEBUGGER__
|
|
338
344
|
frame_self.instance_eval(src)
|
339
345
|
end
|
340
346
|
|
347
|
+
SPECIAL_LOCAL_VARS = [
|
348
|
+
[:raised_exception, "_raised"],
|
349
|
+
[:return_value, "_return"],
|
350
|
+
]
|
351
|
+
|
341
352
|
def frame_eval src, re_raise: false
|
342
|
-
|
343
|
-
@success_last_eval = false
|
353
|
+
@success_last_eval = false
|
344
354
|
|
345
|
-
|
355
|
+
b = current_frame.eval_binding
|
346
356
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
else
|
351
|
-
frame_self = current_frame.self
|
352
|
-
instance_eval_for_cmethod(frame_self, src)
|
353
|
-
end
|
354
|
-
@success_last_eval = true
|
355
|
-
result
|
357
|
+
special_local_variables current_frame do |name, var|
|
358
|
+
b.local_variable_set(name, var) if /\%/ !~ name
|
359
|
+
end
|
356
360
|
|
357
|
-
|
358
|
-
|
361
|
+
result = if b
|
362
|
+
f, _l = b.source_location
|
363
|
+
b.eval(src, "(rdbg)/#{f}")
|
364
|
+
else
|
365
|
+
frame_self = current_frame.self
|
366
|
+
instance_eval_for_cmethod(frame_self, src)
|
367
|
+
end
|
368
|
+
@success_last_eval = true
|
369
|
+
result
|
359
370
|
|
360
|
-
|
371
|
+
rescue Exception => e
|
372
|
+
return yield(e) if block_given?
|
361
373
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
374
|
+
puts "eval error: #{e}"
|
375
|
+
|
376
|
+
e.backtrace_locations&.each do |loc|
|
377
|
+
break if loc.path == __FILE__
|
378
|
+
puts " #{loc}"
|
367
379
|
end
|
380
|
+
raise if re_raise
|
368
381
|
end
|
369
382
|
|
370
383
|
def show_src(frame_index: @current_frame_index,
|
371
384
|
update_line: false,
|
372
|
-
max_lines: 10,
|
385
|
+
max_lines: CONFIG[:show_src_lines] || 10,
|
373
386
|
start_line: nil,
|
374
387
|
end_line: nil,
|
375
388
|
dir: +1)
|
@@ -428,15 +441,20 @@ module DEBUGGER__
|
|
428
441
|
|
429
442
|
## cmd: show
|
430
443
|
|
444
|
+
def special_local_variables frame
|
445
|
+
SPECIAL_LOCAL_VARS.each do |mid, name|
|
446
|
+
next unless frame&.send("has_#{mid}")
|
447
|
+
name = name.sub('_', '%') if frame.eval_binding.local_variable_defined?(name)
|
448
|
+
yield name, frame.send(mid)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
431
452
|
def show_locals pat
|
432
453
|
if s = current_frame&.self
|
433
454
|
puts_variable_info '%self', s, pat
|
434
455
|
end
|
435
|
-
|
436
|
-
puts_variable_info
|
437
|
-
end
|
438
|
-
if current_frame&.has_raised_exception
|
439
|
-
puts_variable_info "%raised", current_frame.raised_exception, pat
|
456
|
+
special_local_variables current_frame do |name, val|
|
457
|
+
puts_variable_info name, val, pat
|
440
458
|
end
|
441
459
|
|
442
460
|
if vars = current_frame&.local_variables
|
@@ -633,8 +651,8 @@ module DEBUGGER__
|
|
633
651
|
def make_breakpoint args
|
634
652
|
case args.first
|
635
653
|
when :method
|
636
|
-
klass_name, op, method_name, cond, cmd = args[1..]
|
637
|
-
bp = MethodBreakpoint.new(current_frame.
|
654
|
+
klass_name, op, method_name, cond, cmd, path = args[1..]
|
655
|
+
bp = MethodBreakpoint.new(current_frame.eval_binding, klass_name, op, method_name, cond: cond, command: cmd, path: path)
|
638
656
|
begin
|
639
657
|
bp.enable
|
640
658
|
rescue Exception => e
|
@@ -644,8 +662,8 @@ module DEBUGGER__
|
|
644
662
|
|
645
663
|
bp
|
646
664
|
when :watch
|
647
|
-
ivar, object, result = args[1..]
|
648
|
-
WatchIVarBreakpoint.new(ivar, object, result)
|
665
|
+
ivar, object, result, cond, command, path = args[1..]
|
666
|
+
WatchIVarBreakpoint.new(ivar, object, result, cond: cond, command: command, path: path)
|
649
667
|
else
|
650
668
|
raise "unknown breakpoint: #{args}"
|
651
669
|
end
|
@@ -732,10 +750,11 @@ module DEBUGGER__
|
|
732
750
|
break
|
733
751
|
|
734
752
|
when :finish
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
753
|
+
finish_frames = (iter || 1) - 1
|
754
|
+
goal_depth = @target_frames.first.frame_depth - finish_frames
|
755
|
+
|
756
|
+
step_tp nil, [:return, :b_return] do
|
757
|
+
DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
|
739
758
|
end
|
740
759
|
break
|
741
760
|
|
@@ -887,7 +906,7 @@ module DEBUGGER__
|
|
887
906
|
bp = make_breakpoint args
|
888
907
|
event! :result, :method_breakpoint, bp
|
889
908
|
when :watch
|
890
|
-
ivar = args[1]
|
909
|
+
ivar, cond, command, path = args[1..]
|
891
910
|
result = frame_eval(ivar)
|
892
911
|
|
893
912
|
if @success_last_eval
|
@@ -897,7 +916,7 @@ module DEBUGGER__
|
|
897
916
|
else
|
898
917
|
current_frame.self
|
899
918
|
end
|
900
|
-
bp = make_breakpoint [:watch, ivar, object, result]
|
919
|
+
bp = make_breakpoint [:watch, ivar, object, result, cond, command, path]
|
901
920
|
event! :result, :watch_breakpoint, bp
|
902
921
|
else
|
903
922
|
event! :result, nil
|
@@ -1067,7 +1086,7 @@ module DEBUGGER__
|
|
1067
1086
|
end
|
1068
1087
|
end
|
1069
1088
|
|
1070
|
-
#
|
1089
|
+
# copied from irb
|
1071
1090
|
class Output
|
1072
1091
|
include Color
|
1073
1092
|
|