Optimize dynamic breakpoints for speed
[ghc-hetmet.git] / compiler / ghci / Debugger.hs
1 -----------------------------------------------------------------------------
2 --
3 -- GHCi Interactive debugging commands 
4 --
5 -- Pepe Iborra (supported by Google SoC) 2006
6 --
7 -----------------------------------------------------------------------------
8
9 module Debugger where
10
11 import Linker
12 import Breakpoints
13 import RtClosureInspect
14
15 import PrelNames
16 import HscTypes
17 import IdInfo
18 --import Id
19 import Var hiding ( varName )
20 import VarSet
21 import VarEnv
22 import Name 
23 import NameEnv
24 import RdrName
25 import Module
26 import Finder
27 import UniqSupply
28 import Type
29 import TyCon
30 import DataCon
31 import TcGadt
32 import GHC
33 import GhciMonad
34 import PackageConfig
35
36 import Outputable
37 import Pretty                    ( Mode(..), showDocWith )
38 import ErrUtils
39 import FastString
40 import SrcLoc
41 import Util
42 import Maybes
43
44 import Control.Exception
45 import Control.Monad
46 import qualified Data.Map as Map
47 import Data.Array.Unboxed
48 import Data.Array.Base
49 import Data.List
50 import Data.Typeable             ( Typeable )
51 import Data.Maybe
52 import Data.IORef
53
54 import System.IO
55 import GHC.Exts
56
57 #include "HsVersions.h"
58
59 -------------------------------------
60 -- | The :print & friends commands
61 -------------------------------------
62 pprintClosureCommand :: Bool -> Bool -> String -> GHCi ()
63 pprintClosureCommand bindThings force str = do 
64   cms <- getSession
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   new_ids <- mapM (io . go cms) (catMaybes mb_ids)
70   io$ updateIds cms new_ids
71  where 
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]) 
78
79    -- Do the obtainTerm--bindSuspensions-refineIdType dance
80    -- Warning! This function got a good deal of side-effects
81    go :: Session -> Id -> IO Id
82    go cms id = do
83      Just term <- obtainTerm cms force id
84      term'     <- if not bindThings then return term 
85                    else bindSuspensions cms term                         
86      showterm  <- pprTerm cms term'
87      unqual    <- GHC.getPrintUnqual cms
88      let showSDocForUserOneLine unqual doc = 
89              showDocWith LeftMode (doc (mkErrStyle unqual))
90      (putStrLn . showSDocForUserOneLine unqual) (ppr id <+> char '=' <+> showterm)
91      -- Before leaving, we compare the type obtained to see if it's more specific
92      -- Note how we need the Unknown-clear type returned by obtainTerm
93      let Just reconstructedType = termType term  
94      new_type  <- instantiateTyVarsToUnknown cms 
95                   (mostSpecificType (idType id) reconstructedType)
96      return (setIdType id new_type)
97
98    updateIds :: Session -> [Id] -> IO ()
99    updateIds (Session ref) new_ids = do
100      hsc_env <- readIORef ref
101      let ictxt = hsc_IC hsc_env
102          type_env = ic_type_env ictxt
103          filtered_type_env = delListFromNameEnv type_env (map idName new_ids)
104          new_type_env =  extendTypeEnvWithIds filtered_type_env new_ids
105          new_ic = ictxt {ic_type_env = new_type_env }
106      writeIORef ref (hsc_env {hsc_IC = new_ic })
107
108 isMoreSpecificThan :: Type -> Type -> Bool
109 ty `isMoreSpecificThan` ty1 
110       | Just subst    <- tcUnifyTys bindOnlyTy1 [repType' ty] [repType' ty1] 
111       , substFiltered <- filter (not.isTyVarTy) . varEnvElts . getTvSubstEnv $ subst
112       , not . null $ substFiltered
113       , all (flip notElemTvSubst subst) ty_vars
114       = True
115       | otherwise = False
116       where bindOnlyTy1 tyv | tyv `elem` ty_vars = AvoidMe
117                             | otherwise = BindMe
118             ty_vars = varSetElems$ tyVarsOfType ty
119
120 mostSpecificType ty1 ty2 | ty1 `isMoreSpecificThan` ty2 = ty1
121                          | otherwise = ty2
122
123 -- | Give names, and bind in the interactive environment, to all the suspensions
124 --   included (inductively) in a term
125 bindSuspensions :: Session -> Term -> IO Term
126 bindSuspensions cms@(Session ref) t = do 
127       hsc_env <- readIORef ref
128       inScope <- GHC.getBindings cms
129       let ictxt        = hsc_IC hsc_env
130           rn_env       = ic_rn_local_env ictxt
131           type_env     = ic_type_env ictxt
132           prefix       = "_t"
133           alreadyUsedNames = map (occNameString . nameOccName . getName) inScope
134           availNames   = map ((prefix++) . show) [1..] \\ alreadyUsedNames 
135       availNames_var  <- newIORef availNames
136       (t', stuff)     <- foldTerm (nameSuspensionsAndGetInfos availNames_var) t
137       let (names, tys, hvals) = unzip3 stuff
138       concrete_tys    <- mapM (instantiateTyVarsToUnknown cms) tys
139       let ids = [ mkGlobalId VanillaGlobal name ty vanillaIdInfo
140                   | (name,ty) <- zip names concrete_tys]
141           new_type_env = extendTypeEnvWithIds type_env ids 
142           new_rn_env   = extendLocalRdrEnv rn_env names
143           new_ic       = ictxt { ic_rn_local_env = new_rn_env, 
144                                  ic_type_env     = new_type_env }
145       extendLinkEnv (zip names hvals)
146       writeIORef ref (hsc_env {hsc_IC = new_ic })
147       return t'
148      where    
149
150 --    Processing suspensions. Give names and recopilate info
151         nameSuspensionsAndGetInfos :: IORef [String] -> TermFold (IO (Term, [(Name,Type,HValue)]))
152         nameSuspensionsAndGetInfos freeNames = TermFold 
153                       {
154                         fSuspension = doSuspension freeNames
155                       , fTerm = \ty dc v tt -> do 
156                                     tt' <- sequence tt 
157                                     let (terms,names) = unzip tt' 
158                                     return (Term ty dc v terms, concat names)
159                       , fPrim    = \ty n ->return (Prim ty n,[])
160                       }
161         doSuspension freeNames ct mb_ty hval Nothing = do
162           name <- atomicModifyIORef freeNames (\x->(tail x, head x))
163           n <- newGrimName cms name
164           let ty' = fromMaybe (error "unexpected") mb_ty
165           return (Suspension ct mb_ty hval (Just n), [(n,ty',hval)])
166
167
168 --  A custom Term printer to enable the use of Show instances
169 pprTerm cms@(Session ref) = customPrintTerm customPrint
170  where
171   customPrint = \p-> customPrintShowable : customPrintTermBase p 
172   customPrintShowable t@Term{ty=ty, dc=dc, val=val} = do
173     let hasType = isEmptyVarSet (tyVarsOfType ty)  -- redundant
174         isEvaled = isFullyEvaluatedTerm t
175     if not isEvaled -- || not hasType
176      then return Nothing
177      else do 
178         hsc_env <- readIORef ref
179         dflags  <- GHC.getSessionDynFlags cms
180         do
181            (new_env, bname) <- bindToFreshName hsc_env ty "showme"
182            writeIORef ref (new_env)
183            let noop_log _ _ _ _ = return () 
184                expr = "show " ++ showSDoc (ppr bname)
185            GHC.setSessionDynFlags cms dflags{log_action=noop_log}
186            mb_txt <- withExtendedLinkEnv [(bname, val)] 
187                                          (GHC.compileExpr cms expr)
188            case mb_txt of 
189              Just txt -> return . Just . text . unsafeCoerce# $ txt
190              Nothing  -> return Nothing
191          `finally` do 
192            writeIORef ref hsc_env
193            GHC.setSessionDynFlags cms dflags
194      
195   bindToFreshName hsc_env ty userName = do
196     name <- newGrimName cms userName 
197     let ictxt    = hsc_IC hsc_env
198         rn_env   = ic_rn_local_env ictxt
199         type_env = ic_type_env ictxt
200         id       = mkGlobalId VanillaGlobal name ty vanillaIdInfo
201         new_type_env = extendTypeEnv type_env (AnId id)
202         new_rn_env   = extendLocalRdrEnv rn_env [name]
203         new_ic       = ictxt { ic_rn_local_env = new_rn_env, 
204                                ic_type_env     = new_type_env }
205     return (hsc_env {hsc_IC = new_ic }, name)
206
207 --    Create new uniques and give them sequentially numbered names
208 --    newGrimName :: Session -> String -> IO Name
209 newGrimName cms userName  = do
210     us <- mkSplitUniqSupply 'b'
211     let unique  = uniqFromSupply us
212         occname = mkOccName varName userName
213         name    = mkInternalName unique occname noSrcLoc
214     return name
215
216 ----------------------------------------------------------------------------
217 -- | Replace all the tyvars in a Term with the opaque type GHC.Base.Unknown
218 ----------------------------------------------------------------------------
219 instantiateTyVarsToUnknown :: Session -> Type -> IO Type
220 instantiateTyVarsToUnknown cms ty
221 -- We have a GADT, so just fix its tyvars
222     | Just (tycon, args) <- splitTyConApp_maybe ty
223     , tycon /= funTyCon
224     , isGADT tycon
225     = mapM fixTyVars args >>= return . mkTyConApp tycon
226 -- We have a regular TyCon, so map recursively to its args
227     | Just (tycon, args) <- splitTyConApp_maybe ty
228     , tycon /= funTyCon
229     = do unknownTyVar <- unknownTV
230          args' <- mapM (instantiateTyVarsToUnknown cms) args
231          return$ mkTyConApp tycon args'
232 -- we have a tyvar of kind *
233     | Just tyvar <- getTyVar_maybe ty
234     , ([],_) <- splitKindFunTys (tyVarKind tyvar) 
235     = unknownTV
236 -- we have a higher kind tyvar, so insert an unknown of the appropriate kind
237     | Just tyvar <- getTyVar_maybe ty
238     , (args,_) <- splitKindFunTys (tyVarKind tyvar)
239     = liftM mkTyConTy $ unknownTC !! length args
240 -- Base case
241     | otherwise    = return ty 
242
243  where unknownTV = do 
244          Just (ATyCon unknown_tc) <- lookupName cms unknownTyConName
245          return$ mkTyConTy unknown_tc
246        unknownTC = [undefined, unknownTC1, unknownTC2, unknownTC3]
247        unknownTC1 = do 
248          Just (ATyCon unknown_tc) <- lookupName cms unknown1TyConName
249          return unknown_tc
250        unknownTC2 = do 
251          Just (ATyCon unknown_tc) <- lookupName cms unknown2TyConName
252          return unknown_tc
253        unknownTC3 = do 
254          Just (ATyCon unknown_tc) <- lookupName cms unknown3TyConName
255          return unknown_tc
256 --       isGADT ty | pprTrace' "isGADT" (ppr ty <> colon <> ppr(isGadtSyntaxTyCon ty)) False = undefined
257        isGADT tc | Just dcs <- tyConDataCons_maybe tc = any (not . null . dataConEqSpec) dcs
258                  | otherwise = False
259        fixTyVars ty 
260            | Just (tycon, args) <- splitTyConApp_maybe ty
261            = mapM fixTyVars args >>= return . mkTyConApp tycon
262 -- Fix the tyvar so that the interactive environment doesn't choke on it TODO 
263            | Just tv <- getTyVar_maybe ty = return ty --TODO
264            | otherwise = return ty
265
266 -- | 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
267 stripUnknowns :: [Name] -> Id -> Id
268 stripUnknowns names id = setIdType id . sigmaType . fst . go names . idType 
269                            $ id
270  where 
271    go tyvarsNames@(v:vv) ty 
272     | Just (ty1,ty2) <- splitFunTy_maybe ty = let
273                (ty1',vv') = go tyvarsNames ty1
274                (ty2',vv'')= go vv' ty2
275                in (mkFunTy ty1' ty2', vv'')
276     | Just (ty1,ty2) <- splitAppTy_maybe ty = let
277                (ty1',vv') = go tyvarsNames ty1
278                (ty2',vv'')= go vv' ty2
279                in (mkAppTy ty1' ty2', vv'')
280     | Just (tycon, args) <- splitTyConApp_maybe ty 
281     , Just (tycon', vv') <- (fixTycon tycon tyvarsNames)
282     , (args',vv'') <- foldr (\arg (aa,vv) -> let (arg',vv') = go vv' arg 
283                                              in (arg':aa,vv'))
284                             ([],vv') args
285     = (mkAppTys tycon' args',vv'')
286     | Just (tycon, args) <- splitTyConApp_maybe ty
287     , (args',vv') <- foldr (\arg (aa,vv) -> let (arg',vv') = go vv' arg 
288                                             in (arg':aa,vv'))
289                            ([],tyvarsNames) args
290     = (mkTyConApp tycon args',vv')
291     | otherwise = (ty, tyvarsNames)
292     where  fixTycon tycon (v:vv) = do
293                k <- lookup (tyConName tycon) kinds
294                return (mkTyVarTy$ mkTyVar v k, vv)
295            kinds = [ (unknownTyConName, liftedTypeKind)
296                    , (unknown1TyConName, kind1)
297                    , (unknown2TyConName, kind2)
298                    , (unknown3TyConName, kind3)]
299            kind1 = mkArrowKind liftedTypeKind liftedTypeKind
300            kind2 = mkArrowKind kind1 liftedTypeKind
301            kind3 = mkArrowKind kind2 liftedTypeKind
302 stripUnknowns _ id = id
303
304 -----------------------------
305 -- | The :breakpoint command
306 -----------------------------
307 bkptOptions :: String -> GHCi Bool
308 bkptOptions "continue" = -- We want to quit if in an inferior session
309                          liftM not isTopLevel 
310 bkptOptions "stop" = do
311   inside_break <- liftM not isTopLevel
312   when inside_break $ throwDyn StopChildSession 
313   return False
314
315 bkptOptions cmd = do 
316   dflags <- getDynFlags
317   bt     <- getBkptTable
318   sess   <- getSession
319   bkptOptions' sess (words cmd) bt
320   return False
321    where
322     bkptOptions' _ ["list"] bt = do 
323       let msgs = [ ppr mod <+> colon <+> ppr coords 
324                    | (mod,site) <- btList bt
325                    , let coords = getSiteCoords bt mod site]
326           num_msgs = [parens (int n) <+> msg | (n,msg) <- zip [1..] msgs]
327       msg <- showForUser$ if null num_msgs 
328                             then text "There are no enabled breakpoints"
329                             else vcat num_msgs
330       io$ putStrLn msg
331
332     bkptOptions' s ("add":cmds) bt 
333       | [mod_name,line]<- cmds
334       , [(lineNum,[])] <- reads line
335       =  handleAdd mod_name $ (\mod->addBkptByLine mod lineNum)
336
337       | [mod_name,line,col] <- cmds
338       = handleAdd mod_name $ (\mod->addBkptByCoord mod (read line, read col))
339
340       | otherwise = throwDyn $ CmdLineError $ 
341                        "syntax: :breakpoint add Module line [col]"
342        where 
343          handleAdd mod_name f = do
344            mod         <- io$ GHC.findModule s (GHC.mkModuleName mod_name) Nothing
345            either 
346              (handleBkptEx s mod)
347              (\(newTable, site) -> do
348                setBkptTable newTable 
349                io (putStrLn ("Breakpoint set at " ++ 
350                               show (getSiteCoords newTable mod site))))
351              (f mod bt) 
352
353     bkptOptions' s ("del":cmds) bt 
354       | [i']     <- cmds 
355       , [(i,[])] <- reads i'
356       , bkpts    <- btList bt
357       = if i > length bkpts
358            then throwDyn $ CmdLineError 
359               "Not a valid breakpoint #. Use :breakpoint list to see the current breakpoints."
360            else 
361              let (mod, site) = bkpts !! (i-1)
362              in handleDel mod $ delBkptBySite mod site
363
364       | [fn,line]      <- cmds 
365       , [(lineNum,[])] <- reads line
366       , mod            <- GHC.mkModule mainPackageId (GHC.mkModuleName fn)
367       = handleDel mod $  delBkptByLine mod lineNum
368
369       | [fn,line,col]  <- cmds 
370       , [(lineNum,[])] <- reads line
371       , [(colNum,[])]  <- reads col
372       , mod            <- GHC.mkModule mainPackageId (GHC.mkModuleName fn)
373       = handleDel mod $ delBkptByCoord mod (lineNum, colNum)
374         
375       | otherwise = throwDyn $ CmdLineError $ 
376              "syntax: :breakpoint del (breakpoint # | Module line [col])"
377
378        where delMsg = "Breakpoint deleted"
379              handleDel mod f = either (handleBkptEx s mod)
380                                       (\newtable-> setBkptTable newtable >> io (putStrLn delMsg))
381                                       (f bt)
382                                       
383
384     bkptOptions' _ _ _ = throwDyn $ CmdLineError $ 
385                          "syntax: :breakpoint (list|continue|stop|add|del)"
386
387 -- Error messages
388 --    handleBkptEx :: Session -> Module -> Debugger.BkptException -> a
389     handleBkptEx s m NotHandled  = io$
390                                    findModSummary m                  >>= \mod_summary ->
391                                    isModuleInterpreted s mod_summary >>= \it -> 
392        if it
393         then error$ "Module " ++ showSDoc (ppr m) ++  " was not loaded under debugging mode.\n" 
394                  ++ "Enable debugging mode with -fdebugging (and reload your module)"
395         else error$ "Module " ++ showSDoc (ppr m) ++  " was loaded in compiled (.o) mode.\n" 
396                  ++ "You must load a module in interpreted mode and with -fdebugging on to debug it."
397                      where findModSummary m = getModuleGraph s >>= \mod_graph ->  
398                                case [ modsum | modsum <- mod_graph
399                                              , ms_mod modsum == m ] of
400                                  [modsum] -> return modsum
401     handleBkptEx _ _ e = error (show e)
402
403 -------------------------
404 -- Breakpoint Tables
405 -------------------------
406
407 data BkptTable a  = BkptTable { 
408                            -- | An array of breaks, indexed by site number
409      breakpoints :: Map.Map a (UArray Int Bool)  
410                            -- | A list of lines, each line can have zero or more sites, which are annotated with a column number
411    , sites       :: Map.Map a [[(SiteNumber, Int)]] 
412    }
413                   deriving Show
414
415 sitesOf :: Ord a => BkptTable a -> a -> Maybe [[(SiteNumber, Int)]] 
416 sitesOf bt fn = Map.lookup fn (sites bt)
417 bkptsOf bt fn = Map.lookup fn (breakpoints bt)
418
419
420 data BkptError =
421                     NotHandled  -- Trying to manipulate a element not handled by this BkptTable 
422                   | NoBkptFound
423                   | NotNeeded   -- Used when a breakpoint was already enabled
424   deriving Typeable
425
426 instance Show BkptError where
427   show NoBkptFound = "No suitable breakpoint site found"
428   show NotNeeded  = "Nothing to do"
429   show NotHandled  = "BkptTable: Element not controlled by this table"
430
431 emptyBkptTable :: Ord a => BkptTable a
432 addModule      :: Ord a => a -> [(SiteNumber,Coord)] -> BkptTable a -> BkptTable a
433 -- | Lines start at index 1
434 addBkptByLine  :: Ord a => a -> Int        -> BkptTable a -> Either BkptError (BkptTable a, SiteNumber)
435 addBkptByCoord :: Ord a => a -> Coord      -> BkptTable a -> Either BkptError (BkptTable a, SiteNumber)
436 delBkptByLine  :: Ord a => a -> Int        -> BkptTable a -> Either BkptError (BkptTable a)
437 delBkptBySite  :: Ord a => a -> SiteNumber -> BkptTable a -> Either BkptError (BkptTable a)
438 delBkptByCoord :: Ord a => a -> Coord      -> BkptTable a -> Either BkptError (BkptTable a)
439
440 isBkptEnabled  :: Ord a => BkptTable a -> BkptLocation a -> Bool
441 btElems        :: Ord a => BkptTable a -> [(a, [SiteNumber])]
442 btList         :: Ord a => BkptTable a -> [BkptLocation a]
443 sitesList      :: Ord a => BkptTable a -> [(a, [Coord])]
444 getSiteCoords  :: Ord a => BkptTable a -> a -> SiteNumber -> Coord
445
446 emptyBkptTable = BkptTable Map.empty Map.empty
447
448 addBkptByLine a i bt
449    | Just lines    <- sitesOf bt a
450    , Just bkptsArr <- bkptsOf bt a
451    , i < length lines
452    = case [line | line <- drop i lines, not (null line)] of 
453        ((x:_):_) -> let (siteNum,col) = x
454                         wasAlreadyOn  = bkptsArr ! siteNum
455                         newArr        = bkptsArr // [(siteNum, True)]
456                         newTable      = Map.insert a newArr (breakpoints bt)
457         in if wasAlreadyOn 
458             then Left NotNeeded
459             else Right (bt{breakpoints=newTable}, siteNum)
460        otherwise -> Left NoBkptFound
461
462    | Just sites    <- sitesOf bt a
463    = Left NoBkptFound
464    | otherwise     = Left NotHandled  
465
466 addBkptByCoord a (r,c) bt 
467    | Just lines    <- sitesOf bt a
468    , Just bkptsArr <- bkptsOf bt a
469    , r < length lines
470        = case [ (sn,col) | (sn,col)<-lines!!r, col>=c] of 
471        []    -> Left NoBkptFound
472        (x:_) -> let (siteNum, col) = x
473                     wasAlreadyOn  = bkptsArr ! siteNum
474                     newArr        = bkptsArr // [(siteNum, True)]
475                     newTable      = Map.insert a newArr (breakpoints bt)
476         in if wasAlreadyOn 
477            then Left NotNeeded
478            else Right (bt{breakpoints=newTable}, siteNum)
479
480    | Just sites    <- sitesOf bt a
481    = Left NoBkptFound
482    | otherwise     = Left NotHandled  
483
484 delBkptBySite a i bt 
485    | Just bkptsArr <- bkptsOf bt a
486    , not (inRange (bounds bkptsArr) i)
487    = Left NoBkptFound
488
489    | Just bkptsArr <- bkptsOf bt a
490    , bkptsArr ! i     -- Check that there was a enabled bkpt here 
491    , newArr        <- bkptsArr // [(i,False)] 
492    , newTable      <- Map.insert a newArr (breakpoints bt)
493    = Right bt {breakpoints=newTable}
494
495    | Just sites    <- sitesOf bt a
496    = Left NotNeeded
497
498    | otherwise = Left NotHandled
499
500 delBkptByLine a l bt 
501    | Just sites    <- sitesOf bt a
502    , (site:_)      <- [s | (s,c') <- sites !! l]
503    = delBkptBySite a site bt
504
505    | Just sites    <- sitesOf bt a
506    = Left NoBkptFound
507
508    | otherwise = Left NotHandled
509
510 delBkptByCoord a (r,c) bt 
511    | Just sites    <- sitesOf bt a
512    , (site:_)      <- [s | (s,c') <- sites !! r, c>=c', isBkptEnabled bt (a,s)]
513    = delBkptBySite a site bt
514
515    | Just sites    <- sitesOf bt a
516    = Left NoBkptFound
517
518    | otherwise = Left NotHandled
519
520 btElems bt = [ (a, [i | (i,True) <- assocs siteArr])
521              | (a, siteArr) <- Map.assocs (breakpoints bt) ]
522
523 btList bt =  [(a,site) | (a, sites) <- btElems bt, site <- sites] 
524
525 sitesList bt = [ (a, sitesCoords sitesCols) | (a, sitesCols) <- Map.assocs (sites bt) ]
526     where sitesCoords sitesCols = 
527               [ (row,col) 
528                 | (row, cols) <- zip [0..] sitesCols, (_,col) <- cols ] 
529
530 getSiteCoords bt a site 
531    | Just rows <- sitesOf bt a
532    = head [ (r,c) | (r,row) <- zip [0..] rows
533                   , (s,c)   <- row
534                   , s == site ]
535
536 -- addModule is dumb and inefficient, but it does the job
537 addModule a [] bt = bt {sites = Map.insert a [] (sites bt)}
538 addModule a siteCoords bt 
539    | nrows        <- maximum$ [i | (_,(i,j)) <- siteCoords ]
540    , sitesByRow   <- [ [(s,c) | (s,(r,c)) <- siteCoords, r==i] 
541                        | i <- [0..nrows] ]
542    , nsites       <- length siteCoords
543    , initialBkpts <- listArray (0, nsites+1) (repeat False) 
544    = bt{ sites       = Map.insert a sitesByRow (sites bt) 
545        , breakpoints = Map.insert a initialBkpts (breakpoints bt) }
546
547 -- This MUST be fast
548 isBkptEnabled bt site | bt `seq` site `seq` False = undefined
549 isBkptEnabled bt (a,site) 
550    | Just bkpts <- bkptsOf bt a 
551    = ASSERT (inRange (bounds bkpts) site) 
552      unsafeAt bkpts site
553
554 -----------------
555 -- Other stuff
556 -----------------
557 refreshBkptTable :: Session -> BkptTable Module -> [ModSummary] -> IO (BkptTable Module)
558 refreshBkptTable sess = foldM updIfDebugging
559   where 
560    updIfDebugging bt ms = do
561       isDebugging <- isDebuggingM ms
562       if isDebugging 
563            then addModuleGHC sess bt (GHC.ms_mod ms)
564            else return bt
565    addModuleGHC sess bt mod = do
566       Just mod_info <- GHC.getModuleInfo sess mod
567       dflags <- GHC.getSessionDynFlags sess
568       let sites = GHC.modInfoBkptSites mod_info
569       debugTraceMsg dflags 2 
570                 (ppr mod <> text ": inserted " <> int (length sites) <>
571                  text " breakpoints")
572       return$ addModule mod sites bt
573 #if defined(GHCI) && defined(DEBUGGER)
574    isDebuggingM ms = isModuleInterpreted sess ms >>= \isInterpreted -> 
575                      return (Opt_Debugging `elem` dflags && 
576                              target == HscInterpreted && isInterpreted)
577        where dflags = flags     (GHC.ms_hspp_opts ms)
578              target = hscTarget (GHC.ms_hspp_opts ms)
579 #else
580    isDebuggingM _ = return False
581 #endif