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
49 import Data.Typeable ( Typeable )
56 #include "HsVersions.h"
58 -------------------------------------
59 -- | The :print & friends commands
60 -------------------------------------
61 pprintClosureCommand :: Bool -> Bool -> String -> GHCi ()
62 pprintClosureCommand bindThings force str = do
64 newvarsNames <- io$ do
65 uniques <- liftM uniqsFromSupply (mkSplitUniqSupply 'q')
66 return$ map (\u-> (mkSysTvName u (mkFastString "a"))) uniques
67 mb_ids <- io$ mapM (cleanUp cms newvarsNames) (words str)
68 new_ids <- mapM (io . go cms) (catMaybes mb_ids)
69 io$ updateIds cms new_ids
71 -- Find the Id, clean up 'Unknowns'
72 cleanUp :: Session -> [Name] -> String -> IO (Maybe Id)
73 cleanUp cms newNames str = do
74 tythings <- GHC.parseName cms str >>= mapM (GHC.lookupName cms)
75 return$ listToMaybe (map (stripUnknowns newNames)
76 [ i | Just (AnId i) <- tythings])
78 -- Do the obtainTerm--bindSuspensions-refineIdType dance
79 -- Warning! This function got a good deal of side-effects
80 go :: Session -> Id -> IO Id
82 Just term <- obtainTerm cms force id
83 term' <- if not bindThings then return term
84 else bindSuspensions cms term
85 showterm <- pprTerm cms term'
86 unqual <- GHC.getPrintUnqual cms
87 let showSDocForUserOneLine unqual doc =
88 showDocWith LeftMode (doc (mkErrStyle unqual))
89 (putStrLn . showSDocForUserOneLine unqual) (ppr id <+> char '=' <+> showterm)
90 -- Before leaving, we compare the type obtained to see if it's more specific
91 -- Note how we need the Unknown-clear type returned by obtainTerm
92 let Just reconstructedType = termType term
93 new_type <- instantiateTyVarsToUnknown cms
94 (mostSpecificType (idType id) reconstructedType)
95 return (setIdType id new_type)
97 updateIds :: Session -> [Id] -> IO ()
98 updateIds (Session ref) new_ids = do
99 hsc_env <- readIORef ref
100 let ictxt = hsc_IC hsc_env
101 type_env = ic_type_env ictxt
102 filtered_type_env = delListFromNameEnv type_env (map idName new_ids)
103 new_type_env = extendTypeEnvWithIds filtered_type_env new_ids
104 new_ic = ictxt {ic_type_env = new_type_env }
105 writeIORef ref (hsc_env {hsc_IC = new_ic })
107 isMoreSpecificThan :: Type -> Type -> Bool
108 ty `isMoreSpecificThan` ty1
109 | Just subst <- tcUnifyTys bindOnlyTy1 [repType' ty] [repType' ty1]
110 , substFiltered <- filter (not.isTyVarTy) . varEnvElts . getTvSubstEnv $ subst
111 , not . null $ substFiltered
112 , all (flip notElemTvSubst subst) ty_vars
115 where bindOnlyTy1 tyv | tyv `elem` ty_vars = AvoidMe
117 ty_vars = varSetElems$ tyVarsOfType ty
119 mostSpecificType ty1 ty2 | ty1 `isMoreSpecificThan` ty2 = ty1
122 -- | Give names, and bind in the interactive environment, to all the suspensions
123 -- included (inductively) in a term
124 bindSuspensions :: Session -> Term -> IO Term
125 bindSuspensions cms@(Session ref) t = do
126 hsc_env <- readIORef ref
127 inScope <- GHC.getBindings cms
128 let ictxt = hsc_IC hsc_env
129 rn_env = ic_rn_local_env ictxt
130 type_env = ic_type_env ictxt
132 alreadyUsedNames = map (occNameString . nameOccName . getName) inScope
133 availNames = map ((prefix++) . show) [1..] \\ alreadyUsedNames
134 availNames_var <- newIORef availNames
135 (t', stuff) <- foldTerm (nameSuspensionsAndGetInfos availNames_var) t
136 let (names, tys, hvals) = unzip3 stuff
137 concrete_tys <- mapM (instantiateTyVarsToUnknown cms) tys
138 let ids = [ mkGlobalId VanillaGlobal name ty vanillaIdInfo
139 | (name,ty) <- zip names concrete_tys]
140 new_type_env = extendTypeEnvWithIds type_env ids
141 new_rn_env = extendLocalRdrEnv rn_env names
142 new_ic = ictxt { ic_rn_local_env = new_rn_env,
143 ic_type_env = new_type_env }
144 extendLinkEnv (zip names hvals)
145 writeIORef ref (hsc_env {hsc_IC = new_ic })
149 -- Processing suspensions. Give names and recopilate info
150 nameSuspensionsAndGetInfos :: IORef [String] -> TermFold (IO (Term, [(Name,Type,HValue)]))
151 nameSuspensionsAndGetInfos freeNames = TermFold
153 fSuspension = doSuspension freeNames
154 , fTerm = \ty dc v tt -> do
156 let (terms,names) = unzip tt'
157 return (Term ty dc v terms, concat names)
158 , fPrim = \ty n ->return (Prim ty n,[])
160 doSuspension freeNames ct mb_ty hval Nothing = do
161 name <- atomicModifyIORef freeNames (\x->(tail x, head x))
162 n <- newGrimName cms name
163 let ty' = fromMaybe (error "unexpected") mb_ty
164 return (Suspension ct mb_ty hval (Just n), [(n,ty',hval)])
167 -- A custom Term printer to enable the use of Show instances
168 pprTerm cms@(Session ref) = customPrintTerm customPrint
170 customPrint = \p-> customPrintShowable : customPrintTermBase p
171 customPrintShowable t@Term{ty=ty, dc=dc, val=val} = do
172 let hasType = isEmptyVarSet (tyVarsOfType ty) -- redundant
173 isEvaled = isFullyEvaluatedTerm t
174 if not isEvaled -- || not hasType
177 hsc_env <- readIORef ref
178 dflags <- GHC.getSessionDynFlags cms
180 (new_env, bname) <- bindToFreshName hsc_env ty "showme"
181 writeIORef ref (new_env)
182 let noop_log _ _ _ _ = return ()
183 expr = "show " ++ showSDoc (ppr bname)
184 GHC.setSessionDynFlags cms dflags{log_action=noop_log}
185 mb_txt <- withExtendedLinkEnv [(bname, val)]
186 (GHC.compileExpr cms expr)
188 Just txt -> return . Just . text . unsafeCoerce# $ txt
189 Nothing -> return Nothing
191 writeIORef ref hsc_env
192 GHC.setSessionDynFlags cms dflags
194 bindToFreshName hsc_env ty userName = do
195 name <- newGrimName cms userName
196 let ictxt = hsc_IC hsc_env
197 rn_env = ic_rn_local_env ictxt
198 type_env = ic_type_env ictxt
199 id = mkGlobalId VanillaGlobal name ty vanillaIdInfo
200 new_type_env = extendTypeEnv type_env (AnId id)
201 new_rn_env = extendLocalRdrEnv rn_env [name]
202 new_ic = ictxt { ic_rn_local_env = new_rn_env,
203 ic_type_env = new_type_env }
204 return (hsc_env {hsc_IC = new_ic }, name)
206 -- Create new uniques and give them sequentially numbered names
207 -- newGrimName :: Session -> String -> IO Name
208 newGrimName cms userName = do
209 us <- mkSplitUniqSupply 'b'
210 let unique = uniqFromSupply us
211 occname = mkOccName varName userName
212 name = mkInternalName unique occname noSrcLoc
215 ----------------------------------------------------------------------------
216 -- | Replace all the tyvars in a Term with the opaque type GHC.Base.Unknown
217 ----------------------------------------------------------------------------
218 instantiateTyVarsToUnknown :: Session -> Type -> IO Type
219 instantiateTyVarsToUnknown cms ty
220 -- We have a GADT, so just fix its tyvars
221 | Just (tycon, args) <- splitTyConApp_maybe ty
224 = mapM fixTyVars args >>= return . mkTyConApp tycon
225 -- We have a regular TyCon, so map recursively to its args
226 | Just (tycon, args) <- splitTyConApp_maybe ty
228 = do unknownTyVar <- unknownTV
229 args' <- mapM (instantiateTyVarsToUnknown cms) args
230 return$ mkTyConApp tycon args'
231 -- we have a tyvar of kind *
232 | Just tyvar <- getTyVar_maybe ty
233 , ([],_) <- splitKindFunTys (tyVarKind tyvar)
235 -- we have a higher kind tyvar, so insert an unknown of the appropriate kind
236 | Just tyvar <- getTyVar_maybe ty
237 , (args,_) <- splitKindFunTys (tyVarKind tyvar)
238 = liftM mkTyConTy $ unknownTC !! length args
240 | otherwise = return ty
243 Just (ATyCon unknown_tc) <- lookupName cms unknownTyConName
244 return$ mkTyConTy unknown_tc
245 unknownTC = [undefined, unknownTC1, unknownTC2, unknownTC3]
247 Just (ATyCon unknown_tc) <- lookupName cms unknown1TyConName
250 Just (ATyCon unknown_tc) <- lookupName cms unknown2TyConName
253 Just (ATyCon unknown_tc) <- lookupName cms unknown3TyConName
255 -- isGADT ty | pprTrace' "isGADT" (ppr ty <> colon <> ppr(isGadtSyntaxTyCon ty)) False = undefined
256 isGADT tc | Just dcs <- tyConDataCons_maybe tc = any (not . null . dataConEqSpec) dcs
259 | Just (tycon, args) <- splitTyConApp_maybe ty
260 = mapM fixTyVars args >>= return . mkTyConApp tycon
261 -- Fix the tyvar so that the interactive environment doesn't choke on it TODO
262 | Just tv <- getTyVar_maybe ty = return ty --TODO
263 | otherwise = return ty
265 -- | 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
266 stripUnknowns :: [Name] -> Id -> Id
267 stripUnknowns names id = setIdType id . sigmaType . fst . go names . idType
270 go tyvarsNames@(v:vv) ty
271 | Just (ty1,ty2) <- splitFunTy_maybe ty = let
272 (ty1',vv') = go tyvarsNames ty1
273 (ty2',vv'')= go vv' ty2
274 in (mkFunTy ty1' ty2', vv'')
275 | Just (ty1,ty2) <- splitAppTy_maybe ty = let
276 (ty1',vv') = go tyvarsNames ty1
277 (ty2',vv'')= go vv' ty2
278 in (mkAppTy ty1' ty2', vv'')
279 | Just (tycon, args) <- splitTyConApp_maybe ty
280 , Just (tycon', vv') <- (fixTycon tycon tyvarsNames)
281 , (args',vv'') <- foldr (\arg (aa,vv) -> let (arg',vv') = go vv' arg
284 = (mkAppTys tycon' args',vv'')
285 | Just (tycon, args) <- splitTyConApp_maybe ty
286 , (args',vv') <- foldr (\arg (aa,vv) -> let (arg',vv') = go vv' arg
288 ([],tyvarsNames) args
289 = (mkTyConApp tycon args',vv')
290 | otherwise = (ty, tyvarsNames)
291 where fixTycon tycon (v:vv) = do
292 k <- lookup (tyConName tycon) kinds
293 return (mkTyVarTy$ mkTyVar v k, vv)
294 kinds = [ (unknownTyConName, liftedTypeKind)
295 , (unknown1TyConName, kind1)
296 , (unknown2TyConName, kind2)
297 , (unknown3TyConName, kind3)]
298 kind1 = mkArrowKind liftedTypeKind liftedTypeKind
299 kind2 = mkArrowKind kind1 liftedTypeKind
300 kind3 = mkArrowKind kind2 liftedTypeKind
301 stripUnknowns _ id = id
303 -----------------------------
304 -- | The :breakpoint command
305 -----------------------------
306 bkptOptions :: String -> GHCi Bool
307 bkptOptions "continue" = -- We want to quit if in an inferior session
309 bkptOptions "stop" = do
310 inside_break <- liftM not isTopLevel
311 when inside_break $ throwDyn StopChildSession
315 dflags <- getDynFlags
318 bkptOptions' sess (words cmd) bt
321 bkptOptions' _ ["list"] bt = do
322 let msgs = [ ppr mod <+> colon <+> ppr coords
323 | (mod,site) <- btList bt
324 , let coords = getSiteCoords bt mod site]
325 num_msgs = [parens (int n) <+> msg | (n,msg) <- zip [1..] msgs]
326 msg <- showForUser$ if null num_msgs
327 then text "There are no enabled breakpoints"
331 bkptOptions' s ("add":cmds) bt
332 | [mod_name,line]<- cmds
333 , [(lineNum,[])] <- reads line
334 = handleAdd mod_name $ (\mod->addBkptByLine mod lineNum)
336 | [mod_name,line,col] <- cmds
337 = handleAdd mod_name $ (\mod->addBkptByCoord mod (read line, read col))
339 | otherwise = throwDyn $ CmdLineError $
340 "syntax: :breakpoint add Module line [col]"
342 handleAdd mod_name f = do
343 mod <- io$ GHC.findModule s (GHC.mkModuleName mod_name) Nothing
346 (\(newTable, site) -> do
347 setBkptTable newTable
348 io (putStrLn ("Breakpoint set at " ++
349 show (getSiteCoords newTable mod site))))
352 bkptOptions' s ("del":cmds) bt
354 , [(i,[])] <- reads i'
356 = if i > length bkpts
357 then throwDyn $ CmdLineError
358 "Not a valid breakpoint #. Use :breakpoint list to see the current breakpoints."
360 let (mod, site) = bkpts !! (i-1)
361 in handleDel mod $ delBkptBySite mod site
364 , [(lineNum,[])] <- reads line
365 , mod <- GHC.mkModule mainPackageId (GHC.mkModuleName fn)
366 = handleDel mod $ delBkptByLine mod lineNum
368 | [fn,line,col] <- cmds
369 , [(lineNum,[])] <- reads line
370 , [(colNum,[])] <- reads col
371 , mod <- GHC.mkModule mainPackageId (GHC.mkModuleName fn)
372 = handleDel mod $ delBkptByCoord mod (lineNum, colNum)
374 | otherwise = throwDyn $ CmdLineError $
375 "syntax: :breakpoint del (breakpoint # | Module line [col])"
377 where delMsg = "Breakpoint deleted"
378 handleDel mod f = either (handleBkptEx s mod)
379 (\newtable-> setBkptTable newtable >> io (putStrLn delMsg))
383 bkptOptions' _ _ _ = throwDyn $ CmdLineError $
384 "syntax: :breakpoint (list|continue|stop|add|del)"
387 -- handleBkptEx :: Session -> Module -> Debugger.BkptException -> a
388 handleBkptEx s m NotHandled = io$
389 findModSummary m >>= \mod_summary ->
390 isModuleInterpreted s mod_summary >>= \it ->
392 then error$ "Module " ++ showSDoc (ppr m) ++ " was not loaded under debugging mode.\n"
393 ++ "Enable debugging mode with -fdebugging (and reload your module)"
394 else error$ "Module " ++ showSDoc (ppr m) ++ " was loaded in compiled (.o) mode.\n"
395 ++ "You must load a module in interpreted mode and with -fdebugging on to debug it."
396 where findModSummary m = getModuleGraph s >>= \mod_graph ->
397 case [ modsum | modsum <- mod_graph
398 , ms_mod modsum == m ] of
399 [modsum] -> return modsum
400 handleBkptEx _ _ e = error (show e)
402 -------------------------
404 -------------------------
406 data BkptTable a = BkptTable {
407 -- | An array of breaks, indexed by site number
408 breakpoints :: Map.Map a (UArray Int Bool)
409 -- | A list of lines, each line can have zero or more sites, which are annotated with a column number
410 , sites :: Map.Map a [[(SiteNumber, Int)]]
414 sitesOf :: Ord a => BkptTable a -> a -> Maybe [[(SiteNumber, Int)]]
415 sitesOf bt fn = Map.lookup fn (sites bt)
416 bkptsOf bt fn = Map.lookup fn (breakpoints bt)
420 NotHandled -- Trying to manipulate a element not handled by this BkptTable
422 | NotNeeded -- Used when a breakpoint was already enabled
425 instance Show BkptError where
426 show NoBkptFound = "No suitable breakpoint site found"
427 show NotNeeded = "Nothing to do"
428 show NotHandled = "BkptTable: Element not controlled by this table"
430 emptyBkptTable :: Ord a => BkptTable a
431 addModule :: Ord a => a -> [(SiteNumber,Coord)] -> BkptTable a -> BkptTable a
432 -- | Lines start at index 1
433 addBkptByLine :: Ord a => a -> Int -> BkptTable a -> Either BkptError (BkptTable a, SiteNumber)
434 addBkptByCoord :: Ord a => a -> Coord -> BkptTable a -> Either BkptError (BkptTable a, SiteNumber)
435 delBkptByLine :: Ord a => a -> Int -> BkptTable a -> Either BkptError (BkptTable a)
436 delBkptBySite :: Ord a => a -> SiteNumber -> BkptTable a -> Either BkptError (BkptTable a)
437 delBkptByCoord :: Ord a => a -> Coord -> BkptTable a -> Either BkptError (BkptTable a)
439 isBkptEnabled :: Ord a => BkptTable a -> BkptLocation a -> Bool
440 btElems :: Ord a => BkptTable a -> [(a, [SiteNumber])]
441 btList :: Ord a => BkptTable a -> [BkptLocation a]
442 sitesList :: Ord a => BkptTable a -> [(a, [Coord])]
443 getSiteCoords :: Ord a => BkptTable a -> a -> SiteNumber -> Coord
445 emptyBkptTable = BkptTable Map.empty Map.empty
448 | Just lines <- sitesOf bt a
449 , Just bkptsArr <- bkptsOf bt a
451 = case [line | line <- drop i lines, not (null line)] of
452 ((x:_):_) -> let (siteNum,col) = x
453 wasAlreadyOn = bkptsArr ! siteNum
454 newArr = bkptsArr // [(siteNum, True)]
455 newTable = Map.insert a newArr (breakpoints bt)
458 else Right (bt{breakpoints=newTable}, siteNum)
459 otherwise -> Left NoBkptFound
461 | Just sites <- sitesOf bt a
463 | otherwise = Left NotHandled
465 addBkptByCoord a (r,c) bt
466 | Just lines <- sitesOf bt a
467 , Just bkptsArr <- bkptsOf bt a
469 = case [ (sn,col) | (sn,col)<-lines!!r, col>=c] of
470 [] -> Left NoBkptFound
471 (x:_) -> let (siteNum, col) = x
472 wasAlreadyOn = bkptsArr ! siteNum
473 newArr = bkptsArr // [(siteNum, True)]
474 newTable = Map.insert a newArr (breakpoints bt)
477 else Right (bt{breakpoints=newTable}, siteNum)
479 | Just sites <- sitesOf bt a
481 | otherwise = Left NotHandled
484 | Just bkptsArr <- bkptsOf bt a
485 , not (inRange (bounds bkptsArr) i)
488 | Just bkptsArr <- bkptsOf bt a
489 , bkptsArr ! i -- Check that there was a enabled bkpt here
490 , newArr <- bkptsArr // [(i,False)]
491 , newTable <- Map.insert a newArr (breakpoints bt)
492 = Right bt {breakpoints=newTable}
494 | Just sites <- sitesOf bt a
497 | otherwise = Left NotHandled
500 | Just sites <- sitesOf bt a
501 , (site:_) <- [s | (s,c') <- sites !! l]
502 = delBkptBySite a site bt
504 | Just sites <- sitesOf bt a
507 | otherwise = Left NotHandled
509 delBkptByCoord a (r,c) bt
510 | Just sites <- sitesOf bt a
511 , (site:_) <- [s | (s,c') <- sites !! r, c>=c', isBkptEnabled bt (a,s)]
512 = delBkptBySite a site bt
514 | Just sites <- sitesOf bt a
517 | otherwise = Left NotHandled
519 btElems bt = [ (a, [i | (i,True) <- assocs siteArr])
520 | (a, siteArr) <- Map.assocs (breakpoints bt) ]
522 btList bt = [(a,site) | (a, sites) <- btElems bt, site <- sites]
524 sitesList bt = [ (a, sitesCoords sitesCols) | (a, sitesCols) <- Map.assocs (sites bt) ]
525 where sitesCoords sitesCols =
527 | (row, cols) <- zip [0..] sitesCols, (_,col) <- cols ]
529 getSiteCoords bt a site
530 | Just rows <- sitesOf bt a
531 = head [ (r,c) | (r,row) <- zip [0..] rows
535 -- addModule is dumb and inefficient, but it does the job
536 addModule a [] bt = bt {sites = Map.insert a [] (sites bt)}
537 addModule a siteCoords bt
538 | nrows <- maximum$ [i | (_,(i,j)) <- siteCoords ]
539 , sitesByRow <- [ [(s,c) | (s,(r,c)) <- siteCoords, r==i]
541 , nsites <- length siteCoords
542 , initialBkpts <- listArray (1, nsites) (repeat False)
543 = bt{ sites = Map.insert a sitesByRow (sites bt)
544 , breakpoints = Map.insert a initialBkpts (breakpoints bt) }
546 isBkptEnabled bt (a,site)
547 | Just bkpts <- bkptsOf bt a
548 , inRange (bounds bkpts) site
550 | otherwise = panic "unexpected condition: I don't know that breakpoint site"
555 refreshBkptTable :: Session -> BkptTable Module -> [ModSummary] -> IO (BkptTable Module)
556 refreshBkptTable sess = foldM updIfDebugging
558 updIfDebugging bt ms = do
559 isDebugging <- isDebuggingM ms
561 then addModuleGHC sess bt (GHC.ms_mod ms)
563 addModuleGHC sess bt mod = do
564 Just mod_info <- GHC.getModuleInfo sess mod
565 dflags <- GHC.getSessionDynFlags sess
566 let sites = GHC.modInfoBkptSites mod_info
567 debugTraceMsg dflags 2
568 (ppr mod <> text ": inserted " <> int (length sites) <>
570 return$ addModule mod sites bt
571 #if defined(GHCI) && defined(DEBUGGER)
572 isDebuggingM ms = isModuleInterpreted sess ms >>= \isInterpreted ->
573 return (Opt_Debugging `elem` dflags &&
574 target == HscInterpreted && isInterpreted)
575 where dflags = flags (GHC.ms_hspp_opts ms)
576 target = hscTarget (GHC.ms_hspp_opts ms)
578 isDebuggingM _ = return False