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
262 | MUT_ARR_PTRS_FROZEN0
263 | MUT_ARR_PTRS_FROZEN
276 deriving (Show, Eq, Enum, Ord)
278 {-| This is the main data type of this module, representing a Haskell value on
279 the heap. This reflects
280 <http://hackage.haskell.org/trac/ghc/browser/includes/rts/storage/Closures.h>
282 The data type is parametrized by the type to store references in, which
283 is usually a 'Box' with appropriate type synonym 'Closure'.
311 -- In GHCi, if Linker.h would allow a reverse looup, we could for exported
312 -- functions fun actually find the name here.
313 -- At least the other direction works via "lookupSymbol
314 -- base_GHCziBase_zpzp_closure" and yields the same address (up to tags)
353 -- Card table ignored
370 BlockingQueueClosure {
385 deriving (Show, Functor, Foldable, Traversable)
388 type Closure = GenClosure Box
390 -- | For generic code, this function returns all referenced closures.
391 allPtrs :: GenClosure b -> [b]
392 allPtrs (ConsClosure {..}) = ptrArgs
393 allPtrs (ThunkClosure {..}) = ptrArgs
394 allPtrs (SelectorClosure {..}) = [selectee]
395 allPtrs (IndClosure {..}) = [indirectee]
396 allPtrs (BlackholeClosure {..}) = [indirectee]
397 allPtrs (APClosure {..}) = fun:payload
398 allPtrs (PAPClosure {..}) = fun:payload
399 allPtrs (APStackClosure {..}) = fun:payload
400 allPtrs (BCOClosure {..}) = [instrs,literals,bcoptrs]
401 allPtrs (ArrWordsClosure {..}) = []
402 allPtrs (MutArrClosure {..}) = mccPayload
403 allPtrs (MutVarClosure {..}) = [var]
404 allPtrs (MVarClosure {..}) = [queueHead,queueTail,value]
405 allPtrs (FunClosure {..}) = ptrArgs
406 allPtrs (BlockingQueueClosure {..}) = [link, blackHole, owner, queue]
407 allPtrs (OtherClosure {..}) = hvalues
408 allPtrs (UnsupportedClosure {..}) = []
411 #ifdef PRIM_SUPPORTS_ANY
412 foreign import prim "aToWordzh" aToWord# :: Any -> Word#
413 foreign import prim "slurpClosurezh" slurpClosure# :: Any -> (# Addr#, ByteArray#, Array# b #)
414 foreign import prim "reallyUnsafePtrEqualityUpToTag" reallyUnsafePtrEqualityUpToTag# :: Any -> Any -> Int#
416 -- Workd-around code until http://hackage.haskell.org/trac/ghc/ticket/5931 was
419 -- foreign import prim "aToWordzh" aToWord'# :: Addr# -> Word#
420 foreign import prim "slurpClosurezh" slurpClosure'# :: Word# -> (# Addr#, ByteArray#, Array# b #)
422 foreign import prim "reallyUnsafePtrEqualityUpToTag" reallyUnsafePtrEqualityUpToTag'# :: Word# -> Word# -> Int#
424 -- This is a datatype that has the same layout as Ptr, so that by
425 -- unsafeCoerce'ing, we obtain the Addr of the wrapped value
428 aToWord# :: Any -> Word#
429 aToWord# a = case Ptr' a of mb@(Ptr' _) -> case unsafeCoerce# mb :: Word of W# addr -> addr
431 slurpClosure# :: Any -> (# Addr#, ByteArray#, Array# b #)
432 slurpClosure# a = slurpClosure'# (aToWord# a)
434 reallyUnsafePtrEqualityUpToTag# :: Any -> Any -> Int#
435 reallyUnsafePtrEqualityUpToTag# a b = reallyUnsafePtrEqualityUpToTag'# (aToWord# a) (aToWord# b)
439 -- getClosure x >>= print
441 -- | This returns the raw representation of the given argument. The second
442 -- component of the triple are the words on the heap, and the third component
443 -- are those words that are actually pointers. Once back in Haskell word, the
444 -- 'Word' may be outdated after a garbage collector run, but the corresponding
445 -- 'Box' will still point to the correct value.
446 getClosureRaw :: a -> IO (Ptr StgInfoTable, [Word], [Box])
448 case slurpClosure# (unsafeCoerce# x) of
449 (# iptr, dat, ptrs #) -> do
450 let nelems = (I# (sizeofByteArray# dat)) `div` wORD_SIZE
451 rawWords = [W# (indexWordArray# dat i) | I# i <- [0.. fromIntegral nelems -1] ]
452 pelems = I# (sizeofArray# ptrs)
453 ptrList = amap' Box $ Array 0 (pelems - 1) pelems ptrs
454 -- This is just for good measure, and seems to be not important.
455 mapM_ evaluate ptrList
456 -- This seems to be required to avoid crashes as well
457 void $ evaluate nelems
458 -- The following deep evaluation is crucial to avoid crashes (but why)?
459 mapM_ evaluate rawWords
460 return (Ptr iptr, rawWords, ptrList)
462 -- From compiler/ghci/RtClosureInspect.hs
463 amap' :: (t -> b) -> Array Int t -> [b]
464 amap' f (Array i0 i _ arr#) = map g [0 .. i - i0]
465 where g (I# i#) = case indexArray# arr# i# of
468 -- derived from vacuum-1.0.0.2/src/GHC/Vacuum/Internal.hs, which got it from
469 -- compiler/ghci/DebuggerUtils.hs
470 dataConInfoPtrToNames :: Ptr StgInfoTable -> IO (String, String, String)
471 dataConInfoPtrToNames ptr = do
472 conDescAddress <- getConDescAddress ptr
473 wl <- peekArray0 0 conDescAddress
474 let (pkg, modl, name) = parse wl
475 return (b2s pkg, b2s modl, b2s name)
477 b2s :: [Word8] -> String
478 b2s = fmap (chr . fromIntegral)
480 getConDescAddress :: Ptr StgInfoTable -> IO (Ptr Word8)
481 getConDescAddress ptr'
483 offsetToString <- peek (ptr' `plusPtr` (negate wORD_SIZE))
484 return $ (ptr' `plusPtr` stdInfoTableSizeB)
485 `plusPtr` (fromIntegral (offsetToString :: Word))
486 -- This is code for !ghciTablesNextToCode:
488 | otherwise = peek . intPtrToPtr
494 -- hmmmmmm. Is there any way to tell this?
495 opt_SccProfilingOn = False
497 stdInfoTableSizeW :: Int
498 -- The size of a standard info table varies with profiling/ticky etc,
499 -- so we can't get it from Constants
500 -- It must vary in sync with mkStdInfoTable
502 = size_fixed + size_prof
504 size_fixed = 2 -- layout, type
505 size_prof | opt_SccProfilingOn = 2
508 stdInfoTableSizeB :: Int
509 stdInfoTableSizeB = stdInfoTableSizeW * wORD_SIZE
511 -- From vacuum-1.0.0.2/src/GHC/Vacuum/Internal.hs
512 parse :: [Word8] -> ([Word8], [Word8], [Word8])
513 parse input = if not . all (>0) . fmap length $ [pkg,modl,occ]
514 --then (error . concat)
515 -- ["getConDescAddress:parse:"
516 -- ,"(not . all (>0) . fmap le"
517 -- ,"ngth $ [pkg,modl,occ]"]
518 then ([], [], input) -- Not in the pkg.modl.occ format, for example END_TSO_QUEUE
519 else (pkg, modl, occ)
520 -- = ASSERT (all (>0) (map length [pkg, modl, occ])) (pkg, modl, occ) -- XXXXXXXXXXXXXXXX
522 (pkg, rest1) = break (== fromIntegral (ord ':')) input
524 = (concat $ intersperse [dot] $ reverse modWords, occWord)
526 (modWords, occWord) = if (length rest1 < 1) -- XXXXXXXXx YUKX
527 --then error "getConDescAddress:parse:length rest1 < 1"
528 then parseModOcc [] []
529 else parseModOcc [] (tail rest1)
530 -- ASSERT (length rest1 > 0) (parseModOcc [] (tail rest1))
531 dot = fromIntegral (ord '.')
532 parseModOcc :: [[Word8]] -> [Word8] -> ([[Word8]], [Word8])
534 = case break (== dot) str of
535 (top, []) -> (acc, top)
536 (top, _:bot) -> parseModOcc (top : acc) bot
539 -- | This function returns parsed heap representation of the argument _at this
540 -- moment_, even if it is unevaluated or an indirection or other exotic stuff.
541 -- Beware when passing something to this function, the same caveats as for
543 getClosureData :: a -> IO Closure
544 getClosureData x = do
545 (iptr, wds, ptrs) <- getClosureRaw x
548 t | t >= CONSTR && t <= CONSTR_NOCAF_STATIC -> do
549 (pkg, modl, name) <- dataConInfoPtrToNames iptr
550 if modl == "ByteCodeInstr" && name == "BreakInfo"
551 then return $ UnsupportedClosure itbl
552 else return $ ConsClosure itbl ptrs (drop (length ptrs + 1) wds) pkg modl name
554 t | t >= THUNK && t <= THUNK_STATIC -> do
555 return $ ThunkClosure itbl ptrs (drop (length ptrs + 2) wds)
557 t | t >= FUN && t <= FUN_STATIC -> do
558 return $ FunClosure itbl ptrs (drop (length ptrs + 1) wds)
561 unless (length ptrs >= 1) $
562 fail "Expected at least 1 ptr argument to AP"
563 return $ APClosure itbl
564 (fromIntegral $ wds !! 2)
565 (fromIntegral $ shiftR (wds !! 2) (wORD_SIZE_IN_BITS `div` 2))
566 (head ptrs) (tail ptrs)
569 unless (length ptrs >= 1) $
570 fail "Expected at least 1 ptr argument to PAP"
571 return $ PAPClosure itbl
572 (fromIntegral $ wds !! 2)
573 (fromIntegral $ shiftR (wds !! 2) (wORD_SIZE_IN_BITS `div` 2))
574 (head ptrs) (tail ptrs)
577 unless (length ptrs >= 1) $
578 fail "Expected at least 1 ptr argument to AP_STACK"
579 return $ APStackClosure itbl (head ptrs) (tail ptrs)
582 unless (length ptrs >= 1) $
583 fail "Expected at least 1 ptr argument to THUNK_SELECTOR"
584 return $ SelectorClosure itbl (head ptrs)
587 unless (length ptrs >= 1) $
588 fail "Expected at least 1 ptr argument to IND"
589 return $ IndClosure itbl (head ptrs)
591 unless (length ptrs >= 1) $
592 fail "Expected at least 1 ptr argument to IND_STATIC"
593 return $ IndClosure itbl (head ptrs)
595 unless (length ptrs >= 1) $
596 fail "Expected at least 1 ptr argument to BLACKHOLE"
597 return $ BlackholeClosure itbl (head ptrs)
600 return $ BCOClosure itbl (ptrs !! 0) (ptrs !! 1) (ptrs !! 2)
601 (fromIntegral $ wds !! 4)
602 (fromIntegral $ shiftR (wds !! 4) (wORD_SIZE_IN_BITS `div` 2))
606 return $ ArrWordsClosure itbl (wds !! 1) (drop 2 wds)
608 t | t == MUT_ARR_PTRS_FROZEN || t == MUT_ARR_PTRS_FROZEN0 ->
609 return $ MutArrClosure itbl (wds !! 1) (wds !! 2) ptrs
611 t | t == MUT_VAR_CLEAN || t == MUT_VAR_DIRTY ->
612 return $ MutVarClosure itbl (head ptrs)
614 t | t == MVAR_CLEAN || t == MVAR_DIRTY ->
615 return $ MVarClosure itbl (ptrs !! 0) (ptrs !! 1) (ptrs !! 2)
618 return $ OtherClosure itbl ptrs wds
619 -- return $ BlockingQueueClosure itbl
620 -- (ptrs !! 0) (ptrs !! 1) (ptrs !! 2) (ptrs !! 3)
622 -- return $ OtherClosure itbl ptrs wds
625 return $ UnsupportedClosure itbl
627 -- | Like 'getClosureData', but taking a 'Box', so it is easier to work with.
628 getBoxedClosureData :: Box -> IO Closure
629 getBoxedClosureData (Box a) = getClosureData a
632 isChar :: GenClosure b -> Maybe Char
633 isChar (ConsClosure { name = "C#", dataArgs = [ch], ptrArgs = []}) = Just (chr (fromIntegral ch))
636 isCons :: GenClosure b -> Maybe (b, b)
637 isCons (ConsClosure { name = ":", dataArgs = [], ptrArgs = [h,t]}) = Just (h,t)
640 isTup :: GenClosure b -> Maybe [b]
641 isTup (ConsClosure { dataArgs = [], ..}) =
642 if length name >= 3 &&
643 head name == '(' && last name == ')' &&
644 all (==',') (tail (init name))
645 then Just ptrArgs else Nothing
649 isNil :: GenClosure b -> Bool
650 isNil (ConsClosure { name = "[]", dataArgs = [], ptrArgs = []}) = True
653 -- | A pretty-printer that tries to generate valid Haskell for evalutated data.
654 -- It assumes that for the included boxes, you already replaced them by Strings
655 -- using 'Data.Foldable.map' or, if you need to do IO, 'Data.Foldable.mapM'.
657 -- The parameter gives the precedendence, to avoid avoidable parenthesises.
658 ppClosure :: (Int -> b -> String) -> Int -> GenClosure b -> String
659 ppClosure showBox prec c = case c of
660 _ | Just ch <- isChar c -> app $
662 _ | Just (h,t) <- isCons c -> addBraces (5 <= prec) $
663 showBox 5 h ++ " : " ++ showBox 4 t
664 _ | Just vs <- isTup c ->
665 "(" ++ intercalate "," (map (showBox 0) vs) ++ ")"
666 ConsClosure {..} -> app $
667 name : map (showBox 10) ptrArgs ++ map show dataArgs
668 ThunkClosure {..} -> app $
669 "_thunk" : map (showBox 10) ptrArgs ++ map show dataArgs
670 SelectorClosure {..} -> app
671 ["_sel", showBox 10 selectee]
672 IndClosure {..} -> app
673 ["_ind", showBox 10 indirectee]
674 BlackholeClosure {..} -> app
675 ["_bh", showBox 10 indirectee]
676 APClosure {..} -> app $ map (showBox 10) $
678 PAPClosure {..} -> app $ map (showBox 10) $
680 APStackClosure {..} -> app $ map (showBox 10) $
682 BCOClosure {..} -> app
684 ArrWordsClosure {..} -> app
685 ["toArray", "("++show (length arrWords) ++ " words)", intercalate "," (shorten (map show arrWords)) ]
686 MutArrClosure {..} -> app
687 ["toMutArray", "("++show (length mccPayload) ++ " ptrs)", intercalate "," (shorten (map (showBox 10) mccPayload))]
688 MutVarClosure {..} -> app $
689 ["_mutVar", (showBox 10) var]
690 MVarClosure {..} -> app $
691 ["MVar", (showBox 10) value]
693 "_fun" ++ braceize (map (showBox 0) ptrArgs ++ map show dataArgs)
694 BlockingQueueClosure {..} ->
698 UnsupportedClosure {..} ->
702 app xs = addBraces (10 <= prec) (intercalate " " xs)
704 shorten xs = if length xs > 20 then take 20 xs ++ ["(and more)"] else xs
708 For more global views of the heap, you can use heap maps. These come in
709 variations, either a trees or as graphs, depending on
710 whether you want to detect cycles and sharing or not.
712 The entries of a 'HeapGraph' can be annotated with arbitrary values. Most
713 operations expect this to be in the 'Monoid' class: They use 'mempty' to
714 annotate closures added because the passed values reference them, and they
715 use 'mappend' to combine the annotations when two values conincide, e.g.
716 during 'updateHeapGraph'.
719 -- | Heap maps as tree, i.e. no sharing, no cycles.
720 data HeapTree = HeapTree Box (GenClosure HeapTree) | EndOfHeapTree
722 heapTreeClosure :: HeapTree -> Maybe (GenClosure HeapTree)
723 heapTreeClosure (HeapTree _ c) = Just c
724 heapTreeClosure EndOfHeapTree = Nothing
726 -- | Constructing an 'HeapTree' from a boxed value. It takes a depth parameter
727 -- that prevents it from running ad infinitum for cyclic or infinite
729 buildHeapTree :: Int -> Box -> IO HeapTree
730 buildHeapTree 0 _ = do
731 return $ EndOfHeapTree
732 buildHeapTree n b = do
733 c <- getBoxedClosureData b
734 c' <- T.mapM (buildHeapTree (n-1)) c
735 return $ HeapTree b c'
737 -- | Pretty-Printing a heap Tree
739 -- Example output for @[Just 4, Nothing, *something*]@, where *something* is an
740 -- unevaluated expression depending on the command line argument.
742 -- >[Just (I# 4),Nothing,Just (_thunk ["arg1","arg2"])]
743 ppHeapTree :: HeapTree -> String
746 go _ EndOfHeapTree = "..."
747 go prec t@(HeapTree _ c')
748 | Just s <- isHeapTreeString t = show s
749 | Just l <- isHeapTreeList t = "[" ++ intercalate "," (map ppHeapTree l) ++ "]"
750 | Just bc <- disassembleBCO heapTreeClosure c'
751 = app ("_bco" : map (go 10) (concatMap F.toList bc))
752 | otherwise = ppClosure go prec c'
755 app xs = addBraces (10 <= prec) (intercalate " " xs)
757 isHeapTreeList :: HeapTree -> Maybe ([HeapTree])
758 isHeapTreeList tree = do
759 c <- heapTreeClosure tree
764 t' <- isHeapTreeList t
767 isHeapTreeString :: HeapTree -> Maybe String
768 isHeapTreeString t = do
769 list <- isHeapTreeList t
770 -- We do not want to print empty lists as "" as we do not know that they
771 -- are really strings.
774 else mapM (isChar <=< heapTreeClosure) list
776 -- | For heap graphs, i.e. data structures that also represent sharing and
777 -- cyclic structures, these are the entries. If the referenced value is
778 -- @Nothing@, then we do not have that value in the map, most likely due to
779 -- exceeding the recursion bound passed to 'buildHeapGraph'.
781 -- Besides a pointer to the stored value and the closure representation we
782 -- also keep track of whether the value was still alive at the last update of the
783 -- heap graph. In addition we have a slot for arbitrary data, for the user's convenience.
784 data HeapGraphEntry a = HeapGraphEntry {
786 hgeClosure :: GenClosure (Maybe HeapGraphIndex),
789 deriving (Show, Functor)
790 type HeapGraphIndex = Int
792 -- | The whole graph. The suggested interface is to only use 'lookupHeapGraph',
793 -- as the internal representation may change. Nevertheless, we export it here:
794 -- Sometimes the user knows better what he needs than we do.
795 newtype HeapGraph a = HeapGraph (M.IntMap (HeapGraphEntry a))
798 lookupHeapGraph :: HeapGraphIndex -> (HeapGraph a) -> Maybe (HeapGraphEntry a)
799 lookupHeapGraph i (HeapGraph m) = M.lookup i m
801 heapGraphRoot :: HeapGraphIndex
804 -- | Creates a 'HeapGraph' for the value in the box, but not recursing further
805 -- than the given limit. The initial value has index 'heapGraphRoot'.
808 => Int -- ^ Search limit
809 -> a -- ^ Data value for the root
810 -> Box -- ^ The value to start with
812 buildHeapGraph limit rootD initialBox =
813 fst <$> multiBuildHeapGraph limit [(rootD, initialBox)]
815 -- | Creates a 'HeapGraph' for the values in multiple boxes, but not recursing
816 -- further than the given limit.
818 -- Returns the 'HeapGraph' and the indices of initial values. The arbitrary
819 -- type @a@ can be used to make the connection between the input and the
820 -- resulting list of indices, and to store additional data.
823 => Int -- ^ Search limit
824 -> [(a, Box)] -- ^ Starting values with associated data entry
825 -> IO (HeapGraph a, [(a, HeapGraphIndex)])
826 multiBuildHeapGraph limit = generalBuildHeapGraph limit (HeapGraph M.empty)
828 -- | Adds an entry to an existing 'HeapGraph'.
830 -- Returns the updated 'HeapGraph' and the index of the added value.
833 => Int -- ^ Search limit
834 -> a -- ^ Data to be stored with the added value
835 -> Box -- ^ Value to add to the graph
836 -> HeapGraph a -- ^ Graph to extend
837 -> IO (HeapGraphIndex, HeapGraph a)
838 addHeapGraph limit d box hg = do
839 (hg', [(_,i)]) <- generalBuildHeapGraph limit hg [(d,box)]
842 -- | Adds the given annotation to the entry at the given index, using the
843 -- 'mappend' operation of its 'Monoid' instance.
844 annotateHeapGraph :: Monoid a => a -> HeapGraphIndex -> HeapGraph a -> HeapGraph a
845 annotateHeapGraph d i (HeapGraph hg) = HeapGraph $ M.update go i hg
847 go hge = Just $ hge { hgeData = hgeData hge <> d }
849 generalBuildHeapGraph
854 -> IO (HeapGraph a, [(a, HeapGraphIndex)])
855 generalBuildHeapGraph limit _ _ | limit <= 0 = error "buildHeapGraph: limit has to be positive"
856 generalBuildHeapGraph limit (HeapGraph hg) addBoxes = do
857 -- First collect all boxes from the existing heap graph
858 let boxList = [ (hgeBox hge, i) | (i, hge) <- M.toList hg ]
859 indices | M.null hg = [0..]
860 | otherwise = [1 + fst (M.findMax hg)..]
862 initialState = (boxList, indices, [])
863 -- It is ok to use the Monoid (IntMap a) instance here, because
864 -- we will, besides the first time, use 'tell' only to add singletons not
866 (is, hg') <- runWriterT (evalStateT run initialState)
867 -- Now add the annotations of the root values
868 let hg'' = foldl' (flip (uncurry annotateHeapGraph)) (HeapGraph hg') is
872 lift $ tell hg -- Start with the initial map
873 forM addBoxes $ \(d, b) -> do
874 -- Cannot fail, as limit is not zero here
875 Just i <- add limit b
878 add 0 _ = return Nothing
880 -- If the box is in the map, return the index
881 (existing,_,_) <- get
882 mbI <- liftIO $ findM (areBoxesEqual b . fst) existing
884 Just (_,i) -> return $ Just i
886 -- Otherwise, allocate a new index
889 modify (\(x,y,z) -> ((b,i):x, y, z))
890 -- Look up the closure
891 c <- liftIO $ getBoxedClosureData b
892 -- Find indicies for all boxes contained in the map
893 c' <- T.mapM (add (n-1)) c
894 -- Add add the resulting closure to the map
895 lift $ tell (M.singleton i (HeapGraphEntry b c' True mempty))
898 i <- gets (head . (\(_,b,_) -> b))
899 modify (\(a,b,c) -> (a, tail b, c))
902 -- | This function updates a heap graph to reflect the current state of
903 -- closures on the heap, conforming to the following specification.
905 -- * Every entry whose value has been garbage collected by now is marked as
906 -- dead by setting 'hgeLive' to @False@
907 -- * Every entry whose value is still live gets the 'hgeClosure' field updated
908 -- and newly referenced closures are, up to the given depth, added to the graph.
909 -- * A map mapping previous indicies to the corresponding new indicies is returned as well.
910 -- * The closure at 'heapGraphRoot' stays at 'heapGraphRoot'
911 updateHeapGraph :: Monoid a => Int -> HeapGraph a -> IO (HeapGraph a, HeapGraphIndex -> HeapGraphIndex)
912 updateHeapGraph limit (HeapGraph startHG) = do
913 (hg', indexMap) <- runWriterT $ foldM go (HeapGraph M.empty) (M.toList startHG)
914 return (hg', (M.!) indexMap)
917 (j, hg') <- liftIO $ addHeapGraph limit (hgeData hge) (hgeBox hge) hg
918 tell (M.singleton i j)
921 -- | Pretty-prints a HeapGraph. The resulting string contains newlines. Example
922 -- for @let s = \"Ki\" in (s, s, cycle \"Ho\")@:
925 -- > x6 = C# 'H' : C# 'o' : x6
927 ppHeapGraph :: HeapGraph a -> String
928 ppHeapGraph (HeapGraph m) = letWrapper ++ ppRef 0 (Just heapGraphRoot)
930 -- All variables occuring more than once
931 bindings = boundMultipleTimes (HeapGraph m) [heapGraphRoot]
936 else "let " ++ intercalate "\n " (map ppBinding bindings) ++ "\nin "
938 bindingLetter i = case hgeClosure (iToE i) of
939 ThunkClosure {..} -> 't'
940 SelectorClosure {..} -> 't'
941 APClosure {..} -> 't'
942 PAPClosure {..} -> 'f'
943 BCOClosure {..} -> 't'
944 FunClosure {..} -> 'f'
947 ppBindingMap = M.fromList $
949 map (zipWith (\j (i,c) -> (i, [c] ++ show j)) [(1::Int)..]) $
950 groupBy ((==) `on` snd) $
951 sortBy (compare `on` snd)
952 [ (i, bindingLetter i) | i <- bindings ]
954 ppVar i = ppBindingMap M.! i
955 ppBinding i = ppVar i ++ " = " ++ ppEntry 0 (iToE i)
958 | Just s <- isString hge = show s
959 | Just l <- isList hge = "[" ++ intercalate "," (map (ppRef 0) l) ++ "]"
960 | Just bc <- disassembleBCO (fmap (hgeClosure . iToE)) (hgeClosure hge)
961 = app ("_bco" : map (ppRef 10) (concatMap F.toList bc))
962 | otherwise = ppClosure ppRef prec (hgeClosure hge)
965 app xs = addBraces (10 <= prec) (intercalate " " xs)
967 ppRef _ Nothing = "..."
968 ppRef prec (Just i) | i `elem` bindings = ppVar i
969 | otherwise = ppEntry prec (iToE i)
972 iToUnboundE i = if i `elem` bindings then Nothing else M.lookup i m
974 isList :: HeapGraphEntry a -> Maybe ([Maybe HeapGraphIndex])
976 if isNil (hgeClosure hge)
979 (h,t) <- isCons (hgeClosure hge)
985 isString :: HeapGraphEntry a -> Maybe String
988 -- We do not want to print empty lists as "" as we do not know that they
989 -- are really strings.
992 else mapM (isChar . hgeClosure <=< iToUnboundE <=< id) list
995 -- | In the given HeapMap, list all indices that are used more than once. The
996 -- second parameter adds external references, commonly @[heapGraphRoot]@.
997 boundMultipleTimes :: HeapGraph a -> [HeapGraphIndex] -> [HeapGraphIndex]
998 boundMultipleTimes (HeapGraph m) roots = map head $ filter (not.null) $ map tail $ group $ sort $
999 roots ++ concatMap (catMaybes . allPtrs . hgeClosure) (M.elems m)
1001 -- | This function integrates the disassembler in "GHC.Disassembler". The first
1002 -- argument should a function that dereferences the pointer in the closure to a
1005 -- If any of these return 'Nothing', then 'disassembleBCO' returns Nothing
1006 disassembleBCO :: (a -> Maybe (GenClosure b)) -> GenClosure a -> Maybe [BCI b]
1007 disassembleBCO deref (BCOClosure {..}) = do
1008 opsC <- deref instrs
1009 litsC <- deref literals
1010 ptrsC <- deref bcoptrs
1011 return $ disassemble (mccPayload ptrsC) (arrWords litsC) (toBytes (bytes opsC) (arrWords opsC))
1012 disassembleBCO _ _ = Nothing
1016 findM :: (a -> IO Bool) -> [a] -> IO (Maybe a)
1017 findM _p [] = return Nothing
1020 if b then return (Just x) else findM p xs
1022 addBraces :: Bool -> String -> String
1023 addBraces True t = "(" ++ t ++ ")"
1024 addBraces False t = t
1026 braceize :: [String] -> String
1028 braceize xs = "{" ++ intercalate "," xs ++ "}"
1030 -- This used to be available via GHC.Constants
1031 #include "MachDeps.h"
1032 wORD_SIZE, tAG_MASK, wORD_SIZE_IN_BITS :: Int
1033 wORD_SIZE = SIZEOF_HSWORD
1034 tAG_MASK = (1 `shift` TAG_BITS) - 1
1035 wORD_SIZE_IN_BITS = WORD_SIZE_IN_BITS