1 {-# LANGUAGE MagicHash, UnboxedTuples, CPP, ForeignFunctionInterface, GHCForeignImportPrim, UnliftedFFITypes, BangPatterns, RecordWildCards, DeriveFunctor, DeriveFoldable, DeriveTraversable, PatternGuards #-}
4 Copyright : (c) 2012 Joachim Breitner
6 Maintainer : Joachim Breitner <mail@joachim-breitner.de>
8 With this module, you can investigate the heap representation of Haskell
9 values, i.e. to investigate sharing and lazy evaluation.
21 -- * Reading from the heap
54 import GHC.Exts ( Any,
55 Ptr(..), Addr#, Int(..), Word(..), Word#, Int#,
56 ByteArray#, Array#, sizeofByteArray#, sizeofArray#, indexArray#, indexWordArray#,
59 import GHC.Arr (Array(..))
61 import GHC.Constants ( wORD_SIZE, tAG_MASK, wORD_SIZE_IN_BITS )
63 import System.IO.Unsafe ( unsafePerformIO )
65 import Foreign hiding ( unsafePerformIO )
66 import Numeric ( showHex )
69 import Data.Maybe ( isJust, catMaybes )
70 import Data.Tuple ( swap )
71 import System.Mem.Weak
74 import Data.Foldable ( Foldable )
75 import Data.Traversable ( Traversable )
76 import qualified Data.Traversable as T
77 import qualified Data.IntMap as M
79 import Control.Monad.Trans.State
80 import Control.Monad.Trans.Class
81 import Control.Monad.IO.Class
82 import Control.Monad.Trans.Writer.Strict
84 #include "ghcautoconf.h"
86 -- | An arbitrarily Haskell value in a safe Box. The point is that even
87 -- unevaluated thunks can safely be moved around inside the Box, and when
88 -- required, e.g. in 'getBoxedClosureData', the function knows how far it has
89 -- to evalue the argument.
92 #if SIZEOF_VOID_P == 8
93 type HalfWord = Word32
95 type HalfWord = Word16
98 instance Show Box where
99 -- From libraries/base/GHC/Ptr.lhs
100 showsPrec _ (Box a) rs =
101 -- unsafePerformIO (print "↓" >> pClosure a) `seq`
102 pad_out (showHex addr "") ++ (if tag>0 then "/" ++ show tag else "") ++ rs
104 ptr = W# (aToWord# a)
105 tag = ptr .&. fromIntegral tAG_MASK -- ((1 `shiftL` TAG_BITS) -1)
107 -- want 0s prefixed to pad it out to a fixed length.
109 '0':'x':(replicate (2*wORD_SIZE - length ls) '0') ++ ls
111 instance Eq Box where
112 Box a == Box b = case reallyUnsafePtrEqualityUpToTag# a b of
117 This takes an arbitrary value and puts it into a box. Note that calls like
121 will put the thunk \"head list\" into the box, /not/ the element at the head
122 of the list. For that, use careful case expressions:
124 > case list of x:_ -> asBox x
127 asBox x = Box (unsafeCoerce# x)
130 StgInfoTable parsing derived from ByteCodeItbls.lhs
131 Removed the code parameter for now
132 Replaced Type by an enumeration
133 Remove stuff dependent on GHCI_TABLES_NEXT_TO_CODE
136 {-| This is a somewhat faithful representation of an info table. See
137 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/InfoTables.h>
138 for more details on this data structure. Note that the 'Storable' instance
139 provided here does _not_ support writing.
141 data StgInfoTable = StgInfoTable {
149 instance Storable StgInfoTable where
156 sizeOf (undefined :: HalfWord),
164 = error "Storable StgInfoTable is read-only"
167 = flip (evalStateT) (castPtr a0)
177 tipe = toEnum (fromIntegral (tipe'::HalfWord)),
181 fieldSz :: (Storable a, Storable b) => (a -> b) -> a -> Int
182 fieldSz sel x = sizeOf (sel x)
184 load :: Storable a => PtrIO a
185 load = do addr <- advance
188 type PtrIO = StateT (Ptr Word8) IO
190 advance :: Storable a => PtrIO (Ptr a)
191 advance = StateT adv where
192 adv addr = case castPtr addr of { addrCast -> return
193 (addrCast, addr `plusPtr` sizeOfPointee addrCast) }
195 sizeOfPointee :: (Storable a) => Ptr a -> Int
196 sizeOfPointee addr = sizeOf (typeHack addr)
197 where typeHack = undefined :: Ptr a -> a
200 Data Type representing Closures
204 {-| A closure type enumeration, in order matching the actual value on the heap.
205 Needs to be synchronized with
206 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/ClosureTypes.h>
217 | CONSTR_NOCAF_STATIC
256 | MUT_ARR_PTRS_FROZEN0
257 | MUT_ARR_PTRS_FROZEN
270 deriving (Show, Eq, Enum, Ord)
272 {-| This is the main data type of this module, representing a Haskell value on
273 the heap. This reflects
274 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/Closures.h>
276 The data type is parametrized by the type to store references in, which
277 should be either 'Box' or 'WeakBox', with appropriate type synonyms 'Closure'
306 -- In GHCi, if Linker.h would allow a reverse looup, we could for exported
307 -- functions fun actually find the name here.
308 -- At least the other direction works via "lookupSymbol
309 -- base_GHCziBase_zpzp_closure" and yields the same address (up to tags)
348 -- Card table ignored
365 BlockingQueueClosure {
380 deriving (Show, Functor, Foldable, Traversable)
383 type Closure = GenClosure Box
385 -- | For generic code, this function returns all referenced closures.
386 allPtrs :: GenClosure b -> [b]
387 allPtrs (ConsClosure {..}) = ptrArgs
388 allPtrs (ThunkClosure {..}) = ptrArgs
389 allPtrs (SelectorClosure {..}) = [selectee]
390 allPtrs (IndClosure {..}) = [indirectee]
391 allPtrs (BlackholeClosure {..}) = [indirectee]
392 allPtrs (APClosure {..}) = fun:payload
393 allPtrs (PAPClosure {..}) = fun:payload
394 allPtrs (APStackClosure {..}) = fun:payload
395 allPtrs (BCOClosure {..}) = [instrs,literals,bcoptrs]
396 allPtrs (ArrWordsClosure {..}) = []
397 allPtrs (MutArrClosure {..}) = mccPayload
398 allPtrs (MutVarClosure {..}) = [var]
399 allPtrs (MVarClosure {..}) = [queueHead,queueTail,value]
400 allPtrs (FunClosure {..}) = ptrArgs
401 allPtrs (BlockingQueueClosure {..}) = [link, blackHole, owner, queue]
402 allPtrs (OtherClosure {..}) = hvalues
403 allPtrs (UnsupportedClosure {..}) = []
406 #ifdef PRIM_SUPPORTS_ANY
407 foreign import prim "aToWordzh" aToWord# :: Any -> Word#
408 foreign import prim "slurpClosurezh" slurpClosure# :: Any -> (# Addr#, ByteArray#, Array# b #)
409 foreign import prim "reallyUnsafePtrEqualityUpToTag" reallyUnsafePtrEqualityUpToTag# :: Any -> Any -> Int#
411 -- Workd-around code until http://hackage.haskell.org/trac/ghc/ticket/5931 was
414 -- foreign import prim "aToWordzh" aToWord'# :: Addr# -> Word#
415 foreign import prim "slurpClosurezh" slurpClosure'# :: Word# -> (# Addr#, ByteArray#, Array# b #)
417 foreign import prim "reallyUnsafePtrEqualityUpToTag" reallyUnsafePtrEqualityUpToTag'# :: Word# -> Word# -> Int#
419 -- This is a datatype that has the same layout as Ptr, so that by
420 -- unsafeCoerce'ing, we obtain the Addr of the wrapped value
423 aToWord# :: Any -> Word#
424 aToWord# a = case Ptr' a of mb@(Ptr' _) -> case unsafeCoerce# mb :: Word of W# addr -> addr
426 slurpClosure# :: Any -> (# Addr#, ByteArray#, Array# b #)
427 slurpClosure# a = slurpClosure'# (aToWord# a)
429 reallyUnsafePtrEqualityUpToTag# :: Any -> Any -> Int#
430 reallyUnsafePtrEqualityUpToTag# a b = reallyUnsafePtrEqualityUpToTag'# (aToWord# a) (aToWord# b)
434 -- getClosure x >>= print
436 -- | This returns the raw representation of the given argument. The second
437 -- component of the triple are the words on the heap, and the third component
438 -- are those words that are actually pointers. Once back in Haskell word, the
439 -- 'Word' may be outdated after a garbage collector run, but the corresponding
440 -- 'Box' will still point to the correct value.
441 getClosureRaw :: a -> IO (Ptr StgInfoTable, [Word], [Box])
443 case slurpClosure# (unsafeCoerce# x) of
444 (# iptr, dat, ptrs #) -> do
445 let nelems = (I# (sizeofByteArray# dat)) `div` wORD_SIZE
446 rawWords = [W# (indexWordArray# dat i) | I# i <- [0.. fromIntegral nelems -1] ]
447 pelems = I# (sizeofArray# ptrs)
448 ptrList = amap' Box $ Array 0 (pelems - 1) pelems ptrs
449 ptrList `seq` rawWords `seq` return (Ptr iptr, rawWords, ptrList)
451 -- From compiler/ghci/RtClosureInspect.hs
452 amap' :: (t -> b) -> Array Int t -> [b]
453 amap' f (Array i0 i _ arr#) = map g [0 .. i - i0]
454 where g (I# i#) = case indexArray# arr# i# of
457 -- derived from vacuum-1.0.0.2/src/GHC/Vacuum/Internal.hs, which got it from
458 -- compiler/ghci/DebuggerUtils.hs
459 dataConInfoPtrToNames :: Ptr StgInfoTable -> IO (String, String, String)
460 dataConInfoPtrToNames ptr = do
461 conDescAddress <- getConDescAddress ptr
462 wl <- peekArray0 0 conDescAddress
463 let (pkg, modl, name) = parse wl
464 return (b2s pkg, b2s modl, b2s name)
466 b2s :: [Word8] -> String
467 b2s = fmap (chr . fromIntegral)
469 getConDescAddress :: Ptr StgInfoTable -> IO (Ptr Word8)
470 getConDescAddress ptr'
472 offsetToString <- peek (ptr' `plusPtr` (negate wORD_SIZE))
473 return $ (ptr' `plusPtr` stdInfoTableSizeB)
474 `plusPtr` (fromIntegral (offsetToString :: Word))
475 -- This is code for !ghciTablesNextToCode:
477 | otherwise = peek . intPtrToPtr
483 -- hmmmmmm. Is there any way to tell this?
484 opt_SccProfilingOn = False
486 stdInfoTableSizeW :: Int
487 -- The size of a standard info table varies with profiling/ticky etc,
488 -- so we can't get it from Constants
489 -- It must vary in sync with mkStdInfoTable
491 = size_fixed + size_prof
493 size_fixed = 2 -- layout, type
494 size_prof | opt_SccProfilingOn = 2
497 stdInfoTableSizeB :: Int
498 stdInfoTableSizeB = stdInfoTableSizeW * wORD_SIZE
500 -- From vacuum-1.0.0.2/src/GHC/Vacuum/Internal.hs
501 parse :: [Word8] -> ([Word8], [Word8], [Word8])
502 parse input = if not . all (>0) . fmap length $ [pkg,modl,occ]
503 --then (error . concat)
504 -- ["getConDescAddress:parse:"
505 -- ,"(not . all (>0) . fmap le"
506 -- ,"ngth $ [pkg,modl,occ]"]
507 then ([], [], input) -- Not in the pkg.modl.occ format, for example END_TSO_QUEUE
508 else (pkg, modl, occ)
509 -- = ASSERT (all (>0) (map length [pkg, modl, occ])) (pkg, modl, occ) -- XXXXXXXXXXXXXXXX
511 (pkg, rest1) = break (== fromIntegral (ord ':')) input
513 = (concat $ intersperse [dot] $ reverse modWords, occWord)
515 (modWords, occWord) = if (length rest1 < 1) -- XXXXXXXXx YUKX
516 --then error "getConDescAddress:parse:length rest1 < 1"
517 then parseModOcc [] []
518 else parseModOcc [] (tail rest1)
519 -- ASSERT (length rest1 > 0) (parseModOcc [] (tail rest1))
520 dot = fromIntegral (ord '.')
521 parseModOcc :: [[Word8]] -> [Word8] -> ([[Word8]], [Word8])
523 = case break (== dot) str of
524 (top, []) -> (acc, top)
525 (top, _:bot) -> parseModOcc (top : acc) bot
528 -- | This function returns parsed heap representation of the argument _at this
529 -- moment_, even if it is unevaluated or an indirection or other exotic stuff.
530 -- Beware when passing something to this function, the same caveats as for
532 getClosureData :: a -> IO Closure
533 getClosureData x = do
534 (iptr, wds, ptrs) <- getClosureRaw x
537 t | t >= CONSTR && t <= CONSTR_NOCAF_STATIC -> do
538 (pkg, modl, name) <- dataConInfoPtrToNames iptr
539 return $ ConsClosure itbl ptrs (drop (length ptrs + 1) wds) pkg modl name
541 t | t >= THUNK && t <= THUNK_STATIC -> do
542 return $ ThunkClosure itbl ptrs (drop (length ptrs + 2) wds)
544 t | t >= FUN && t <= FUN_STATIC -> do
545 return $ FunClosure itbl ptrs (drop (length ptrs + 1) wds)
548 return $ APClosure itbl
549 (fromIntegral $ wds !! 2)
550 (fromIntegral $ shiftR (wds !! 2) (wORD_SIZE_IN_BITS `div` 2))
551 (head ptrs) (tail ptrs)
554 return $ PAPClosure itbl
555 (fromIntegral $ wds !! 2)
556 (fromIntegral $ shiftR (wds !! 2) (wORD_SIZE_IN_BITS `div` 2))
557 (head ptrs) (tail ptrs)
560 return $ APStackClosure itbl (head ptrs) (tail ptrs)
563 return $ SelectorClosure itbl (head ptrs)
566 return $ IndClosure itbl (head ptrs)
568 return $ IndClosure itbl (head ptrs)
570 return $ BlackholeClosure itbl (head ptrs)
573 return $ BCOClosure itbl (ptrs !! 0) (ptrs !! 1) (ptrs !! 2)
574 (fromIntegral $ wds !! 4)
575 (fromIntegral $ shiftR (wds !! 4) (wORD_SIZE_IN_BITS `div` 2))
579 return $ ArrWordsClosure itbl (wds !! 1) (drop 2 wds)
581 t | t == MUT_ARR_PTRS_FROZEN || t == MUT_ARR_PTRS_FROZEN0 ->
582 return $ MutArrClosure itbl (wds !! 1) (wds !! 2) ptrs
584 t | t == MUT_VAR_CLEAN || t == MUT_VAR_DIRTY ->
585 return $ MutVarClosure itbl (head ptrs)
587 t | t == MVAR_CLEAN || t == MVAR_DIRTY ->
588 return $ MVarClosure itbl (ptrs !! 0) (ptrs !! 1) (ptrs !! 2)
591 return $ OtherClosure itbl ptrs wds
592 -- return $ BlockingQueueClosure itbl
593 -- (ptrs !! 0) (ptrs !! 1) (ptrs !! 2) (ptrs !! 3)
595 -- return $ OtherClosure itbl ptrs wds
598 return $ UnsupportedClosure itbl
600 -- | Like 'getClosureData', but taking a 'Box', so it is easier to work with.
601 getBoxedClosureData :: Box -> IO Closure
602 getBoxedClosureData (Box a) = getClosureData a
605 isChar :: GenClosure b -> Maybe Char
606 isChar (ConsClosure { name = "C#", dataArgs = [ch], ptrArgs = []}) = Just (chr (fromIntegral ch))
609 isCons :: GenClosure b -> Maybe (b, b)
610 isCons (ConsClosure { name = ":", dataArgs = [], ptrArgs = [h,t]}) = Just (h,t)
613 isTup :: GenClosure b -> Maybe [b]
614 isTup (ConsClosure { dataArgs = [], ..}) =
615 if length name >= 3 &&
616 head name == '(' && last name == ')' &&
617 all (==',') (tail (init name))
618 then Just ptrArgs else Nothing
622 isNil :: GenClosure b -> Bool
623 isNil (ConsClosure { name = "[]", dataArgs = [], ptrArgs = []}) = True
626 -- | A pretty-printer that tries to generate valid Haskell for evalutated data.
627 -- It assumes that for the included boxes, you already replaced them by Strings
628 -- using 'Data.Foldable.map' or, if you need to do IO, 'Data.Foldable.mapM'.
630 -- The parameter gives the precedendence, to avoid avoidable parenthesises.
631 ppClosure :: (Int -> b -> String) -> Int -> GenClosure b -> String
632 ppClosure showBox prec c = case c of
633 _ | Just ch <- isChar c -> app $
635 _ | Just (h,t) <- isCons c -> addBraces (5 <= prec) $
636 showBox 5 h ++ " : " ++ showBox 4 t
637 _ | Just vs <- isTup c ->
638 "(" ++ intercalate "," (map (showBox 0) vs) ++ ")"
639 ConsClosure {..} -> app $
640 name : map (showBox 10) ptrArgs ++ map show dataArgs
641 ThunkClosure {..} -> app $
642 "_thunk" : map (showBox 10) ptrArgs ++ map show dataArgs
643 SelectorClosure {..} -> app
644 ["_sel", showBox 10 selectee]
645 IndClosure {..} -> app
646 ["_ind", showBox 10 indirectee]
647 BlackholeClosure {..} -> app
648 ["_bh", showBox 10 indirectee]
649 APClosure {..} -> app $ map (showBox 10) $
651 PAPClosure {..} -> app $ map (showBox 10) $
653 APStackClosure {..} -> app $ map (showBox 10) $
655 BCOClosure {..} -> app
657 ArrWordsClosure {..} -> app
658 ["toArray", intercalate "," (map show arrWords) ]
659 MutArrClosure {..} -> app
660 ["toMutArray", intercalate "," (map (showBox 10) mccPayload)]
661 MutVarClosure {..} -> app $
662 ["_mutVar", (showBox 10) var]
663 MVarClosure {..} -> app $
664 ["MVar", (showBox 10) value]
666 "_fun" ++ braceize (map (showBox 0) ptrArgs ++ map show dataArgs)
667 BlockingQueueClosure {..} ->
671 UnsupportedClosure {..} ->
674 addBraces True t = "(" ++ t ++ ")"
675 addBraces False t = t
678 app xs = addBraces (10 <= prec) (intercalate " " xs)
680 braceize xs = "{" ++ intercalate "," xs ++ "}"
683 -- For more global views of the heap, you can use heap maps. These come in
684 -- variations, either a trees or as graphs, depending on
685 -- whether you want to detect cycles and sharing or not.
687 -- | Heap maps as tree, i.e. no sharing, no cycles.
688 data HeapTree = HeapTree WeakBox (GenClosure HeapTree) | EndOfHeapTree
690 heapTreeClosure :: HeapTree -> Maybe (GenClosure HeapTree)
691 heapTreeClosure (HeapTree _ c) = Just c
692 heapTreeClosure EndOfHeapTree = Nothing
694 -- | Constructing an 'HeapTree' from a boxed value. It takes a depth parameter
695 -- that prevents it from running ad infinitum for cyclic or infinite
697 buildHeapTree :: Int -> Box -> IO HeapTree
698 buildHeapTree 0 _ = do
699 return $ EndOfHeapTree
700 buildHeapTree n b = do
702 c <- getBoxedClosureData b
703 c' <- T.mapM (buildHeapTree (n-1)) c
704 return $ HeapTree w c'
706 -- | Pretty-Printing a heap Tree
708 -- Example output for @[Just 4, Nothing, *something*]@, where *something* is an
709 -- unevaluated expression depending on the command line argument.
711 -- >[Just (I# 4),Nothing,Just (_thunk ["arg1","arg2"])]
712 ppHeapTree :: HeapTree -> String
715 go _ EndOfHeapTree = "..."
716 go prec t@(HeapTree _ c')
717 | Just s <- isHeapTreeString t = show s
718 | Just l <- isHeapTreeList t = "[" ++ intercalate "," (map ppHeapTree l) ++ "]"
719 | otherwise = ppClosure go prec c'
721 isHeapTreeList :: HeapTree -> Maybe ([HeapTree])
722 isHeapTreeList tree = do
723 c <- heapTreeClosure tree
728 t' <- isHeapTreeList t
731 isHeapTreeString :: HeapTree -> Maybe String
732 isHeapTreeString t = do
733 list <- isHeapTreeList t
734 -- We do not want to print empty lists as "" as we do not know that they
735 -- are really strings.
738 else mapM (isChar <=< heapTreeClosure) list
740 -- | For heap graphs, i.e. data structures that also represent sharing and
741 -- cyclic structures, these are the entries. If the referenced value is
742 -- @Nothing@, then we do not have that value in the map, most likely due to
743 -- exceeding the recursion bound passed to 'buildHeapGraph'.
744 data HeapGraphEntry = HeapGraphEntry WeakBox (GenClosure (Maybe HeapGraphIndex))
746 type HeapGraphIndex = Int
748 -- | The whole graph. The suggested interface is to only use 'lookupHeapGraph',
749 -- as the internal representation may change. Nevertheless, we export it here:
750 -- Sometimes the user knows better what he needs than we do.
751 newtype HeapGraph = HeapGraph (M.IntMap HeapGraphEntry)
754 lookupHeapGraph :: HeapGraphIndex -> HeapGraph -> Maybe HeapGraphEntry
755 lookupHeapGraph i (HeapGraph m) = M.lookup i m
757 heapGraphRoot :: HeapGraphIndex
760 -- | Creates a 'HeapGraph' for the value in the box, but not recursing further
761 -- than the given limit. The initial value has index 'heapGraphRoot'.
762 buildHeapGraph :: Int -> Box -> IO HeapGraph
763 buildHeapGraph limit initialBox = fst <$> generalBuildHeapGraph [] [0..] limit [((),initialBox)]
765 -- | Creates a 'HeapGraph' for the values in multiple boxes, but not recursing
766 -- further than the given limit.
768 -- Returns the 'HeapGraph' and the indices of initial values. The arbitrary
769 -- type @a@ can be used to make the connection between the input and the
770 -- resulting list of indices.
771 multiBuildHeapGraph :: Int -> [(a, Box)] -> IO (HeapGraph, [(a, HeapGraphIndex)])
772 multiBuildHeapGraph = generalBuildHeapGraph [] [0..]
774 -- | Adds an entry to an existing 'HeapGraph'.
776 -- Returns the updated 'HeapGraph' and the index of the added value.
777 addHeapGraph :: HeapGraph -> Int -> Box -> IO (HeapGraphIndex, HeapGraph)
778 addHeapGraph (HeapGraph hg) limit initialBox = do
779 newStart <- foldM toStartList [] $ M.toList hg
780 let newIndex = 1 + (maximum $ map snd newStart)
781 (HeapGraph newHG, _) <- generalBuildHeapGraph newStart [newIndex..] limit [((),initialBox)]
782 return $ (newIndex, HeapGraph $ M.union hg newHG)
783 where toStartList xs (i, HeapGraphEntry wb _) = do
784 derefWeakBox wb >>= \mbB -> return $ case mbB of
788 generalBuildHeapGraph :: [(Box, HeapGraphIndex)] -> [HeapGraphIndex] -> Int -> [(a,Box)] -> IO (HeapGraph, [(a, HeapGraphIndex)])
789 generalBuildHeapGraph _ _ limit _ | limit <= 0 = error "buildHeapGraph: limit has to be positive"
790 generalBuildHeapGraph knownEntries newIndices limit initialBoxes = do
791 let initialState = (knownEntries, newIndices, [])
792 (is, hg) <- runWriterT (evalStateT run initialState)
793 return (HeapGraph hg, is)
796 _ <- mapM (add limit) $ map snd initialBoxes
800 add 0 _ = return Nothing
802 -- If the box is in the map, return the index
803 (existing,_,_) <- get
804 case lookup b existing of
805 Just i -> return $ Just i
807 -- Otherwise, allocate a new index
810 modify (\(x,y,z) -> ((b,i):x, y, z))
811 c <- liftIO $ getBoxedClosureData b
812 -- Find indicies for all boxes contained in the map
813 c' <- T.mapM (add (n-1)) c
814 w <- liftIO $ weakBox b
815 -- Add add the resulting closure to the map
816 lift $ tell (M.singleton i (HeapGraphEntry w c'))
817 case initialLookup b of
819 Just val -> modify (\(x,y,z) -> (x,y,(val,i):z))
822 i <- gets (head . (\(_,b,_) -> b))
823 modify (\(a,b,c) -> (a, tail b, c))
826 initialLookup b = lookup b $ map swap initialBoxes
828 -- | Pretty-prints a HeapGraph. The resulting string contains newlines. Example
829 -- for @let s = "Ki" in (s, s, cycle "Ho")@:
832 -- > x6 = C# 'H' : C# 'o' : x6
834 ppHeapGraph :: HeapGraph -> String
835 ppHeapGraph (HeapGraph m) = letWrapper ++ ppRef 0 (Just heapGraphRoot)
837 -- All variables occuring more than once
838 bindings = boundMultipleTimes (HeapGraph m) [heapGraphRoot]
843 else "let " ++ intercalate "\n " (map ppBinding bindings) ++ "\nin "
845 bindingLetter i = case iToE i of
846 HeapGraphEntry _ c -> case c of
847 ThunkClosure {..} -> 't'
848 SelectorClosure {..} -> 't'
849 APClosure {..} -> 't'
850 PAPClosure {..} -> 'f'
851 BCOClosure {..} -> 't'
852 FunClosure {..} -> 'f'
855 ppBindingMap = M.fromList $
857 map (zipWith (\j (i,c) -> (i, [c] ++ show j)) [(1::Int)..]) $
858 groupBy ((==) `on` snd) $
859 sortBy (compare `on` snd)
860 [ (i, bindingLetter i) | i <- bindings ]
862 ppVar i = ppBindingMap M.! i
863 ppBinding i = ppVar i ++ " = " ++ ppEntry 0 (iToE i)
865 ppEntry prec e@(HeapGraphEntry _ c)
866 | Just s <- isString e = show s
867 | Just l <- isList e = "[" ++ intercalate "," (map (ppRef 0) l) ++ "]"
868 | otherwise = ppClosure ppRef prec c
870 ppRef _ Nothing = "..."
871 ppRef prec (Just i) | i `elem` bindings = ppVar i
872 | otherwise = ppEntry prec (iToE i)
875 iToUnboundE i = if i `elem` bindings then Nothing else M.lookup i m
877 isList :: HeapGraphEntry -> Maybe ([Maybe HeapGraphIndex])
878 isList (HeapGraphEntry _ c) =
888 isString :: HeapGraphEntry -> Maybe String
891 -- We do not want to print empty lists as "" as we do not know that they
892 -- are really strings.
895 else mapM (isChar . (\(HeapGraphEntry _ c) -> c) <=< iToUnboundE <=< id) list
898 -- | In the given HeapMap, list all indices that are used more than once. The
899 -- second parameter adds external references, commonly @[heapGraphRoot]@.
900 boundMultipleTimes :: HeapGraph -> [HeapGraphIndex] -> [HeapGraphIndex]
901 boundMultipleTimes (HeapGraph m) roots = map head $ filter (not.null) $ map tail $ group $ sort $
902 roots ++ concatMap (\(HeapGraphEntry _ c) -> catMaybes (allPtrs c)) (M.elems m)
904 -- | An a variant of 'Box' that does not keep the value alive.
906 -- Like 'Box', its 'Show' instance is highly unsafe.
907 newtype WeakBox = WeakBox (Weak Box)
910 type WeakClosure = GenClosure WeakBox
912 instance Show WeakBox where
913 showsPrec p (WeakBox w) rs = case unsafePerformIO (deRefWeak w) of
914 Nothing -> let txt = "(freed)" in
915 replicate (2*wORD_SIZE - length txt) ' ' ++ txt ++ rs
916 Just b -> showsPrec p b rs
919 Turns a 'Box' into a 'WeakBox', allowing the referenced value to be garbage
922 weakBox :: Box -> IO WeakBox
923 weakBox b@(Box a) = WeakBox `fmap` mkWeak a b Nothing
926 Checks whether the value referenced by a weak box is still alive
928 isAlive :: WeakBox -> IO Bool
929 isAlive (WeakBox w) = isJust `fmap` deRefWeak w
932 Dereferences the weak box
934 derefWeakBox :: WeakBox -> IO (Maybe Box)
935 derefWeakBox (WeakBox w) = deRefWeak w
937 weakenClosure :: Closure -> IO WeakClosure
938 weakenClosure = T.mapM weakBox