Rodrigo Mesquita pushed to branch wip/romes/exec-break-has-resume at Glasgow Haskell Compiler / GHC

Commits:

5 changed files:

Changes:

  • changelog.d/ghc-api-exec-break-resume
    1
    +section: ghc-lib
    
    2
    +synopsis: An ``ExecBreak`` now stores its ``Resume`` and ``resumeExec`` receives it explicitly
    
    3
    +issues: #28058
    
    4
    +mrs: !16889
    
    5
    +
    
    6
    +description: {
    
    7
    +- ``ExecBreak`` has a new field ``breakResume :: Resume`` with the context needed
    
    8
    +  to resume the thread stopped at that breakpoint.
    
    9
    +- ``resumeExec`` takes a specific ``Resume`` to continue as an extra argument,
    
    10
    +  instead of resuming the most recent breakpoint in ``ic_resume``.
    
    11
    +- ``resumeExec`` no longer pops the ``Resume`` off ``ic_resume``. Callers that
    
    12
    +  rely on the ``ic_resume`` stack must now update it themselves, for instance
    
    13
    +  if if calling ``resumeExec`` on the first ``Resume`` in the context.
    
    14
    +}

  • compiler/GHC/Runtime/Eval.hs
    ... ... @@ -290,7 +290,8 @@ emptyHistory size = nilBL size
    290 290
     -- breakpoint if we don't care about that breakpoint (e.g. if using :steplocal
    
    291 291
     -- or :stepmodule, rather than :step, we only care about certain breakpoints).
    
    292 292
     handleRunStatus :: GhcMonad m
    
    293
    -                => SingleStep -> String
    
    293
    +                => SingleStep
    
    294
    +                -> String
    
    294 295
                     -> ResumeBindings
    
    295 296
                     -> [Id]
    
    296 297
                     -> EvalStatus_ [ForeignHValue] [HValueRef]
    
    ... ... @@ -343,7 +344,7 @@ handleRunStatus step expr bindings final_ids status history0 = do
    343 344
             hsc_env2 = pushResume hsc_env1 resume
    
    344 345
     
    
    345 346
           setSession hsc_env2
    
    346
    -      return (ExecBreak break_ids Nothing)
    
    347
    +      return (ExecBreak break_ids Nothing resume)
    
    347 348
     
    
    348 349
         -- EvalBreak (Just ...) case: the interpreter stopped at a breakpoint
    
    349 350
         --
    
    ... ... @@ -389,7 +390,7 @@ handleRunStatus step expr bindings final_ids status history0 = do
    389 390
                 }
    
    390 391
               hsc_env2 = pushResume hsc_env1 resume
    
    391 392
             setSession hsc_env2
    
    392
    -        return (ExecBreak break_ids (Just ibi))
    
    393
    +        return (ExecBreak break_ids (Just ibi) resume)
    
    393 394
           else do
    
    394 395
             -- resume with the same step type
    
    395 396
             let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
    
    ... ... @@ -404,65 +405,67 @@ handleRunStatus step expr bindings final_ids status history0 = do
    404 405
       tracing | RunAndLogSteps <- step = True
    
    405 406
               | otherwise              = False
    
    406 407
     
    
    407
    -resumeExec :: GhcMonad m => SingleStep -> Maybe Int
    
    408
    +-- | Resume a paused thread given its 'Resume' context.
    
    409
    +--
    
    410
    +-- The interactive things in scope are restored to the things available at the
    
    411
    +-- time this paused thread hit the breakpoint.
    
    412
    +--
    
    413
    +-- This 'Resume' is not popped off the interactive `ic_resume` breakpoint
    
    414
    +-- stack, since there's no way to tell which of the breakpoints in the stack is
    
    415
    +-- the one we're resuming.
    
    416
    +resumeExec :: GhcMonad m
    
    417
    +           => SingleStep
    
    418
    +           -> Maybe Int
    
    419
    +           -> Resume
    
    408 420
                -> m ExecResult
    
    409
    -resumeExec step mbCnt
    
    410
    - = do
    
    411
    -   hsc_env <- getSession
    
    412
    -   let ic = hsc_IC hsc_env
    
    413
    -       resume = ic_resume ic
    
    414
    -
    
    415
    -   case resume of
    
    416
    -     [] -> liftIO $
    
    417
    -           throwGhcExceptionIO (ProgramError "not stopped at a breakpoint")
    
    418
    -     (r:rs) -> do
    
    419
    -        -- unbind the temporary locals by restoring the TypeEnv from
    
    420
    -        -- before the breakpoint, and drop this Resume from the
    
    421
    -        -- InteractiveContext.
    
    422
    -        let (resume_tmp_te,resume_gre_cache) = resumeBindings r
    
    423
    -            ic' = ic { ic_tythings = resume_tmp_te,
    
    424
    -                       ic_gre_cache = resume_gre_cache,
    
    425
    -                       ic_resume   = rs }
    
    426
    -        setSession hsc_env{ hsc_IC = ic' }
    
    427
    -
    
    428
    -        -- remove any bindings created since the breakpoint from the
    
    429
    -        -- linker's environment
    
    430
    -        let old_names = map getName resume_tmp_te
    
    431
    -            new_names = [ n | thing <- ic_tythings ic
    
    432
    -                            , let n = getName thing
    
    433
    -                            , not (n `elem` old_names) ]
    
    434
    -            interp    = hscInterp hsc_env
    
    435
    -            dflags    = hsc_dflags hsc_env
    
    436
    -        liftIO $ Loader.deleteFromLoadedHomeEnv interp new_names
    
    437
    -
    
    438
    -        case r of
    
    439
    -          Resume { resumeStmt = expr
    
    440
    -                 , resumeContext = fhv
    
    441
    -                 , resumeBindings = bindings
    
    442
    -                 , resumeFinalIds = final_ids
    
    443
    -                 , resumeApStack = apStack
    
    444
    -                 , resumeBreakpointId = mb_brkpt
    
    445
    -                 , resumeSpan = span
    
    446
    -                 , resumeHistory = hist } ->
    
    447
    -               do
    
    448
    -                -- When the user specified a break ignore count, set it
    
    449
    -                -- in the interpreter
    
    450
    -                case (mb_brkpt, mbCnt) of
    
    451
    -                  (Just brkpt, Just cnt) -> setupBreakpoint interp brkpt cnt
    
    452
    -                  _ -> return ()
    
    453
    -
    
    454
    -                let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
    
    455
    -                status <- liftIO $ GHCi.resumeStmt interp eval_opts fhv
    
    456
    -                let prevHistoryLst = fromListBL 50 hist
    
    457
    -                    hug = hsc_HUG hsc_env
    
    458
    -                    hist' = case mb_brkpt of
    
    459
    -                       Nothing -> pure prevHistoryLst
    
    460
    -                       Just bi
    
    461
    -                         | breakHere False step span -> do
    
    462
    -                            hist1 <- liftIO (mkHistory hug apStack bi)
    
    463
    -                            return $ hist1 `consBL` fromListBL 50 hist
    
    464
    -                         | otherwise -> pure prevHistoryLst
    
    465
    -                handleRunStatus step expr bindings final_ids status =<< hist'
    
    421
    +resumeExec step mbCnt r = do
    
    422
    +  hsc_env <- getSession
    
    423
    +  let ic = hsc_IC hsc_env
    
    424
    +  -- unbind the temporary locals by restoring the TypeEnv from
    
    425
    +  -- before the breakpoint
    
    426
    +  let (resume_tmp_te,resume_gre_cache) = resumeBindings r
    
    427
    +      ic' = ic { ic_tythings = resume_tmp_te,
    
    428
    +                 ic_gre_cache = resume_gre_cache }
    
    429
    +  setSession hsc_env{ hsc_IC = ic' }
    
    430
    +
    
    431
    +  -- remove any bindings created since the breakpoint from the
    
    432
    +  -- linker's environment
    
    433
    +  let old_names = map getName resume_tmp_te
    
    434
    +      new_names = [ n | thing <- ic_tythings ic
    
    435
    +                      , let n = getName thing
    
    436
    +                      , not (n `elem` old_names) ]
    
    437
    +      interp    = hscInterp hsc_env
    
    438
    +      dflags    = hsc_dflags hsc_env
    
    439
    +  liftIO $ Loader.deleteFromLoadedHomeEnv interp new_names
    
    440
    +
    
    441
    +  case r of
    
    442
    +    Resume { resumeStmt = expr
    
    443
    +           , resumeContext = fhv
    
    444
    +           , resumeBindings = bindings
    
    445
    +           , resumeFinalIds = final_ids
    
    446
    +           , resumeApStack = apStack
    
    447
    +           , resumeBreakpointId = mb_brkpt
    
    448
    +           , resumeSpan = span
    
    449
    +           , resumeHistory = hist } ->
    
    450
    +         do
    
    451
    +          -- When the user specified a break ignore count, set it
    
    452
    +          -- in the interpreter
    
    453
    +          case (mb_brkpt, mbCnt) of
    
    454
    +            (Just brkpt, Just cnt) -> setupBreakpoint interp brkpt cnt
    
    455
    +            _ -> return ()
    
    456
    +
    
    457
    +          let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
    
    458
    +          status <- liftIO $ GHCi.resumeStmt interp eval_opts fhv
    
    459
    +          let prevHistoryLst = fromListBL 50 hist
    
    460
    +              hug = hsc_HUG hsc_env
    
    461
    +              hist' = case mb_brkpt of
    
    462
    +                 Nothing -> pure prevHistoryLst
    
    463
    +                 Just bi
    
    464
    +                   | breakHere False step span -> do
    
    465
    +                      hist1 <- liftIO (mkHistory hug apStack bi)
    
    466
    +                      return $ hist1 `consBL` fromListBL 50 hist
    
    467
    +                   | otherwise -> pure prevHistoryLst
    
    468
    +          handleRunStatus step expr bindings final_ids status =<< hist'
    
    466 469
     
    
    467 470
     setupBreakpoint :: GhcMonad m => Interp -> InternalBreakpointId -> Int -> m ()   -- #19157
    
    468 471
     setupBreakpoint interp ibi cnt = do
    

  • compiler/GHC/Runtime/Eval/Types.hs
    ... ... @@ -162,6 +162,8 @@ data ExecResult
    162 162
         | ExecBreak
    
    163 163
            { breakNames   :: [Id]
    
    164 164
            , breakPointId :: Maybe InternalBreakpointId
    
    165
    +       , breakResume  :: Resume
    
    166
    +         -- ^ to resume this thread blocked on this break
    
    165 167
            }
    
    166 168
     
    
    167 169
     -- | Essentially a GlobalRdrEnv, but with additional cached values to allow
    

  • ghc/GHCi/UI.hs
    ... ... @@ -1618,7 +1618,7 @@ runStmt input step = do
    1618 1618
                m_result <- GhciMonad.runStmt stmt input step
    
    1619 1619
                case m_result of
    
    1620 1620
                    Nothing     -> return Nothing
    
    1621
    -               Just result -> Just <$> afterRunStmt step (RunStmtResult result)
    
    1621
    +               Just result -> Just <$> afterRunStmt (RunStmtResult result)
    
    1622 1622
     
    
    1623 1623
         -- `x = y` (a declaration) should be treated as `let x = y` (a statement).
    
    1624 1624
         -- The reason is because GHCi wasn't designed to support `x = y`, but then
    
    ... ... @@ -1650,7 +1650,7 @@ runStmt input step = do
    1650 1650
           _ <- liftIO $ tryIO $ hFlushAll stdin
    
    1651 1651
           m_result <- GhciMonad.runDecls' decls
    
    1652 1652
           forM m_result $ \result -> do
    
    1653
    -        afterRunStmt step (RunDeclsResult result)
    
    1653
    +        afterRunStmt (RunDeclsResult result)
    
    1654 1654
     
    
    1655 1655
         mk_stmt :: SrcSpan -> HsBind GhcPs -> GhciLStmt GhcPs
    
    1656 1656
         mk_stmt loc bind =
    
    ... ... @@ -1671,10 +1671,9 @@ data RunResult
    1671 1671
       | RunDeclsResult [TyThing]
    
    1672 1672
     
    
    1673 1673
     -- | Clean up the GHCi environment after a statement has run
    
    1674
    -afterRunStmt :: GhciMonad m => SingleStep {-^ Type of step we took just before -}
    
    1675
    -             -> RunResult -> m RunResult
    
    1676
    -afterRunStmt step run_result = do
    
    1677
    -  resumes <- GHC.getResumeContext
    
    1674
    +afterRunStmt :: GhciMonad m
    
    1675
    +             => RunResult -> m RunResult
    
    1676
    +afterRunStmt run_result = do
    
    1678 1677
       case run_result of
    
    1679 1678
         RunDeclsResult things -> do
    
    1680 1679
           show_types <- isOptionSet ShowType
    
    ... ... @@ -1685,20 +1684,15 @@ afterRunStmt step run_result = do
    1685 1684
             Right ids -> do
    
    1686 1685
               show_types <- isOptionSet ShowType
    
    1687 1686
               when show_types $ printTypeOfIds ids
    
    1688
    -    RunStmtResult (GHC.ExecBreak ids mb_info)
    
    1689
    -        | first_resume : _ <- resumes
    
    1690
    -        -> do mb_id_loc <- toBreakIdAndLocation mb_info
    
    1691
    -              let bCmd = maybe "" ( \(_,l) -> onBreakCmd l ) mb_id_loc
    
    1692
    -              if (null bCmd)
    
    1693
    -                then printStoppedAtBreakInfo first_resume ids
    
    1694
    -                else enqueueCommands [bCmd]
    
    1695
    -              -- run the command set with ":set stop <cmd>"
    
    1696
    -              st <- getGHCiState
    
    1697
    -              enqueueCommands [stop st]
    
    1698
    -              return ()
    
    1699
    -
    
    1700
    -        | otherwise -> resume step Nothing >>=
    
    1701
    -                       afterRunStmt step . RunStmtResult >> return ()
    
    1687
    +    RunStmtResult (GHC.ExecBreak ids mb_info resume) -> do
    
    1688
    +      mb_id_loc <- toBreakIdAndLocation mb_info
    
    1689
    +      let bCmd = maybe "" ( \(_,l) -> onBreakCmd l ) mb_id_loc
    
    1690
    +      if (null bCmd)
    
    1691
    +        then printStoppedAtBreakInfo resume ids
    
    1692
    +        else enqueueCommands [bCmd]
    
    1693
    +      -- run the command set with ":set stop <cmd>"
    
    1694
    +      st <- getGHCiState
    
    1695
    +      enqueueCommands [stop st]
    
    1702 1696
     
    
    1703 1697
       flushInterpBuffers
    
    1704 1698
       withSignalHandlers $ do
    
    ... ... @@ -4401,7 +4395,7 @@ doContinue step = doContinue' step Nothing
    4401 4395
     doContinue' :: GhciMonad m => SingleStep -> Maybe Int -> m ()
    
    4402 4396
     doContinue' step mbCnt= do
    
    4403 4397
       runResult <- resume step mbCnt
    
    4404
    -  _ <- afterRunStmt step (RunStmtResult runResult)
    
    4398
    +  _ <- afterRunStmt (RunStmtResult runResult)
    
    4405 4399
       return ()
    
    4406 4400
     
    
    4407 4401
     abandonCmd :: GhciMonad m => String -> m ()
    

  • ghc/GHCi/UI/Monad.hs
    ... ... @@ -38,6 +38,7 @@ import GHCi.UI.Info (ModInfo)
    38 38
     import qualified GHC
    
    39 39
     import GHC.Driver.Monad hiding (liftIO)
    
    40 40
     import GHC.Utils.Outputable
    
    41
    +import GHC.Utils.Panic
    
    41 42
     import qualified GHC.Driver.Ppr as Ppr
    
    42 43
     import GHC.Types.Name.Occurrence
    
    43 44
     import GHC.Driver.Session
    
    ... ... @@ -412,7 +413,12 @@ resume step mbIgnoreCnt = do
    412 413
         withProgName (progname st) $
    
    413 414
         withArgs (args st) $
    
    414 415
           reflectGHCi x $ do
    
    415
    -        GHC.resumeExec step mbIgnoreCnt
    
    416
    +        GHC.getResumeContext >>= \case
    
    417
    +          [] -> liftIO $
    
    418
    +                throwGhcExceptionIO (GHC.ProgramError "not stopped at a breakpoint")
    
    419
    +          (r:rs) -> do
    
    420
    +            modifySession $ \env -> env{hsc_IC = (hsc_IC env){ ic_resume = rs }}
    
    421
    +            GHC.resumeExec step mbIgnoreCnt r
    
    416 422
     
    
    417 423
     -- --------------------------------------------------------------------------
    
    418 424
     -- timing & statistics