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
52 import GHC.Exts ( Any,
53 Ptr(..), Addr#, Int(..), Word(..), Word#, Int#,
54 ByteArray#, Array#, sizeofByteArray#, sizeofArray#, indexArray#, indexWordArray#,
57 import GHC.Arr (Array(..))
60 import Foreign hiding ( unsafePerformIO, void )
61 import Numeric ( showHex )
64 import Data.Maybe ( catMaybes )
65 import Data.Monoid ( Monoid, (<>), mempty )
68 import Data.Foldable ( Foldable )
69 import qualified Data.Foldable as F
70 import Data.Traversable ( Traversable )
71 import qualified Data.Traversable as T
72 import qualified Data.IntMap as M
74 import Control.Monad.Trans.State
75 import Control.Monad.Trans.Class
76 import Control.Monad.IO.Class
77 import Control.Monad.Trans.Writer.Strict
78 import Control.Exception.Base (evaluate)
80 import GHC.Disassembler
82 #include "ghcautoconf.h"
84 -- | An arbitrarily Haskell value in a safe Box. The point is that even
85 -- unevaluated thunks can safely be moved around inside the Box, and when
86 -- required, e.g. in 'getBoxedClosureData', the function knows how far it has
87 -- to evalue the argument.
90 #if SIZEOF_VOID_P == 8
91 type HalfWord = Word32
93 type HalfWord = Word16
96 instance Show Box where
97 -- From libraries/base/GHC/Ptr.lhs
98 showsPrec _ (Box a) rs =
99 -- unsafePerformIO (print "↓" >> pClosure a) `seq`
100 pad_out (showHex addr "") ++ (if tag>0 then "/" ++ show tag else "") ++ rs
102 ptr = W# (aToWord# a)
103 tag = ptr .&. fromIntegral tAG_MASK -- ((1 `shiftL` TAG_BITS) -1)
105 -- want 0s prefixed to pad it out to a fixed length.
107 '0':'x':(replicate (2*wORD_SIZE - length ls) '0') ++ ls
109 -- | Boxes can be compared, but this is not pure, as different heap objects can,
110 -- after garbage collection, become the same object.
111 areBoxesEqual :: Box -> Box -> IO Bool
112 areBoxesEqual (Box a) (Box b) = case reallyUnsafePtrEqualityUpToTag# a b of
118 This takes an arbitrary value and puts it into a box. Note that calls like
122 will put the thunk \"head list\" into the box, /not/ the element at the head
123 of the list. For that, use careful case expressions:
125 > case list of x:_ -> asBox x
128 asBox x = Box (unsafeCoerce# x)
131 StgInfoTable parsing derived from ByteCodeItbls.lhs
132 Removed the code parameter for now
133 Replaced Type by an enumeration
134 Remove stuff dependent on GHCI_TABLES_NEXT_TO_CODE
137 {-| This is a somewhat faithful representation of an info table. See
138 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/InfoTables.h>
139 for more details on this data structure. Note that the 'Storable' instance
140 provided here does _not_ support writing.
142 data StgInfoTable = StgInfoTable {
150 instance Storable StgInfoTable where
157 sizeOf (undefined :: HalfWord),
165 = error "Storable StgInfoTable is read-only"
168 = flip (evalStateT) (castPtr a0)
178 tipe = toEnum (fromIntegral (tipe'::HalfWord)),
182 fieldSz :: (Storable a, Storable b) => (a -> b) -> a -> Int
183 fieldSz sel x = sizeOf (sel x)
185 load :: Storable a => PtrIO a
186 load = do addr <- advance
189 type PtrIO = StateT (Ptr Word8) IO
191 advance :: Storable a => PtrIO (Ptr a)
192 advance = StateT adv where
193 adv addr = case castPtr addr of { addrCast -> return
194 (addrCast, addr `plusPtr` sizeOfPointee addrCast) }
196 sizeOfPointee :: (Storable a) => Ptr a -> Int
197 sizeOfPointee addr = sizeOf (typeHack addr)
198 where typeHack = undefined :: Ptr a -> a
201 Data Type representing Closures
205 {-| A closure type enumeration, in order matching the actual value on the heap.
206 Needs to be synchronized with
207 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/ClosureTypes.h>
218 | CONSTR_NOCAF_STATIC
257 | MUT_ARR_PTRS_FROZEN0
258 | MUT_ARR_PTRS_FROZEN
271 deriving (Show, Eq, Enum, Ord)
273 {-| This is the main data type of this module, representing a Haskell value on
274 the heap. This reflects
275 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/Closures.h>
277 The data type is parametrized by the type to store references in, which
278 is usually a 'Box' with appropriate type synonym '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 -- This is just for good measure, and seems to be not important.
450 mapM_ evaluate ptrList
451 -- This seems to be required to avoid crashes as well
452 void $ evaluate nelems
453 -- The following deep evaluation is crucial to avoid crashes (but why)?
454 mapM_ evaluate rawWords
455 return (Ptr iptr, rawWords, ptrList)
457 -- From compiler/ghci/RtClosureInspect.hs
458 amap' :: (t -> b) -> Array Int t -> [b]
459 amap' f (Array i0 i _ arr#) = map g [0 .. i - i0]
460 where g (I# i#) = case indexArray# arr# i# of
463 -- derived from vacuum-1.0.0.2/src/GHC/Vacuum/Internal.hs, which got it from
464 -- compiler/ghci/DebuggerUtils.hs
465 dataConInfoPtrToNames :: Ptr StgInfoTable -> IO (String, String, String)
466 dataConInfoPtrToNames ptr = do
467 conDescAddress <- getConDescAddress ptr
468 wl <- peekArray0 0 conDescAddress
469 let (pkg, modl, name) = parse wl
470 return (b2s pkg, b2s modl, b2s name)
472 b2s :: [Word8] -> String
473 b2s = fmap (chr . fromIntegral)
475 getConDescAddress :: Ptr StgInfoTable -> IO (Ptr Word8)
476 getConDescAddress ptr'
478 offsetToString <- peek (ptr' `plusPtr` (negate wORD_SIZE))
479 return $ (ptr' `plusPtr` stdInfoTableSizeB)
480 `plusPtr` (fromIntegral (offsetToString :: Word))
481 -- This is code for !ghciTablesNextToCode:
483 | otherwise = peek . intPtrToPtr
489 -- hmmmmmm. Is there any way to tell this?
490 opt_SccProfilingOn = False
492 stdInfoTableSizeW :: Int
493 -- The size of a standard info table varies with profiling/ticky etc,
494 -- so we can't get it from Constants
495 -- It must vary in sync with mkStdInfoTable
497 = size_fixed + size_prof
499 size_fixed = 2 -- layout, type
500 size_prof | opt_SccProfilingOn = 2
503 stdInfoTableSizeB :: Int
504 stdInfoTableSizeB = stdInfoTableSizeW * wORD_SIZE
506 -- From vacuum-1.0.0.2/src/GHC/Vacuum/Internal.hs
507 parse :: [Word8] -> ([Word8], [Word8], [Word8])
508 parse input = if not . all (>0) . fmap length $ [pkg,modl,occ]
509 --then (error . concat)
510 -- ["getConDescAddress:parse:"
511 -- ,"(not . all (>0) . fmap le"
512 -- ,"ngth $ [pkg,modl,occ]"]
513 then ([], [], input) -- Not in the pkg.modl.occ format, for example END_TSO_QUEUE
514 else (pkg, modl, occ)
515 -- = ASSERT (all (>0) (map length [pkg, modl, occ])) (pkg, modl, occ) -- XXXXXXXXXXXXXXXX
517 (pkg, rest1) = break (== fromIntegral (ord ':')) input
519 = (concat $ intersperse [dot] $ reverse modWords, occWord)
521 (modWords, occWord) = if (length rest1 < 1) -- XXXXXXXXx YUKX
522 --then error "getConDescAddress:parse:length rest1 < 1"
523 then parseModOcc [] []
524 else parseModOcc [] (tail rest1)
525 -- ASSERT (length rest1 > 0) (parseModOcc [] (tail rest1))
526 dot = fromIntegral (ord '.')
527 parseModOcc :: [[Word8]] -> [Word8] -> ([[Word8]], [Word8])
529 = case break (== dot) str of
530 (top, []) -> (acc, top)
531 (top, _:bot) -> parseModOcc (top : acc) bot
534 -- | This function returns parsed heap representation of the argument _at this
535 -- moment_, even if it is unevaluated or an indirection or other exotic stuff.
536 -- Beware when passing something to this function, the same caveats as for
538 getClosureData :: a -> IO Closure
539 getClosureData x = do
540 (iptr, wds, ptrs) <- getClosureRaw x
543 t | t >= CONSTR && t <= CONSTR_NOCAF_STATIC -> do
544 (pkg, modl, name) <- dataConInfoPtrToNames iptr
545 if modl == "ByteCodeInstr" && name == "BreakInfo"
546 then return $ UnsupportedClosure itbl
547 else return $ ConsClosure itbl ptrs (drop (length ptrs + 1) wds) pkg modl name
549 t | t >= THUNK && t <= THUNK_STATIC -> do
550 return $ ThunkClosure itbl ptrs (drop (length ptrs + 2) wds)
552 t | t >= FUN && t <= FUN_STATIC -> do
553 return $ FunClosure itbl ptrs (drop (length ptrs + 1) wds)
556 unless (length ptrs >= 1) $
557 fail "Expected at least 1 ptr argument to AP"
558 return $ APClosure itbl
559 (fromIntegral $ wds !! 2)
560 (fromIntegral $ shiftR (wds !! 2) (wORD_SIZE_IN_BITS `div` 2))
561 (head ptrs) (tail ptrs)
564 unless (length ptrs >= 1) $
565 fail "Expected at least 1 ptr argument to PAP"
566 return $ PAPClosure itbl
567 (fromIntegral $ wds !! 2)
568 (fromIntegral $ shiftR (wds !! 2) (wORD_SIZE_IN_BITS `div` 2))
569 (head ptrs) (tail ptrs)
572 unless (length ptrs >= 1) $
573 fail "Expected at least 1 ptr argument to AP_STACK"
574 return $ APStackClosure itbl (head ptrs) (tail ptrs)
577 unless (length ptrs >= 1) $
578 fail "Expected at least 1 ptr argument to THUNK_SELECTOR"
579 return $ SelectorClosure itbl (head ptrs)
582 unless (length ptrs >= 1) $
583 fail "Expected at least 1 ptr argument to IND"
584 return $ IndClosure itbl (head ptrs)
586 unless (length ptrs >= 1) $
587 fail "Expected at least 1 ptr argument to IND_STATIC"
588 return $ IndClosure itbl (head ptrs)
590 unless (length ptrs >= 1) $
591 fail "Expected at least 1 ptr argument to BLACKHOLE"
592 return $ BlackholeClosure itbl (head ptrs)
595 return $ BCOClosure itbl (ptrs !! 0) (ptrs !! 1) (ptrs !! 2)
596 (fromIntegral $ wds !! 4)
597 (fromIntegral $ shiftR (wds !! 4) (wORD_SIZE_IN_BITS `div` 2))
601 return $ ArrWordsClosure itbl (wds !! 1) (drop 2 wds)
603 t | t == MUT_ARR_PTRS_FROZEN || t == MUT_ARR_PTRS_FROZEN0 ->
604 return $ MutArrClosure itbl (wds !! 1) (wds !! 2) ptrs
606 t | t == MUT_VAR_CLEAN || t == MUT_VAR_DIRTY ->
607 return $ MutVarClosure itbl (head ptrs)
609 t | t == MVAR_CLEAN || t == MVAR_DIRTY ->
610 return $ MVarClosure itbl (ptrs !! 0) (ptrs !! 1) (ptrs !! 2)
613 return $ OtherClosure itbl ptrs wds
614 -- return $ BlockingQueueClosure itbl
615 -- (ptrs !! 0) (ptrs !! 1) (ptrs !! 2) (ptrs !! 3)
617 -- return $ OtherClosure itbl ptrs wds
620 return $ UnsupportedClosure itbl
622 -- | Like 'getClosureData', but taking a 'Box', so it is easier to work with.
623 getBoxedClosureData :: Box -> IO Closure
624 getBoxedClosureData (Box a) = getClosureData a
627 isChar :: GenClosure b -> Maybe Char
628 isChar (ConsClosure { name = "C#", dataArgs = [ch], ptrArgs = []}) = Just (chr (fromIntegral ch))
631 isCons :: GenClosure b -> Maybe (b, b)
632 isCons (ConsClosure { name = ":", dataArgs = [], ptrArgs = [h,t]}) = Just (h,t)
635 isTup :: GenClosure b -> Maybe [b]
636 isTup (ConsClosure { dataArgs = [], ..}) =
637 if length name >= 3 &&
638 head name == '(' && last name == ')' &&
639 all (==',') (tail (init name))
640 then Just ptrArgs else Nothing
644 isNil :: GenClosure b -> Bool
645 isNil (ConsClosure { name = "[]", dataArgs = [], ptrArgs = []}) = True
648 -- | A pretty-printer that tries to generate valid Haskell for evalutated data.
649 -- It assumes that for the included boxes, you already replaced them by Strings
650 -- using 'Data.Foldable.map' or, if you need to do IO, 'Data.Foldable.mapM'.
652 -- The parameter gives the precedendence, to avoid avoidable parenthesises.
653 ppClosure :: (Int -> b -> String) -> Int -> GenClosure b -> String
654 ppClosure showBox prec c = case c of
655 _ | Just ch <- isChar c -> app $
657 _ | Just (h,t) <- isCons c -> addBraces (5 <= prec) $
658 showBox 5 h ++ " : " ++ showBox 4 t
659 _ | Just vs <- isTup c ->
660 "(" ++ intercalate "," (map (showBox 0) vs) ++ ")"
661 ConsClosure {..} -> app $
662 name : map (showBox 10) ptrArgs ++ map show dataArgs
663 ThunkClosure {..} -> app $
664 "_thunk" : map (showBox 10) ptrArgs ++ map show dataArgs
665 SelectorClosure {..} -> app
666 ["_sel", showBox 10 selectee]
667 IndClosure {..} -> app
668 ["_ind", showBox 10 indirectee]
669 BlackholeClosure {..} -> app
670 ["_bh", showBox 10 indirectee]
671 APClosure {..} -> app $ map (showBox 10) $
673 PAPClosure {..} -> app $ map (showBox 10) $
675 APStackClosure {..} -> app $ map (showBox 10) $
677 BCOClosure {..} -> app
679 ArrWordsClosure {..} -> app
680 ["toArray", "("++show (length arrWords) ++ " words)", intercalate "," (shorten (map show arrWords)) ]
681 MutArrClosure {..} -> app
682 ["toMutArray", "("++show (length mccPayload) ++ " ptrs)", intercalate "," (shorten (map (showBox 10) mccPayload))]
683 MutVarClosure {..} -> app $
684 ["_mutVar", (showBox 10) var]
685 MVarClosure {..} -> app $
686 ["MVar", (showBox 10) value]
688 "_fun" ++ braceize (map (showBox 0) ptrArgs ++ map show dataArgs)
689 BlockingQueueClosure {..} ->
693 UnsupportedClosure {..} ->
697 app xs = addBraces (10 <= prec) (intercalate " " xs)
699 shorten xs = if length xs > 20 then take 20 xs ++ ["(and more)"] else xs
703 For more global views of the heap, you can use heap maps. These come in
704 variations, either a trees or as graphs, depending on
705 whether you want to detect cycles and sharing or not.
707 The entries of a 'HeapGraph' can be annotated with arbitrary values. Most
708 operations expect this to be in the 'Monoid' class: They use 'mempty' to
709 annotate closures added because the passed values reference them, and they
710 use 'mappend' to combine the annotations when two values conincide, e.g.
711 during 'updateHeapGraph'.
714 -- | Heap maps as tree, i.e. no sharing, no cycles.
715 data HeapTree = HeapTree Box (GenClosure HeapTree) | EndOfHeapTree
717 heapTreeClosure :: HeapTree -> Maybe (GenClosure HeapTree)
718 heapTreeClosure (HeapTree _ c) = Just c
719 heapTreeClosure EndOfHeapTree = Nothing
721 -- | Constructing an 'HeapTree' from a boxed value. It takes a depth parameter
722 -- that prevents it from running ad infinitum for cyclic or infinite
724 buildHeapTree :: Int -> Box -> IO HeapTree
725 buildHeapTree 0 _ = do
726 return $ EndOfHeapTree
727 buildHeapTree n b = do
728 c <- getBoxedClosureData b
729 c' <- T.mapM (buildHeapTree (n-1)) c
730 return $ HeapTree b c'
732 -- | Pretty-Printing a heap Tree
734 -- Example output for @[Just 4, Nothing, *something*]@, where *something* is an
735 -- unevaluated expression depending on the command line argument.
737 -- >[Just (I# 4),Nothing,Just (_thunk ["arg1","arg2"])]
738 ppHeapTree :: HeapTree -> String
741 go _ EndOfHeapTree = "..."
742 go prec t@(HeapTree _ c')
743 | Just s <- isHeapTreeString t = show s
744 | Just l <- isHeapTreeList t = "[" ++ intercalate "," (map ppHeapTree l) ++ "]"
745 | Just bc <- disassembleBCO heapTreeClosure c'
746 = app ("_bco" : map (go 10) (concatMap F.toList bc))
747 | otherwise = ppClosure go prec c'
750 app xs = addBraces (10 <= prec) (intercalate " " xs)
752 isHeapTreeList :: HeapTree -> Maybe ([HeapTree])
753 isHeapTreeList tree = do
754 c <- heapTreeClosure tree
759 t' <- isHeapTreeList t
762 isHeapTreeString :: HeapTree -> Maybe String
763 isHeapTreeString t = do
764 list <- isHeapTreeList t
765 -- We do not want to print empty lists as "" as we do not know that they
766 -- are really strings.
769 else mapM (isChar <=< heapTreeClosure) list
771 -- | For heap graphs, i.e. data structures that also represent sharing and
772 -- cyclic structures, these are the entries. If the referenced value is
773 -- @Nothing@, then we do not have that value in the map, most likely due to
774 -- exceeding the recursion bound passed to 'buildHeapGraph'.
776 -- Besides a pointer to the stored value and the closure representation we
777 -- also keep track of whether the value was still alive at the last update of the
778 -- heap graph. In addition we have a slot for arbitrary data, for the user's convenience.
779 data HeapGraphEntry a = HeapGraphEntry {
781 hgeClosure :: GenClosure (Maybe HeapGraphIndex),
784 deriving (Show, Functor)
785 type HeapGraphIndex = Int
787 -- | The whole graph. The suggested interface is to only use 'lookupHeapGraph',
788 -- as the internal representation may change. Nevertheless, we export it here:
789 -- Sometimes the user knows better what he needs than we do.
790 newtype HeapGraph a = HeapGraph (M.IntMap (HeapGraphEntry a))
793 lookupHeapGraph :: HeapGraphIndex -> (HeapGraph a) -> Maybe (HeapGraphEntry a)
794 lookupHeapGraph i (HeapGraph m) = M.lookup i m
796 heapGraphRoot :: HeapGraphIndex
799 -- | Creates a 'HeapGraph' for the value in the box, but not recursing further
800 -- than the given limit. The initial value has index 'heapGraphRoot'.
803 => Int -- ^ Search limit
804 -> a -- ^ Data value for the root
805 -> Box -- ^ The value to start with
807 buildHeapGraph limit rootD initialBox =
808 fst <$> multiBuildHeapGraph limit [(rootD, initialBox)]
810 -- | Creates a 'HeapGraph' for the values in multiple boxes, but not recursing
811 -- further than the given limit.
813 -- Returns the 'HeapGraph' and the indices of initial values. The arbitrary
814 -- type @a@ can be used to make the connection between the input and the
815 -- resulting list of indices, and to store additional data.
818 => Int -- ^ Search limit
819 -> [(a, Box)] -- ^ Starting values with associated data entry
820 -> IO (HeapGraph a, [(a, HeapGraphIndex)])
821 multiBuildHeapGraph limit = generalBuildHeapGraph limit (HeapGraph M.empty)
823 -- | Adds an entry to an existing 'HeapGraph'.
825 -- Returns the updated 'HeapGraph' and the index of the added value.
828 => Int -- ^ Search limit
829 -> a -- ^ Data to be stored with the added value
830 -> Box -- ^ Value to add to the graph
831 -> HeapGraph a -- ^ Graph to extend
832 -> IO (HeapGraphIndex, HeapGraph a)
833 addHeapGraph limit d box hg = do
834 (hg', [(_,i)]) <- generalBuildHeapGraph limit hg [(d,box)]
837 -- | Adds the given annotation to the entry at the given index, using the
838 -- 'mappend' operation of its 'Monoid' instance.
839 annotateHeapGraph :: Monoid a => a -> HeapGraphIndex -> HeapGraph a -> HeapGraph a
840 annotateHeapGraph d i (HeapGraph hg) = HeapGraph $ M.update go i hg
842 go hge = Just $ hge { hgeData = hgeData hge <> d }
844 generalBuildHeapGraph
849 -> IO (HeapGraph a, [(a, HeapGraphIndex)])
850 generalBuildHeapGraph limit _ _ | limit <= 0 = error "buildHeapGraph: limit has to be positive"
851 generalBuildHeapGraph limit (HeapGraph hg) addBoxes = do
852 -- First collect all boxes from the existing heap graph
853 let boxList = [ (hgeBox hge, i) | (i, hge) <- M.toList hg ]
854 indices | M.null hg = [0..]
855 | otherwise = [1 + fst (M.findMax hg)..]
857 initialState = (boxList, indices, [])
858 -- It is ok to use the Monoid (IntMap a) instance here, because
859 -- we will, besides the first time, use 'tell' only to add singletons not
861 (is, hg') <- runWriterT (evalStateT run initialState)
862 -- Now add the annotations of the root values
863 let hg'' = foldl' (flip (uncurry annotateHeapGraph)) (HeapGraph hg') is
867 lift $ tell hg -- Start with the initial map
868 forM addBoxes $ \(d, b) -> do
869 -- Cannot fail, as limit is not zero here
870 Just i <- add limit b
873 add 0 _ = return Nothing
875 -- If the box is in the map, return the index
876 (existing,_,_) <- get
877 mbI <- liftIO $ findM (areBoxesEqual b . fst) existing
879 Just (_,i) -> return $ Just i
881 -- Otherwise, allocate a new index
884 modify (\(x,y,z) -> ((b,i):x, y, z))
885 -- Look up the closure
886 c <- liftIO $ getBoxedClosureData b
887 -- Find indicies for all boxes contained in the map
888 c' <- T.mapM (add (n-1)) c
889 -- Add add the resulting closure to the map
890 lift $ tell (M.singleton i (HeapGraphEntry b c' True mempty))
893 i <- gets (head . (\(_,b,_) -> b))
894 modify (\(a,b,c) -> (a, tail b, c))
897 -- | This function updates a heap graph to reflect the current state of
898 -- closures on the heap, conforming to the following specification.
900 -- * Every entry whose value has been garbage collected by now is marked as
901 -- dead by setting 'hgeLive' to @False@
902 -- * Every entry whose value is still live gets the 'hgeClosure' field updated
903 -- and newly referenced closures are, up to the given depth, added to the graph.
904 -- * A map mapping previous indicies to the corresponding new indicies is returned as well.
905 -- * The closure at 'heapGraphRoot' stays at 'heapGraphRoot'
906 updateHeapGraph :: Monoid a => Int -> HeapGraph a -> IO (HeapGraph a, HeapGraphIndex -> HeapGraphIndex)
907 updateHeapGraph limit (HeapGraph startHG) = do
908 (hg', indexMap) <- runWriterT $ foldM go (HeapGraph M.empty) (M.toList startHG)
909 return (hg', (M.!) indexMap)
912 (j, hg') <- liftIO $ addHeapGraph limit (hgeData hge) (hgeBox hge) hg
913 tell (M.singleton i j)
916 -- | Pretty-prints a HeapGraph. The resulting string contains newlines. Example
917 -- for @let s = \"Ki\" in (s, s, cycle \"Ho\")@:
920 -- > x6 = C# 'H' : C# 'o' : x6
922 ppHeapGraph :: HeapGraph a -> String
923 ppHeapGraph (HeapGraph m) = letWrapper ++ ppRef 0 (Just heapGraphRoot)
925 -- All variables occuring more than once
926 bindings = boundMultipleTimes (HeapGraph m) [heapGraphRoot]
931 else "let " ++ intercalate "\n " (map ppBinding bindings) ++ "\nin "
933 bindingLetter i = case hgeClosure (iToE i) of
934 ThunkClosure {..} -> 't'
935 SelectorClosure {..} -> 't'
936 APClosure {..} -> 't'
937 PAPClosure {..} -> 'f'
938 BCOClosure {..} -> 't'
939 FunClosure {..} -> 'f'
942 ppBindingMap = M.fromList $
944 map (zipWith (\j (i,c) -> (i, [c] ++ show j)) [(1::Int)..]) $
945 groupBy ((==) `on` snd) $
946 sortBy (compare `on` snd)
947 [ (i, bindingLetter i) | i <- bindings ]
949 ppVar i = ppBindingMap M.! i
950 ppBinding i = ppVar i ++ " = " ++ ppEntry 0 (iToE i)
953 | Just s <- isString hge = show s
954 | Just l <- isList hge = "[" ++ intercalate "," (map (ppRef 0) l) ++ "]"
955 | Just bc <- disassembleBCO (fmap (hgeClosure . iToE)) (hgeClosure hge)
956 = app ("_bco" : map (ppRef 10) (concatMap F.toList bc))
957 | otherwise = ppClosure ppRef prec (hgeClosure hge)
960 app xs = addBraces (10 <= prec) (intercalate " " xs)
962 ppRef _ Nothing = "..."
963 ppRef prec (Just i) | i `elem` bindings = ppVar i
964 | otherwise = ppEntry prec (iToE i)
967 iToUnboundE i = if i `elem` bindings then Nothing else M.lookup i m
969 isList :: HeapGraphEntry a -> Maybe ([Maybe HeapGraphIndex])
971 if isNil (hgeClosure hge)
974 (h,t) <- isCons (hgeClosure hge)
980 isString :: HeapGraphEntry a -> Maybe String
983 -- We do not want to print empty lists as "" as we do not know that they
984 -- are really strings.
987 else mapM (isChar . hgeClosure <=< iToUnboundE <=< id) list
990 -- | In the given HeapMap, list all indices that are used more than once. The
991 -- second parameter adds external references, commonly @[heapGraphRoot]@.
992 boundMultipleTimes :: HeapGraph a -> [HeapGraphIndex] -> [HeapGraphIndex]
993 boundMultipleTimes (HeapGraph m) roots = map head $ filter (not.null) $ map tail $ group $ sort $
994 roots ++ concatMap (catMaybes . allPtrs . hgeClosure) (M.elems m)
996 -- | This function integrates the disassembler in "GHC.Disassembler". The first
997 -- argument should a function that dereferences the pointer in the closure to a
1000 -- If any of these return 'Nothing', then 'disassembleBCO' returns Nothing
1001 disassembleBCO :: (a -> Maybe (GenClosure b)) -> GenClosure a -> Maybe [BCI b]
1002 disassembleBCO deref (BCOClosure {..}) = do
1003 opsC <- deref instrs
1004 litsC <- deref literals
1005 ptrsC <- deref bcoptrs
1006 return $ disassemble (mccPayload ptrsC) (arrWords litsC) (toBytes (bytes opsC) (arrWords opsC))
1007 disassembleBCO _ _ = Nothing
1011 findM :: (a -> IO Bool) -> [a] -> IO (Maybe a)
1012 findM _p [] = return Nothing
1015 if b then return (Just x) else findM p xs
1017 addBraces :: Bool -> String -> String
1018 addBraces True t = "(" ++ t ++ ")"
1019 addBraces False t = t
1021 braceize :: [String] -> String
1023 braceize xs = "{" ++ intercalate "," xs ++ "}"
1025 -- This used to be available via GHC.Constants
1026 #include "MachDeps.h"
1027 wORD_SIZE, tAG_MASK, wORD_SIZE_IN_BITS :: Int
1028 wORD_SIZE = SIZEOF_HSWORD
1029 tAG_MASK = (1 `shift` TAG_BITS) - 1
1030 wORD_SIZE_IN_BITS = WORD_SIZE_IN_BITS