packages feed

packman (empty) → 0.3.0

raw patch · 20 files changed

+4464/−0 lines, 20 filesdep +Cabaldep +QuickCheckdep +arraysetup-changedbinary-added

Dependencies added: Cabal, QuickCheck, array, base, binary, bytestring, directory, ghc-prim, packman, primitive

Files

+ GHC/Packing.hs view
@@ -0,0 +1,224 @@+{-# OPTIONS_HADDOCK prune #-}+{-# LANGUAGE ScopedTypeVariables #-}++{- | ++Module      : GHC.Packing+Copyright   : (c) Jost Berthold, 2010-2015,+License     : BSD3+Maintainer  : jost.berthold@gmail.com+Stability   : experimental+Portability : no (depends on GHC internals)++= Serialisation of Haskell data structures (independent of evaluation)++Haskell heap structures can be serialised, capturing their current+state of evaluation, and deserialised later during the same program+run (effectively duplicating the data). Serialised data can also be+written to storage or sent over a network, and deserialised in a+different run or different instance of the /same/ executable binary.++The feature can be used to implement message passing over a network+(which is where the runtime support originated), or for various+applications based on data persistence, for instance checkpointing and+memoisation.++The library described here supports an operation to serialise Haskell+heap data:++> trySerialize :: a -> IO (Serialized a)++The routine will throw a 'PackException' if an error occurs inside the+C code which accesses the Haskell heap (see @'PackException'@).+In presence of concurrent threads, another thread might be evaluating+data /referred to/ by the data to be serialised. In this case, the calling+thread will /block/ on the ongoing evaluation and continue when evaluated+data is available.+Internally, there is a 'PackException' 'P_BLACKHOLE' to signal the+condition, but it is hidden inside the core library+(see <#background Background Information> below).++The inverse operation to serialisation is++> deserialize :: Serialized a -> IO a++The data type 'Serialized' a includes a phantom type @a@ to ensure+type safety within one and the same program run. Type @a@ can be+polymorphic (at compile time, that is) when 'Serialized' @a@ is not used+apart from being argument to 'deserialize'.++The @Show@, @Read@, and @Binary@ instances of @Serialized a@ require an+additional 'Typeable' context (which requires @a@ to be monomorphic)+in order to implement dynamic type checks when parsing and deserialising+data from external sources.+Consequently, the 'PackException' type contains exceptions which indicate+parse errors and type/binary mismatch.++-}++module GHC.Packing+    ( -- * Serialisation Operations+      trySerialize, trySerializeWith+    , deserialize++      -- * Data Types and instances+    , Serialized+      -- $ShowReadBinary+    , PackException(..)+      -- $packexceptions++      -- * Serialisation and binary file I/O+    , encodeToFile +    , decodeFromFile++    -- * Background Information+      -- $primitives+   )+    where++-- all essentials are defined in other modules, and reexported here+import GHC.Packing.PackException+import GHC.Packing.Type+import GHC.Packing.Core++import Data.Binary+import Control.Exception+import Data.Typeable+++-- | Write serialised binary data directly to a file. May throw 'PackException's.+encodeToFile :: Typeable a => FilePath -> a -> IO ()+encodeToFile path x = trySerialize x >>= encodeFile path++-- | Directly read binary serialised data from a file. May throw+-- 'PackException's (catches I/O and Binary exceptions from decoding+-- the file and re-throws 'P_ParseError')+decodeFromFile :: Typeable a => FilePath -> IO a+decodeFromFile path = do ser <- (decodeFile path) +                                  `catch` +                                  (\(e::ErrorCall) -> throw P_ParseError)+                         deserialize ser -- exceptions here go through++----------------------------------------+-- digressive documentation++{- $ShowReadBinary++The power of evaluation-orthogonal serialisation is that one can+/externalise/ partially evaluated data (containing thunks), for+instance write it to disk or send it over a network.++Therefore, the module defines a 'Data.Binary' instance for+'Serialized' a, as well as instances for 'Read' and 'Show'@ which+satisfy @ 'read' . 'show' == 'id' :: 'Serialized' a -> 'Serialized' a@.++The phantom type is enough to ensure type-correctness when serialised+data remain in one single program run. However, when data from+previous runs are read in from an external source, their type needs to+be checked at runtime. Type information must be stored together with+the (binary) serialisation data.++The serialised data contain pointers to static data in the generating+program (top-level functions and constants) and very likely to+additional library code. Therefore, the /exact same binary/ must be+used when reading in serialised data from an external source. A hash+of the executable is therefore included in the representation as well.++-}++{- $packexceptions++'PackException's can occur at Haskell level or in the foreign primop.+The Haskell-level exceptions all occur when reading in+'GHC.Packing.Serialised' data, and are:++* 'P_BinaryMismatch': the serialised data have been produced by a+different executable (must be the same binary).+* 'P_TypeMismatch': the serialised data have the wrong type+* 'P_ParseError': serialised data could not be parsed (from binary or+text format)++The other exceptions are return codes of the foreign primitive+operation, and indicate errors at the C level. Most of them occur when+serialising data; the exception is 'P_GARBLED' which indicates corrupt+serialised data.++-}++{- $primitives++  #background#++The functionality exposed by this module builds on serialisation of+Haskell heap graph structures, first implemented in the context of+implementing the GpH implementation GUM (Graph reduction on a +Unified Memory System) and later adopted by the implementation of+Eden. Independent of its evaluation state, data and thunks can be+transferred between the (independent) heaps of several running Haskell+runtime system instances which execute the same executable.++The idea to expose the heap data serialisation functionality +(often called /packing/) to Haskell by itself was first described in + Jost Berthold. /Orthogonal Serialisation for Haskell/.+ In Jurriaan Hage and Marco Morazan, editors, + /IFL'10, 22nd Symposium on Implementation and Application of + Functional Languages/, Springer LNCS 6647, pages 38-53, 2011.+This paper can be found at +<http://www.mathematik.uni-marburg.de/~eden/papers/mainIFL10-withCopyright.pdf>,+the original publication is available at +<http://www.springerlink.com/content/78642611n7623551/>.++The core runtime support consists of just two operations:+(slightly paraphrasing the way in which GHC implements the IO monad here)++> serialize#   :: a -> IO ByteArray# -- OUTDATED, see below+> deserialize# :: ByteArray# -> IO a -- which is actually pure from a mathematical POW++However, these operations are completely unsafe with respect to Haskell+types, and may fail at runtime for various other reasons as well. +Type safety can be established by a phantom type, but needs to be checked+at runtime when the resulting data structure is externalised (for instance,+saved to a file). Besides prohibiting unprotected type casts, another+restriction that needs to be explicitly checked in this case is that +different programs cannot exchange data by this serialisation. When data are+serialised during execution, they can only be deserialised by exactly the +same executable binary because they contain code pointers that will change+even by recompilation.++Other failures can occur because of the runtime system's limitations, +and because some mutable data types are not allowed to be serialised.+A newer API therefore suggests additions towards exception handling+and better usability.+The original primitive @'serialize'@ is modified and now returns error+codes, leading to the following type (again paraphrasing):++> trySerialize# :: a -> IO ( Int# , ByteArray# )++where the @Int#@ encodes potential error conditions returned by the runtime.++A second primitive operation has been defined, which uses a pre-allocated+@ByteArray#@++> trySerializeWith# :: a -> ByteArray# -> IO ( Int# , ByteArray# )++Further to returning error codes, the newer primitive operation do not block+the calling thread when the serialisation encounters a blackhole in the+heap.+It would be possible to observe the existence of blackholes from Haskell by+the return code of these primitive operation. This could - in theory - be+used to explicitly control and avoid blocking (avoiding unresponsive behaviour).+In practice, however, making blackholes observable from Haskell is+certainly undesirable. The primitive operations return the address of the+blackhole, and the caller will block on this blackhole at +the Haskell level (see code in the @GHC.Packing.Core@ module).++The Haskell layer and its types protect the interface function @'deserialize'@+from being applied to  grossly wrong data (by checking a fingerprint of the +executable and the expected type), but deserialisation is still rather fragile +(unpacking code pointers and data).+The primitive operation in the runtime system will only detect grossly wrong+formats, and the primitive will return error code @'P_GARBLED'@ when data+corruption is detected.++> deserialize# :: ByteArray# -> IO ( Int# , a )+-}
+ GHC/Packing/Core.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE CPP, MagicHash, UnboxedTuples #-}+{-# LANGUAGE GHCForeignImportPrim, ForeignFunctionInterface, +             UnliftedFFITypes #-}++{-| ++Module      : GHC.Packing+Copyright   : (c) Jost Berthold, 2010-2015,+License     : BSD3+Maintainer  : jost.berthold@gmail.com+Stability   : experimental+Portability : no (depends on GHC internals)++= Wrapper module for the foreign primitive operations++-}++module GHC.Packing.Core+    ( trySerialize, trySerializeWith, deserialize+    ) where++import GHC.Packing.Type+import GHC.Packing.PackException++import GHC.Exts+import GHC.Prim+import Control.Monad.Primitive+import Data.Primitive.ByteArray++import Control.Exception(throw)++-- the entire package won't support GHC < 7.8+#if __GLASGOW_HASKELL__ < 708+#error This module assumes GHC-7.8 or above+#endif++-- | Serialises its argument (in current evaluation state, as a thunk).+-- May block if the argument captures (blackhole'd) data under evaluation,+-- may throw 'PackException's to signal errors.+-- This version uses a default buffer of 10MB (see 'trySerializeWith'+-- for a version with flexible buffer size).+trySerialize :: a -> IO (Serialized a) -- throws PackException (RTS)+trySerialize x = trySerializeWith x defaultBufSize++-- | default buffer size used by trySerialize+defaultBufSize :: Int+defaultBufSize = 10 * 2^20 -- 10 MB++-- | Extended serialisation interface: Allocates a buffer of given size (in+-- bytes), serialises data into it, then truncates the buffer to the+-- required size before returning it (as @'Serialized' a@)+trySerializeWith :: a -> Int -> IO (Serialized a) -- using instance PrimMonad IO+trySerializeWith dat bufsize+    = do buf <- newByteArray bufsize+         size <- trySerializeInto buf dat+         buf' <- truncate' buf size+         ByteArray b# <- unsafeFreezeByteArray buf'+         return (Serialized { packetData = b# })++-- | core routine. Packs x into mutable byte array buf, returns size+-- of packed x in buf+trySerializeInto :: MutableByteArray RealWorld -> a -> IO Int+trySerializeInto (MutableByteArray buf# ) x +    = primitive (tryPack (unsafeCoerce# x :: Any) buf# )++-- | calls primitive, decodes/throws errors + wraps Int# size into Int+tryPack :: Any -> MutableByteArray# s+        -> State# s -> (# State# s , Int #)+tryPack x# buf# s = case tryPack# x# buf# s of+                      (# s', 0#, size# #) -> (# s', I# size# #)+                      (# s', e#,   0#  #) +                          | isBHExc e# -> repack s'+                          | otherwise  -> (# s', throw (decodeEx e#) #)+    where -- packing blocked, eval the blocking closure that we found+          -- (i.e. block on it) and re-pack afterwards. The first+          -- StgWord of the ByteArray contains the address (written by+          -- the packing routine, see BLACKHOLE case in packClosure).+      repack s = case readAddrArray# buf# 0# s of+                   (# s', bh #) -> case (addrToAny# bh) of -- or seq it?+                                     _ -> tryPack x# buf# s'++-- | serialisation primitive, implemented in C. Returns: a+-- status/error code and size used inside the array+foreign import prim "stg_tryPack" tryPack#+    :: Any -> MutableByteArray# s -> State# s -> (# State# s, Int#, Int# #)++-- GHC-7.8 does not have an in-place shrink operation for MutableByteArrays+-- (added in GHC-7.9 on August 16, 2014)+-- GHC-7.9, August 2014 :: MutableByteArray# s -> Int# -> State# s -> State# s+-- with this one available, tryPack could do the work+-- for GHC-7.8, we copy+truncate' :: PrimMonad m => MutableByteArray (PrimState m) -> Int -> m (MutableByteArray (PrimState m))+truncate' b size +    = if sizeofMutableByteArray b < size+      then throw P_NOBUFFER -- XXX other error?+      else do b' <- newByteArray size+              copyMutableByteArray b' 0 b 0 size+              return b'++--------------------------------------------------------++-- | Deserialisation function. May throw @'PackException'@ @'P_GARBLED'@+deserialize :: Serialized a -> IO a+deserialize p = primitive (deser (packetData p))++deser :: ByteArray# -> State# s -> (# State# s, a #)+deser buf s = case unpack# buf s of+                (# s', 0#, x #) -> (# s', x #)+                (# s', n#, _ #) -> (# s', throw (decodeEx n#) #)++foreign import prim "stg_unpack" unpack# :: ByteArray# -> State# s -> (# State# s, Int#, a #)+
+ GHC/Packing/PackException.hsc view
@@ -0,0 +1,100 @@+{-# LANGUAGE MagicHash, DeriveDataTypeable #-}++{-| ++Module      : GHC.Packing.PackException+Copyright   : (c) Jost Berthold, 2010-2015,+License     : BSD3+Maintainer  : jost.berthold@gmail.com+Stability   : experimental+Portability : no (depends on GHC internals)++Exception type for packman library, using magic constants #include'd+from a C header file shared with the foreign primitive operation code.++'PackException's can occur at Haskell level or in the foreign primop.++All Haskell-level exceptions are cases of invalid data when /reading/+and /deserialising/ 'GHC.Packing.Serialised' data:++* 'P_BinaryMismatch': serialised data were produced by a+different executable (must be the same binary).+* 'P_TypeMismatch': serialised data have the wrong type+* 'P_ParseError': serialised data could not be parsed (from binary or+text format)++The exceptions caused by the foreign primops (return codes) +indicate errors at the C level. Most of them can occur when+serialising data; the exception is 'P_GARBLED' which indicates that+serialised data is garbled.++-}++module GHC.Packing.PackException+    ( PackException(..)+    , decodeEx+    , isBHExc+    ) where++-- bring in error codes from cbits/Errors.h+#include "Errors.h"++import GHC.Exts+import GHC.Prim+import Control.Exception+import Data.Typeable++-- | Packing exception codes, matching error codes implemented in the+-- runtime system or describing errors which can occur within Haskell.+data PackException =+    -- keep in sync with Errors.h+    P_SUCCESS      -- ^ no error, ==0.+        -- Internal code, should never be seen by users.+        | P_BLACKHOLE    -- ^ RTS: packing hit a blackhole.+        -- Used internally, not passed to users.+        | P_NOBUFFER     -- ^ RTS: buffer too small+        | P_CANNOTPACK  -- ^ RTS: contains closure which cannot be packed (MVar, TVar)+        | P_UNSUPPORTED  -- ^ RTS: contains unsupported closure type (implementation missing)+        | P_IMPOSSIBLE   -- ^ RTS: impossible case (stack frame, message,...RTS bug!)+        | P_GARBLED       -- ^ RTS: corrupted data for deserialisation++        -- Error codes from inside Haskell+        | P_ParseError     -- ^ Haskell: Packet data could not be parsed+        | P_BinaryMismatch -- ^ Haskell: Executable binaries do not match+        | P_TypeMismatch   -- ^ Haskell: Packet data encodes unexpected type+     deriving (Eq, Ord, Typeable)++-- | decodes an 'Int#' to a @'PackException'@. Magic constants are read+-- from file /cbits///Errors.h/.+decodeEx :: Int## -> PackException+decodeEx #{const P_SUCCESS}##     = P_SUCCESS -- unexpected+decodeEx #{const P_BLACKHOLE}##   = P_BLACKHOLE+decodeEx #{const P_NOBUFFER}##    = P_NOBUFFER+decodeEx #{const P_CANNOTPACK}## = P_CANNOTPACK+decodeEx #{const P_UNSUPPORTED}## = P_UNSUPPORTED+decodeEx #{const P_IMPOSSIBLE}##  = P_IMPOSSIBLE+decodeEx #{const P_GARBLED}##     = P_GARBLED+decodeEx #{const P_ParseError}##     = P_ParseError+decodeEx #{const P_BinaryMismatch}## = P_BinaryMismatch+decodeEx #{const P_TypeMismatch}##   = P_TypeMismatch+decodeEx i##  = error $ "Error value " ++ show (I## i##) ++ " not defined!"++instance Show PackException where+    -- keep in sync with Errors.h+    show P_SUCCESS = "No error." -- we do not expect to see this+    show P_BLACKHOLE     = "Packing hit a blackhole"+    show P_NOBUFFER      = "Pack buffer too small"+    show P_CANNOTPACK    = "Data contain a closure that cannot be packed (MVar, TVar)"+    show P_UNSUPPORTED   = "Contains an unsupported closure type (whose implementation is missing)"+    show P_IMPOSSIBLE    = "An impossible case happened (stack frame, message). This is probably a bug."+    show P_GARBLED       = "Garbled data for deserialisation"+    show P_ParseError     = "Packet parse error"+    show P_BinaryMismatch = "Executable binaries do not match"+    show P_TypeMismatch   = "Packet data has unexpected type"++instance Exception PackException++-- | internal: checks if the given code indicates 'P_BLACKHOLE'+isBHExc :: Int## -> Bool+isBHExc #{const P_BLACKHOLE}##   = True+isBHExc e## = False
+ GHC/Packing/Type.hs view
@@ -0,0 +1,294 @@+{-# LANGUAGE CPP, MagicHash, BangPatterns, ScopedTypeVariables #-}++{-|++Module      : GHC.Packing.Type+Copyright   : (c) Jost Berthold, 2010-2015,+License     : BSD3+Maintainer  : Jost Berthold <jost.berthold@gmail.com>+Stability   : experimental+Portability : no (depends on GHC internals)++= Serialized type for the packman library, instances and helpers++The data type @'Serialized' a@ includes a phantom type @a@ to ensure+type safety within one and the same program run. Type @a@ can be+polymorphic (at compile time, that is) when @'Serialized' a@ is not used+apart from being argument to @'deserialize'@.++The @Show@, @Read@, and @Binary@ instances of @Serialized a@ require an+additional @Typeable@ context (which requires @a@ to be monomorphic)+in order to implement dynamic type checks when parsing and deserialising+data from external sources.++-}++module GHC.Packing.Type+--    ( Serialized(..)+-- TOOD assemble export list with structure and headings/text blocks+--    , ...    ) +        where++import GHC.Prim -- ByteArray#+import GHC.Exts ( Int(..)) -- I#++-- Read and Show instances+import Text.Printf ( printf )+import Text.ParserCombinators.ReadP (sepBy1, many1, ReadP, munch,+    munch1, pfail, readP_to_S, satisfy, skipSpaces, string )+import Data.Char ( isDigit )++-- Binary instance+import Data.Binary ( Get, Binary(..), encode, decode, encodeFile, decodeFile )++-- we use UArrays of machine word size (TargetWord)+import Data.Word( Word, Word64, Word32 )+import Data.Array.Base ( UArray(..), elems, listArray )+import Foreign.Storable ( sizeOf )++-- for dynamic type checks when parsing+import Data.Typeable (Typeable(..), typeOf)+#if MIN_VERSION_base(4,8,0)+import Data.Typeable.Internal (TypeRep(..), typeRepFingerprint)+#else+import Data.Typeable.Internal (TypeRep(..))+#endif+import qualified GHC.Fingerprint++-- for a hash of the executable. Using GHC.Fingerprint.getFileHash+import GHC.Fingerprint(getFileHash)+import System.Environment+import System.IO.Unsafe++-- for control flow and exceptions+import Control.Monad(when)+import Control.Exception(throw)++import GHC.Packing.PackException++-- | The type of Serialized data. Phantom type 'a' ensures that we+-- unpack data as the expected type.+data Serialized a = Serialized { packetData :: ByteArray# }++{- $ShowReadBinary++The power of evaluation-orthogonal serialisation is that one can+/externalise/ partially evaluated data (containing thunks), for+instance write it to disk or send it over a network.+Therefore, the module defines a 'Binary' instance for 'Serialized a',+as well as instances for 'Read' and 'Show'@ which satisfy ++> read . show == id :: 'Serialized' a -> 'Serialized' a++The phantom type is enough to ensure type-correctness when serialised+data remain in one single program run. However, when data from+previous runs are read  from an external source, their type needs to+be checked at runtime. Type information must be stored together with+the (binary) serialisation data.++The serialised data contain pointers to static data in the generating+program (top-level functions and constants) and very likely to+additional library code. Therefore, the /exact same binary/ must be+used when reading in serialised data from an external source. A hash+of the executable is included in the representation to ensure this.++-}++-- | prints packet as Word array in 4 columns (/Word/ meaning the+-- machine word size), and additionally includes Fingerprint hash+-- values for executable binary and type.+instance Typeable a => Show (Serialized a) where+    show p = unlines [ "Serialization Packet, size " ++ show size,+                       ", program " ++ show prgHash,+                       ", type fingerprint" ++ show t,+                       showWArray (UArray 0 (size-1) size dat) ]+        where size = case sizeofByteArray# dat of+                          sz# -> (I# sz# ) `div` sizeOf(undefined::TargetWord)+              t    = typeFP ( undefined :: a )+              dat  = packetData p++-- | Helper to show a serialized structure as a packet (Word Array)+showWArray :: UArray Int TargetWord -> String+showWArray arr = unlines [ show i ++ ":" ++ unwords (map showH row)+                         | (i,row) <- zip  [0,4..] elRows ]+    where showH w = -- "\t0x" ++ showHex w " "+                    printf ('\t':hexWordFmt) w+          elRows = takeEach4 (elems arr)+          +          takeEach4 :: [a] -> [[a]]+          takeEach4 [] = []+          takeEach4 xs = first:takeEach4 rest+            where (first,rest) = splitAt 4 xs++-----------------------------------------------+-- | Reads the format generated by the 'Show' instance, checks+--   hash values for executable and type and parses exactly as much as+--   the included data size announces.+instance Typeable a => Read (Serialized a)+    -- using ReadP parser (base-4.x)+    where readsPrec _ input+           = case parseP input of+              []  -> throw P_ParseError -- no parse+              [((sz,tp,dat),r)]+                  -> let !(UArray _ _ _ arr# ) = listArray (0,sz-1) dat+                         t = typeFP (undefined::a)+                     in if t == tp+                              then [(Serialized arr# , r)]+                              else throw P_TypeMismatch+              other-> throw P_ParseError+                       -- ambiguous parse for packet++-- | Packet Parser, reads the format generated by the @Read@ instance.+-- Could also consume other formats of the array (not implemented).+-- Returns: (data size in words, type fingerprint, array values)+parseP :: ReadS (Int, FP, [TargetWord]) +parseP = readP_to_S $+-- read header with size and type, then iterate over array values,+-- reading several hex words in one row, separated by+-- tab and space. Packet size needed to avoid returning a prefix.+         do string "Serialization Packet, size "+            sz_str <- munch1 isDigit+            let sz = read sz_str::Int+            string ", program "+            h  <- munch1 (not . (== '\n'))+            when (read h /= prgHash) (throw P_BinaryMismatch)+              -- executables do not match. No ambiguous parses here,+              -- so just throw; otherwise we would only pfail.+            newline+            string ", type "+            tp <- munch1 (not . (== '\n'))+            newline+            let startRow = do { many1 digit; colon; tabSpace }+                row = do { startRow; sepBy1 hexNum tabSpace }+            valss <- sepBy1 row newline+            skipSpaces -- eat remaining spaces+            let vals = concat valss+                l    = length vals+            -- filter out wrong lengths:+            if (sz /= length vals) then pfail+                                   else return (sz, read tp, vals)++digit = satisfy isDigit+colon = satisfy (==':')+tabSpace = munch1 ( \x -> x `elem` " \t" )+newline = munch1 (\x -> x `elem` " \n")++hexNum :: ReadP TargetWord+hexNum = do string "0x"+            ds <- munch hexDigit+            return (read ("0x" ++ ds))+  where hexDigit = (\x -> x `elem` "0123456789abcdefABCDEF")++------------------------------------------------------------------++-- | The binary format of @'Serialized' a@ data includes FingerPrint+--   hash values for type and executable binary, which are checked+--   when reading Serialized data back in using @get@.+instance Typeable a => Binary (Serialized a) where+    -- We make our life simple and construct/deconstruct Word+    -- (U)Arrays, quite as we did in the Show/Read instances.+    put (Serialized bArr#)+        = do put prgHash+             put (typeFP (undefined :: a))+             let arr    = UArray 0 (sz-1) sz bArr# :: UArray Int TargetWord+                 sz     = case sizeofByteArray# bArr# of+                            sz# -> (I# sz# ) `div` sizeOf(undefined::TargetWord)+             put arr+    get = do hash   <- get :: Get FP+             when (hash /= prgHash) +               (throw P_BinaryMismatch) +             -- executables do not match+             tp <- get :: Get FP+             when (tp /= typeFP (undefined :: a))+               (throw P_TypeMismatch)+                -- Type error during packet parse+             uarr   <- get :: Get (UArray Int TargetWord)+             let !(UArray _ _ sz bArr#) = uarr+             return ( Serialized bArr# )++------------------------------------------------------------------+-- $ComparingTypes+-----------------------------------------------+-- Helper functions to compare types at runtime:+--   We use type "fingerprints" defined in 'GHC.Fingerprint.Type'++-- This should ensure (as of GHC.7.8) that types with the same name+-- but different definition get different hashes.  (however, we also+-- require the executable to be exactly the same, so this is not+-- strictly necessary anyway).++-- Typeable context for dynamic type checks. ++-- | The module uses a custom GHC fingerprint type with its two Word64+--   fields, to be able to /read/ fingerprints+data FP = FP Word64 Word64 deriving (Read, Show, Eq)++-- | checks whether the type of the given expression matches the given Fingerprint+matches :: Typeable a => a -> FP -> Bool+matches x (FP c1 c2) = f1 == c1 && f2 == c2+  where  (GHC.Fingerprint.Fingerprint f1 f2) = typeRepFingerprint (typeOf x)++#if ! MIN_VERSION_base(4,8,0)+-- typeRepFingerprint is provided since base-4.8.0.0+typeRepFingerprint typeRep = ghcFP+    where TypeRep ghcFP _ _ = typeRep+#endif++-- | creates an 'FP' from a GHC 'Fingerprint'+toFP :: GHC.Fingerprint.Fingerprint -> FP+toFP (GHC.Fingerprint.Fingerprint f1 f2) = FP f1 f2++-- | returns the type fingerprint of an expression+typeFP :: Typeable a => a -> FP+typeFP = toFP . typeRepFingerprint . typeOf++-- | Binary instance for fingerprint data (encoding TypeRep and+--   executable in binary-encoded @Serialized a@)+instance Binary FP where+  put (FP f1 f2) = do put f1+                      put f2+  get            = do f1 <- get :: Get Word64+                      f2 <- get :: Get Word64+                      return (FP f1 f2)++-----------------------------------------------++-- | To check that the program (executable) is identical when packing+-- and unpacking, the fingerprint type from above is used (Read/Show+-- instances required). An 'FP' fingerprint of the executable is+-- computed once, by unsafePerformIO inside this CAF (safe to inline,+-- just inefficient).++{-# NOINLINE prgHash #-}+prgHash :: FP+prgHash = unsafePerformIO $ +          getExecutablePath >>= getFileHash >>= return . toFP++-----------------------------------------------++-- | The target word size is the size of a machine word on the+-- platform we run on.+--+-- This type is only used in Binary, Read and Show instances, where+-- packets are stored as 'UArrays' of 'TargetWord'.+--+-- Actually, GHC uses machine word size (as Haskell 2010 spec. does+-- not fix it) so we could just use Word. See+-- <http://www.haskell.org/ghc/docs/7.8.3/html/users_guide/bugs-and-infelicities.html#haskell-98-2010-undefined>++-- We'd rather just import 'GHC.Constants.TargetWord' but it was+-- removed. This code here is a cheap and incomplete hack, as the+-- package would otherwise need a configure script.++#if x86_64_BUILD_ARCH+type TargetWord = Word64+hexWordFmt = "0x%016x"+#elif i386_BUILD_ARCH+type TargetWord = Word32+hexWordFmt = "0x%08x"+#elif powerpc_BUILD_ARCH+#error Don't know word size of your Power-PC model+#else+#warning Don't know the word size on your machine.+type TargetWord = Word+#endif
+ LICENSE view
@@ -0,0 +1,30 @@+The packman serialisation library for GHC+Copyright (c) 2014-15, Jost Berthold <jost.berthold@gmail.com>+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++1. Redistributions of source code must retain the above copyright+notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+notice, this list of conditions and the following disclaimer in the+documentation and/or other materials provided with the distribution.++3. Neither the name of the copyright holder nor the names of its+contributors may be used to endorse or promote products derived from+this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Test/AllTests.hs view
@@ -0,0 +1,98 @@+{-+  Some tests to verify that serialisation works as expected+-}+module AllTests(tests)+    where++import GHC.Packing++import qualified Data.Array.IArray as A+import Control.Concurrent++import System.Environment+import System.IO+import System.Directory+import qualified Data.ByteString as B+import Control.Exception+import Data.Typeable++import Distribution.TestSuite++-- this test uses the trySerialize routine. We expect to trigger some+-- exceptions and catch them as appropriate.++catchPackExc :: IO () -> IO ()+catchPackExc io = io `catch` (\e -> putStrLn (show (e::PackException)))++-- need a time-wasting function which allocates...+nfib :: Integer -> Integer+nfib 0 = 1+nfib 1 = 1+nfib n = let n1 = nfib (n-1)+             n2 = nfib (n-2)+         in 1 + 2*n1 + n2 - n1++-- make a test instance. Action should check result and return Bool+runIt :: String -> IO Bool -> TestInstance+runIt name action+    = TestInstance+        { run = action >>= return . Finished . +                           (\b -> if b then Pass +                                  else Fail "unexpected output (see log)")+        , name = "Test case " ++ name+        , tags = []+        , options = []+        , setOption = \_ _ -> Right (runIt name action)+        }++tests :: IO [ Test ]+tests = do putStrLn "Running all tests"+           mapM (return . Test . uncurry runIt) mytests++-- all configured tests, see below+mytests = [eval_array, pack_array, pack_ThreadId, pack_MVar ]++-- test data+arr, output :: A.Array Int Int+arr  = A.array (0,127) [ (i,i) | i <- [0..127] ]+output = A.amap (2*) arr ++n :: Int+n = 3++eval_array :: (String, IO Bool)+eval_array = ("eval. array",+              do let out = show $ take n $ A.elems output+                 putStrLn $ "Evaluated: " ++ out+                 return (out == "[0,2,4]")+             )++pack_array :: (String, IO Bool)+pack_array = ("duplicating an array of 128 elements",+              do packet1 <- trySerialize output+                 putStrLn (take (3*80) (show packet1) ++ "...")+                 putStrLn "now unpacking (deserialize):"+                 copy <- deserialize packet1+                 putStrLn ("unpacked, now evaluate")+                 putStrLn (show copy)+                 return $ copy == A.amap (2*) arr+             )++expectException :: Typeable a => PackException -> IO (Serialized a) -> IO Bool+expectException exception action+    = do putStrLn ("expect exception " ++ show exception)+         action >>= print+         return False+      `catch` \e -> do putStrLn ("Got: " ++ show e)+                       return (e == exception)++pack_ThreadId :: (String, IO Bool)+pack_ThreadId = ("packing a thread ID (unsupported)",+                 do t <- myThreadId+                    expectException P_UNSUPPORTED $ trySerialize t+                )+pack_MVar :: (String, IO Bool)+pack_MVar = ("packing an MVar (should be cannotpack)",+             do m <- newEmptyMVar :: IO (MVar Integer)+                expectException P_CANNOTPACK $ trySerialize m+            )
+ Test/QCTest.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE RecordWildCards, DeriveFunctor #-}+module QCTest(tests) where++import Distribution.TestSuite+import Test.QuickCheck++import Data.Traversable(Traversable, traverse, sequenceA)+import Data.Foldable hiding (foldl,foldr)+import qualified Data.Foldable as F+import Control.Applicative++import GHC.Packing++-- use "detailed" interface: defining test instances+tests :: IO [Test]+tests = mapM (return . Test . uncurry (runQC 10))+        [boldTrees, foldmap square (+) 0, foldmapforce square (+) 0 ]++square x = x*x++-- boldTrees :: (Arbitrary a, Eq a, Show a) => (String, a -> Property)+boldTrees :: (String, RoseTree Int -> Property)+boldTrees = ("bold trees", +             \t -> ioProperty (do t' <- duplicate t+                                  return (t == t')))++foldmap f g x = ("foldmap",+                 \t -> ioProperty (do t' <- duplicate t+                                      return (fm t == fm t')))+    where fm tree = F.foldr g x (fmap f tree)++foldmapforce f g x = ("foldmapforce",+                      \t -> ioProperty (do let t'' = partEval t+                                           t' <- t'' `seq` duplicate t''+                                           return (fm t == fm t')))+    where fm tree = F.foldr g x (fmap f tree)++duplicate x = trySerialize x >>= deserialize++------------------------++runQC :: (Arbitrary a, Show a, Testable p) => +         Int -> String -> (a -> p) -> TestInstance+runQC size n prop = TestInstance +               { name = "QC test " ++ n+               , tags = [], options = []+               , setOption = \_ _ -> Right (runQC size n prop)+               , run = do r <- quickCheckWithResult stdArgs{maxSize=size} prop+                          return (Finished (readResult r))+               }+    where readResult :: Test.QuickCheck.Result -> Distribution.TestSuite.Result+          readResult Success{..} = Pass+          readResult GaveUp{..}  = Fail ("Insufficient amount of tests ("+                                         ++ show numTests ++ ")")+          readResult Failure{..} = Fail output+          readResult NoExpectedFailure{..} = Fail output++-- our test data. Uses a number of different constructors...+data RoseTree a +    = Withered a+    | Rose1 a (RoseTree a)+    | Rose2 a (RoseTree a) (RoseTree a)+    | Rose3 a (RoseTree a) (RoseTree a) (RoseTree a)+    | Rose4 a (RoseTree a) (RoseTree a) (RoseTree a) (RoseTree a)+    -- finally, the normal one.+    | RoseN a [RoseTree a]+      deriving (Eq, Show, Read, Functor)++-- meaningless function to force parts of a tree+partEval :: RoseTree a -> RoseTree a+partEval (Withered x) = x `seq` Withered x+partEval (Rose2 x t u)  = partEval u `seq` x `seq` Rose2 x t u+partEval (Rose4 x t u v w)  = partEval u `seq` partEval w `seq` x +                              `seq` Rose4 x t u v w+partEval t = t++instance Foldable RoseTree +    where -- foldr :: (a -> b -> b) -> b -> Rosetree a -> b+          foldr f x (Withered a) = f a x+          foldr f x (Rose1 a t)  = f a (F.foldr f x t)+          foldr f x (Rose2 a t u)+              = -- f a (foldr (F.foldr f) x [t, u])+                f a (F.foldr f (F.foldr f x u) t)+          foldr f x (Rose3 a t u v)  = f a (foldl (F.foldr f) x [t,u,v])+          foldr f x (Rose4 a t u v w)  = f a (foldl (F.foldr f) x [t,u,v,w])+          foldr f x (RoseN a ts) = f a (foldl (F.foldr f) x ts)++instance Traversable RoseTree where+    -- traverse :: Applicative f => (a -> f b) -> RoseTree a -> f (RoseTree b)+    traverse f (Withered a) = Withered <$> f a+    traverse f (Rose1 a t)  = Rose1 <$> f a <*> traverse f t+    traverse f (Rose2 a t u) = Rose2 <$> f a <*> traverse f t <*> traverse f u+    traverse f (Rose3 a t u v) +        = Rose3 <$> f a <*> traverse f t <*> traverse f u <*> traverse f v+    traverse f (Rose4 a t u v w) +        = Rose4 <$> f a <*> traverse f t <*> traverse f u +                        <*> traverse f v <*> traverse f w+    traverse f (RoseN a ts) = RoseN <$> f a <*> sequenceA (map (traverse f) ts)++instance Arbitrary a => Arbitrary (RoseTree a) where+    arbitrary = sized rt++rt :: Arbitrary a => Int -> Gen (RoseTree a)+rt 0 = arbitrary >>= return . Withered+rt n = oneof [ do t <- rt (n-1)+                  x <- arbitrary+                  return (Rose1 x t)+             , do t <- rt (n-1); u <- rt (n-1)+                  x <- arbitrary+                  return (Rose2 x t u) +             , do t <- rt (n-1); u <- rt (n-1); v <- rt (n-1)+                  x <- arbitrary+                  return (Rose3 x t u v) +             , do t <- rt (n-1); u <- rt (n-1); v <- rt (n-1); w <- rt (n-1)+                  x <- arbitrary+                  return (Rose4 x t u v w) +             , do i <- choose (5,n)+                  ts <- sequence (replicate i (rt (n-1)))+                  x <- arbitrary+                  return (RoseN x ts) +             ]++norm :: RoseTree a -> RoseTree a+norm (RoseN x []) = Withered x+norm (RoseN x [t]) = Rose1 x (norm t)+norm (RoseN x [t,u]) = Rose2 x (norm t) (norm u)+norm (RoseN x [t,u,v]) = Rose3 x (norm t) (norm u) (norm v)+norm (RoseN x [t,u,v,w]) = Rose4 x (norm t) (norm u) (norm v) (norm w)+norm t = t+
+ Test/TestExceptions.hs view
@@ -0,0 +1,156 @@+{-+  Some tests to verify that serialisation works as expected+-}+module Main(mytests, main)+    where++import GHC.Packing++import qualified Data.Array.IArray as A+import Control.Concurrent++import System.Environment+import System.IO++import System.Directory+import qualified Data.ByteString as B+import Control.Exception+import Data.Typeable++-- import Distribution.TestSuite+import System.Exit+import Control.Monad++-- this test uses the trySerialize routine. We expect to trigger some+-- exceptions and catch them as appropriate.++catchPackExc :: IO () -> IO ()+catchPackExc io = io `catch` (\e -> putStrLn (show (e::PackException)))++expectException :: (Show a) => PackException -> IO a -> IO Bool+expectException exception action+    = do putStrLn ("expect exception:\n" ++ show exception)+         action >>= print+         return False+      `catch` \e -> do putStrLn ("Got: " ++ show e)+                       return (e == exception)++main :: IO ()+main = do putStrLn "Running all tests"+          args <- getArgs+          let n    = if (length args < 2) then 3 else read (args!!1)+              size = if null args then 128 else read (head args)::Int+          -- test data from arguments, avoiding over-optimisation+              arr, arr2, output :: A.Array Int Int+              arr  = A.array (0,127) [ (i,i) | i <- [0..127] ]+              output = A.amap (2*) arr+              arr2 = A.listArray (0,2*n-1) (take (2*n) (A.elems arr))+              cfg  = (output, arr2, n)+          (mapM_ (runIt cfg) mytests) `finally` (rmv testfile)++-- file used for all tests+testfile :: FilePath+testfile = "testfile"++type Config = (A.Array Int Int, A.Array Int Int, Int)++type MyTest = Config -> (String, IO Bool)++-- run a defined test+runIt :: Config -> MyTest -> IO ()+runIt cfg f+    = do putStrLn $ "Test: " ++ name ++ ": "+         result <- action+         putStrLn $ "Result: " ++ show result+         if not result then error "test failed!" else return ()+    where (name, action) = f cfg++-- all configured tests, see below+mytests :: [ MyTest ]+mytests = [ evalArray,  packArray, packThreadId, packMVar,+            unpackOther, unpackWrongType, unpackTruncated, unpackGarbled ]++evalArray :: MyTest+evalArray (output, _, n)+    = ("eval. array",+       do let out = show $ take n $ A.elems output+          putStrLn $ "Evaluated: " ++ out+          return (out == "[0,2,4]"))++packArray :: MyTest+packArray (output, arr2, n)+    = ("duplicating an array of 128 elements",+              do packet1 <- trySerialize output+                 putStrLn (take (3*80) (show packet1) ++ "...")+                 putStrLn "now unpacking (deserialize):"+                 copy <- deserialize packet1+                 putStrLn ("unpacked, now evaluate")+                 putStrLn (show copy)+                 return $+                    take (2*n) (A.elems copy) == A.elems (A.amap (2*) arr2)+             )++packThreadId :: MyTest+packThreadId _ = ("packing a thread ID (unsupported)",+                  do t <- myThreadId+                     expectException P_UNSUPPORTED $ trySerialize t+                 )+packMVar :: MyTest+packMVar _ = ("packing an MVar (should be cannotpack)",+              do m <- newEmptyMVar :: IO (MVar Integer)+                 expectException P_CANNOTPACK $ trySerialize m+             )++unpackOther :: MyTest+unpackOther _ = ("deserialise other binary's data (binary mismatch)",+                 expectException P_BinaryMismatch+                 (decodeFromFile "pack.old" :: IO (A.Array Int Int))+                )++unpackWrongType :: MyTest+unpackWrongType  (output, arr2, n)+    = ("deserialise wrong type from file (type mismatch)",+                   do encodeToFile testfile arr2+                      expectException P_TypeMismatch+                       (decodeFromFile testfile :: IO (A.Array Int Double))+                  )++unpackTruncated :: MyTest+unpackTruncated  (output, arr2, n)+    = ("deserialise truncated data. Expected: parse error",+       do encodeToFile testfile arr2+          blob <- B.readFile testfile+          B.writeFile testfile (B.take 50 blob)+               -- take more than FingerPrint (4 x Word64)+          expectException P_ParseError+              (decodeFromFile testfile :: IO (A.Array Int Int))+      )++unpackGarbled :: MyTest+unpackGarbled (output, arr2, n)+    = ("deserialise garbled data. Expected: garbled data",+       do encodeToFile testfile arr2+          blob <- B.readFile testfile+          B.writeFile testfile (tamperWith blob)+          expectException P_GARBLED+           (decodeFromFile testfile :: IO (A.Array Int Int))+      )++tamperWith :: B.ByteString -> B.ByteString+tamperWith b = B.concat [b1, B.pack [11,11], B.drop 2 b2]+    where n       = B.length b+          (b1,b2) = B.splitAt (n - 10) b+-- do not touch the FingerPrints (4 x Word64)++-- file cleanup, catching all exceptions+rmv :: FilePath -> IO ()+rmv f = (removeFile f) `catch` (\e -> print (e::SomeException))+++-- need a time-wasting function which allocates...+nfib :: Integer -> Integer+nfib 0 = 1+nfib 1 = 1+nfib n = let n1 = nfib (n-1)+             n2 = nfib (n-2)+         in 1 + 2*n1 + n2 - n1
+ Test/TestMThread.hs view
@@ -0,0 +1,106 @@+-- testing serialisation with multiple concurrent threads++import GHC.Packing++import qualified Data.Array.IArray as A+import Control.Concurrent++import System.Environment+import System.IO+import System.Directory+import System.Exit++import qualified Data.ByteString as B++import Control.Exception++-- need a time-wasting function which allocates...+nfib :: Integer -> Integer+nfib 0 = 1+nfib 1 = 1+nfib n = let n1 = nfib (n-1)+             n2 = nfib (n-2)+         in 1 + 2*n1 + n2 - n1++fib :: Int -> Int +fib x | x <= 1   = 1+      |otherwise = fib (x-1) + fib (x-2)++-- | duplicate data+duplicate :: a -> IO a+duplicate x = (deserialize =<< trySerialize x)++testeval :: (Show b) => String -> a -> (a -> b) -> String -> IO ()+testeval name dat f expected+    = do putStr name+         dat' <- duplicate dat+         let out = show (f dat')+         if (out /= expected)+         then do putStrLn (": FAILED\t unexpected " ++ out) +                 error (name ++ " failed")+         else putStrLn (": OK\t" ++ out)++main+   = do hSetBuffering stdout NoBuffering++        args <- getArgs+        let u = if (null args) then 40 else read (head args)+            n = if length args < 2 then 10 else read (args!!1)++        putStrLn "Multithreaded test program for serialisation"+        let input  = [20..u]+            flt    =  filter (> 10000) +            fibL   = (flt . map fib) input+-- [10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269]++        putStrLn "normal evaluation (take 3), should be \n[10946,17711,28657]"+        putStrLn (show $ take 3 fibL)++        let doThread i = +                do v <- newEmptyMVar+                   -- note that all threads use fibL. Many calls to+                   -- trySerialize will block on blackholes (the core+                   -- operation will block and retry)+                   forkIO (do testeval (show i)+                                       (cycle fibL) (!!i)+                                       (show (fibL!!(i `mod` length fibL)))+                              putMVar v ())+                   return v+        vs <- mapM doThread [0..n-1]++        putStrLn "forked, waiting"+        mapM_ takeMVar vs+        putStrLn "done"+{-++        let f2 x y = fib (x+y)+        testeval "packing a function (2 arg.s)" f2 +                     (\f -> (take 3 . flt . map (f 0)) input)+                     "[10946,17711,28657]"++        let f3 x a b c y d = head $ map (map fib) +                             [[(x+a+b+c+d)-const .. (y+a+b+c+d)-const ]]+            const  = head fibL - 1+            f3'    = f3 (head fibL) 0 0 +        const `seq` f3' `seq` +         testeval "packing a list function (2 arg.s, 1 supplied)"+                  f3' (\f -> take 3 (flt (f 0 (u+const) 0)))+                  "[10946,17711,28657]"++        putStrLn "DONE"++        let n    = max 10 u+            size = 3*u -- default 120+            arr :: A.Array Int Int+            arr  = A.array (0,size-1)+                   [ (i,i) | i <- [0..size-1] ]+            output = A.amap (2*) arr++        -- output some of the original elements+        putStrLn $ show $ take n $ A.elems arr++        testeval "packing an unevaluated array"+                 output (take n . A.elems)+                 (show (take n [0,2..]))++-}
+ Test/TestSerialisation.hs view
@@ -0,0 +1,98 @@+-- module TestSerialisation(tests)+--    where++import GHC.Packing++import qualified Data.Array.IArray as A+import Control.Concurrent++import System.Environment+import System.IO+import System.Directory+import System.Exit++import qualified Data.ByteString as B++import Control.Exception++-- this test uses the trySerialize routine. We expect to trigger some+-- exceptions and catch them as appropriate.++catchPackExc :: IO () -> IO ()+catchPackExc io = io `catch` (\e -> putStrLn (show (e::PackException)))++-- need a time-wasting function which allocates...+nfib :: Integer -> Integer+nfib 0 = 1+nfib 1 = 1+nfib n = let n1 = nfib (n-1)+             n2 = nfib (n-2)+         in 1 + 2*n1 + n2 - n1++fib :: Int -> Int +fib x | x <= 1   = 1+      |otherwise = fib (x-1) + fib (x-2)++-- | duplicate data+duplicate :: a -> IO a+duplicate x = deserialize =<< trySerialize x ++-- | +testeval :: (Show b) => String -> a -> (a -> b) -> String -> IO ()+testeval name dat f expected+    = do putStr name+         dat' <- duplicate dat+         let out = show (f dat')+         if (out /= expected)+         then do putStrLn (": FAILED\t unexpected " ++ out) +                 error (name ++ " failed")+         else putStrLn (": OK\t" ++ out)++main+   = do hSetBuffering stdout NoBuffering++        args <- getArgs+        let u = if (null args) then 40 else read (head args)+        putStrLn "Test program for serialisation"+        putStrLn "Evaluating some fib expressions before and after packing"+        let input  = [20..u]+            flt    =  filter (> 10000) +            fibL   = (flt . map fib) input+-- [10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269]++        putStrLn "normal evaluation (take 3), should be \n[10946,17711,28657]"+        putStrLn (show $ take 3 fibL)++        testeval "packing one part" fibL (take 5) +                     "[10946,17711,28657,46368,75025]"++        let f2 x y = fib (x+y)+        testeval "packing a function (2 arg.s)" f2 +                     (\f -> (take 3 . flt . map (f 0)) input)+                     "[10946,17711,28657]"++        let f3 x a b c y d = head $ map (map fib) +                             [[(x+a+b+c+d)-const .. (y+a+b+c+d)-const ]]+            const  = head fibL - 1+            f3'    = f3 (head fibL) 0 0 +        const `seq` f3' `seq` +         testeval "packing a list function (2 arg.s, 1 supplied)"+                  f3' (\f -> take 3 (flt (f 0 (u+const) 0)))+                  "[10946,17711,28657]"++        putStrLn "DONE"++        let n    = max 10 u+            size = 3*u -- default 120+            arr :: A.Array Int Int+            arr  = A.array (0,size-1)+                   [ (i,i) | i <- [0..size-1] ]+            output = A.amap (2*) arr++        -- output some of the original elements+        putStrLn $ show $ take n $ A.elems arr++        testeval "packing an unevaluated array"+                 output (take n . A.elems)+                 (show (take n [0,2..]))+
+ cbits/Errors.h view
@@ -0,0 +1,29 @@++#ifndef ERRORS_H+#define ERRORS_H++/*+ * Return codes for the packing routine (rts/parallel/Pack.c)+ * Must be in sync with library code.+ * We need them here for use in Cmm code in PrimOps.cmm+ */+#define P_SUCCESS        0x00 /* used for return value of PackToMemory only */+#define P_BLACKHOLE      0x01 /* possibly also blocking the packing thread */+#define P_NOBUFFER       0x02 /* buffer too small */+#define P_CANNOTPACK     0x03 /* type cannot be packed (MVar, TVar) */+#define P_UNSUPPORTED    0x04 /* type not supported (but could/should be) */+#define P_IMPOSSIBLE     0x05 /* impossible type found (stack frame,msg, etc) */+#define P_GARBLED        0x06 /* invalid data for deserialisation */+// for completeness, we also include the Haskell error codes here:+#define P_ParseError     0x07 /* Packet parse error */+#define P_BinaryMismatch 0x08 /* Executable binaries do not match */+#define P_TypeMismatch   0x09 /* Packet data has unexpected type */++#undef P_ERRCODEMAX+#define P_ERRCODEMAX     0x09++// predicate for checks:+#define isPackError(bufptr) (((StgWord) (bufptr)) <= P_ERRCODEMAX)++#endif+
+ cbits/GHCFunctions.h view
@@ -0,0 +1,33 @@+/* Packing as a library:+ *+ * GHC functions linked into the C code we use+ *+ */++#include <Rts.h>+// This brings in a lot of declared functions.++// All these are internal functions of the GHC runtime. While their+// functionality is usually very stable, future versions might need to+// #ifdef-out or modify some of these declarations.+++// Internal functions in the GHC runtime+extern char* info_type(StgClosure*);+extern char* info_type_by_ip(StgInfoTable*);++// Internal hash table implementation+typedef struct hashtable HashTable;+extern HashTable *allocHashTable(void);+extern void *lookupHashTable(HashTable *table, StgWord key);+extern void  insertHashTable(HashTable *table, StgWord key, void *data);+extern void *removeHashTable(HashTable *table, StgWord key, void *data);+extern void  freeHashTable(HashTable *table, void (*freeDataFun)(void *));++// Internal malloc wrapper functions+extern void *stgMallocBytes(int n, char *msg) GNUC3_ATTRIBUTE(__malloc__);+extern void  stgFree(void* p);++#ifdef DEBUG+extern void checkClosure(StgClosure*);+#endif
+ cbits/Pack.c view
@@ -0,0 +1,2791 @@+/*+   Packing for the Generic RTE:+--------++   Graph packing and unpacking code for sending it to another processor+   and retrieving the original graph structure from the packet.+   Derived from RTS code used in GUM and Eden.++   Documentation for heap closures can be found at+   http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Storage/HeapObjects+   However, the best documentation is includes/rts/storage/Closure*h+   and rts/sm/Scav.c++   This file: a heavily revised version which uses a thread-local+   internal pack state to make the code thread-safe.++   Code is shared between the library and the parallel RTS. Library code is+   separated by PP symbol LIBRARY_CODE (code without it is shared or in-RTS).++*/++#ifdef LIBRARY_CODE++#include <Rts.h>+#include <string.h>++#include "Types.h"+#include "Errors.h"+#include "GHCFunctions.h"++#else++// in-RTS version uses different includes+#include "Rts.h"+#include "RtsUtils.h"+#include "Hash.h"+#include "Threads.h" // updateThunk+#include "Messages.h" // messageBlackHole++# if defined(DEBUG)+# include "sm/Sanity.h"+# endif++#include "Printer.h" // printing closure info (also non-debug-enabled)++#include <string.h> // memset+#endif++#ifdef DEBUG+#define DBG_HEADROOM 1+#define END_OF_BUFFER_MARKER 0xdededeee+#else+#define DBG_HEADROOM 0+#endif++// debugging macros for library and in-RTS version+#ifdef LIBRARY_CODE+// for the library version, borrow flags "scheduler" and "sparks"+# define PACKDEBUG(s) IF_DEBUG(scheduler, s)+# define PACKETDEBUG(s) IF_DEBUG(sparks, s)+#else+// for the in-RTS version, use the usual macros+// XXX maybe drop support for the non-parallel in-RTS version+# ifdef PARALLEL_RTS+#  define PACKDEBUG(s) IF_PAR_DEBUG(pack, s)+#  define PACKETDEBUG(s) IF_PAR_DEBUG(packet, s)+# else+#  define PACKDEBUG(s) IF_DEBUG(scheduler, s)+#  define PACKETDEBUG(s) /* nothing */+# endif+#endif++// size of the (fixed) Closure header in words+#define HEADERSIZE sizeof(StgHeader)/sizeof(StgWord)++// markers for packed/unpacked type+#define PLC     1L+#define OFFSET  2L+#define CLOSURE 3L+// marker for small bitmap in PAP packing+#define SMALL_BITMAP_TAG (~0UL)++/* Tagging macros will work for any word-sized type, not only+  closures. In the packet, we tag info pointers instead of+  closure pointers.+  See "pointer tagging" before "PackNearbyGraph" routine for use.+*/+#define UNTAG_CAST(type,p) ((type) UNTAG_CLOSURE((StgClosure*) (p)))++// Info pointer <--> Info offset (also for PLC pointers)+// See "relocatable binaries" before "PackNearbyGraph" routine for use.++// a fixed reference point when using relocatable binaries, to offset+// info pointers and plc pointers.+//  See "relocatable binaries" before "PackNearbyGraph" routine for use.+#define BASE_SYM ZCMain_main_info // base symbol for offset+extern const StgInfoTable BASE_SYM[];++// use this one on info pointers before they go into a packet+#define P_OFFSET(ip) ((StgWord) ((StgWord) (ip)) - (StgWord) BASE_SYM)+// use this one on info offsets taken from packets+#define P_POINTER(val) ((StgWord)(val) + (StgWord) BASE_SYM)++// padding for offsets into the already-packed data (failing lookup in the+// hashtable will produce 0, but offset 0 would be the graph root without+// padding)+#define PADDING 1++// internal types++// closure queue: array implementation with wrap-around+// Could use WSDeQue from the RTS, but we don't need its thread-safety+typedef struct ClosureQ_ {+    StgClosure** queue;+    nat size; // all in units of StgClosure*+    nat head;+    nat tail;+} ClosureQ;++// packing state: buffer, queue, offset table+typedef struct PackState_ {+    StgWord  *buffer;+    nat       size;     // buffer size in StgWords+    nat       position; // position in buffer, in StgWords+    // nat unpacked_size;  // record unpacked size? only interesting to debug+#ifndef LIBRARY_CODE+    StgTSO *tso;        // in-RTS version: may block when accessing a blackhole+#endif+    ClosureQ  *queue;+    HashTable *offsets;+} PackState;++// forward declarations++// Init module at startup+static void init(void) __attribute__((constructor));++// init/destruct pack data structure+#ifdef LIBRARY_CODE+static PackState* initPacking(StgArrWords *mutArr);+#else+static PackState* initRtsPacking(StgWord *buffer, nat size, StgTSO *tso);+#endif+static void donePacking(PackState *state);++// closure queue+static ClosureQ* initClosureQ(nat size);+static void freeClosureQ(ClosureQ* q);+STATIC_INLINE rtsBool queueEmpty(ClosureQ* q);+STATIC_INLINE nat queueSize(ClosureQ* q);+static void queueClosure(ClosureQ* q, StgClosure *closure);+static StgClosure *deQueueClosure(ClosureQ* q);++/***************************************************************+ *  packing+ */++// little helpers:+STATIC_INLINE void registerOffset(PackState* p, StgClosure *closure);+STATIC_INLINE StgWord offsetFor(PackState* p, StgClosure *closure);+STATIC_INLINE rtsBool roomToPack(PackState* p, nat size);++// closure information+STATIC_INLINE StgInfoTable* getClosureInfo(StgClosure* node, StgInfoTable* info,+                                           nat *size, nat *ptrs,+                                           nat *nonptrs, nat *vhs);+#ifdef LIBRARY_CODE+// remains local when code is stand-alone for the library+STATIC_INLINE rtsBool pmIsBlackhole(StgClosure* node);+#define isBlackhole pmIsBlackhole+#else+// if compiling for the RTS: used in other files, declared in Parallel.h+// rtsBool isBlackhole(StgClosure* node);+#endif++/************************+ *  interface for packing+ */+#ifdef LIBRARY_CODE+// interface function used in foreign primop: pack graph to given array, return+// size in bytes (offset by P_ERRCODEMAX) or an error code+int pmtryPackToBuffer(StgClosure* closure, StgArrWords* mutArr);+#else+// in-RTS version: packToBuffer, declared in Parallel.h+// int packToBuffer(StgClosure* closure,+//                  StgWord *buffer, nat bufsize, StgTSO *caller);+// serialisation into a Haskell Byte array, returning error codes on failure+// StgClosure* tryPackToMemory(StgClosure* graphroot, StgTSO* tso,+//                             Capability* cap);+#endif++// packing static addresses and offsets+STATIC_INLINE void PackPLC(PackState* p, StgPtr addr);+STATIC_INLINE void PackOffset(PackState* p, StgWord offset);+// packing routine, branches into special cases+static StgWord packClosure(PackState* p, StgClosure *closure);++// low-level packing: fill one StgWord of data into the buffer+STATIC_INLINE void Pack(PackState* p, StgWord data);++// the workhorses: generic heap-alloc'ed (ptrs-first) closure+static StgWord PackGeneric(PackState* p, StgClosure *closure);+// and special cases+static StgWord PackPAP(PackState* p, StgPAP *pap);+static StgWord PackArray(PackState* p, StgClosure* array);++/***************************************************************+ * unpacking+ */++/**************************+ *  interface for unpacking+ */+#ifdef LIBRARY_CODE+// interface unpacking from a Haskell array (using the Haskell Byte Array)+// may return error code P_GARBLED+StgClosure* pmUnpackGraphWrapper(StgArrWords* packBufferArray, Capability* cap);+#else+// in-RTS unpacking: unpacks from rtsPackBuffer and wipes it, aborts on failure+// declared in Parallel.h+// StgClosure* unpackGraph(rtsPackBuffer *packBuffer, Capability* cap);+// StgClosure* unpackGraphWrapper(StgArrWords* packBufferArray, Capability* cap);+#endif++// internal function working on the raw data buffer+static StgClosure* unpackGraph_(StgWord *buffer, StgInt size, Capability* cap);++// helper function to find next pointer (filling in pointers)+STATIC_INLINE void locateNextParent(ClosureQ* q, StgClosure **parentP,+                                    nat* pptrP, nat* pptrsP, nat* pvhsP);++// core unpacking function+static  StgClosure* UnpackClosure (ClosureQ* q, HashTable* offsets,+                                   StgWord **bufptrP, Capability* cap);++// helpers+STATIC_INLINE StgClosure *UnpackOffset(HashTable* offsets, StgWord **bufptrP);+STATIC_INLINE  StgClosure *UnpackPLC(StgWord **bufptrP);+static StgClosure * UnpackPAP(ClosureQ *queue, StgInfoTable *info,+                              StgWord **bufptrP, Capability* cap);+static StgClosure* UnpackArray(ClosureQ *queue, StgInfoTable* info,+                               StgWord **bufptrP, Capability* cap);+++/***********************************************+ * additional interface (used by in-RTS version)+ */+#ifndef LIBRARY_CODE+// creating fresh nodes in the heap. Used by in-RTS version (from other files,+// therefore all declared in Parallel.h)+//+//  used here and by the primitive which creates new channels:+//    creating a blackhole closure from scratch.+//    Declared in Parallel.h+// StgClosure* createBH(Capability *cap);++//    used in HLComms: creating a list node+//    Declared in Parallel.h+// StgClosure* createListNode(Capability *cap,+//                            StgClosure *head, StgClosure *tail);++// A special structure used as the "owning thread" of system-generated+// blackholes.  Layout [ hdr | payload ], holds a TSO header.info and blocking+// queues in the payload field.+//+// Used in:+//     createBH (here),+//     Threads::updateThunk + Messages::messageBlackHole (special treatment)+//     ParInit::synchroniseSystem(init),+//     Evac::evacuate (do not evacuate) and GC::garbageCollect (evac. BQueue)+StgInd stg_system_tso;++#endif++#if defined(DEBUG)+// finger print: "type hash" of packed graph, for quick debugging+// checks+#define MAX_FINGER_PRINT_LEN  1023+static void graphFingerPrint(char* fp, StgClosure *graphroot);++static void checkPacket(StgWord* buffer, nat size);+#endif++/***************************************************************+ * init function (called when loading the module)+ */+static void init(void) {+    // we must retain all CAFs, as packet data might refer to it.+    // This variable lives in Storage.c, inhibits GC for CAFs.+    keepCAFs = rtsTrue;+}++/***************************************************************+ * pack state and queue functions+ */++// Pack state constructor, allocates space, queue and hash table.+#ifdef LIBRARY_CODE+// A mutable array is passed as the buffer space. Note that its size comes in+// bytes, while internally all is managed in units of StgWord.+static PackState* initPacking(StgArrWords *mutArr) {+    PackState *ret;++    ret = (PackState*) stgMallocBytes(sizeof(PackState), "pack state");++    ASSERT(mutArr->bytes > 0);++    ret->buffer = mutArr->payload;+    ret->size = mutArr->bytes / sizeof(StgWord);++    ret->position = 0;++    // create a closure queue "big enough" => about what the array can hold+    ret->queue = initClosureQ(ret->size / 2);+    // new hash table+    ret->offsets = allocHashTable();++    return ret;+}+#else+// in-RTS version uses a raw buffer instead of an array, and carries a tso+static PackState* initRtsPacking(StgWord *buffer, nat size, StgTSO *tso) {+    PackState *ret;++    ret = (PackState*) stgMallocBytes(sizeof(PackState), "pack state");++    // assume buffer and size provided by caller are correct+    ret->buffer = buffer;+    ret->size = size;++    ret->position = 0;+    ret->tso = tso;++    // create a closure queue "big enough" => about what the array can hold+    ret->queue = initClosureQ(ret->size / 2);+    // new hash table+    ret->offsets = allocHashTable();++    return ret;+}+#endif++// Pack state destructor: frees hashtable and queue. Mutable array used when+// initialising has now been mutated.+static void donePacking(PackState *state) {+    freeHashTable(state->offsets, NULL);+    freeClosureQ(state->queue);+    stgFree(state);+    return;+}++// initialise a closure queue for "size" many closures+static ClosureQ* initClosureQ(nat size) {+    ClosureQ* ret;+    ret = (ClosureQ*) stgMallocBytes(sizeof(ClosureQ), "cl.queue");+    ret->queue = (StgClosure**)+        stgMallocBytes(size * sizeof(StgClosure*), "cl.queue data");+    ret->size = size;+    ret->head = ret->tail = 0;+    return ret;+}++// free an allocated closure queue+static void freeClosureQ(ClosureQ* q) {+    stgFree(q->queue);+    stgFree(q);+}++// queue empty if head == tail+STATIC_INLINE rtsBool queueEmpty(ClosureQ* q) {+    return (q->head == q->tail);+}++// compute size from distance between head and tail (with wrap-around)+STATIC_INLINE nat queueSize(ClosureQ* q) {+    // queue can wrap around+    int span = q->head - q->tail;++    ASSERT(span < (int) q->size && 0 - (int) q->size < span);++    if (span >= 0) {+        return (nat) span;+    } else {+        // wrapped around+        return (q->size - span);+    }+}++// enqueue a closure+static void queueClosure(ClosureQ* q, StgClosure *closure) {++    // next index, wrapping around when required+    nat idx = (q->head == q->size - 1) ? 0 : q->head + 1;++    if (idx == q->tail) {+        // queue full, stop (should not happen with sizes used here)+        errorBelch("Pack.c: Closure Queue Overflow");+        stg_exit(EXIT_FAILURE);+    }+    q->queue[q->head] = closure;+    PACKETDEBUG(debugBelch(">__> Q: %p (%s) at %ld\n", closure,+                           info_type(UNTAG_CLOSURE(closure)), (long) q->head));+    q->head = idx;++}++// dequeue a closure+static StgClosure *deQueueClosure(ClosureQ* q) {+    if (!queueEmpty(q)) {+        StgClosure* c = q->queue[q->tail];+        q->tail = (q->tail == q->size-1) ? 0 : (q->tail + 1);+        PACKETDEBUG(debugBelch(">__> DeQ: %p (%s); %ld elems in q\n",+                               c, info_type(UNTAG_CLOSURE(c)),+                               (long) queueSize(q)));+        return c;+    } else {+        PACKETDEBUG(debugBelch("Q empty\n "));+        return ((StgClosure*)NULL);+    }+}++++/***************************************************************+ * Helper functions for packing+ */++// RegisterOffset records that/where the closure is packed+STATIC_INLINE void registerOffset(PackState* p, StgClosure *closure) {+    insertHashTable(p->offsets, UNTAG_CAST(StgWord, closure),+                    // remove tag for offset+                    (void *) (StgWord) (p->position + PADDING));+    // note: offset is never 0 (indicates failing lookup), PADDING is 1+}++// OffsetFor returns an offset for a closure which has already been packed.+// offsetFor returns 0 => closure has _not_ been packed+// (root closure gets offset 1, see PADDING above)+STATIC_INLINE StgWord offsetFor(PackState* p, StgClosure *closure) {+    // avoid typecast warnings...+    void* offset;+    offset = lookupHashTable(p->offsets, UNTAG_CAST(StgWord, closure));+                             // remove tag for offset+    return (StgWord) offset;+}++// roomToPack checks if the buffer has enough space to pack the given size (in+// StgWords). For GUM, it would also include queue size * FETCHME-size.+STATIC_INLINE rtsBool roomToPack(PackState* p, nat size)+{+    if ((p->position +  // where we are in the buffer right now+         size +         // space needed for the current closure+#ifdef GUM+         queueSize(q) * FETCH_ME_PACKED_SIZE ++#endif+         1)             // closure tag+        >= p->size) {+        PACKDEBUG(debugBelch("Pack buffer full (size %d). ", p->position));+        return rtsFalse;+    }+    return rtsTrue;+}++// quick test for blackholes. Available somewhere else?++#ifdef LIBRARY_CODE+STATIC_INLINE+#endif+rtsBool isBlackhole(StgClosure* node) {+    // since ghc-7.0, blackholes are used as indirections. inspect indirectee.+    if(((StgInfoTable*)get_itbl(UNTAG_CLOSURE(node)))->type == BLACKHOLE) {+        StgClosure* indirectee = ((StgInd*)node)->indirectee;+        // some Blackholes are actually indirections since ghc-7.0+        switch (((StgInfoTable*)get_itbl(UNTAG_CLOSURE(indirectee)))->type) {+        case TSO:+        case BLOCKING_QUEUE:+            return rtsTrue;+        default:+            return rtsFalse;+        }+    }+    return rtsFalse;+}++// unwind (chains of) indirections, return the actual data closure+// Blackholes are one kind of indirection, see above.+STATIC_INLINE StgClosure* unwindInd(StgClosure *closure)+{+    StgClosure *start = closure;++    while (closure_IND(start))+        start = (UNTAG_CAST(StgInd*, start))->indirectee;++    return start;+}++/***************************************************************+ * general helper function used here+ */++/*  getClosureInfo: returns payload structure for closures.+    Only used in here+    IN:  node   - ptr to the closure / into the packet+         info   - (optional) info _table_ (not info ptr!) for closure+                  to be computed from info offset by caller when in packet+    OUT: size   - total size of closure in heap (for allocation)+         ptrs   - number of pointers in payload+         nonptrs- number of non-pointers in payload+         vhs    - variable header size+    RETURNS: info _table_ (pointer)+*/+STATIC_INLINE StgInfoTable*+getClosureInfo(StgClosure* node, StgInfoTable* info,+                 nat *size, nat *ptrs, nat *nonptrs, nat *vhs) {+    // We remove the potential tag before doing anything.+    node = UNTAG_CLOSURE(node);++    if (info == NULL) {+        // Supposed to compute info table by ourselves. This will go very wrong+        // if we use an info _offset_ instead (if we are supposed to look at a+        // packet slot instead of the heap) which is the case if we find+        // something tagged.+        ASSERT(!GET_CLOSURE_TAG((StgClosure*) node->header.info));+        // not tagged, OK+        info = get_itbl(node);+    }+    // ClosureMacros.h. NB relies on variable header for PAP, AP, Arrays+    *size = closure_sizeW_(node, info);++    /* Caution: layout field is union, may contain different information+       according to closure type! see InfoTables.h: THUNK_SELECTOR:+       selector_offset stack frames, ret. vec.s, whatever: bitmap / ptr. to+       large_bitmap other closures: ptrs | nptrs+       */+    switch (info->type) {+    case THUNK_SELECTOR:+        *ptrs = 1; // selectee is a pointer+        *vhs  = *size - 1 - sizeofW(StgHeader);+        *nonptrs = 0;+        break;++        // PAP/AP/AP_STACK contain a function field,+        // treat this field as a (= the one single) pointer+    case PAP:+        *vhs = 1; // arity/args+        *ptrs = 1;+        // wrong (some are ptrs), but not used in the unpacking code!+        *nonptrs = *size - 2 - sizeofW(StgHeader);+        break;++    case AP_STACK:+    case AP:+        // thunk header and arity/args field+        *vhs = sizeofW(StgThunkHeader) - sizeofW(StgHeader) + 1;+        *ptrs = 1;+        // wrong (some are ptrs), but not used in the unpacking code!+        *nonptrs = *size - 2 - sizeofW(StgThunkHeader);+        break;++        /* For Word arrays, no pointers need to be filled in.+         * (the generic "ptrs-first" treatment works for them)+         */+    case ARR_WORDS:+        *vhs = 1;+        *ptrs = 0;+        *nonptrs = (((StgArrWords*) node)->bytes) / sizeof(StgWord);+        break;++        /* For Arrays of pointers, we need to fill in all the pointers and+           allocate additional space for the card table at the end.+        */+    case MUT_ARR_PTRS_CLEAN:+    case MUT_ARR_PTRS_DIRTY:+    case MUT_ARR_PTRS_FROZEN0:+    case MUT_ARR_PTRS_FROZEN:+        *vhs = 2;+        *ptrs = ((StgMutArrPtrs*) node)->ptrs;+        *nonptrs = ((StgMutArrPtrs*) node)->size - *ptrs; // count card table+        // NB nonptrs field for array closures is only used in checkPacket+        break;++#if __GLASGOW_HASKELL__ > 708+        // Small arrays do not have card tables, straightforward+    case SMALL_MUT_ARR_PTRS_CLEAN:+    case SMALL_MUT_ARR_PTRS_DIRTY:+    case SMALL_MUT_ARR_PTRS_FROZEN0:+    case SMALL_MUT_ARR_PTRS_FROZEN:+        *vhs = 1; // ptrs field+        *ptrs = ((StgSmallMutArrPtrs*) node)->ptrs;+        *nonptrs = 0;+        break;+#endif++        // we do not want to see these here (until thread migration)+    case CATCH_STM_FRAME:+    case CATCH_RETRY_FRAME:+    case ATOMICALLY_FRAME:+    case UPDATE_FRAME:+    case CATCH_FRAME:+    case UNDERFLOW_FRAME:+    case STOP_FRAME:+    case RET_SMALL:+    case RET_BIG:+    case RET_BCO:+        barf("getClosureInfo: stack frame!");+        break;++    default:+        // this works for all pointers-first layouts+        *ptrs = (nat) (info->layout.payload.ptrs);+        *nonptrs = (nat) (info->layout.payload.nptrs);+        *vhs = *size - *ptrs - *nonptrs - sizeofW(StgHeader);+    }++    ASSERT(*size == sizeofW(StgHeader) + *vhs + *ptrs + *nonptrs);++    return info;+}++/*******************************************************************+ * packing a graph structure:+ *+ * The graph is packed breadth-first into a given buffer of StgWords.+ *+ * In the buffer, 3 different types of entities are packed+ *  0L - closure with static address        - PackPLC+ *  1L - offset (closure already in packet) - PackOffset+ *  2L - a heap closure follows             - PackGeneric/specialised routines+ *+ *  About "pointer tagging":+ *   Every closure pointer carries a tag in its l.s. bits (those which+ *   are not needed since closures are word-aligned anyway).+ *   These tags indicate that data pointed at is fully evaluated, and+ *   allow for a shortcut in case of small constructors (selecting+ *   arguments).+ *   The tagged pointers are *references* to a closure. RTS must ensure+ *   that every occurrence of one and the same pointer has the same tag.+ *+ *   Tags should survive packing-sending-unpacking, so we must store+ *   them in the packet somehow.+ *   => Closure pointers in the closure queue are stored *WITH TAGS*,+ *   and we pack the tags together with the closure.+ *   OTOH, *offsets* (HashTable entries) are stored without tags, in+ *   order to catch the case when two references with different tags+ *   exist (possible?)+ *   (the tag of the first occurrence will win, a problem?)+ *+ *   We use the last bits of the info-ptr (stored anyway), which is+ *   aligned just as closure pointers, in word size.+ *+ *   Anyway, closures are enqueued with tags, and the tag handled in+ *   functions called from PackClosure(): PackGeneric, or specialised+ *   ones.+ *+ *   Restoring the tags: Tags must be restored at every place where we+ *   put a reference to the closure. Here: when we fill in the+ *   pointers to a closure. The tags are restored right after+ *   unpacking, inside unpackClosure(). See comments there for details.+ *+ *  About relocatable binaries:+ *   Packing relies on shipping pointers to info tables between+ *   running RTS instances. When these addresses are unstable+ *   (relocatable binaries), they need to be computed using a known+ *   (and usually very low) address is subtracted from any info+ *   pointer packed.+ *+ *   We use the terminology of an "info offset" as opposed to an "info+ *   pointer".  (Furthermore there are proper "info tables", see+ *   ClosureMacros.h for the difference ->TABLES_NEXT_TO_CODE ).+ *+ *   Info offsets are computed from the info pointer in the heap+ *   closure's header when packing, tagged (see above) and put into+ *   the packet. The receiver restores the info pointer and uses the+ *   tag for pointers to the respective unpacked closure.+ *+ *******************************************************************/++// packing a static value+STATIC_INLINE void PackPLC(PackState* p, StgPtr addr) {+    Pack(p, PLC);                     // weight+    // pointer tag of addr still present, packed as-is (with offset)+    Pack(p, (StgWord) P_OFFSET(addr)); // address+}++// packing an offset (repeatedly packed same closure)+STATIC_INLINE void PackOffset(PackState* p, StgWord offset) {+    Pack(p, OFFSET);    // weight+    //  Pack(0L);       // pe+    Pack(p, offset);    // slot/offset+}++// helper accessing the pack buffer+STATIC_INLINE void Pack(PackState* p, StgWord data) {+    ASSERT(p->position < p->size);+    p->buffer[p->position++] = data;+}++#ifdef LIBRARY_CODE+// pmtryPackToBuffer: interface function called by the foreign primop.+// Returns packed size (in bytes!) + P_ERRCODEMAX when successful, or+// error codes upon failure+int pmtryPackToBuffer(StgClosure* closure, StgArrWords* mutArr) {+    int errcode = P_SUCCESS; // error code returned by PackClosure+    PackState* p;+    nat size;++    PACKDEBUG( {+            char fpstr[MAX_FINGER_PRINT_LEN];+            graphFingerPrint(fpstr, closure);+            debugBelch("Packing subgraph @ %p\nGraph fingerprint is\n"+                       "\t{%s}\n", closure, fpstr);+        });+    p = initPacking(mutArr);++    queueClosure(p->queue, closure);+    do {+        errcode = packClosure(p, deQueueClosure(p->queue));+        if (errcode != P_SUCCESS) {+            donePacking(p);+            return (errcode);+            // small value => error (real size offset by P_ERRCODEMAX)+        }+    } while (!queueEmpty(p->queue));++    /* Check for buffer overflow (again) */+    ASSERT((p->position + DBG_HEADROOM) < p->size);+    IF_DEBUG(sanity, // write magic end-of-buffer word+             p->buffer[p->position++] = END_OF_BUFFER_MARKER);++    /* Record how much space the graph needs in packet and in heap */+    size = p->position; // need to offset it for the primop to recognise errors+    // globalPackBuffer->unpacked_size = unpacked_size; XXX unpackedSize++    PACKDEBUG(debugBelch("** Finished packing graph %p (%s); "+                         "packed size: %d words; size of graph: %d\n",+                         closure, info_type(UNTAG_CLOSURE(closure)),+                         size, 0)); // globalPackBuffer->unpacked_size));++    /* done packing */+    donePacking(p);++    IF_DEBUG(sanity, checkPacket(mutArr->payload, size));++    size = size*sizeof(StgWord) + P_ERRCODEMAX;+    // need offset to recognise errors in primop++    return (int) size;+}+#else+// packToBuffer: interface function for the RTS (DataComms).+// Returns packed size (in bytes!) + P_ERRCODEMAX when successful, or+// error codes upon failure+int packToBuffer(StgClosure* closure,+                 StgWord *buffer, nat bufsize, StgTSO *caller) {+    int errcode = P_SUCCESS; // error code returned by PackClosure+    PackState* p;+    nat size;++    PACKDEBUG( {+            char fpstr[MAX_FINGER_PRINT_LEN];+            graphFingerPrint(fpstr, closure);+            debugBelch("RTS packs subgraph @ %p\nGraph fingerprint is\n"+                       "\t{%s}\n", closure, fpstr);+        });+    p = initRtsPacking(buffer, bufsize, caller);++    queueClosure(p->queue, closure);+    do {+        errcode = packClosure(p, deQueueClosure(p->queue));+        if (errcode != P_SUCCESS) {+            donePacking(p);+            return (errcode);+            // small value => error (real size offset by P_ERRCODEMAX)+        }+    } while (!queueEmpty(p->queue));++    /* Check for buffer overflow (again) */+    ASSERT((p->position + DBG_HEADROOM) < p->size);+    IF_DEBUG(sanity, // write magic end-of-buffer word+             p->buffer[p->position++] = END_OF_BUFFER_MARKER);++    /* Record how much space the graph needs in packet and in heap */+    size = p->position; // need to offset it for the primop to recognise errors+    // unpacked_size = p->unpacked_size; XXX unpackedSize++    PACKDEBUG(debugBelch("** Finished packing graph %p (%s); "+                         "packed size: %d words; size of graph: %d\n",+                         closure, info_type(UNTAG_CLOSURE(closure)),+                         size, 0)); // globalPackBuffer->unpacked_size));++    /* done packing */+    donePacking(p);++    IF_DEBUG(sanity, checkPacket(buffer, size));++    size = size*sizeof(StgWord) + P_ERRCODEMAX;+    // need offset to recognise errors in primop++    return (int) size;+}++// pack, then copy the buffer into newly (Haskell-)allocated space+// (unless packing was blocked, in which case we return the error code)+// This implements primitive serialize# and #trySerialize (if tso==NULL).+StgClosure* tryPackToMemory(StgClosure* graphroot,+                            StgTSO* tso, Capability* cap) {+    StgWord *buffer;+    StgWord packedSize, trySize;+    StgArrWords* wordArray;++#define ONEMEGABYTE 1048576+    trySize = ONEMEGABYTE; // start with 1MB buffer, increase if it fails++    buffer = (StgWord*) stgMallocBytes(trySize, "serialize buffer");+    packedSize = packToBuffer(graphroot, buffer, trySize, tso);++    while (packedSize == P_NOBUFFER // packing failed due to buffer overflow+           && trySize <= RtsFlags.ParFlags.packBufferSize) {+        // increase and retry (until max, given as RtsFlag)+        stgFree(buffer);+        trySize += ONEMEGABYTE;+        buffer = (StgWord*) stgMallocBytes(trySize, "serialize buffer");+        packedSize = packToBuffer(graphroot, buffer, trySize, tso);+    }++    // here: not failing due to NOBUFFER++    if (isPackError(packedSize)) {+        // packing hit an error, return this error to caller+        stgFree(buffer);+#ifndef DEBUG+        // if we are not debugging, crash the system upon impossible cases.+        if (packedSize == P_IMPOSSIBLE) {+            barf("GHC RTS found an impossible closure during packing.");+            // never returns+        }+#endif+        return ((StgClosure*) packedSize);+    }++    packedSize -= P_ERRCODEMAX; // now size is correct, in bytes++    // allocate space to hold an array+    //   +---------+----------+------------------------++    //   |ARR_WORDS| n_bytes  | data (array of words)  |+    //   +---------+----------+------------------------++    wordArray = (StgArrWords*) allocate(cap, 2 + packedSize / sizeof(StgWord));+    SET_HDR(wordArray, &stg_ARR_WORDS_info, CCS_SYSTEM);+    wordArray->bytes = packedSize;+    memcpy((void*) &(wordArray->payload), buffer, packedSize);+    stgFree(buffer);++    return ((StgClosure*) wordArray);+}+#endif++/*+ * @packClosure@ is the heart of the normal packing code.  It packs a+ * single closure into the pack buffer, skipping over any+ * indirections, queues any child pointers for further packing.+ *+ * The routine returns error codes (see Errors.h) indicating error+ * status when packing a closure fails.+ */++static StgWord packClosure(PackState* p, StgClosure *closure) {++    StgInfoTable *info;+    StgWord offset;++    // Ensure we can always pack this closure as an offset/PLC.+    if (!roomToPack(p, sizeofW(StgWord)))+        return P_NOBUFFER;++loop:+    closure = unwindInd(closure);+    // now closure is the thing we want to pack+    // ... but might still be tagged.++    offset = offsetFor(p, closure);+    // If the closure has been packed already, pack an indirection+    if (offset != 0) {+        PackOffset(p, offset);+        return P_SUCCESS;+    }++    // remove the tag (temporary, subroutines will handle tag as needed)+    info = get_itbl(UNTAG_CLOSURE(closure));++    // code relies on info-pointers being word-aligned (they are tagged)+    ASSERT(info == UNTAG_CAST(StgInfoTable*, info));++    switch (info->type) {++        // follows order of ClosureTypes.h...+    case INVALID_OBJECT:+        barf("Found invalid object");++    case CONSTR:+    case CONSTR_1_0:+    case CONSTR_0_1:+    case CONSTR_2_0:+    case CONSTR_1_1:+    case CONSTR_0_2:+        return PackGeneric(p, closure);++    case CONSTR_STATIC:        // We pack indirections to CAFs:+    case CONSTR_NOCAF_STATIC:  // Therefore, we need keepCAFs==rtsTrue+    case FUN_STATIC:           // (otherwise GC leaves dangling pointers+    case THUNK_STATIC:         // from original CAF site to the heap)+        // all these are packed with their tag (closure is still tagged)+        PACKETDEBUG(debugBelch("*>~~ Packing a %p (%s) as a PLC\n",+                               closure, info_type_by_ip(info)));++        PackPLC(p, (StgPtr)closure);+        // NB: unpacked_size of a PLC is 0+        return P_SUCCESS;++    case FUN:+    case FUN_1_0:+    case FUN_0_1:+    case FUN_2_0:+    case FUN_1_1:+    case FUN_0_2:+        return PackGeneric(p, closure);++    case THUNK:+    case THUNK_1_0:+    case THUNK_0_1:+    case THUNK_2_0:+    case THUNK_1_1:+    case THUNK_0_2:+        // !different layout! (smp update field, see Closures.h)+        // the update field should better not be shipped...+        return PackGeneric(p, closure);++    case THUNK_SELECTOR:+        // a thunk selector extracts one of the arguments of another+        // closure. See GC.c::eval_thunk_selector. Selectee might be+        // CONSTR*, or IND* or unevaluated (THUNK*, AP, AP_STACK,+        // BLACKHOLE).++        // GC tries to evaluate and eliminate THUNK_SELECTORS by+        // following them. unwindInd could include them in the normal+        // case, but this is fatal in case of a loop. Therefore, just+        // pack selector and selectee instead. getClosureInfo treats+        // the selectee in this closure type as a pointer field.++        return PackGeneric(p, closure);++    case BCO:+        goto unsupported;++    case AP:+    case PAP:+        return PackPAP(p, (StgPAP *)closure); // types with bitmap-layout++    case AP_STACK:+        // this is a stack from an evaluation that was interrupted+        // (by an exception or alike). Slightly unclear whether it+        // would ever make sense to pack/replicate it.+        goto unsupported;++    case IND:+    case IND_PERM:+    case IND_STATIC:+        // clearly a bug!+        barf("Pack: found IND_... after shorting out indirections %d (%s)",+             (nat)(info->type), info_type_by_ip(info));++        // return vectors+    case RET_BCO:+    case RET_SMALL:+    case RET_BIG:+    case RET_FUN:+        goto impossible;++        // stack frames+    case UPDATE_FRAME:+    case CATCH_FRAME:+    case UNDERFLOW_FRAME:+    case STOP_FRAME:+        goto impossible;++    case BLOCKING_QUEUE:+        goto impossible;++    case BLACKHOLE:+        //  case RBH:+        {+            StgClosure* indirectee = ((StgInd*)closure)->indirectee;++            // some Blackholes are actually indirections since ghc-7.0+            switch (((StgInfoTable*)get_itbl(UNTAG_CLOSURE(indirectee)))->type) {++            case IND: // race cond. when threaded (blackhole just got updated)+                // This case is analogous with the one in StgMiscClosures.cmm+                goto loop;+            case TSO: // blackhole without blocking queue+            case BLOCKING_QUEUE: // another thread already blocked here++#ifndef LIBRARY_CODE+                // For the in-RTS version: If the calling TSO is known, it can+                // block on this Blackhole until it is updated/data arrives.+                // The TSO should then restart packing when woken up.+                if (p->tso != NULL) {+                    StgTSO *tso = p->tso;+                    MessageBlackHole *msg = NULL;++                    PACKETDEBUG(debugBelch("TSO %d blocks on %s (at %p) "+                                           "while packing.", (int)tso->id,+                                           info_type_by_ip(info), closure));++                    // blocking the tso: create a message, call msgBlackHole,+                    // set fields in tso. If msgBlackHole signals we can+                    // continue (threaded rts case), we jump back.++                    msg = (MessageBlackHole*)+                        allocate(tso->cap, sizeofW(MessageBlackHole));+                    SET_HDR(msg, &stg_MSG_BLACKHOLE_info, CCS_SYSTEM);+                    msg->tso = tso;+                    msg->bh  = closure;++                    if (messageBlackHole(tso->cap, msg)) {+                        tso->why_blocked = BlockedOnBlackHole;+                        tso->block_info.bh = msg;+                        // packing failed, TSO blocked, caller should suspend it+                        return P_BLACKHOLE;+                    } else {+                        goto loop; // could not block (race condition), retry+                    }+                }+                // else (we don't know the packing TSO):+                // In GUM, we would globalise and pack a FetchMe.+#endif+                // Without global addresses and virtual shared heap, packing+                // just fails, an error code is returned to Haskell. +                // Likewise in library code: would be good to just block on the+                // blackhole, but there is no way to return to the scheduler.+                PACKETDEBUG(debugBelch("packing hit a %s at %p (returning).\n",+                                       info_type_by_ip(info), closure));++                // Packing will fail anyway, so write the blackhole address into+                // the buffer (first word), to enable blocking from Haskell by a+                // whnf evaluation. Caller to do the rest.+                *p->buffer = (StgWord) closure;++                return P_BLACKHOLE;++            default: // an indirection, pack the indirectee (jump back to start)+                closure = indirectee;+                // race condition, "unwindInd" should have removed this.+                goto loop;+            }+        }++    case MVAR_CLEAN:+    case MVAR_DIRTY:+    case TVAR:+        PACKDEBUG(errorBelch("Pack: packing type %s (%p) not possible",+                             info_type_by_ip(info), closure));+        return P_CANNOTPACK;++    case ARR_WORDS:+        // Word arrays follow the "pointers-first" layout (with no pointers)+        return PackGeneric(p, closure);++    case MUT_ARR_PTRS_CLEAN:+    case MUT_ARR_PTRS_DIRTY:+    case MUT_ARR_PTRS_FROZEN0:+    case MUT_ARR_PTRS_FROZEN:+        // Arrays of pointers have a card table to indicate dirty cells,+        // therefore not the simple pointers/nonpointers layout.+        // NB At this level, we cannot distinguish immutable arrays+        // from mutable ones+        return PackArray(p, closure);++    case MUT_VAR_CLEAN:+    case MUT_VAR_DIRTY: // known as IORefs in the Haskell world+        PACKDEBUG(errorBelch("Pack: packing type %s (%p) not possible",+                             info_type_by_ip(info),closure));+        return P_CANNOTPACK;++    case WEAK:+        goto unsupported;++    case PRIM:+        // Prim type holds internal immutable closures: MSG_TRY_WAKEUP,+        // MSG_THROWTO, MSG_BLACKHOLE, MSG_NULL, MVAR_TSO_QUEUE+    case MUT_PRIM:+        // Mut.Prim type holds internal mutable closures:+        // TVAR_WATCH_Q, ATOMIC_INVARIANT, INVARIANT_CHECK_Q, TREC_HEADER+    case TSO:+        // this might actually happen if the user is smart and brave+        // enough (a thread id in Haskell is a TSO ptr in the RTS)+        goto unsupported;++    case STACK:+    case TREC_CHUNK:  // recorded transaction on STM. Should not occur+        goto impossible;++        // more stack frames:+    case ATOMICALLY_FRAME:+    case CATCH_RETRY_FRAME:+    case CATCH_STM_FRAME:  // STM-related stack frames. Should not occur+        goto impossible;++    case WHITEHOLE:+        // closure is spin-locked, loop back and spin until changed. Take the big+        // round to avoid compiler optimisations getting into the way+        write_barrier();+        goto loop;+        // valid only for the threaded RTS... cannot distinguish here++#if __GLASGOW_HASKELL__ > 708+    case SMALL_MUT_ARR_PTRS_CLEAN:+    case SMALL_MUT_ARR_PTRS_DIRTY:+    case SMALL_MUT_ARR_PTRS_FROZEN:+    case SMALL_MUT_ARR_PTRS_FROZEN0:+        // unlike the standard arrays, small arrays do not have a card table+        // Layout is thus: +------------------------------++        //                 | hdr | #ptrs | payload (ptrs) |+        //                 +------------------------------++        // No problem with using PackGeneric and vhs=1 in getClosureInfo+        return PackGeneric(p, closure);+#endif++unsupported:+        PACKDEBUG(errorBelch("Pack: packing type %s (%p) not implemented",+                             info_type_by_ip(info), closure));+        return P_UNSUPPORTED;++impossible:+        PACKDEBUG(errorBelch("{Pack}Daq Qagh: found %s (%p) when packing",+                             info_type_by_ip(info), closure));+        return P_IMPOSSIBLE;++    default:+        barf("Pack: strange closure %d", (nat)(info->type));+    } // closure type switch++}++// XXX rename packGeneric+// packGeneric packs all closures with "pointers-first" layout+//       +-------------------------------------------------++//       | FIXED HEADER | VARIABLE HEADER | PTRS | NON-PRS |+//       +-------------------------------------------------++//  The first (and only, in the default system) word of the header is+//  the info pointer. It is tagged and offset to a known base.+static StgWord PackGeneric(PackState* p, StgClosure* closure)+{+    nat size, ptrs, nonptrs, vhs, i;+    StgWord tag=0;+    StgClosure* infoptr; // actually just a pointer...++    // store tag separately, pack with info ptr+    tag = GET_CLOSURE_TAG(closure);+    closure = UNTAG_CLOSURE(closure);++    // get info about basic layout of the closure+    getClosureInfo(closure, NULL, &size, &ptrs, &nonptrs, &vhs);++    ASSERT(!isBlackhole(closure));++    PACKETDEBUG(debugBelch("*>== %p (%s): generic packing"+                           "(size=%d, ptrs=%d, nonptrs=%d, and tag %d)\n",+                           closure, info_type(closure), size, ptrs, nonptrs,+                           (int)tag));++    // make sure we can pack this closure into the current buffer+    if (!roomToPack(p, HEADERSIZE + vhs + nonptrs))+        return P_NOBUFFER;++    // Record that this has been packed+    registerOffset(p, closure);++    // GUM would allocate a GA for the packed closure if it is a thunk+#ifdef GUM+    // Checks for globalisation scheme; default: globalise everything thunks+    if ( RtsFlags.ParFlags.globalising == 0 ||+         (closure_THUNK(closure) && !closure_UNPOINTED(closure)) )+        GlobaliseAndPackGA(closure);+    else+#endif+        Pack(p, (StgWord) CLOSURE);  // marker for unglobalised closure++    // At last! A closure we can actually pack!++    // pack fixed and variable header+    // First word (==infopointer) is tagged and offset using macros+    infoptr = *((StgClosure**) closure);+    Pack(p, (StgWord) (P_OFFSET(TAG_CLOSURE(tag, infoptr))));++    // pack the rest of the header (variable header)+    for (i = 1; i < HEADERSIZE + vhs; ++i) {+        Pack(p, (StgWord)*(((StgPtr)closure)+i));+    }++    // register all ptrs for further packing+    for (i = 0; i < ptrs; ++i) {+        queueClosure(p->queue, ((StgClosure *) *(((StgPtr)closure)+(HEADERSIZE+vhs)+i)));+    }++    // pack non-ptrs+    for (i = 0; i < nonptrs; ++i) {+        Pack(p, (StgWord)*(((StgPtr)closure)+(HEADERSIZE+vhs)+ptrs+i));+    }++    ASSERT(HEADERSIZE+vhs+ptrs+nonptrs==size); // no slop in closure, all packed++    // unpacked_size += size; XXX unpacked_size in PackState++#ifdef GUM+    // Record that this is a revertable black hole so that we can fill+    // in its address from the fetch reply.  Problem: unshared thunks+    // may cause space leaks this way, their GAs should be deallocated+    // following an ACK.++    // convert to RBH+    if (closure_THUNK(closure) && !closure_UNPOINTED(closure)) {+        StgClosure *rbh;+        rbh = convertToRBH(closure);+        ASSERT(size>=HEADERSIZE+MIN_UPD_SIZE); // min size for updatable closure+        ASSERT(rbh == closure);         // rbh at same position (minced version)++        // record the thunk that has been packed so that we may abort and revert+        if (thunks_packed < MAX_THUNKS_PER_PACKET)+            thunks[thunks_packed++] = closure;+        // otherwise: abort packing right now (should not happen at all).+    }+#endif++    return P_SUCCESS;+}++// Packing PAPs and APs:++// a PAP (partial application) represents a function which has been+// given too few arguments for complete evaluation (thereby defining a+// new function with fewer arguments).+//+// PAP/AP closure layout in GHC (see Closures.h, InfoTables.h):+//   +--------------------------------------------------------------++//   | Header | (arity | n_args) | Function | Stack.|Stack.|Stack...|+//   +--------------------------------------------------------------++//                                     |+//                   (info table has bitmap for stack)+//+// The _arity_ of the PAP informs about how many arguments are still+// missing to saturate the function call. n_args, in turn, is how many+// arguments are already present (i.e. the stack size).+//+// An APs (generic application) has similar layout, but actually has+// all its arguments, i.e. the application has not been evaluated to+// WNHF yet. Therefore, APs have a thunk header (one extra word).+//+//  PAPs/APs are packed by packing the function and the argument stack,+// where both can point to static or dynamic (heap-allocated) closures+// which must be packed later, and enqueued here.+// The stack may contain either pointers or non-pointer words, indicated+// by a _bitmap_ that comes with the function (but is only used up to the+// indicated n_args size).+// Old code used tags on all stored values (doubling the stack size),+// this version packs the btimap instead.+static StgWord PackPAP(PackState *p, StgPAP *pap) {++    nat i;+    nat hsize;          // header size+    StgWord bitmap;     // small bitmap+    StgLargeBitmap *lbm;// large bitmap+    nat bsize;          // bitmap size+    nat bsizeW;         // bitmap size in words+    StgFunInfoTable *funInfo; // to get bitmap++    nat n_args;         // arg. count on stack+    StgClosure *fun;    // function in PAP/AP+    StgPtr ptr;         // stack object currently packed+    StgWord tag = 0;++    tag = GET_CLOSURE_TAG((StgClosure*) pap);+    pap = UNTAG_CAST(StgPAP*, (StgClosure*) pap);++    ASSERT(LOOKS_LIKE_CLOSURE_PTR(pap));+    ASSERT(get_itbl((StgClosure*)pap)->type == PAP ||+            get_itbl((StgClosure*)pap)->type == AP);++    switch (get_itbl((StgClosure*)pap)->type) {+    case PAP:+        n_args = pap->n_args;+        hsize  = HEADERSIZE+1;+        fun    = pap->fun;+        ptr    = (StgPtr) pap->payload;+        break;++    case AP:+        n_args = ((StgAP*) pap)->n_args;+        hsize  = sizeofW(StgThunkHeader)+1;+        fun    = ((StgAP*) pap)->fun;+        ptr    = (StgPtr) ((StgAP*) pap)->payload;+        break;++    default: // checked in packClosure, should not happen here+        barf("PackPAP: strange info pointer, type %d ",+             get_itbl((StgClosure*)pap)->type);+    }++    PACKETDEBUG( {+            debugBelch("Packing %s closure @ %p,"+                       "with stack of size %d\n",+                       info_type((StgClosure*) pap), pap, n_args);+        });++    // Extract the bitmap from the function.+    // Bitmaps can be either small (1 StgWord) or large+    // (StgLargeBitmap, see InfoTables.h) with a size field and+    // multiple bitmap fields.+    // Note that only the bits up to n_args are used in the packing code,+    // therefore the packed bitmap is not necessarily the complete one.+    //+    // small bitmap:+    // (32 bit StgWord) [ bits 5-31: bitmap | bits 0-4: size ]+    // (64 bit StgWord) [ bits 5-63: bitmap | bits 0-5: size ]+    //                          <--reading--|+    //+    // note that reading direction for bitmaps is right-to-left per+    // StgWord (but left-to-right in the large for large bitmaps)+    // see rts/sm/Scav.c::scavenge_(small|large)_bitmap++    lbm = (StgLargeBitmap*) NULL;+    funInfo = get_fun_itbl(UNTAG_CLOSURE(fun));+    switch (funInfo->f.fun_type) {++        // these two use a large bitmap.+        case ARG_GEN_BIG:+            errorBelch("PackPAP at %p: large bitmap not implemented",+                       pap);+            return P_UNSUPPORTED;+            // lbm set indicates a large bitmap (bad if all non-pointers! :-)+            lbm   = GET_FUN_LARGE_BITMAP(funInfo);+            bsizeW = lbm->size / BITS_IN(StgWord);+            break;+        case ARG_BCO:+            errorBelch("PackPAP at %p: large bitmap not implemented",+                       pap);+            return P_UNSUPPORTED;+            // lbm indicates large bitmap. BCO macro needs fun ptr, not info+            lbm   = BCO_BITMAP(fun);+            bsizeW = lbm->size / BITS_IN(StgWord);+            break;++        // another clever solution: fields in info table different for+        // some cases... and referring to autogenerated constants (Apply.h)+        case ARG_GEN:+            bitmap = funInfo->f.b.bitmap;+            bsizeW  = 1;+            break;++        default:+            bitmap = stg_arg_bitmaps[funInfo->f.fun_type];+            bsizeW  = 1;+    }+++    // check that we have enough space... upper bound on required size:+    //         header + arg.s (all non-ptrs) + bitmap and its tag+    if (!roomToPack(p, hsize + n_args + 1 + bsizeW))+        return P_NOBUFFER;++    // XXX unpacked_size += hsize + 1 + n_args; // == closure_size(pap)++    // register closure+    registerOffset(p, (StgClosure*) pap);++    // do the actual packing!+    // PAP layout in pack buffer+    //   +---------------------------....................---------------------++    //   | Header | (arity | n_args) | bsizeW | bitmap.. | nonPtr | nonPtr|...|+    //   +---------------------------....................---------------------++    // Function field and pointers on stack are not packed but enqueued. In+    // turn, the packet contains the bitmap, together with its size (or value+    // 0xFF..FF to tag a small bitmap)++    // pack closure marker+    Pack(p, (StgWord) CLOSURE);++    // pack header. First word (infoptr) is tagged and offset+    Pack(p, (StgWord) (P_OFFSET(TAG_CLOSURE(tag, (StgClosure*) *((StgPtr) pap+                                                                 )))));+    // rest of header packed as-is (possibly padding, then arity|n_args)+    for(i = 1; i < hsize; i++) {+        Pack(p, (StgWord) *(((StgWord*)pap)+i));+    }++    // queue the function closure for later packing+    queueClosure(p->queue, fun);++    // pack the bitmap+    // the bitmap is preceded by a tag, SMALL_BITMAP_TAG == ~0L for a small one,+    // its size in bits for a large bitmap.++    // Then we pack the bitmap itself.+    // Note that packing only n_args/BITS_IN(StgWord) bits would do, only those+    // bits are actually used in the packing/unpacking code). However, we do not+    // save much, and the case is very rare anyway.+    if ( lbm == NULL ) {+        // small bitmap, tag and pack it+        // SMALL_BITMAP_TAG is ~0L, a very unlikely size+        Pack(p, SMALL_BITMAP_TAG);+        Pack(p, bitmap);+    } else {+        // large bitmap, a lot of gymnastics+        PACKETDEBUG(debugBelch("yuck, large bitmap"));+        return P_UNSUPPORTED; // XXX following code is an unchecked draft+        // use size as tag for large bitmap (see above, ~0L is unlikely size)+        Pack(p, bsizeW);+        // for (i=0; i * BITS_IN(StgWord) < n_args; i++) { // meeh, we pack all+        for (i=0; i < bsizeW; i++) {+            Pack(p, lbm->bitmap[i]);+        }+    }++    // now walk the stack, packing non-pointers and enqueueing pointers, as+    // indicated by bitmap, see Scav.c::scavenge_(small|large)_bitmap (which+    // only evacuates pointers)++    // ptr = first word of payload (PAP/AP cases separated above)+    if (lbm == NULL) {+        bsize = BITMAP_SIZE(bitmap);+        bitmap = BITMAP_BITS(bitmap);+        while (bsize > 0) {+            if (bitmap & 1) {+                // bit set => non-pointer, pack+                Pack(p, *ptr);+            } else {+                // bit not set => pointer+                queueClosure(p->queue, (StgClosure*) *ptr);+                // XXX unpacked_size += sizeofW(StgInd); // unpacking creates IND+            }+            ptr++;+            bitmap = bitmap >> 1;+            bsize--;+        }+    } else {+        debugBelch("yuck, large bitmap again");+        return P_UNSUPPORTED;+        // XXX following code UNCHECKED!+        // written to closely match Scav.c::scavenge_large_bitmap+        nat j, b;+        b = 0;+        bsize = lbm->size;+        for(i = 0; i < bsize; b++) {+            bitmap = lbm->bitmap[b];+            j = stg_min(bsize-i, BITS_IN(StgWord));+            i += j;+            for (; j > 0; j--, ptr++) {+                if (bitmap & 1) { // bit set => non-pointer+                    Pack(p, *ptr);+                } else { // bit not set => pointer+                    queueClosure(p->queue, (StgClosure*) *ptr);+                    // XXX unpacked_size += sizeofW(StgInd); // unpacking creates IND+                }+                bitmap = bitmap >> 1;+            }+        }+    }++    return P_SUCCESS;+}++// Packing Arrays.++// An Array in the heap can contain StgWords or Pointers (to+// closures), and is thus of type StgArrWords or StgMutArrPtrs.+//+//     Array layout in heap/buffer is the following:+//+//  (packed into the buffer)+// +------------------------+......................................++// | IP'| Hdr | ptrs | size | ptr1 | ptr2 | .. | ptrN | card space |+// +------------------------+......................................++//                               (added in heap when unpacking)+//+// The array size is stored in bytes, but will always be word-aligned.+//+// Historically, this routine was also packing ArrWords, but they can+// equally well be treated as "pointers-first" generic layout (with no+// pointers), and are packed simply by copying all words (as non-ptrs).+//+// MutArrPtrs (MUT_ARRAY_PTRS_* types) contain pointers to other+// closures instead of words.+// Packing MutArrPtrs means to enqueue/pack all pointers found.+// OTOH, packing=copying a mutable array is not a good idea at all.+// We implement it even though, leave it to higher levels to restrict.+static StgWord PackArray(PackState *p, StgClosure *closure) {++    StgClosure *infoptr;+    nat i, payloadsize, packsize;++    /* remove tag, store it in infopointer (same as above) */+    StgWord tag=0;++    tag = GET_CLOSURE_TAG(closure);+    closure = UNTAG_CLOSURE(closure);++#if DEBUG+    /* get info about basic layout of the closure */+    StgInfoTable *info = get_itbl(closure);++    ASSERT( info->type == MUT_ARR_PTRS_CLEAN+            || info->type == MUT_ARR_PTRS_DIRTY+            || info->type == MUT_ARR_PTRS_FROZEN0+            || info->type == MUT_ARR_PTRS_FROZEN);+#endif++    // MUT_ARR_PTRS_* {HDR,(no. of)ptrs,size(total incl.card table)}+    // Only pack header, not card table which follows the data.+    packsize = HEADERSIZE + 2;+    payloadsize = ((StgMutArrPtrs *)closure)->ptrs;++    // the function in ClosureMacros.h would include the header:+    // arr_words_sizeW(stgCast(StgArrWords*,q));+    PACKETDEBUG(debugBelch("*>== %p (%s): packing array"+                           "(%d words) (size=%d)\n",+                           closure, info_type(closure), payloadsize,+                           (int)closure_sizeW(closure)));++    // check if enough room in the pack buffer+    if (!roomToPack(p, packsize)) return P_NOBUFFER;++    // record offset of the closure */+    registerOffset(p, closure);++    Pack(p, (StgWord) CLOSURE);  // marker for unglobalised closure (array)++    // Pack the header and the number of bytes/ptrs that follow)+    // First word (info pointer) is tagged and offset+    infoptr = *((StgClosure**) closure);+    Pack(p, (StgWord) (P_OFFSET(TAG_CLOSURE(tag, infoptr))));++    // pack the rest of the header (variable header)+    for (i = 1; i < HEADERSIZE; ++i)+        Pack(p, (StgWord)*(((StgPtr)closure)+i));++    // pack no. of ptrs and total size, enqueue pointers+    Pack(p, (StgWord) ((StgMutArrPtrs *)closure)->ptrs);+    Pack(p, (StgWord) ((StgMutArrPtrs*)closure)->size);+    for (i=0; i<payloadsize; i++)+        queueClosure(p->queue, ((StgMutArrPtrs *) closure)->payload[i]);++    // unpacked_size += closure_sizeW(closure); XXX unpacked_size++    return P_SUCCESS;+}++/*******************************************************************+ *   unpacking a graph structure:+ *******************************************************************/++/*+  @UnpackGraph@ unpacks the graph contained in a message buffer.  It+  returns a pointer to the new graph.++  Formerly, there was also a globalAddr** @gamap@ parameter: set to+  point to an array of (oldGA,newGA) pairs which were created as a result+  of unpacking the buffer; and nat* @nGAs@ set to the number of GA pairs which+  were created.++  for "pointer tagging", we assume here that all stored+  info pointers (each first word of a packed closure) also carry the+  tag found at the sender side when enqueueing it (for the first+  time!). When closures are unpacked, the tag must be added before+  inserting the result of unpacking into other closures as a pointer.+  Done by UnpackClosure(), see there.+*/++#ifdef LIBRARY_CODE+// unpacking from a Haskell array (using the Haskell Byte Array)+// may return error code P_GARBLED+StgClosure* pmUnpackGraphWrapper(StgArrWords* packBufferArray, Capability* cap)+#else+StgClosure* unpackGraphWrapper(StgArrWords* packBufferArray, Capability* cap)+#endif+{+    nat size;+    StgWord *buffer;+    StgClosure* newGraph;++    size = packBufferArray->bytes / sizeof(StgWord);+    buffer = (StgWord*) packBufferArray->payload;++    // unpack. Might return NULL in case the buffer was inconsistent.+    newGraph = unpackGraph_(buffer, size, cap);++    return (newGraph == NULL ? (StgClosure *) P_GARBLED : newGraph);+}++#ifndef LIBRARY_CODE+StgClosure*+unpackGraph(rtsPackBuffer *packBuffer, Capability* cap) {++  StgClosure *graphroot;++  IF_DEBUG(sanity, // do a sanity check on the incoming packet+           checkPacket(packBuffer->buffer, packBuffer->size));++  PACKDEBUG(debugBelch("Packing: Header unpacked. (bufsize=%" FMT_Word+                       ", heapsize=%" FMT_Word ")\nUnpacking closures...\n",+                       packBuffer->size, packBuffer->unpacked_size));++  graphroot = unpackGraph_(packBuffer->buffer, packBuffer->size, cap);++  // if this fails outside the library code, complain and abort the program+  if (graphroot == NULL) {+    barf("Failure during unpacking, aborting program");+  }++  // wipe the pack buffer if we do sanity checks.+  // Only valid for the in-RTS version where data is never reused+  IF_DEBUG(sanity, {+          StgPtr p;+          for (p=(StgPtr)packBuffer->buffer;+               p<(StgPtr)(packBuffer->buffer)+(packBuffer->size);)+              *p++ = 0xdeadbeef;+      });++  return (graphroot);+}+#endif++// Internal worker function, not allowed to edit the buffer at all+// (used with with an immutable Haskell ByteArray# as buffer for+// deserialisation). This function returns NULL upon+// errors/inconsistencies in buffer (avoiding to abort the program).+static StgClosure* unpackGraph_(StgWord *buffer, StgInt size, Capability* cap) {+    StgWord* bufptr;+    StgClosure *closure, *parent, *graphroot;+    nat pptr = 0, pptrs = 0, pvhs = 0;+    nat currentOffset;+    HashTable* offsets;+    ClosureQ* queue;++    PACKDEBUG(debugBelch("Unpacking buffer @ %p (%" FMT_Word " words)\n",+                         buffer, size));+    IF_DEBUG(sanity, checkPacket(buffer, size));++    offsets = allocHashTable();+    queue   = initClosureQ(size);++    graphroot = parent = (StgClosure *) NULL;+    bufptr = buffer;++    do {+        // check that we aren't at the end of the buffer, yet+        IF_DEBUG(sanity, ASSERT(*bufptr != END_OF_BUFFER_MARKER));++        // Compute the offset to register for future back references+        // If this is itself an offset, or a PLC, we do not store anything+        if (*bufptr == OFFSET || *bufptr == PLC) {+            currentOffset = 0;+        } else {+            currentOffset = ((nat) (bufptr - buffer)) + PADDING;+            // ...which is at least 1 (PADDING)+        }++        // Unpack one closure (or offset or PLC). This allocates heap+        // space, checks for PLC/offset etc. The returned pointer is+        // tagged with the tag found in the info pointer.+        closure = UnpackClosure (queue, offsets, &bufptr, cap);++        if (closure == NULL) {+            // something is wrong with the packet, give up immediately+            // we do not try to find out details of what is wrong...+            PACKDEBUG(debugBelch("Unpacking error at address %p",bufptr));+            freeHashTable(offsets, NULL);+            freeClosureQ(queue);+            return (StgClosure *) NULL;+        }++        // store closure address for offsets (if we should, see above)+        if (currentOffset != 0) {+            PACKETDEBUG(debugBelch("---> Entry in Offset Table: (%d, %p)\n",+                                   currentOffset, closure));+            // note that the offset is stored WITH TAG+            insertHashTable(offsets, currentOffset, (void*) closure);+        }++        // Set the pointer in the parent to point to chosen+        // closure. If we're at the top of the graph (our parent is+        // NULL), then we want to return this closure to our caller.+        if (parent == NULL) {+            /* we are at the root. Do not remove the tag */+            graphroot = closure;+            PACKDEBUG(debugBelch("Graph root %p, tag %x", closure,+                                 (int) GET_CLOSURE_TAG(closure)));+        } else {+            // packet fragmentation code would need to check whether+            // there is a temporary blackhole here. Not supported.++            // write ptr to new closure into parent at current position (pptr)+            ((StgPtr) parent)[HEADERSIZE + pvhs + pptr] = (StgWord) closure;+        }++        // Locate next parent pointer (incr ppr, dequeue next closure at end)+        locateNextParent(queue, &parent, &pptr, &pptrs, &pvhs);++        // stop when buffer size has been reached or end of graph+    } while ((parent != NULL) && (size > (bufptr-buffer)));++    if (parent != NULL) {+        // this case is valid when one graph can stretch across+        // several packets (fragmentation), in which case we would+        // save the state. Not supported here.++        PACKDEBUG(errorBelch("Pack buffer overrun"));+        return (StgClosure *) NULL;+    }++    freeHashTable(offsets, NULL);+    freeClosureQ(queue);++    // check magic end-of-buffer word+    IF_DEBUG(sanity, ASSERT(*(bufptr++) == END_OF_BUFFER_MARKER));++    // assert we unpacked exactly as many words as there are in the buffer+    ASSERT(size == (nat) (bufptr-buffer));++    // ToDo: are we *certain* graphroot has been set??? WDP 95/07+    ASSERT(graphroot!=NULL);++    PACKDEBUG( {+            char fpstr[MAX_FINGER_PRINT_LEN];+            graphFingerPrint(fpstr, graphroot);+            debugBelch(">>> unpacked graph at %p\n Fingerprint is\n"+                       "\t{%s}\n", graphroot, fpstr);+        });++    return graphroot;+}++// locateNextParent finds the next pointer field in the parent+// closure, retrieve information about its variable header size and+// no. of pointers. If the current parent has been completely unpacked+// already, get the next closure from the global closure queue, and+// register the new variable header size and no. of pointers.+//+// Example situation:+//+// *parentP+//    |+//    V+//  +--------------------------------------------------------------------++//  |hdr| variable hdr  | ptr1 | ptr2 | ptr3 | ... | ptrN | non-pointers |+//  +--------------------------------------------------------------------++//      <-- *pvhsP=2 --->                A+//                                       |+//         *pptrs = N                 *pptr=3+STATIC_INLINE void locateNextParent(ClosureQ* q, StgClosure** parentP,+                                    nat* pptrP, nat* pptrsP, nat* pvhsP) {+    nat size, nonptrs;++    // pptr as an index into the current parent; find the next pointer+    // field in the parent by increasing pptr; if that takes us off+    // the closure (i.e. *pptr + 1 > *pptrs) grab a new parent from+    // the closure queue++    (*pptrP)++;+    while (*pptrP + 1 > *pptrsP) {+        // *parentP has been constructed (all pointer set); so check it now+        IF_DEBUG(sanity,+                if (*parentP != (StgClosure*)NULL) // not root+                checkClosure(*parentP));++        *parentP = deQueueClosure(q);++        if (*parentP == NULL) {+            break;+        } else {+            getClosureInfo(*parentP, NULL, &size, pptrsP, &nonptrs, pvhsP);+            *pptrP = 0;+        }+    }+    // *parentP points to the new (or old) parent;+    // *pptr, *vhsP, and *pptrs have been updated referring to the new parent+}++//  UnpackClosure is the heart of the unpacking routine. It is called for+//  every closure found in the packBuffer.+//  UnpackClosure does the following:+//    - check for the kind of the closure (PLC, Offset, std closure)+//    - copy the contents of the closure from the buffer into the heap+//    - update LAGA tables (in particular if we end up with 2 closures+//      having the same GA, we make one an indirection to the other)+//    - set the GAGA map in order to send back an ACK message+// In case of any unexpected data, the routine returns NULL.+//+//  At the end of this function,+//  *bufptrP points to the next word in the pack buffer to be unpacked.+//+//  "pointer tagging":+// When unpacking, UnpackClosure() we add the tag to its return value,+// but enqueue the closure address WITHOUT A TAG, so we can access the+// unpacked closure directly by the enqueued pointer.+// The closure WITH TAG is saved as offset value in the offset hash+// table (key=offset, value=address WITH TAG), to be filled in other+// closures as a pointer field.+// When packing, we did the reverse: saved the closure address WITH TAG+// in the queue, but stored it WITHOUT TAG in the offset table (as a+// key, value was the offset).+static  StgClosure*+UnpackClosure (ClosureQ* q, HashTable* offsets,+               StgWord **bufptrP, Capability* cap) {+    StgClosure *closure;+    nat size,ptrs,nonptrs,vhs,i;+    StgInfoTable *ip;+    StgWord tag = 0;++    // Unpack the closure body, if there is one; three cases:+    //   - PLC: closure is just a pointer to a static closure+    //   - Offset: closure has been unpacked already+    //   - else: copy data from packet into closure+    switch ((StgWord) **bufptrP) {+        // these two cases respect the "pointer tags" by+        // design: either the tag was not removed at all (PLC case), or+        // the offset refers to an already unpacked (=> tagged) closure.+        case PLC:+            closure = UnpackPLC(bufptrP);+            break;+        case OFFSET:+            closure = UnpackOffset(offsets, bufptrP);+            break;+        case CLOSURE:++            (*bufptrP)++; // skip marker++            /* The first word of a closure is the info pointer. In contrast,+               in the packet (where (*bufptrP) points to a packed closure),+               the first word is an info _offset_, which additionally was+               tagged before packing. We remove and store the tag added to the+               info offset, and compute the untagged info table pointer from+               the info offset.+               (NB The original value must be untouched inside the buffer!)+            */+            tag = GET_CLOSURE_TAG((StgClosure*) **bufptrP);+            ip  = UNTAG_CAST(StgInfoTable*, P_POINTER(**bufptrP));+            PACKETDEBUG(debugBelch("pointer tagging: removed tag %d "+                                   "from info pointer %p in packet\n",+                                   (int) tag, ip));++            // The essential part starts here: allocate heap, fill in+            // closure, queue it to fill pointer payload later.+            if (!LOOKS_LIKE_INFO_PTR((StgWord) ip)) {+                errorBelch("Invalid info pointer in packet");+                return (StgClosure *) NULL;+            }++            /* Historic comment:+             * Close your eyes.  You don't want to see where we're+             * looking. You can't get closure info until you've unpacked the+             * variable header, but you don't know how big it is until you've+             * got closure info.  So...we trust that the closure in the buffer+             * is organized the same way as they will be in the heap...at+             * least up through the end of the variable header.+             */++            getClosureInfo((StgClosure *) *bufptrP, INFO_PTR_TO_STRUCT(ip),+                           &size, &ptrs, &nonptrs, &vhs);++            switch (INFO_PTR_TO_STRUCT(ip)->type) {+                // bitmap layouts branch into special routines:+            case PAP:+            case AP:+                closure = UnpackPAP(q, ip, bufptrP, cap);+                // creates/enQs INDirections for pointers on stack+                break;++                // MUT_ARR* need to allocate (but not fill) card table+                // space after data space, and enqueue the closure+            case MUT_ARR_PTRS_CLEAN:+            case MUT_ARR_PTRS_DIRTY:+            case MUT_ARR_PTRS_FROZEN0:+            case MUT_ARR_PTRS_FROZEN:+                closure = UnpackArray(q, ip, bufptrP, cap);+                break;++                // word arrays follow the "ptrs first" layout (with no pointers)+            case ARR_WORDS:++                // normal closures with pointers-first layout (these are exactly+                // the closures handled by PackGeneric above):+            case CONSTR:+            case CONSTR_1_0:+            case CONSTR_0_1:+            case CONSTR_2_0:+            case CONSTR_1_1:+            case CONSTR_0_2:+            case FUN:+            case FUN_1_0:+            case FUN_0_1:+            case FUN_2_0:+            case FUN_1_1:+            case FUN_0_2:+            case THUNK:+            case THUNK_1_0:+            case THUNK_0_1:+            case THUNK_2_0:+            case THUNK_1_1:+            case THUNK_0_2:+            case THUNK_SELECTOR:+#if __GLASGOW_HASKELL__ > 708+            case SMALL_MUT_ARR_PTRS_CLEAN:+            case SMALL_MUT_ARR_PTRS_DIRTY:+            case SMALL_MUT_ARR_PTRS_FROZEN0:+            case SMALL_MUT_ARR_PTRS_FROZEN:+#endif++                PACKETDEBUG(+                     debugBelch("Allocating %d heap words for %s-closure:\n"+                                "(%d ptrs, %d non-ptrs, vhs = %d)\n"+                                , size, info_type_by_ip(INFO_PTR_TO_STRUCT(ip)),+                                ptrs, nonptrs, vhs));++                closure = (StgClosure*) allocate(cap, size);++                // Remember, the generic closure layout is as follows:+                //     +------------------------------------------------++                //     | IP | FIXED HDR | VARIABLE HDR | PTRS | NON-PRS |+                //     +------------------------------------------------++                //     Note that info ptr (IP) is assumed to be first hdr. field++                // Fill in the info pointer (extracted before)+                ((StgPtr)closure)[0] = (StgWord) ip;+                (*bufptrP)++;++                // Fill in the rest of the fixed header (if any)+                for (i = 1; i < HEADERSIZE; i++)+                    ((StgPtr)closure)[i] = *(*bufptrP)++;++                // Fill in the packed variable header+                for (i = 0; i < vhs; i++)+                    ((StgPtr)closure)[HEADERSIZE + i] = *(*bufptrP)++;++                // Pointers will be filled in later, but set zero here to+                // easily check if there is a temporary BH.+                for (i = 0; i < ptrs; i++)+                    ((StgPtr)closure)[HEADERSIZE + vhs + i] = 0;++                // Fill in the packed non-pointers+                for (i = 0; i < nonptrs; i++)+                    ((StgPtr)closure)[HEADERSIZE + i + vhs + ptrs]+                        =  *(*bufptrP)++;++                ASSERT(HEADERSIZE+vhs+ptrs+nonptrs == size);++                queueClosure(q, closure);+                break;++                // other cases are unsupported/unexpected, and caught here+            default:+                errorBelch("Unpacking unexpected closure type (%x)\n",+                           INFO_PTR_TO_STRUCT(ip)->type);+                return (StgClosure *) NULL;+            } // switch(INFO_PTR_TO_STRUCT(ip)->type)+            break;++    default:+        // invalid markers (not OFFSET, PLC, CLOSURE) are caught here+        errorBelch("unpackClosure: Found invalid marker %" FMT_Word ".\n",+                   (long) **bufptrP);+        return (StgClosure *) NULL;+    }++    return TAG_CLOSURE(tag, closure);+}++// look up the closure's address for an offset in the hashtable+// advance buffer pointer while reading data+STATIC_INLINE StgClosure *UnpackOffset(HashTable* offsets, StgWord **bufptrP) {+    StgClosure* existing;+    int offset;++    ASSERT((long) **bufptrP == OFFSET);++    (*bufptrP)++; // skip marker+    // unpack nat; find closure for this offset+    offset = (nat) **bufptrP;+    (*bufptrP)++; // skip offset++    ASSERT(offset != 0);+    // find this closure in an offset hashtable (we can have several packets)+    existing = (StgClosure *) lookupHashTable(offsets, offset);++    PACKETDEBUG(debugBelch("*<__ Unpacked indirection to closure %p"+                           " (was OFFSET %d)", existing, offset));++    // we should have found something...+    ASSERT(existing!= NULL);++    return existing;+}++// unpack a static address (advancing buffer pointer while reading)+STATIC_INLINE  StgClosure *UnpackPLC(StgWord **bufptrP) {+    StgClosure* plc;++    ASSERT((long) **bufptrP == PLC);++    (*bufptrP)++; // skip marker+    // Not much to unpack; just a static local address+    // but need to correct the offset+    plc = (StgClosure*) P_POINTER(**bufptrP);+    (*bufptrP)++; // skip address+    PACKETDEBUG(debugBelch("*<^^ Unpacked PLC at %p\n", plc));+    return plc;+}++// unpacking PAPs (and other bitmap layout). Returns NULL in case of errors+static StgClosure * UnpackPAP(ClosureQ *queue, StgInfoTable *info,+                              StgWord **bufptrP, Capability* cap) {++    nat n_args, size, hsize, i;+    StgWord bsizeW;+    StgPtr pap; // PAP/AP is constructed here, but untyped (would need+                // to distinguish the AP case all the time)++    // PAP layout in pack buffer+    //   +---------------------------....................---------------------++    //   | Header | (arity | n_args) | bsizeW | bitmap.. | nonPtr | nonPtr|...|+    //   +---------------------------....................---------------------++    //+    // The bitmap indicating pointers on the stack is packed after the header.+    // For large bitmaps, their size in words is stored in the buffer; small+    // bitmaps are indicated by a size of 0xFF..FF (SMALL_BITMAP_TAG).+    //+    // In the heap, there will be a function field instead of this bitmap, and+    // the payload (stack) will have pointers interspersed with the packed+    // nonptrs.+    // The function field is filled in the usual way (as if a PAP was+    // "ptrs-first"), the stack will be constructed with pointers to new+    // indirections filled later.++    // Unpacking should result in the following layout in the heap:+    // +----------------------------------------------------------------++    // | Header | (arity , n_args) | Fct. | Arg/&Ind1 | Arg/&Ind2 | ... |+    // +----------------------------------------------------------------++    // followed by <= n_args indirections pointed at from the stack++    // calc./alloc. needed closure space in the heap, using common macros.+    switch (INFO_PTR_TO_STRUCT(info)->type) {+    case PAP:+        hsize = HEADERSIZE + 1;+        n_args  = ((StgPAP*) *bufptrP)->n_args;+        size  = PAP_sizeW(n_args);+        break;+    case AP:+        hsize = sizeofW(StgThunkHeader) + 1;+        n_args  = ((StgAP*)  *bufptrP)->n_args;+        size  = AP_sizeW(n_args);+        break;+    default:+        PACKDEBUG(errorBelch("UnpackPAP: strange info pointer, type %d ",+                             INFO_PTR_TO_STRUCT(info)->type));+        return (StgClosure*) NULL;+    }+    PACKETDEBUG(debugBelch("allocating %d heap words for a PAP (%d args)\n",+                           size,  n_args));+    pap = (StgPtr) allocate(cap, size);++    // fill in info ptr (extracted and given as argument by caller)+    pap[0] = (StgWord) info;+    (*bufptrP)++;++    // fill in header fields (includes ( arity | n_args ) )+    for(i = 1; i < hsize; i++) {+        pap[i] = (StgWord) *(*bufptrP)++;+    }+    // enqueue to get function field filled (see getClosureInfo)+    queueClosure(queue, (StgClosure*) pap);+    // zero the function field+    pap[hsize] = (StgWord) NULL;++    // read bitmap size and bitmap+    bsizeW = *(*bufptrP)++;++    // unpack the stack (size == args), starting at pap[hsize]+    // make room for fct. pointer, thus start at hsize+1++    if (bsizeW == SMALL_BITMAP_TAG) {+        StgWord bitmap;+        // small bitmap, just read it and unpack accordingly+        bitmap = (StgWord)  *(*bufptrP)++;++        // bitmap size irrelevant here, but should be >= n_args+        ASSERT(n_args <= BITMAP_SIZE(bitmap));+        bitmap = BITMAP_BITS(bitmap);+        for (i = hsize + 1; i < hsize + n_args; i++, bitmap >>= 1) {+            if (bitmap & 1) {+                // non-pointer, just unpack it+                pap[i] = (StgWord) *(*bufptrP)++;+            } else {+                // pointer: create and enqueue a new indirection, store a+                // pointer to it on the stack+                StgInd *ind;+                // allocate a new closure+                ind = (StgInd*) allocate(cap, sizeofW(StgInd));+                SET_HDR(ind, &stg_IND_info, CCS_SYSTEM); // set ccs+                // zero the indirectee field (should be filled later)+                ind->indirectee = (StgClosure*) NULL;+                // store a pointer+                pap[i] = (StgWord) ind;+                queueClosure(queue, (StgClosure*) ind);+            }+            bitmap >>= 1;+        }++    } else {+        debugBelch("yuck, unpacking large bitmap");+        return (StgClosure*) NULL;+        // need to repeatedly read a new bitmap and proceed+        StgPtr bitmapPos;+        nat j;+        StgWord bitmap;++        // ... walk through the bitmap until n_args have been unpacked+        bitmapPos = *bufptrP;+        bitmap    = *bitmapPos;+        j = BITS_IN(StgWord);+        for (i = hsize + 1; i < hsize + n_args; i++) {+            if (bitmap & 1) {+                // non-pointer, just unpack it+                pap[i] = (StgWord) *(*bufptrP)++;+            } else {+                // pointer: create and enqueue a new indirection, store a+                // pointer to it on the stack+                StgInd *ind;+                // allocate a new closure+                ind = (StgInd*) allocate(cap, sizeofW(StgInd));+                SET_HDR(ind, &stg_IND_info, CCS_SYSTEM); // set ccs+                // zero the indirectee field (should be filled later)+                ind->indirectee = (StgClosure*) NULL;+                // store a pointer+                pap[i] = (StgWord) ind;+                queueClosure(queue, (StgClosure*) ind);+            }+            // advance into next part of bitmap when current one done+            j--;+            if (j == 0) {+                bitmapPos++;+                bitmap = *bitmapPos;+                j = BITS_IN(StgWord);+            } else {+                bitmap >>= 1;+            }+        }+    }++    return (StgClosure*) pap;+}++// unpacking arrays. Returns NULL in case of errors.+static StgClosure* UnpackArray(ClosureQ *queue, StgInfoTable* info,+                               StgWord **bufptrP, Capability* cap) {+    nat size;+    StgMutArrPtrs *array;++    nat type = INFO_PTR_TO_STRUCT(info)->type;++    // refuse to work if not an array+    if (type != MUT_ARR_PTRS_CLEAN && type != MUT_ARR_PTRS_DIRTY &&+        type != MUT_ARR_PTRS_FROZEN0 && type != MUT_ARR_PTRS_FROZEN) {++        PACKDEBUG(errorBelch("UnpackArray: unexpected closure type %d",+                             INFO_PTR_TO_STRUCT(info)->type));+            return (StgClosure *) NULL;+    }++    // Since GHC-6.13, ptr arrays additionally carry a "card table"+    // for generational GC (to indicate mutable/dirty elements). For+    // unpacking, allocate the card table and fill it with zero.+    // Array layout in buffer:+    // +------------------------+......................................++    // | IP'| Hdr | ptrs | size | ptr1 | ptr2 | .. | ptrN | card space |+    // +------------------------+......................................++    //                               (added in heap when unpacking)+    // ptrs indicates how many pointers to come (N). Size field gives+    // total size for pointers and card table behind (to add).++    // size = sizeofW(StgMutArrPtrs) + (StgWord) *((*bufptrP)+2);+    size = closure_sizeW_((StgClosure*) *bufptrP, INFO_PTR_TO_STRUCT(info));+    ASSERT(size == sizeofW(StgMutArrPtrs) + ((StgMutArrPtrs*) *bufptrP)->size);+    PACKETDEBUG(debugBelch("Unpacking ptrs array, %" FMT_Word+                           " ptrs, size %d\n",+                           (StgWord) *((*bufptrP)+1), size));+    array = (StgMutArrPtrs *) allocate(cap, size);++    // set area 0 (Blackhole-test in unpacking and card table)+    memset(array, 0, size*sizeof(StgWord));+    // write header+    for (size = 0; size < (sizeof(StgMutArrPtrs)/sizeof(StgWord)); size++)+        ((StgPtr) array)[size] = (StgWord) *(*bufptrP)++;+    // correct first word (info ptr, stored with offset in packet)+    ((StgPtr)array)[0] = (StgWord) info;+    // and enqueue it, pointers will be filled in subsequently+    queueClosure(queue, (StgClosure*)array);++    PACKETDEBUG(debugBelch(" Array created @ %p.\n",array));++    return (StgClosure*) array;+}+++#ifndef LIBRARY_CODE+// creating new heap closures:++// creating a black hole (to receive remote data), owned by the system tso+StgClosure* createBH(Capability *cap) {+  StgClosure *new;++  // a blackhole carries one pointer of payload, see StgMiscClosures.cmm, so+  // we allocate 2 words. The payload indicates the blackhole owner, in our+  // case it is the "system" (or later, the cap, for -threaded rts).+  new = (StgClosure*) allocate(cap, 2);++  SET_HDR(new, &stg_BLACKHOLE_info, CCS_SYSTEM); // ccs to be checked!++  new->payload[0] = (StgClosure*) &stg_system_tso; +          // see above. Pseudo-TSO (has TSO info pointer) owning all+          // system-created black holes, and storing BQs.++  return new;+}++// cons node info pointer, from GHC.Base+#define CONS_INFO ghczmprim_GHCziTypes_ZC_con_info+// constructor tag for pointer tagging. We return a tagged pointer here!+#define CONS_TAG  2+extern const StgInfoTable CONS_INFO[];++// creating a list node. returns a tagged pointer.+StgClosure* createListNode(Capability *cap, StgClosure *head, StgClosure *tail) {+  StgClosure *new;++  // a list node (CONS) carries two pointers => 3 words to allocate+  // if we have given a capability, we can allocateLocal (cheaper, no lock)+  new = (StgClosure*) allocate(cap, 3);++  SET_HDR(new, CONS_INFO, CCS_SYSTEM); // to be checked!!!+  new->payload[0] = head;+  new->payload[1] = tail;++  return TAG_CLOSURE(CONS_TAG,new);+}+#endif++// debugging functions+#if defined(DEBUG)++/*+  Generate a finger-print for a graph.  A finger-print is a string,+  with each char representing one node; depth-first traversal.+  Will only be called inside this module.+*/++/* this array has to be kept in sync with includes/ClosureTypes.h */+#if __GLASGOW_HASKELL__ == 708+# if !(N_CLOSURE_TYPES == 61 )+# error Wrong closure type count in fingerprint array. Check code.+# endif+#elif __GLASGOW_HASKELL__ > 708+# if !(N_CLOSURE_TYPES == 65 )+# error Wrong closure type count in fingerprint array. Check code.+# endif+#endif+static char* fingerPrintChar =+  "0ccccccCC"    /* INVALID CONSTRs (0-8) */+  "fffffff"      /* FUNs (9-15) */+  "ttttttt"      /* THUNKs (16-23) */+  "TBAPP___"     /* SELECTOR BCO AP PAP AP_STACK INDs (24-31) */+  "RRRRFFFF"     /* RETs FRAMEs (32-39) */+  "*@MMT"        /* BQ BLACKHOLE MVARs TVAR (40-43) */+  "aAAAAmmwppXS" /* ARRAYs MUT_VARs WEAK PRIM MUT_PRIM TSO STACK (44-55) */+  "&FFFW"        /* TREC (STM-)FRAMEs WHITEHOLE (56-60)*/+#if __GLASGOW_HASKELL__ >= 708+  "ZZZZ"         /* SmallArr (61-64) */+#endif+  ;+++// recursive worker function:+static void graphFingerPrint_(char* fingerPrintStr,+                              HashTable* tmpClosureTable, StgClosure *p);++static void graphFingerPrint(char* fingerPrintStr, StgClosure *p)+{+    HashTable* visitTable;++    // delete old fingerprint:+    fingerPrintStr[0]='\0';++    /* init hash table */+    visitTable = allocHashTable();++    /* now do the real work */+    graphFingerPrint_(fingerPrintStr, visitTable, p);++    /* nuke hash table */+    freeHashTable(visitTable, NULL);++    ASSERT(strlen(fingerPrintStr)<=MAX_FINGER_PRINT_LEN);+}++/*+  This is the actual worker functions.+  All recursive calls should be made to this function.+*/+static void graphFingerPrint_(char* fp, HashTable* visited, StgClosure *p) {+    nat i, len, args, arity;+    const StgInfoTable *info;+    StgWord *payload;++    // first remove potential pointer tags+    p = UNTAG_CLOSURE(p);++    len = strlen(fp);+    ASSERT(len<=MAX_FINGER_PRINT_LEN);+    if (len+2 >= MAX_FINGER_PRINT_LEN)+        return;+    /* at most 7 chars added immediately (unchecked) for this node */+    if (len+7 >= MAX_FINGER_PRINT_LEN) {+        strcat(fp, "--end");+        return;+    }+    /* check whether we have met this node already to break cycles */+    if (lookupHashTable(visited, (StgWord)p)) { // ie. already touched+        strcat(fp, ".");+        return;+    }++    /* record that we are processing this closure */+    insertHashTable(visited, (StgWord) p, (void *)rtsTrue/*non-NULL*/);++    ASSERT(LOOKS_LIKE_CLOSURE_PTR(p));++    info = get_itbl((StgClosure *)p);++    // append char for this node+    fp[len] = fingerPrintChar[info->type];+    fp[len+1] = '\0';+    /* the rest of this fct recursively traverses the graph */+    switch (info -> type) {++        // simple and static objects+        case CONSTR_STATIC:+        case CONSTR_NOCAF_STATIC:+        case FUN_STATIC:+        case THUNK_STATIC:+            break;++        /* CONSTRs, THUNKs, FUNs are written with arity */+        case THUNK_2_0:+            // append char for this node+            strcat(fp, "20(");+            // special treatment for thunks... extra smp header field+            graphFingerPrint_(fp, visited, ((StgThunk *)p)->payload[0]);+            graphFingerPrint_(fp, visited, ((StgThunk *)p)->payload[1]);+            if (strlen(fp)+2<MAX_FINGER_PRINT_LEN)+                strcat(fp, ")");+            break;++        case FUN_2_0:+        case CONSTR_2_0:+            // append char for this node+            strcat(fp, "20(");+            graphFingerPrint_(fp, visited, ((StgClosure *)p)->payload[0]);+            graphFingerPrint_(fp, visited, ((StgClosure *)p)->payload[1]);+            if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                strcat(fp, ")");+            break;++        case THUNK_1_0:+            // append char for this node+            strcat(fp, "10(");+            graphFingerPrint_(fp, visited, ((StgThunk *)p)->payload[0]);+            if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                strcat(fp, ")");+            break;++        case FUN_1_0:+        case CONSTR_1_0:+            // append char for this node+            strcat(fp, "10(");+            graphFingerPrint_(fp, visited, ((StgClosure *)p)->payload[0]);+            if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                strcat(fp, ")");+            break;++        case THUNK_0_1:+        case FUN_0_1:+        case CONSTR_0_1:+            // append char for this node+            strcat(fp, "01");+            break;++        case THUNK_0_2:+        case FUN_0_2:+        case CONSTR_0_2:+            // append char for this node+            strcat(fp, "02");+            break;++        case THUNK_1_1:+            // append char for this node+            strcat(fp, "11(");+            graphFingerPrint_(fp, visited, ((StgThunk *)p)->payload[0]);+            if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                strcat(fp, ")");+            break;++        case FUN_1_1:+        case CONSTR_1_1:+            // append char for this node+            strcat(fp, "11(");+            graphFingerPrint_(fp, visited, ((StgClosure *)p)->payload[0]);+            if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                strcat(fp, ")");+            break;++        case THUNK:+            {+                char str[6];+                sprintf(str,"%d?(", info->layout.payload.ptrs);+                strcat(fp,str);+                for (i = 0; i < info->layout.payload.ptrs; i++)+                    graphFingerPrint_(fp, visited, ((StgThunk *)p)->payload[i]);+                if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                    strcat(fp, ")");+            }+            break;++        case FUN:+        case CONSTR:+            {+                char str[6];+                sprintf(str,"%d?(",info->layout.payload.ptrs);+                strcat(fp,str);+                for (i = 0; i < info->layout.payload.ptrs; i++)+                    graphFingerPrint_(fp, visited,+                                      ((StgClosure *)p)->payload[i]);+                if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                    strcat(fp, ")");+            }+            break;++        case THUNK_SELECTOR:+            graphFingerPrint_(fp, visited, ((StgSelector *)p)->selectee);+            break;++        case BCO:+            break;++        case AP_STACK:+            // unsure how to handle this one+            break;+        case AP:+            arity = ((StgAP*)p)->arity;+            args  = ((StgAP*)p)->n_args;+            payload = (StgPtr)((StgAP*)p)->payload;+            p = ((StgAP*)p)->fun;+            goto print;++        case PAP:+            /* note the arity (total #args) and n_args (how many supplied) */+            arity = ((StgPAP*)p)->arity;+            args  = ((StgPAP*)p)->n_args;+            payload = (StgPtr) ((StgPAP*)p)->payload;+            p = ((StgPAP*)p)->fun;+print:+            { char str[6];+                sprintf(str,"%d/%d(", arity, args);+                strcat(fp, str);+                // follow the function, and everything on the stack+                graphFingerPrint_(fp, visited, (StgClosure *) (p));+                if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN) {+                    StgWord bitmap;+                    StgFunInfoTable *funInfo = get_fun_itbl(UNTAG_CLOSURE(p));+                    strcat(fp, "|");+                    switch (funInfo->f.fun_type) {+                        /* these two use a large bitmap. We do not follow...*/+                        case ARG_GEN_BIG:+                        case ARG_BCO:+                            bitmap = (StgWord) (~0); // all ones+                            break;+                        case ARG_GEN:+                            bitmap = funInfo->f.b.bitmap;+                            break;+                        default:+                            bitmap = stg_arg_bitmaps[funInfo->f.fun_type];+                    }+                    // size = BITMAP_SIZE(bitmap);+                    bitmap = BITMAP_BITS(bitmap);+                    while (args > 0) {+                        if ((bitmap & 1) == 0)+                            graphFingerPrint_(fp, visited,+                                              (StgClosure *)(*payload));+                        else {+                            if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                                strcat(fp, "x");+                        }+                        payload++;+                        args--;+                        bitmap = bitmap>>1;+                    }+                }+                if (strlen(fp)+2 < MAX_FINGER_PRINT_LEN)+                    strcat(fp, ")");+            }+            break;++        case IND:+        case IND_PERM:+        case IND_STATIC:+            /* do not print the '_' for indirections */+            fp[len] = '\0';+            /* could also be different type StgIndStatic */+            graphFingerPrint_(fp, visited, ((StgInd*)p)->indirectee);+            break;++        case RET_BCO:+        case RET_SMALL:+        case RET_BIG:+        case RET_FUN:+        case UPDATE_FRAME:+        case CATCH_FRAME:+        case UNDERFLOW_FRAME:+        case STOP_FRAME:+        case BLOCKING_QUEUE:+        case BLACKHOLE:+            // check if this is actually an indirection. See above in+            // packing code, some Blackholes are actually indirections+            // since ghc-7.0+            switch (((StgInfoTable*)+                     get_itbl(UNTAG_CLOSURE(((StgInd*)p)->indirectee)))->type) {+                case TSO:+                case BLOCKING_QUEUE:+                    debugBelch("Woops! Found blackhole during fingerprint!\n");+                    break;+                default:+                    /* do not print the '_' for indirections */+                    fp[len] = '\0';+                    graphFingerPrint_(fp, visited, ((StgInd*)p)->indirectee);+                    break;+            }+            break;++        case MVAR_CLEAN:+        case MVAR_DIRTY:+            // follow MVar contents unless empty (END_TSO_QUEUE is magic)+            if (((StgMVar *)p)->value != (StgClosure*) END_TSO_QUEUE)+                graphFingerPrint_(fp, visited, ((StgMVar *)p)->value);+            break;++        case TVAR:+            // The TVAR type subsumes both the var itself and a watch+            // queue; the latter holds a TSO or an "Atomic Invariant"+            // where the former (clean/dirty) holds the current value+            // as its first payload. Anyways, while useful for GC, the+            // double meaning of the first payload is not useful for+            // fingerprinting. We do not descend into TVars.+            break;++        case ARR_WORDS:+            { // record size only (contains StgWords, not pointers)+                char str[6];+                sprintf(str, "%ld", (long) arr_words_words((StgArrWords*)p));+                strcat(fp, str);+            }+            break;++        case MUT_ARR_PTRS_CLEAN:+        case MUT_ARR_PTRS_DIRTY:+        case MUT_ARR_PTRS_FROZEN0:+        case MUT_ARR_PTRS_FROZEN:+            {+                char str[6];+                sprintf(str, "%ld", (long)((StgMutArrPtrs*)p)->ptrs);+                strcat(fp, str);+                nat i;+                for (i = 0; i < ((StgMutArrPtrs*)p)->ptrs; i++) {+                    //contains closures... follow+                    graphFingerPrint_(fp, visited,+                                      ((StgMutArrPtrs*)p)->payload[i]);+                }+                break;+            }+        case MUT_VAR_CLEAN:+        case MUT_VAR_DIRTY:+            graphFingerPrint_(fp, visited, ((StgMutVar *)p)->var);+            break;++        case WEAK:+        case PRIM:+            break;+        case MUT_PRIM:+            break;+        case TSO:+            break;+        case STACK:+            break;++        case TREC_CHUNK:+        case ATOMICALLY_FRAME:+        case CATCH_RETRY_FRAME:+        case CATCH_STM_FRAME:+        case WHITEHOLE:+            break;++#if __GLASGOW_HASKELL__ > 708+        case SMALL_MUT_ARR_PTRS_CLEAN:+        case SMALL_MUT_ARR_PTRS_DIRTY:+        case SMALL_MUT_ARR_PTRS_FROZEN0:+        case SMALL_MUT_ARR_PTRS_FROZEN:+            {+                char str[6];+                sprintf(str,"%ld",(long)((StgSmallMutArrPtrs*)p)->ptrs);+                strcat(fp,str);+                nat i;+                for (i = 0; i < ((StgSmallMutArrPtrs*)p)->ptrs; i++) {+                    //contains closures... follow+                    graphFingerPrint_(fp, visited,+                                      ((StgSmallMutArrPtrs*)p)->payload[i]);+                }+                break;+            }+#endif++        default:+            barf("graphFingerPrint_: unknown closure %d",+                 info -> type);+    }++}++//  Sanity check on a packet.+//    This does a full iteration over the packet, as in UnpackGraph.+//  Arguments: buffer data ptr, buffer size in words+static void checkPacket(StgWord* buffer, nat size) {+    StgInt packsize, openptrs;+    nat clsize, ptrs, nonptrs, vhs;+    StgWord *bufptr;+    HashTable *offsets;++    PACKDEBUG(debugBelch("checking packet (@ %p), size %ld words ...",+                         buffer, (long) size));++    offsets = allocHashTable(); // used to identify valid offsets+    packsize = 0; // compared against argument+    openptrs = 1; // counting pointers (but no need for a queue to fill them in)+    // initially, one pointer is open (graphroot)+    bufptr = buffer;++    do {+        StgWord tag;+        StgInfoTable *ip;++        ASSERT(*bufptr != END_OF_BUFFER_MARKER);++        // unpackclosure essentials are mimicked here+        tag = *bufptr; // marker in buffer (PLC | OFFSET | CLOSURE)++        if (tag == PLC) {+            bufptr++; // skip marker+            // check that this looks like a PLC (static data)+            // which is however complicated when code and data mix... TODO++            bufptr++; // move forward+            packsize += 2;+        } else if (tag == OFFSET) {+            bufptr++; // skip marker+            if (!lookupHashTable(offsets, *bufptr)) {+                barf("invalid offset %" FMT_Word " in packet "+                        " at position %p", *bufptr,  bufptr);+            }+            bufptr++; // move forward+            packsize += 2;+        } else if (tag == CLOSURE) {+            bufptr++; // skip marker++            // untag info offset and compute info pointer (first word of the+            // closure), then compute a proper info table+            ip = UNTAG_CAST(StgInfoTable*,P_POINTER(*bufptr));++            // check info ptr+            if (!LOOKS_LIKE_INFO_PTR((StgWord) ip)) {+                barf("Non-closure found in packet"+                        " at position %p (value %p)\n",+                         bufptr, ip);+            }++            // analogous to unpacking, we pretend the buffer is a heap closure+            ip = getClosureInfo((StgClosure*) bufptr, INFO_PTR_TO_STRUCT(ip),+                                &clsize, &ptrs, &nonptrs, &vhs);++            // PACKETDEBUG(debugBelch("size (%ld + %d + %d +%d, = %d)",+            //              HEADERSIZE, vhs, ptrs, nonptrs, clsize));++            // This is rather a test for getClosureInfo...but used here+            if (clsize != (nat) HEADERSIZE + vhs + ptrs + nonptrs) {+                barf("size mismatch in packed closure at %p :"+                     "(%d + %d + %d +%d != %d)", bufptr,+                     HEADERSIZE, vhs, ptrs, nonptrs, clsize);+            }++            // do a plausibility check on the values. Assume we never see+            // large numbers of ptrs and non-ptrs simultaneously+            if (ptrs > 99 && nonptrs > 99) {+                barf("Found weird infoptr %p in packet "+                        " (position %p): vhs %d, %d ptrs, %d non-ptrs, size %d",+                        ip, bufptr, vhs, ptrs, nonptrs, clsize);+            }++            // Register the pack location as a valid offset. Offsets are+            // one-based (we incremented bufptr before) in units of StgWord+            // (which is magically adjusted by the C compiler here!).+            insertHashTable(offsets,+                    (StgWord) ( bufptr - buffer ),+                    bufptr);  // No need to store any value++            switch (ip->type) {+                // some closures need special treatment, as their size in the+                // packet is unobvious++                case PAP:+                    // all arg.s packed as tag + value, 2 words per arg.+                    bufptr += sizeofW(StgHeader) + 1 + 2*((StgPAP*) bufptr)->n_args;+                    packsize += 1 + sizeofW(StgHeader) + 1 + 2*((StgPAP*) bufptr)->n_args;+                    break;+                case AP: // same, but thunk header+                    bufptr += sizeofW(StgThunkHeader) + 1 + 2*((StgAP*) bufptr)->n_args;+                    packsize += 1 + sizeofW(StgThunkHeader) + 1 + 2*((StgAP*) bufptr)->n_args;+                    break;+                case MUT_ARR_PTRS_CLEAN:+                case MUT_ARR_PTRS_DIRTY:+                case MUT_ARR_PTRS_FROZEN0:+                case MUT_ARR_PTRS_FROZEN:+                    // card table is counted as non-pointer, but not in packet+                    bufptr += sizeofW(StgHeader) + vhs;+                    packsize += 1 + sizeofW(StgHeader) + vhs;+                    break;+                default: // standard (ptrs. first) layout+                    bufptr += HEADERSIZE + vhs + nonptrs;+                    packsize += (StgInt) 1 + HEADERSIZE + vhs + nonptrs;+            }++            openptrs += (StgInt) ptrs; // closure needs some pointers to be filled in+        } else {+            barf("found invalid tag %x in packet", *bufptr);+        }++        openptrs--; // one thing was unpacked++    } while (openptrs != 0 && packsize < size);++    PACKDEBUG(debugBelch(" traversed %" FMT_Word " words.", packsize));++    if (openptrs != 0) {+        barf("%d open pointers at end of packet ",+                openptrs);+    }++    IF_DEBUG(sanity, ASSERT(*(bufptr++) == END_OF_BUFFER_MARKER && packsize++));++    if (packsize != size) {+        barf("surplus data (%" FMT_Word " words) at end of packet ",+             size - packsize);+    }++    freeHashTable(offsets, NULL);+    PACKDEBUG(debugBelch("packet OK\n"));++}++/* END OF DEBUG */+#endif++// Local Variables:+// mode: C+++// fill-column: 80+// indent-tabs-mode: nil+// c-basic-offset: 4+// buffer-file-coding-system: utf-8-unix+// End:
+ cbits/Types.h view
@@ -0,0 +1,17 @@+#ifndef PACKMAN_TYPES_H+#define PACKMAN_TYPES_H++++// packing and sending:+// Pack Buffer for constructing messages between PEs+// defined here instead of in RtsTypes.h due to FLEXIBLE_ARRAY usage+typedef struct pmPackBuffer_ {+    // for data messages only,+    StgInt /* nat */     size;+    StgInt /* nat */     unpacked_size;+    StgWord              buffer[FLEXIBLE_ARRAY];+} pmPackBuffer;+++#endif /* PACKMAN_TYPES_H */
+ cbits/Wrapper.cmm view
@@ -0,0 +1,54 @@+#include <Cmm.h>+#include "Errors.h"++STRING(debugval, "debugval %d")++stg_tryPack (gcptr original, gcptr buffer)+{+    // args: closure (graph root to serialize)+    //       buffer (MutableByteArray to pack into)+    W_ errCode;+    W_ size;++    MAYBE_GC_PP(stg_tryPack, original, buffer);++    // assign something to keep cmm reg.allocator happy+    errCode = P_SUCCESS;++    // call packing function (without giving the TSO, no blocking)+    (size) = ccall pmtryPackToBuffer(original "ptr", buffer "ptr");+    // small values indicate failure (size biased with P_ERRCODEMAX,+    // see Errors.h and Pack.c+    // NB seems Cmm always compares unsigned (wanted to use negative values here)+    if (size < P_ERRCODEMAX ) {+        errCode = size;+        size = 0;+    } else {+        size = size - P_ERRCODEMAX;+    }+    +    return (errCode, size);+}++stg_unpack (gcptr buff)+{+    // args: R1 ByteArray# containing a serialized subgraph+    W_ new;+    W_ errCode;++    // assign something to keep cmm reg.allocator happy+    new = ghczmprim_GHCziTypes_False_closure;+    errCode = P_SUCCESS;++    MAYBE_GC_P(stg_unpack, buff);++    // call packing function+    ("ptr" new) = ccall pmUnpackGraphWrapper(buff "ptr", MyCapability() "ptr");++    if (new <= P_ERRCODEMAX) {+        errCode = new;+        new = ghczmprim_GHCziTypes_False_closure;+    }++    return (errCode, new);+}
+ dist/build/alltestsStub/alltestsStub-tmp/alltestsStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import AllTests ( tests )+main :: IO ()+main = stubMain tests
+ dist/build/quickchecktestStub/quickchecktestStub-tmp/quickchecktestStub.hs view
@@ -0,0 +1,5 @@+module Main ( main ) where+import Distribution.Simple.Test.LibV09 ( stubMain )+import QCTest ( tests )+main :: IO ()+main = stubMain tests
+ pack.old view

binary file changed (absent → 168 bytes)

+ packman.cabal view
@@ -0,0 +1,180 @@+name:                packman+version:             0.3.0+synopsis:            Serialization library for GHC++description:+  This package provides Haskell data serialisation independent of evaluation,+  by accessing the Haskell heap using foreign primitive operations.+  Any Haskell data structure  apart from mutable data structures (@MVar@s+  and @TVar@s) can be serialised and later deserialised during the same run,+  or loaded into a new run, of the same program (the same executable file).+  .+  The library provides operations to @serialize@ Haskell heap data,+  and to @deserialize@ it:+  .+  > trySerializeWith :: a -> Int -> IO (Serialized a) -- Int is maximum buffer size to use+  > trySerialize :: a -> IO (Serialized a)            -- uses default (maximum) buffer size+  > deserialize :: Serialized a -> IO a+  .+  The data type @Serialized a@ is an opaque representation of serialised+  Haskell data (it contains a @ByteArray@).+  A phantom type @a@ ensures type safety within the same program run.+  Type @a@ can be polymorphic (at compile time, that is) when @Serialized a@+  is not used apart from being argument to @deserialize@.+  When data are externalised (written to disk or communicated over the+  network) using the provided instances of @Binary@ or @Read@ and @Show@,+  @a@ needs to be monomorphic because they require @Typeable@ context.+  The instances for @Show@ and @Read@ satisfy @read . show == id@.+  .+  Packman serialisation is /orthogonal/ to evaluation, heap data are+  serialised /in their current state of evaluation/, they might be entirely+  unevaluated (a thunk) or only partially evaluated (containing thunks).+  Therefore, there can be cases where a mutable data structure is captured by+  a thunk, and lead to serialisation failures (typically related to lazy I/O).+  .+  The serialisation routine will throw a @PackException@ if an error occurs+  inside the C code which accesses the Haskell heap, if a mutable data+  structure is serialised, or if the serialised data is too large.+  In presence of concurrent threads, another thread might be evaluating+  data /referred to/ by the data to be serialised. In this case, the calling+  thread will /block/ on the ongoing evaluation and continue when evaluated+  data is available.+  Internally, there is a @PackException@ @P_BLACKHOLE@ to signal the+  condition, but it is hidden inside the core library++category:            Serialization, Data, GHC+license:             BSD3+license-file:        LICENSE+author:              Michael Budde, Ásbjørn V. Jøkladal, Jost Berthold+maintainer:          jost.berthold@gmail.com+build-type:          Simple+cabal-version:       >= 1.18+tested-with:         GHC==7.8.2, GHC==7.8.3, GHC==7.10.2+extra-source-files:  cbits/Wrapper.cmm+                     cbits/Pack.c+                     cbits/Errors.h+                     cbits/Types.h+                     cbits/GHCFunctions.h+                     pack.old++flag debug+  manual:            True+  default:           False+  description:       Enable debug support+-- we abuse flags "prof(p)" and "sparks(r)" and use "sanity(S)"++library+  exposed-modules:   GHC.Packing+                     GHC.Packing.PackException+                     GHC.Packing.Type+                     GHC.Packing.Core+  build-depends:     base >= 4.7 && < 5,+                     ghc-prim >= 0.3,+                     array >= 0.5,+                     binary >= 0.7,+                     bytestring >= 0.10,+                     primitive >= 0.5+  if flag(debug)+    ghc-options:     -debug -optc-DDEBUG -optc-g++  c-sources:         cbits/Wrapper.cmm+                     cbits/Pack.c+  include-dirs:      cbits+  includes:+  default-language:  Haskell2010+  if flag(debug)+    cc-options:      -g -DDEBUG -DLIBRARY_CODE+  else+    cc-options:      -DLIBRARY_CODE++test-suite simpletest+  type:              exitcode-stdio-1.0+  main-is:           TestSerialisation.hs+  hs-source-dirs:    Test+  build-depends:     base >= 4.7,+                     directory >= 1.2,+                     ghc-prim >= 0.3,+                     array >= 0.5,+                     binary >= 0.7,+                     bytestring >= 0.10,+                     primitive >= 0.5,+                     packman+  default-language:  Haskell2010+  if flag(debug)+    ghc-options:     -debug -optc-g -optc-DDEBUG++test-suite testexceptions+  type:              exitcode-stdio-1.0+  main-is:           TestExceptions.hs+  hs-source-dirs:    Test+  build-depends:     base >= 4.7,+                     directory >= 1.2,+                     ghc-prim >= 0.3,+                     array >= 0.5,+                     binary >= 0.7,+                     bytestring >= 0.10,+                     primitive >= 0.5,+                     Cabal >= 1.18,+                     packman+  default-language:  Haskell2010+  if flag(debug)+    ghc-options:     -debug -optc-g -optc-DDEBUG++test-suite alltests+  type:              detailed-0.9+  test-module:       AllTests+  hs-source-dirs:    Test+  build-depends:     base >= 4.7,+                     directory >= 1.2,+                     ghc-prim >= 0.3,+                     array >= 0.5,+                     binary >= 0.7,+                     bytestring >= 0.10,+                     primitive >= 0.5,+                     Cabal >= 1.20,+                     packman+  default-language:  Haskell2010+  if flag(debug)+    ghc-options:     -debug -optc-g -optc-DDEBUG++test-suite testmthread+  type:              exitcode-stdio-1.0+  main-is:           TestMThread.hs+  hs-source-dirs:    Test+  build-depends:     base >= 4.7,+                     directory >= 1.2,+                     ghc-prim >= 0.3,+                     array >= 0.5,+                     binary >= 0.7,+                     bytestring >= 0.10,+                     primitive >= 0.5,+                     Cabal >= 1.20,+                     packman+  default-language:  Haskell2010+  if flag(debug)+    ghc-options:     -with-rtsopts=-N4 -debug -threaded -optc-g -optc-DDEBUG+  else+    ghc-options:     -with-rtsopts=-N4 -threaded++test-suite quickchecktest+  type:              detailed-0.9+  test-module:       QCTest+  hs-source-dirs:    Test+  build-depends:     base >= 4.7,+                     directory >= 1.2,+                     ghc-prim >= 0.3,+                     array >= 0.5,+                     binary >= 0.7,+                     bytestring >= 0.10,+                     primitive >= 0.5,+                     Cabal >= 1.20,+                     QuickCheck >= 2.6,+                     packman+  default-language:  Haskell2010+  if flag(debug)+    ghc-options:     -debug -optc-g -optc-DDEBUG+++source-repository head+  type:     git+  location: git://github.com/jberthold/packman.git