debug 1.3.4 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|