@@ -55,6 +55,7 @@ def first_line
5555
5656module DEBUGGER__
5757 PresetCommand = Struct . new ( :commands , :source , :auto_continue )
58+ class PostmortemError < RuntimeError ; end
5859
5960 class Session
6061 def initialize ui
@@ -74,6 +75,8 @@ def initialize ui
7475 @tc = nil
7576 @tc_id = 0
7677 @preset_command = nil
78+ @postmortem_hook = nil
79+ @postmortem = false
7780
7881 @frame_map = { } # {id => [threadId, frame_depth]} for DAP
7982 @var_map = { 1 => [ :globals ] , } # {id => ...} for DAP
@@ -245,6 +248,14 @@ def wait_command_loop tc
245248 @tc = nil
246249 end
247250
251+ def prompt
252+ if @postmortem
253+ '(rdbg:postmortem) '
254+ else
255+ '(rdbg) '
256+ end
257+ end
258+
248259 def wait_command
249260 if @preset_command
250261 if @preset_command . commands . empty?
@@ -262,7 +273,7 @@ def wait_command
262273 end
263274 else
264275 @ui . puts "INTERNAL_INFO: #{ JSON . generate ( @internal_info ) } " if ENV [ 'RUBY_DEBUG_TEST_MODE' ]
265- line = @ui . readline
276+ line = @ui . readline prompt
266277 end
267278
268279 case line
@@ -298,18 +309,21 @@ def process_command line
298309 # * Step in. Resume the program until next breakable point.
299310 when 's' , 'step'
300311 cancel_auto_continue
312+ check_postmortem
301313 @tc << [ :step , :in ]
302314
303315 # * `n[ext]`
304316 # * Step over. Resume the program until next line.
305317 when 'n' , 'next'
306318 cancel_auto_continue
319+ check_postmortem
307320 @tc << [ :step , :next ]
308321
309322 # * `fin[ish]`
310323 # * Finish this frame. Resume the program until the current frame is finished.
311324 when 'fin' , 'finish'
312325 cancel_auto_continue
326+ check_postmortem
313327 @tc << [ :step , :finish ]
314328
315329 # * `c[ontinue]`
@@ -370,6 +384,8 @@ def process_command line
370384 # * break if: `<expr>` is true at any lines.
371385 # * Note that this feature is super slow.
372386 when 'b' , 'break'
387+ check_postmortem
388+
373389 if arg == nil
374390 show_bps
375391 return :retry
@@ -386,6 +402,7 @@ def process_command line
386402
387403 # skip
388404 when 'bv'
405+ check_postmortem
389406 require 'json'
390407
391408 h = Hash . new { |h , k | h [ k ] = [ ] }
@@ -412,6 +429,8 @@ def process_command line
412429 # * `catch <Error>`
413430 # * Set breakpoint on raising `<Error>`.
414431 when 'catch'
432+ check_postmortem
433+
415434 if arg
416435 bp = add_catch_breakpoint arg
417436 show_bps bp if bp
@@ -424,6 +443,8 @@ def process_command line
424443 # * Stop the execution when the result of current scope's `@ivar` is changed.
425444 # * Note that this feature is super slow.
426445 when 'wat' , 'watch'
446+ check_postmortem
447+
427448 if arg && arg . match? ( /\A @\w +/ )
428449 @tc << [ :breakpoint , :watch , arg ]
429450 else
@@ -436,6 +457,8 @@ def process_command line
436457 # * `del[ete] <bpnum>`
437458 # * delete specified breakpoint.
438459 when 'del' , 'delete'
460+ check_postmortem
461+
439462 bp =
440463 case arg
441464 when nil
@@ -784,6 +807,9 @@ def process_command line
784807 return :retry
785808 rescue SystemExit
786809 raise
810+ rescue PostmortemError => e
811+ @ui . puts e . message
812+ return :retry
787813 rescue Exception => e
788814 @ui . puts "[REPL ERROR] #{ e . inspect } "
789815 @ui . puts e . backtrace . map { |e | ' ' + e }
@@ -1250,6 +1276,56 @@ def check_forked
12501276 raise 'DEBUGGER: stop at forked process is not supported yet.'
12511277 end
12521278 end
1279+
1280+ def check_postmortem
1281+ if @postmortem
1282+ raise PostmortemError , "Can not use this command on postmortem mode."
1283+ end
1284+ end
1285+
1286+ def enter_postmortem_session frames
1287+ @postmortem = true
1288+ ThreadClient . current . on_suspend :postmortem , postmortem_frames : frames
1289+ ensure
1290+ @postmortem = false
1291+ end
1292+
1293+ def postmortem = ( is_enable )
1294+ if is_enable
1295+ unless @postmortem_hook
1296+ @postmortem_hook = TracePoint . new ( :raise ) { |tp |
1297+ exc = tp . raised_exception
1298+ frames = DEBUGGER__ . capture_frames ( __dir__ )
1299+ exc . instance_variable_set ( :@postmortem_frames , frames )
1300+ }
1301+ at_exit {
1302+ @postmortem_hook . disable
1303+ if CONFIG [ :postmortem ] && ( exc = $!) != nil
1304+ begin
1305+ @ui . puts "Enter postmortem mode with #{ exc . inspect } "
1306+ @ui . puts exc . backtrace . map { |e | ' ' + e }
1307+ @ui . puts "\n "
1308+
1309+ enter_postmortem_session exc . instance_variable_get ( :@postmortem_frames )
1310+ rescue SystemExit
1311+ exit!
1312+ rescue Exception => e
1313+ @ui = STDERR unless @ui
1314+ @ui . puts "Error while postmortem console: #{ e . inspect } "
1315+ end
1316+ end
1317+ }
1318+ end
1319+
1320+ if !@postmortem_hook . enabled?
1321+ @postmortem_hook . enable
1322+ end
1323+ else
1324+ if @postmortem_hook && @postmortem_hook . enabled?
1325+ @postmortem_hook . disable
1326+ end
1327+ end
1328+ end
12531329 end
12541330
12551331 class UI_Base
0 commit comments