[Git][ghc/ghc][wip/romes/multi-threaded-debugger] 7 commits: add break033 checks continue after unsetting break on exception works
Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC Commits: 19e11d17 by Rodrigo Mesquita at 2026-10-27T11:14:36+00:00 add break033 checks continue after unsetting break on exception works - - - - - 97fc0948 by Rodrigo Mesquita at 2026-10-27T11:18:08+00:00 Add test checking we don't stop at breakpoints in TH - - - - - c6de4b33 by Rodrigo Mesquita at 2026-10-27T11:22:38+00:00 Test that :abandon doesn't hang when -fbreak-on-error - - - - - 09768c87 by Rodrigo Mesquita at 2026-10-27T11:33:40+00:00 run conc033 in ghci too - - - - - 275f5fe7 by Rodrigo Mesquita at 2026-10-27T11:42:40+00:00 Add test checking a ^C interrupt of a :force interrupts the expression - - - - - 061d37f4 by Rodrigo Mesquita at 2026-10-27T11:44:38+00:00 fixup! TSO_STOP_ON_EXCEPTION -- per-thread break-on-exception - - - - - bfd992fe by Rodrigo Mesquita at 2026-10-27T12:10:06+00:00 setBreakAction should also unset if flag is off. (e.g. we don't want a :continue to keep flags if the :continue resume request has unset things) - - - - - 18 changed files: - libraries/ghci/GHCi/Run.hs - rts/Interpreter.h - testsuite/tests/concurrent/should_run/all.T - testsuite/tests/ghci.debugger/scripts/all.T - + testsuite/tests/ghci.debugger/scripts/break032.hs - + testsuite/tests/ghci.debugger/scripts/break032.script - + testsuite/tests/ghci.debugger/scripts/break032.stdout - + testsuite/tests/ghci.debugger/scripts/break033.hs - + testsuite/tests/ghci.debugger/scripts/break033.script - + testsuite/tests/ghci.debugger/scripts/break033.stdout - + testsuite/tests/ghci.debugger/scripts/break034.hs - + testsuite/tests/ghci.debugger/scripts/break034.script - + testsuite/tests/ghci.debugger/scripts/break034.stdout - + testsuite/tests/ghci/should_run/ForceInterrupt.script - + testsuite/tests/ghci/should_run/ForceInterrupt.stdout - + testsuite/tests/ghci/should_run/ForceInterruptExt.stdout - testsuite/tests/ghci/should_run/Makefile - testsuite/tests/ghci/should_run/all.T Changes: ===================================== libraries/ghci/GHCi/Run.hs ===================================== @@ -367,7 +367,7 @@ sandboxIO opts io -- tracing, etc.) need the expression to be running in a -- separate thread, so debugging is only enabled when -- using the sandbox. - tid <- forkIO (pure ()) + tid <- forkIO (pure ()) -- TODO doesn't work runIt id >>= writeThreadEvalStatus tid mkRemoteRef tid -- We `runIt` on the main thread, but fork a new thread to store to the @@ -419,16 +419,16 @@ setBreakAction opts tid@(ThreadId tid#) = do where setBreakAction' ctx = do - runIf# singleStep rts_enableStopNextBreakpoint - runIf# stepOut rts_enableStopAfterReturn + runIf# singleStep rts_enableStopNextBreakpoint rts_disableStopNextBreakpoint + runIf# stepOut rts_enableStopAfterReturn rts_disableStopAfterReturn -- Breaking on exceptions is not enabled by default, since it might be a -- bit surprising. The exception flag is turned off as soon as it is hit. - runIf# breakOnException rts_enableStopOnException + runIf# breakOnException rts_enableStopOnException rts_disableStopOnException if isolateThreadBreaks opts then setIsolatedCtx ctx else unsetIsolatedCtx ctx - runIf# pred what = when (pred opts) (what tid#) + runIf# pred what what_off = if pred opts then what tid# else what_off tid# ===================================== rts/Interpreter.h ===================================== @@ -21,3 +21,9 @@ void rts_disableStopNextBreakpoint ( StgTSO* ); // Debugger step out void rts_enableStopAfterReturn ( StgTSO* ); void rts_disableStopAfterReturn ( StgTSO* ); + +// Debugger stop on exception +void rts_enableStopOnException ( StgTSO* ); +void rts_disableStopOnException ( StgTSO* ); +void rts_enableStopOnExceptionAll ( void ); +void rts_disableStopOnExceptionAll ( void ); ===================================== testsuite/tests/concurrent/should_run/all.T ===================================== @@ -168,7 +168,10 @@ test('conc030', extra_run_opts('+RTS -K4M -RTS'), compile_and_run, ['']) test('conc031', exit_code(1), compile_and_run, ['']) test('conc032', normal, compile_and_run, ['']) -test('conc033', normal, compile_and_run, ['']) +# Also run in GHCi: the sandbox thread must still be detected as deadlocked +test('conc033', + [extra_ways(['ghci'] + (['ghci-ext'] if config.have_ext_interp else []))], + compile_and_run, ['']) # Omit for GHCi, because it just sits there waiting for you to press ^C test('conc034', [ ===================================== testsuite/tests/ghci.debugger/scripts/all.T ===================================== @@ -155,3 +155,7 @@ test('T26042f2', [extra_hc_opts('-O -fno-unoptimized-core-for-interpreter'), ext test('T26042g', extra_files(['T26042g.hs']), ghci_script, ['T26042g.script']) test('T27633c', extra_files(['T27633c_obj.hs']), ghci_script, ['T27633c.script']) + +test('break032', extra_files(['break032.hs']), ghci_script, ['break032.script']) +test('break033', extra_files(['break033.hs']), ghci_script, ['break033.script']) +test('break034', extra_files(['break034.hs']), ghci_script, ['break034.script']) ===================================== testsuite/tests/ghci.debugger/scripts/break032.hs ===================================== @@ -0,0 +1,4 @@ +module Break032 where + +f :: Int -> Int +f x = x + 1 ===================================== testsuite/tests/ghci.debugger/scripts/break032.script ===================================== @@ -0,0 +1,7 @@ +:set -XTemplateHaskell +:l break032 +:break f +-- Running a statement installs the debugger's breakpoint action (TODO: not rely on this.) +print (1 :: Int) +-- A breakpoint hit while running a splice must be ignored, not block forever +y = $(Language.Haskell.TH.Syntax.lift (f 41)) ===================================== testsuite/tests/ghci.debugger/scripts/break032.stdout ===================================== @@ -0,0 +1,3 @@ +Breakpoint 0 activated at break032.hs:4:7-11 +1 +*** Ignoring breakpoint ===================================== testsuite/tests/ghci.debugger/scripts/break033.hs ===================================== @@ -0,0 +1,11 @@ +module Break033 where + +import Control.Exception + +f :: Int -> IO Int +f x = do + r <- try (evaluate (x `div` 0)) + case r of + Left DivideByZero -> pure x + Left e -> throwIO e + Right y -> pure y ===================================== testsuite/tests/ghci.debugger/scripts/break033.script ===================================== @@ -0,0 +1,8 @@ +:l break033 +:break f +:set -fbreak-on-exception +f 1 +-- stopped on entry, hasn't hit exception yet +:unset -fbreak-on-exception +-- The DivideByZero must not stop us after break-on-exception was unset +:continue ===================================== testsuite/tests/ghci.debugger/scripts/break033.stdout ===================================== @@ -0,0 +1,5 @@ +Breakpoint 0 activated at break033.hs:(6,7)-(11,31) +Stopped in Break033.f, break033.hs:(6,7)-(11,31) +_result :: IO Int = _ +x :: Int = 1 +1 ===================================== testsuite/tests/ghci.debugger/scripts/break034.hs ===================================== @@ -0,0 +1,4 @@ +module Break034 where + +f :: Int -> Int +f x = x + 1 ===================================== testsuite/tests/ghci.debugger/scripts/break034.script ===================================== @@ -0,0 +1,11 @@ +:l break034 +:set -fbreak-on-error +:break f +f 1 +-- Killing the thread stopped at the breakpoint may break again, on the +-- ThreadKilled exception; :abandon must still clean up without errors and +-- without blocking there. +:abandon +f 2 +:abandon +putStrLn "done" ===================================== testsuite/tests/ghci.debugger/scripts/break034.stdout ===================================== @@ -0,0 +1,8 @@ +Breakpoint 0 activated at break034.hs:4:7-11 +Stopped in Break034.f, break034.hs:4:7-11 +_result :: Int = _ +x :: Int = 1 +Stopped in Break034.f, break034.hs:4:7-11 +_result :: Int = _ +x :: Int = 2 +done ===================================== testsuite/tests/ghci/should_run/ForceInterrupt.script ===================================== @@ -0,0 +1,11 @@ +import Control.Concurrent +import Control.Monad +import Data.IORef +import System.IO.Unsafe +ticks <- newIORef (0 :: Int) +let x = unsafePerformIO (writeFile "force.started" "" >> forever (modifyIORef' ticks (+1) >> threadDelay 20000)) :: () +-- blocks the interpreter waiting for the expression being forced to terminate. +-- Test that a SIGINT (from the Makefile) interrupts the :force and kills the +-- expression which is running in a (possibly remote) thread. +:force x +do { a <- readIORef ticks; threadDelay 500000; b <- readIORef ticks; putStrLn (if a == b then "stopped" else "still running") } ===================================== testsuite/tests/ghci/should_run/ForceInterrupt.stdout ===================================== @@ -0,0 +1,2 @@ +Interrupted. +stopped ===================================== testsuite/tests/ghci/should_run/ForceInterruptExt.stdout ===================================== @@ -0,0 +1,2 @@ +Interrupted. +stopped ===================================== testsuite/tests/ghci/should_run/Makefile ===================================== @@ -18,6 +18,15 @@ T3171 T3171ext: while [ ! -e $@.started ] && kill -0 $$! 2>/dev/null; do sleep 0.1; done; \ kill -INT $$! $$(pgrep -P $$!); wait +# Like T3171, but ^C interrupts a :force rather than an expression: the +# computation being forced must be interrupted too, not left running. +ForceInterruptExt: ForceInterrupt_OPTS = -fexternal-interpreter +ForceInterrupt ForceInterruptExt: + rm -f force.started + "$(TEST_HC)" $(TEST_HC_OPTS_INTERACTIVE) $(ForceInterrupt_OPTS) < ForceInterrupt.script & \ + while [ ! -e force.started ] && kill -0 $$! 2>/dev/null; do sleep 0.1; done; \ + kill -INT $$! $$(pgrep -P $$!); wait + TopEnvIface: "$(TEST_HC)" $(TEST_HC_OPTS_INTERACTIVE) TopEnvIface -v1 -fno-hide-source-paths -e "a" -e ":q" -fwrite-if-simplified-core -fwrite-interface # Second compilation starts from interface files, but still can print "a" ===================================== testsuite/tests/ghci/should_run/all.T ===================================== @@ -23,6 +23,21 @@ test('T3171ext', combined_output], makefile_test, []) +test('ForceInterrupt', + [when(opsys('mingw32'),skip), + req_interp, + extra_files(['ForceInterrupt.script']), + combined_output], + makefile_test, []) + +test('ForceInterruptExt', + [when(opsys('mingw32'),skip), + req_interp, + unless(config.have_ext_interp, skip), + extra_files(['ForceInterrupt.script']), + combined_output], + makefile_test, []) + test('ghcirun004', just_ghci, compile_and_run, ['']) test('T8377', just_ghci, compile_and_run, ['']) test('T9914', just_ghci, ghci_script, ['T9914.script']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/51dfa0c5b5b6d5295aa3ed8430ab3c8... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/51dfa0c5b5b6d5295aa3ed8430ab3c8... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
participants (1)
-
Rodrigo Mesquita (@alt-romes)