1 -----------------------------------------------------------------------------
3 -- GHCi Interactive debugging commands
5 -- Pepe Iborra (supported by Google SoC) 2006
7 -----------------------------------------------------------------------------
13 import RtClosureInspect
19 import Var hiding ( varName )
37 import Pretty ( Mode(..), showDocWith )
44 import Control.Exception
46 import qualified Data.Map as Map
47 import Data.Array.Unboxed
48 import Data.Array.Base
50 import Data.Typeable ( Typeable )
57 #include "HsVersions.h"
59 -------------------------------------
60 -- | The :print & friends commands
61 -------------------------------------
62 pprintClosureCommand :: Bool -> Bool -> String -> GHCi ()
63 pprintClosureCommand bindThings force str = do
65 newvarsNames <- io$ do
66 uniques <- liftM uniqsFromSupply (mkSplitUniqSupply 'q')
67 return$ map (\u-> (mkSysTvName u (mkFastString "a"))) uniques
68 mb_ids <- io$ mapM (cleanUp cms newvarsNames) (words str)
69 mb_new_ids <- mapM (io . go cms) (catMaybes mb_ids)
70 io$ updateIds cms (catMaybes mb_new_ids)
72 -- Find the Id, clean up 'Unknowns'
73 cleanUp :: Session -> [Name] -> String -> IO (Maybe Id)
74 cleanUp cms newNames str = do
75 tythings <- GHC.parseName cms str >>= mapM (GHC.lookupName cms)
76 return$ listToMaybe (map (stripUnknowns newNames)
77 [ i | Just (AnId i) <- tythings])
79 -- Do the obtainTerm--bindSuspensions-refineIdType dance
80 -- Warning! This function got a good deal of side-effects
81 go :: Session -> Id -> IO (Maybe Id)
83 mb_term <- obtainTerm cms force id
84 maybe (return Nothing) `flip` mb_term $ \term -> do
85 term' <- if not bindThings then return term
86 else bindSuspensions cms term
87 showterm <- pprTerm cms term'
88 unqual <- GHC.getPrintUnqual cms
89 let showSDocForUserOneLine unqual doc =
90 showDocWith LeftMode (doc (mkErrStyle unqual))
91 (putStrLn . showSDocForUserOneLine unqual) (ppr id <+> char '=' <+> showterm)
92 -- Before leaving, we compare the type obtained to see if it's more specific
93 -- Note how we need the Unknown-clear type returned by obtainTerm
94 let Just reconstructedType = termType term
95 new_type <- instantiateTyVarsToUnknown cms
96 (mostSpecificType (idType id) reconstructedType)
97 return . Just $ setIdType id new_type
99 updateIds :: Session -> [Id] -> IO ()
100 updateIds (Session ref) new_ids = do
101 hsc_env <- readIORef ref
102 let ictxt = hsc_IC hsc_env
103 type_env = ic_type_env ictxt
104 filtered_type_env = delListFromNameEnv type_env (map idName new_ids)
105 new_type_env = extendTypeEnvWithIds filtered_type_env new_ids
106 new_ic = ictxt {ic_type_env = new_type_env }
107 writeIORef ref (hsc_env {hsc_IC = new_ic })
109 isMoreSpecificThan :: Type -> Type -> Bool
110 ty `isMoreSpecificThan` ty1
111 | Just subst <- tcUnifyTys bindOnlyTy1 [repType' ty] [repType' ty1]
112 , substFiltered <- filter (not.isTyVarTy) . varEnvElts . getTvSubstEnv $ subst
113 , not . null $ substFiltered
114 , all (flip notElemTvSubst subst) ty_vars
117 where bindOnlyTy1 tyv | tyv `elem` ty_vars = AvoidMe
119 ty_vars = varSetElems$ tyVarsOfType ty
121 mostSpecificType ty1 ty2 | ty1 `isMoreSpecificThan` ty2 = ty1
124 -- | Give names, and bind in the interactive environment, to all the suspensions
125 -- included (inductively) in a term
126 bindSuspensions :: Session -> Term -> IO Term
127 bindSuspensions cms@(Session ref) t = do
128 hsc_env <- readIORef ref
129 inScope <- GHC.getBindings cms
130 let ictxt = hsc_IC hsc_env
131 rn_env = ic_rn_local_env ictxt
132 type_env = ic_type_env ictxt
134 alreadyUsedNames = map (occNameString . nameOccName . getName) inScope
135 availNames = map ((prefix++) . show) [1..] \\ alreadyUsedNames
136 availNames_var <- newIORef availNames
137 (t', stuff) <- foldTerm (nameSuspensionsAndGetInfos availNames_var) t
138 let (names, tys, hvals) = unzip3 stuff
139 concrete_tys <- mapM (instantiateTyVarsToUnknown cms) tys
140 let ids = [ mkGlobalId VanillaGlobal name ty vanillaIdInfo
141 | (name,ty) <- zip names concrete_tys]
142 new_type_env = extendTypeEnvWithIds type_env ids
143 new_rn_env = extendLocalRdrEnv rn_env names
144 new_ic = ictxt { ic_rn_local_env = new_rn_env,
145 ic_type_env = new_type_env }
146 extendLinkEnv (zip names hvals)
147 writeIORef ref (hsc_env {hsc_IC = new_ic })
151 -- Processing suspensions. Give names and recopilate info
152 nameSuspensionsAndGetInfos :: IORef [String] -> TermFold (IO (Term, [(Name,Type,HValue)]))
153 nameSuspensionsAndGetInfos freeNames = TermFold
155 fSuspension = doSuspension freeNames
156 , fTerm = \ty dc v tt -> do
158 let (terms,names) = unzip tt'
159 return (Term ty dc v terms, concat names)
160 , fPrim = \ty n ->return (Prim ty n,[])
162 doSuspension freeNames ct mb_ty hval Nothing = do
163 name <- atomicModifyIORef freeNames (\x->(tail x, head x))
164 n <- newGrimName cms name
165 let ty' = fromMaybe (error "unexpected") mb_ty
166 return (Suspension ct mb_ty hval (Just n), [(n,ty',hval)])
169 -- A custom Term printer to enable the use of Show instances
170 pprTerm cms@(Session ref) = customPrintTerm customPrint
172 customPrint = \p-> customPrintShowable : customPrintTermBase p
173 customPrintShowable t@Term{ty=ty, dc=dc, val=val} = do
174 let hasType = isEmptyVarSet (tyVarsOfType ty) -- redundant
175 isEvaled = isFullyEvaluatedTerm t
176 if not isEvaled -- || not hasType
179 hsc_env <- readIORef ref
180 dflags <- GHC.getSessionDynFlags cms
182 (new_env, bname) <- bindToFreshName hsc_env ty "showme"
183 writeIORef ref (new_env)
184 let noop_log _ _ _ _ = return ()
185 expr = "show " ++ showSDoc (ppr bname)
186 GHC.setSessionDynFlags cms dflags{log_action=noop_log}
187 mb_txt <- withExtendedLinkEnv [(bname, val)]
188 (GHC.compileExpr cms expr)
190 Just txt -> return . Just . text . unsafeCoerce# $ txt
191 Nothing -> return Nothing
193 writeIORef ref hsc_env
194 GHC.setSessionDynFlags cms dflags
196 bindToFreshName hsc_env ty userName = do
197 name <- newGrimName cms userName
198 let ictxt = hsc_IC hsc_env
199 rn_env = ic_rn_local_env ictxt
200 type_env = ic_type_env ictxt
201 id = mkGlobalId VanillaGlobal name ty vanillaIdInfo
202 new_type_env = extendTypeEnv type_env (AnId id)
203 new_rn_env = extendLocalRdrEnv rn_env [name]
204 new_ic = ictxt { ic_rn_local_env = new_rn_env,
205 ic_type_env = new_type_env }
206 return (hsc_env {hsc_IC = new_ic }, name)
208 -- Create new uniques and give them sequentially numbered names
209 -- newGrimName :: Session -> String -> IO Name
210 newGrimName cms userName = do
211 us <- mkSplitUniqSupply 'b'
212 let unique = uniqFromSupply us
213 occname = mkOccName varName userName
214 name = mkInternalName unique occname noSrcLoc
217 ----------------------------------------------------------------------------
218 -- | Replace all the tyvars in a Term with the opaque type GHC.Base.Unknown
219 ----------------------------------------------------------------------------
220 instantiateTyVarsToUnknown :: Session -> Type -> IO Type
221 instantiateTyVarsToUnknown cms ty
222 -- We have a GADT, so just fix its tyvars
223 | Just (tycon, args) <- splitTyConApp_maybe ty
226 = mapM fixTyVars args >>= return . mkTyConApp tycon
227 -- We have a regular TyCon, so map recursively to its args
228 | Just (tycon, args) <- splitTyConApp_maybe ty
230 = do unknownTyVar <- unknownTV
231 args' <- mapM (instantiateTyVarsToUnknown cms) args
232 return$ mkTyConApp tycon args'
233 -- we have a tyvar of kind *
234 | Just tyvar <- getTyVar_maybe ty
235 , ([],_) <- splitKindFunTys (tyVarKind tyvar)
237 -- we have a higher kind tyvar, so insert an unknown of the appropriate kind
238 | Just tyvar <- getTyVar_maybe ty
239 , (args,_) <- splitKindFunTys (tyVarKind tyvar)
240 = liftM mkTyConTy $ unknownTC !! length args
242 | otherwise = return ty
245 Just (ATyCon unknown_tc) <- lookupName cms unknownTyConName
246 return$ mkTyConTy unknown_tc
247 unknownTC = [undefined, unknownTC1, unknownTC2, unknownTC3]
249 Just (ATyCon unknown_tc) <- lookupName cms unknown1TyConName
252 Just (ATyCon unknown_tc) <- lookupName cms unknown2TyConName
255 Just (ATyCon unknown_tc) <- lookupName cms unknown3TyConName
257 -- isGADT ty | pprTrace' "isGADT" (ppr ty <> colon <> ppr(isGadtSyntaxTyCon ty)) False = undefined
258 isGADT tc | Just dcs <- tyConDataCons_maybe tc = any (not . null . dataConEqSpec) dcs
261 | Just (tycon, args) <- splitTyConApp_maybe ty
262 = mapM fixTyVars args >>= return . mkTyConApp tycon
263 -- Fix the tyvar so that the interactive environment doesn't choke on it TODO
264 | Just tv <- getTyVar_maybe ty = return ty --TODO
265 | otherwise = return ty
267 -- | The inverse function. Strip the GHC.Base.Unknowns in the type of the id, they correspond to tyvars. The caller must provide an infinite list of fresh names
268 stripUnknowns :: [Name] -> Id -> Id
269 stripUnknowns names id = setIdType id . fst . go names . idType
272 go tyvarsNames@(v:vv) ty
273 | Just (ty1,ty2) <- splitFunTy_maybe ty = let
274 (ty1',vv') = go tyvarsNames ty1
275 (ty2',vv'')= go vv' ty2
276 in (mkFunTy ty1' ty2', vv'')
277 | Just (ty1,ty2) <- splitAppTy_maybe ty = let
278 (ty1',vv') = go tyvarsNames ty1
279 (ty2',vv'')= go vv' ty2
280 in (mkAppTy ty1' ty2', vv'')
281 | Just (tycon, args) <- splitTyConApp_maybe ty
282 , Just (tycon', vv') <- (fixTycon tycon tyvarsNames)
283 , (args',vv'') <- foldr (\arg (aa,vv) -> let (arg',vv') = go vv' arg
286 = (mkAppTys tycon' args',vv'')
287 | Just (tycon, args) <- splitTyConApp_maybe ty
288 , (args',vv') <- foldr (\arg (aa,vv) -> let (arg',vv') = go vv' arg
290 ([],tyvarsNames) args
291 = (mkTyConApp tycon args',vv')
292 | otherwise = (ty, tyvarsNames)
293 where fixTycon tycon (v:vv) = do
294 k <- lookup (tyConName tycon) kinds
295 return (mkTyVarTy$ mkTyVar v k, vv)
296 kinds = [ (unknownTyConName, liftedTypeKind)
297 , (unknown1TyConName, kind1)
298 , (unknown2TyConName, kind2)
299 , (unknown3TyConName, kind3)]
300 kind1 = mkArrowKind liftedTypeKind liftedTypeKind
301 kind2 = mkArrowKind kind1 liftedTypeKind
302 kind3 = mkArrowKind kind2 liftedTypeKind
303 stripUnknowns _ id = id
305 -----------------------------
306 -- | The :breakpoint command
307 -----------------------------
308 bkptOptions :: String -> GHCi Bool
309 bkptOptions "continue" = -- We want to quit if in an inferior session
311 bkptOptions "stop" = do
312 inside_break <- liftM not isTopLevel
313 when inside_break $ throwDyn StopChildSession
317 dflags <- getDynFlags
320 bkptOptions' sess (words cmd) bt
323 bkptOptions' _ ["list"] bt = do
324 let msgs = [ ppr mod <+> colon <+> ppr coords
325 | (mod,site) <- btList bt
326 , let coords = getSiteCoords bt mod site]
327 num_msgs = [parens (int n) <+> msg | (n,msg) <- zip [1..] msgs]
328 msg <- showForUser$ if null num_msgs
329 then text "There are no enabled breakpoints"
333 bkptOptions' s ("add":cmds) bt
335 , [(lineNum,[])] <- reads line
336 = do (toplevel,_) <- io$ GHC.getContext s
338 (m:_) -> handleAdd (\mod->addBkptByLine mod lineNum) m
339 [] -> throwDyn $ CmdLineError $ "No module loaded in debugging mode"
341 | [mod_name,line]<- cmds
342 , [(lineNum,[])] <- reads line
343 = io(GHC.findModule s (GHC.mkModuleName mod_name) Nothing) >>=
344 handleAdd (\mod->addBkptByLine mod lineNum)
346 | [mod_name,line,col] <- cmds
347 = io(GHC.findModule s (GHC.mkModuleName mod_name) Nothing) >>=
348 handleAdd (\mod->addBkptByCoord mod (read line, read col))
350 | otherwise = throwDyn $ CmdLineError $
351 "syntax: :breakpoint add Module line [col]"
356 (\(newTable, site) -> do
357 setBkptTable newTable
358 let (x,y) = getSiteCoords newTable mod site
359 io (putStrLn ("Breakpoint set at " ++ showSDoc (ppr mod)
360 ++ ':' : show x ++ ':' : show y)))
363 bkptOptions' s ("del":cmds) bt
365 , [(i,[])] <- reads i'
367 = if i > length bkpts
368 then throwDyn $ CmdLineError
369 "Not a valid breakpoint #. Use :breakpoint list to see the current breakpoints."
371 let (mod, site) = bkpts !! (i-1)
372 in handleDel mod $ delBkptBySite mod site
375 , [(lineNum,[])] <- reads line
376 , mod <- GHC.mkModule mainPackageId (GHC.mkModuleName fn)
377 = handleDel mod $ delBkptByLine mod lineNum
379 | [fn,line,col] <- cmds
380 , [(lineNum,[])] <- reads line
381 , [(colNum,[])] <- reads col
382 , mod <- GHC.mkModule mainPackageId (GHC.mkModuleName fn)
383 = handleDel mod $ delBkptByCoord mod (lineNum, colNum)
385 | otherwise = throwDyn $ CmdLineError $
386 "syntax: :breakpoint del (breakpoint # | [Module] line [col])"
388 where delMsg = "Breakpoint deleted"
389 handleDel mod f = either (handleBkptEx s mod)
390 (\newtable-> setBkptTable newtable >> io (putStrLn delMsg))
393 bkptOptions' _ _ _ = throwDyn $ CmdLineError $
394 "syntax: :breakpoint (list|continue|stop|add|del)"
397 -- handleBkptEx :: Session -> Module -> Debugger.BkptException -> a
398 handleBkptEx s m NotHandled = io$ do
399 isInterpreted <- findModSummary m >>= isModuleInterpreted s
401 then error$ "Module " ++ showSDoc (ppr m) ++ " was not loaded under debugging mode.\n"
402 ++ "Enable debugging mode with -fdebugging (and reload your module)"
403 else error$ "Module " ++ showSDoc (ppr m) ++ " was loaded in compiled (.o) mode.\n"
404 ++ "You must load a module in interpreted mode and with -fdebugging on to debug it."
405 where findModSummary m = do
406 mod_graph <- getModuleGraph s
407 return$ head [ modsum | modsum <- mod_graph, ms_mod modsum == m]
408 handleBkptEx _ _ e = error (show e)
410 -------------------------
412 -------------------------
414 data BkptTable a = BkptTable {
415 -- | An array of breaks, indexed by site number
416 breakpoints :: Map.Map a (UArray Int Bool)
417 -- | A list of lines, each line can have zero or more sites, which are annotated with a column number
418 , sites :: Map.Map a [[(SiteNumber, Int)]]
422 sitesOf :: Ord a => BkptTable a -> a -> Maybe [[(SiteNumber, Int)]]
423 sitesOf bt fn = Map.lookup fn (sites bt)
424 bkptsOf bt fn = Map.lookup fn (breakpoints bt)
428 NotHandled -- Trying to manipulate a element not handled by this BkptTable
430 | NotNeeded -- Used when a breakpoint was already enabled
433 instance Show BkptError where
434 show NoBkptFound = "No suitable breakpoint site found"
435 show NotNeeded = "Nothing to do"
436 show NotHandled = "BkptTable: Element not controlled by this table"
438 emptyBkptTable :: Ord a => BkptTable a
439 addModule :: Ord a => a -> [(SiteNumber,Coord)] -> BkptTable a -> BkptTable a
440 -- | Lines start at index 1
441 addBkptByLine :: Ord a => a -> Int -> BkptTable a -> Either BkptError (BkptTable a, SiteNumber)
442 addBkptByCoord :: Ord a => a -> Coord -> BkptTable a -> Either BkptError (BkptTable a, SiteNumber)
443 delBkptByLine :: Ord a => a -> Int -> BkptTable a -> Either BkptError (BkptTable a)
444 delBkptBySite :: Ord a => a -> SiteNumber -> BkptTable a -> Either BkptError (BkptTable a)
445 delBkptByCoord :: Ord a => a -> Coord -> BkptTable a -> Either BkptError (BkptTable a)
447 isBkptEnabled :: Ord a => BkptTable a -> BkptLocation a -> Bool
448 btElems :: Ord a => BkptTable a -> [(a, [SiteNumber])]
449 btList :: Ord a => BkptTable a -> [BkptLocation a]
450 sitesList :: Ord a => BkptTable a -> [(a, [Coord])]
451 getSiteCoords :: Ord a => BkptTable a -> a -> SiteNumber -> Coord
453 emptyBkptTable = BkptTable Map.empty Map.empty
456 | Just lines <- sitesOf bt a
457 , Just bkptsArr <- bkptsOf bt a
459 = case [line | line <- drop i lines, not (null line)] of
460 ((x:_):_) -> let (siteNum,col) = x
461 wasAlreadyOn = bkptsArr ! siteNum
462 newArr = bkptsArr // [(siteNum, True)]
463 newTable = Map.insert a newArr (breakpoints bt)
466 else Right (bt{breakpoints=newTable}, siteNum)
467 otherwise -> Left NoBkptFound
469 | Just sites <- sitesOf bt a
471 | otherwise = Left NotHandled
473 addBkptByCoord a (r,c) bt
474 | Just lines <- sitesOf bt a
475 , Just bkptsArr <- bkptsOf bt a
477 = case [ (sn,col) | (sn,col)<-lines!!r, col>=c] of
478 [] -> Left NoBkptFound
479 (x:_) -> let (siteNum, col) = x
480 wasAlreadyOn = bkptsArr ! siteNum
481 newArr = bkptsArr // [(siteNum, True)]
482 newTable = Map.insert a newArr (breakpoints bt)
485 else Right (bt{breakpoints=newTable}, siteNum)
487 | Just sites <- sitesOf bt a
489 | otherwise = Left NotHandled
492 | Just bkptsArr <- bkptsOf bt a
493 , not (inRange (bounds bkptsArr) i)
496 | Just bkptsArr <- bkptsOf bt a
497 , bkptsArr ! i -- Check that there was a enabled bkpt here
498 , newArr <- bkptsArr // [(i,False)]
499 , newTable <- Map.insert a newArr (breakpoints bt)
500 = Right bt {breakpoints=newTable}
502 | Just sites <- sitesOf bt a
505 | otherwise = Left NotHandled
508 | Just sites <- sitesOf bt a
509 , (site:_) <- [s | (s,c') <- sites !! l]
510 = delBkptBySite a site bt
512 | Just sites <- sitesOf bt a
515 | otherwise = Left NotHandled
517 delBkptByCoord a (r,c) bt
518 | Just sites <- sitesOf bt a
519 , (site:_) <- [s | (s,c') <- sites !! r, c>=c', isBkptEnabled bt (a,s)]
520 = delBkptBySite a site bt
522 | Just sites <- sitesOf bt a
525 | otherwise = Left NotHandled
527 btElems bt = [ (a, [i | (i,True) <- assocs siteArr])
528 | (a, siteArr) <- Map.assocs (breakpoints bt) ]
530 btList bt = [(a,site) | (a, sites) <- btElems bt, site <- sites]
532 sitesList bt = [ (a, sitesCoords sitesCols) | (a, sitesCols) <- Map.assocs (sites bt) ]
533 where sitesCoords sitesCols =
535 | (row, cols) <- zip [0..] sitesCols, (_,col) <- cols ]
537 getSiteCoords bt a site
538 | Just rows <- sitesOf bt a
539 = head [ (r,c) | (r,row) <- zip [0..] rows
543 -- addModule is dumb and inefficient, but it does the job
544 addModule a [] bt = bt {sites = Map.insert a [] (sites bt)}
545 addModule a siteCoords bt
546 | nrows <- maximum$ [i | (_,(i,j)) <- siteCoords ]
547 , sitesByRow <- [ [(s,c) | (s,(r,c)) <- siteCoords, r==i]
549 , nsites <- length siteCoords
550 , initialBkpts <- listArray (0, nsites+1) (repeat False)
551 = bt{ sites = Map.insert a sitesByRow (sites bt)
552 , breakpoints = Map.insert a initialBkpts (breakpoints bt) }
555 isBkptEnabled bt site | bt `seq` site `seq` False = undefined
556 isBkptEnabled bt (a,site)
557 | Just bkpts <- bkptsOf bt a
558 = ASSERT (inRange (bounds bkpts) site)
564 refreshBkptTable :: Session -> BkptTable Module -> [ModSummary] -> IO (BkptTable Module)
565 refreshBkptTable sess = foldM updIfDebugging
567 updIfDebugging bt ms = do
568 isDebugging <- isDebuggingM ms
570 then addModuleGHC sess bt (GHC.ms_mod ms)
572 addModuleGHC sess bt mod = do
573 Just mod_info <- GHC.getModuleInfo sess mod
574 dflags <- GHC.getSessionDynFlags sess
575 let sites = GHC.modInfoBkptSites mod_info
576 debugTraceMsg dflags 2
577 (ppr mod <> text ": inserted " <> int (length sites) <>
579 return$ addModule mod sites bt
580 #if defined(GHCI) && defined(DEBUGGER)
581 isDebuggingM ms = isModuleInterpreted sess ms >>= \isInterpreted ->
582 return (Opt_Debugging `elem` dflags &&
583 target == HscInterpreted && isInterpreted)
584 where dflags = flags (GHC.ms_hspp_opts ms)
585 target = hscTarget (GHC.ms_hspp_opts ms)
587 isDebuggingM _ = return False