diff options
author | Roland Senn <rsx@bluewin.ch> | 2019-11-11 11:56:59 +0100 |
---|---|---|
committer | Roland Senn <rsx@bluewin.ch> | 2020-02-29 17:36:59 +0100 |
commit | 3979485bd97771373214c44d14b7830ba447ad23 (patch) | |
tree | b596caf792f542f563b20d0a30746e9b89b4d95f /libraries/ghci | |
parent | 04d30137771a6cf8a18fda1ced25f78d0b2eb204 (diff) | |
download | haskell-3979485bd97771373214c44d14b7830ba447ad23.tar.gz |
Show breakpoint locations of breakpoints which were ignored during :force (#2950)
GHCi is split up into 2 major parts: The user-interface (UI)
and the byte-code interpreter. With `-fexternal-interpreter`
they even run in different processes. Communication between
the UI and the Interpreter (called `iserv`) is done using
messages over a pipe. This is called `Remote GHCI` and
explained in the Note [Remote GHCi] in `compiler/ghci/GHCi.hs`.
To process a `:force` command the UI sends a `Seq` message
to the `iserv` process. Then `iserv` does the effective
evaluation of the value. When during this process a breakpoint
is hit, the `iserv` process has no additional information to
enhance the `Ignoring breakpoint` output with the breakpoint
location.
To be able to print additional breakpoint information,
there are 2 possible implementation choices:
1. Store the needed information in the `iserv` process.
2. Print the `Ignoring breakpoint` from the UI process.
For option 1 we need to store the breakpoint info redundantely
in 2 places and this is bad. Therfore option 2 was implemented
in this MR:
- The user enters a `force` command
- The UI sends a `Seq` message to the `iserv` process.
- If processing of the `Seq` message hits a breakpoint,
the `iserv` process returns control to the UI process.
- The UI looks up the source location of the breakpoint,
and prints the enhanced `Ignoring breakpoint` output.
- The UI sends a `ResumeSeq` message to the `iserv` process,
to continue forcing.
Diffstat (limited to 'libraries/ghci')
-rw-r--r-- | libraries/ghci/GHCi/Message.hs | 9 | ||||
-rw-r--r-- | libraries/ghci/GHCi/Run.hs | 34 |
2 files changed, 41 insertions, 2 deletions
diff --git a/libraries/ghci/GHCi/Message.hs b/libraries/ghci/GHCi/Message.hs index 70c532fc94..7e96601b99 100644 --- a/libraries/ghci/GHCi/Message.hs +++ b/libraries/ghci/GHCi/Message.hs @@ -215,7 +215,12 @@ data Message a where -- | Evaluate something. This is used to support :force in GHCi. Seq :: HValueRef - -> Message (EvalResult ()) + -> Message (EvalStatus ()) + + -- | Resume forcing a free variable in a breakpoint (#2950) + ResumeSeq + :: RemoteRef (ResumeContext ()) + -> Message (EvalStatus ()) deriving instance Show (Message a) @@ -492,6 +497,7 @@ getMessage = do 35 -> Msg <$> (GetClosure <$> get) 36 -> Msg <$> (Seq <$> get) 37 -> Msg <$> return RtsRevertCAFs + 38 -> Msg <$> (ResumeSeq <$> get) _ -> error $ "Unknown Message code " ++ (show b) putMessage :: Message a -> Put @@ -534,6 +540,7 @@ putMessage m = case m of GetClosure a -> putWord8 35 >> put a Seq a -> putWord8 36 >> put a RtsRevertCAFs -> putWord8 37 + ResumeSeq a -> putWord8 38 >> put a -- ----------------------------------------------------------------------------- -- Reading/writing messages diff --git a/libraries/ghci/GHCi/Run.hs b/libraries/ghci/GHCi/Run.hs index a931e620cc..37dc7f2f48 100644 --- a/libraries/ghci/GHCi/Run.hs +++ b/libraries/ghci/GHCi/Run.hs @@ -95,7 +95,8 @@ run m = case m of GetClosure ref -> do clos <- getClosureData =<< localRef ref mapM (\(Box x) -> mkRemoteRef (HValue x)) clos - Seq ref -> tryEval (void $ evaluate =<< localRef ref) + Seq ref -> doSeq ref + ResumeSeq ref -> resumeSeq ref _other -> error "GHCi.Run.run" evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (EvalStatus [HValueRef]) @@ -130,6 +131,37 @@ evalStringToString r str = do r <- (unsafeCoerce io :: String -> IO String) str evaluate (force r) +-- | Process the Seq message to force a value. #2950 +-- If during this processing a breakpoint is hit, return +-- an EvalBreak value in the EvalStatus to the UI process, +-- otherwise return an EvalComplete. +-- The UI process has more and therefore also can show more +-- information about the breakpoint than the current iserv +-- process. +doSeq :: RemoteRef a -> IO (EvalStatus ()) +doSeq ref = do + sandboxIO evalOptsSeq $ do + _ <- (void $ evaluate =<< localRef ref) + return () + +-- | Process a ResumeSeq message. Continue the :force processing #2950 +-- after a breakpoint. +resumeSeq :: RemoteRef (ResumeContext ()) -> IO (EvalStatus ()) +resumeSeq hvref = do + ResumeContext{..} <- localRef hvref + withBreakAction evalOptsSeq resumeBreakMVar resumeStatusMVar $ + mask_ $ do + putMVar resumeBreakMVar () -- this awakens the stopped thread... + redirectInterrupts resumeThreadId $ takeMVar resumeStatusMVar + +evalOptsSeq :: EvalOpts +evalOptsSeq = EvalOpts + { useSandboxThread = True + , singleStep = False + , breakOnException = False + , breakOnError = False + } + -- When running a computation, we redirect ^C exceptions to the running -- thread. ToDo: we might want a way to continue even if the target -- thread doesn't die when it receives the exception... "this thread |