Skip to content

Commit

Permalink
Add history/trace functionality to the GHCi debugger
Browse files Browse the repository at this point in the history
The debugger can now log each step of the evaluation without actually
stopping, keeping a history of the recent steps (currently 50).  When
a (real) breakpoint is hit, you can examine previous steps in the
history (and their free variables) using the :history, :back and
:forward commands.
  • Loading branch information
Simon Marlow committed May 3, 2007
1 parent cddf971 commit e1b8996
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 202 deletions.
51 changes: 7 additions & 44 deletions compiler/ghci/GhciMonad.hs
Expand Up @@ -21,7 +21,6 @@ import SrcLoc
import Module

import Numeric
import Control.Concurrent
import Control.Exception as Exception
import Data.Array
import Data.Char
Expand All @@ -47,7 +46,8 @@ data GHCiState = GHCiState
session :: GHC.Session,
options :: [GHCiOption],
prelude :: GHC.Module,
breaks :: !ActiveBreakPoints,
break_ctr :: !Int,
breaks :: ![(Int, BreakLocation)],
tickarrays :: ModuleEnv TickArray
-- tickarrays caches the TickArray for loaded modules,
-- so that we don't rebuild it each time the user sets
Expand All @@ -62,19 +62,6 @@ data GHCiOption
| RevertCAFs -- revert CAFs after every evaluation
deriving Eq

data ActiveBreakPoints
= ActiveBreakPoints
{ breakCounter :: !Int
, breakLocations :: ![(Int, BreakLocation)] -- break location uniquely numbered
}

instance Outputable ActiveBreakPoints where
ppr activeBrks = prettyLocations $ breakLocations activeBrks

emptyActiveBreakPoints :: ActiveBreakPoints
emptyActiveBreakPoints
= ActiveBreakPoints { breakCounter = 0, breakLocations = [] }

data BreakLocation
= BreakLocation
{ breakModule :: !GHC.Module
Expand All @@ -90,43 +77,19 @@ prettyLocations locs = vcat $ map (\(i, loc) -> brackets (int i) <+> ppr loc) $
instance Outputable BreakLocation where
ppr loc = (ppr $ breakModule loc) <+> ppr (breakLoc loc)

getActiveBreakPoints :: GHCi ActiveBreakPoints
getActiveBreakPoints = liftM breaks getGHCiState

-- don't reset the counter back to zero?
discardActiveBreakPoints :: GHCi ()
discardActiveBreakPoints = do
st <- getGHCiState
let oldActiveBreaks = breaks st
newActiveBreaks = oldActiveBreaks { breakLocations = [] }
setGHCiState $ st { breaks = newActiveBreaks }

deleteBreak :: Int -> GHCi ()
deleteBreak identity = do
st <- getGHCiState
let oldActiveBreaks = breaks st
oldLocations = breakLocations oldActiveBreaks
newLocations = filter (\loc -> fst loc /= identity) oldLocations
newActiveBreaks = oldActiveBreaks { breakLocations = newLocations }
setGHCiState $ st { breaks = newActiveBreaks }

recordBreak :: BreakLocation -> GHCi (Bool{- was already present -}, Int)
recordBreak brkLoc = do
st <- getGHCiState
let oldActiveBreaks = breaks st
let oldLocations = breakLocations oldActiveBreaks
-- don't store the same break point twice
case [ nm | (nm, loc) <- oldLocations, loc == brkLoc ] of
case [ nm | (nm, loc) <- oldActiveBreaks, loc == brkLoc ] of
(nm:_) -> return (True, nm)
[] -> do
let oldCounter = breakCounter oldActiveBreaks
let oldCounter = break_ctr st
newCounter = oldCounter + 1
newActiveBreaks =
oldActiveBreaks
{ breakCounter = newCounter
, breakLocations = (oldCounter, brkLoc) : oldLocations
}
setGHCiState $ st { breaks = newActiveBreaks }
setGHCiState $ st { break_ctr = newCounter,
breaks = (oldCounter, brkLoc) : oldActiveBreaks
}
return (False, oldCounter)

newtype GHCi a = GHCi { unGHCi :: IORef GHCiState -> IO a }
Expand Down

0 comments on commit e1b8996

Please sign in to comment.