summaryrefslogtreecommitdiff
path: root/compiler/ghci/RtClosureInspect.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/ghci/RtClosureInspect.hs')
-rw-r--r--compiler/ghci/RtClosureInspect.hs178
1 files changed, 127 insertions, 51 deletions
diff --git a/compiler/ghci/RtClosureInspect.hs b/compiler/ghci/RtClosureInspect.hs
index d540983139..b7614078e6 100644
--- a/compiler/ghci/RtClosureInspect.hs
+++ b/compiler/ghci/RtClosureInspect.hs
@@ -27,6 +27,7 @@ module RtClosureInspect(
import GhcPrelude
+import GHCi
import GHCi.RemoteTypes
import HscTypes
@@ -62,8 +63,12 @@ import GHC.IO ( IO(..) )
import SMRep ( roundUpTo )
import Control.Monad
+import Data.Array.Base
import Data.Maybe
import Data.List
+#if defined(INTEGER_GMP)
+import GHC.Integer.GMP.Internals
+#endif
import qualified Data.Sequence as Seq
import Data.Sequence (viewl, ViewL(..))
import Foreign
@@ -79,7 +84,7 @@ data Term = Term { ty :: RttiType
-- Carries a text representation if the datacon is
-- not exported by the .hi file, which is the case
-- for private constructors in -O0 compiled libraries
- , val :: HValue
+ , val :: ForeignHValue
, subTerms :: [Term] }
| Prim { ty :: RttiType
@@ -87,7 +92,7 @@ data Term = Term { ty :: RttiType
| Suspension { ctype :: ClosureType
, ty :: RttiType
- , val :: HValue
+ , val :: ForeignHValue
, bound_to :: Maybe Name -- Useful for printing
}
| NewtypeWrap{ -- At runtime there are no newtypes, and hence no
@@ -126,22 +131,22 @@ isThunk APStackClosure{} = True
isThunk _ = False
-- Lookup the name in a constructor closure
-constrClosToName :: HscEnv -> Closure -> IO (Either String Name)
+constrClosToName :: HscEnv -> GenClosure a -> IO (Either String Name)
constrClosToName hsc_env ConstrClosure{pkg=pkg,modl=mod,name=occ} = do
let occName = mkOccName OccName.dataName occ
modName = mkModule (stringToUnitId pkg) (mkModuleName mod)
Right `fmap` lookupOrigIO hsc_env modName occName
constrClosToName _hsc_env clos =
- return (Left ("conClosToName: Expected ConstrClosure, got " ++ show clos))
+ return (Left ("conClosToName: Expected ConstrClosure, got " ++ show (fmap (const ()) clos)))
-----------------------------------
-- * Traversals for Terms
-----------------------------------
-type TermProcessor a b = RttiType -> Either String DataCon -> HValue -> [a] -> b
+type TermProcessor a b = RttiType -> Either String DataCon -> ForeignHValue -> [a] -> b
data TermFold a = TermFold { fTerm :: TermProcessor a a
, fPrim :: RttiType -> [Word] -> a
- , fSuspension :: ClosureType -> RttiType -> HValue
+ , fSuspension :: ClosureType -> RttiType -> ForeignHValue
-> Maybe Name -> a
, fNewtypeWrap :: RttiType -> Either String DataCon
-> a -> a
@@ -152,7 +157,7 @@ data TermFold a = TermFold { fTerm :: TermProcessor a a
data TermFoldM m a =
TermFoldM {fTermM :: TermProcessor a (m a)
, fPrimM :: RttiType -> [Word] -> m a
- , fSuspensionM :: ClosureType -> RttiType -> HValue
+ , fSuspensionM :: ClosureType -> RttiType -> ForeignHValue
-> Maybe Name -> m a
, fNewtypeWrapM :: RttiType -> Either String DataCon
-> a -> m a
@@ -317,19 +322,26 @@ cPprTermBase y =
. subTerms)
, ifTerm (\t -> isTyCon listTyCon (ty t) && subTerms t `lengthIs` 2)
ppr_list
- , ifTerm (isTyCon intTyCon . ty) ppr_int
- , ifTerm (isTyCon charTyCon . ty) ppr_char
- , ifTerm (isTyCon floatTyCon . ty) ppr_float
- , ifTerm (isTyCon doubleTyCon . ty) ppr_double
- , ifTerm (isIntegerTy . ty) ppr_integer
+ , ifTerm' (isTyCon intTyCon . ty) ppr_int
+ , ifTerm' (isTyCon charTyCon . ty) ppr_char
+ , ifTerm' (isTyCon floatTyCon . ty) ppr_float
+ , ifTerm' (isTyCon doubleTyCon . ty) ppr_double
+#if defined(INTEGER_GMP)
+ , ifTerm' (isIntegerTy . ty) ppr_integer
+#endif
]
where
ifTerm :: (Term -> Bool)
-> (Precedence -> Term -> m SDoc)
-> Precedence -> Term -> m (Maybe SDoc)
- ifTerm pred f prec t@Term{}
- | pred t = Just `liftM` f prec t
- ifTerm _ _ _ _ = return Nothing
+ ifTerm pred f = ifTerm' pred (\prec t -> Just <$> f prec t)
+
+ ifTerm' :: (Term -> Bool)
+ -> (Precedence -> Term -> m (Maybe SDoc))
+ -> Precedence -> Term -> m (Maybe SDoc)
+ ifTerm' pred f prec t@Term{}
+ | pred t = f prec t
+ ifTerm' _ _ _ _ = return Nothing
isTupleTy ty = fromMaybe False $ do
(tc,_) <- tcSplitTyConApp_maybe ty
@@ -343,13 +355,67 @@ cPprTermBase y =
(tc,_) <- tcSplitTyConApp_maybe ty
return (tyConName tc == integerTyConName)
- ppr_int, ppr_char, ppr_float, ppr_double, ppr_integer
- :: Precedence -> Term -> m SDoc
- ppr_int _ v = return (Ppr.int (unsafeCoerce# (val v)))
- ppr_char _ v = return (Ppr.char '\'' <> Ppr.char (unsafeCoerce# (val v)) <> Ppr.char '\'')
- ppr_float _ v = return (Ppr.float (unsafeCoerce# (val v)))
- ppr_double _ v = return (Ppr.double (unsafeCoerce# (val v)))
- ppr_integer _ v = return (Ppr.integer (unsafeCoerce# (val v)))
+ ppr_int, ppr_char, ppr_float, ppr_double
+ :: Precedence -> Term -> m (Maybe SDoc)
+ ppr_int _ Term{subTerms=[Prim{valRaw=[w]}]} =
+ return (Just (Ppr.int (fromIntegral w)))
+ ppr_int _ _ = return Nothing
+
+ ppr_char _ Term{subTerms=[Prim{valRaw=[w]}]} =
+ return (Just (Ppr.pprHsChar (chr (fromIntegral w))))
+ ppr_char _ _ = return Nothing
+
+ ppr_float _ Term{subTerms=[Prim{valRaw=[w]}]} = do
+ let f = unsafeDupablePerformIO $
+ alloca $ \p -> poke p w >> peek (castPtr p)
+ return (Just (Ppr.float f))
+ ppr_float _ _ = return Nothing
+
+ ppr_double _ Term{subTerms=[Prim{valRaw=[w]}]} = do
+ let f = unsafeDupablePerformIO $
+ alloca $ \p -> poke p w >> peek (castPtr p)
+ return (Just (Ppr.double f))
+ -- let's assume that if we get two words, we're on a 32-bit
+ -- machine. There's no good way to get a DynFlags to check the word
+ -- size here.
+ ppr_double _ Term{subTerms=[Prim{valRaw=[w1,w2]}]} = do
+ let f = unsafeDupablePerformIO $
+ alloca $ \p -> do
+ poke p (fromIntegral w1 :: Word32)
+ poke (p `plusPtr` 4) (fromIntegral w2 :: Word32)
+ peek (castPtr p)
+ return (Just (Ppr.double f))
+ ppr_double _ _ = return Nothing
+
+ ppr_integer :: Precedence -> Term -> m (Maybe SDoc)
+#if defined(INTEGER_GMP)
+ -- Reconstructing Integers is a bit of a pain. This depends deeply
+ -- on the integer-gmp representation, so it'll break if that
+ -- changes (but there are several tests in
+ -- tests/ghci.debugger/scripts that will tell us if this is wrong).
+ --
+ -- data Integer
+ -- = S# Int#
+ -- | Jp# {-# UNPACK #-} !BigNat
+ -- | Jn# {-# UNPACK #-} !BigNat
+ --
+ -- data BigNat = BN# ByteArray#
+ --
+ ppr_integer _ Term{subTerms=[Prim{valRaw=[W# w]}]} =
+ return (Just (Ppr.integer (S# (word2Int# w))))
+ ppr_integer _ Term{dc=Right con,
+ subTerms=[Term{subTerms=[Prim{valRaw=ws}]}]} = do
+ -- We don't need to worry about sizes that are not an integral
+ -- number of words, because luckily GMP uses arrays of words
+ -- (see GMP_LIMB_SHIFT).
+ let
+ !(UArray _ _ _ arr#) = listArray (0,length ws-1) ws
+ constr
+ | "Jp#" <- occNameString (nameOccName (dataConName con)) = Jp#
+ | otherwise = Jn#
+ return (Just (Ppr.integer (constr (BN# arr#))))
+#endif
+ ppr_integer _ _ = return Nothing
--Note pprinting of list terms is not lazy
ppr_list :: Precedence -> Term -> m SDoc
@@ -357,10 +423,12 @@ cPprTermBase y =
let elems = h : getListTerms t
isConsLast = not (termType (last elems) `eqType` termType h)
is_string = all (isCharTy . ty) elems
+ chars = [ chr (fromIntegral w)
+ | Term{subTerms=[Prim{valRaw=[w]}]} <- elems ]
print_elems <- mapM (y cons_prec) elems
if is_string
- then return (Ppr.doubleQuotes (Ppr.text (unsafeCoerce# (map val elems))))
+ then return (Ppr.doubleQuotes (Ppr.text chars))
else if isConsLast
then return $ cparen (p >= cons_prec)
$ pprDeeperList fsep
@@ -553,7 +621,7 @@ cvObtainTerm
-> Int -- ^ How many times to recurse for subterms
-> Bool -- ^ Force thunks
-> RttiType -- ^ Type of the object to reconstruct
- -> HValue -- ^ Object to reconstruct
+ -> ForeignHValue -- ^ Object to reconstruct
-> IO Term
cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
-- we quantify existential tyvars as universal,
@@ -599,7 +667,7 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
text "Type obtained: " <> ppr (termType term))
return term
where
- go :: Int -> Type -> Type -> HValue -> TcM Term
+ go :: Int -> Type -> Type -> ForeignHValue -> TcM Term
-- I believe that my_ty should not have any enclosing
-- foralls, nor any free RuntimeUnk skolems;
-- that is partly what the quantifyType stuff achieved
@@ -609,29 +677,31 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
go 0 my_ty _old_ty a = do
traceTR (text "Gave up reconstructing a term after" <>
int max_depth <> text " steps")
- clos <- trIO $ getClosureData a
+ clos <- trIO $ GHCi.getClosure hsc_env a
return (Suspension (tipe (info clos)) my_ty a Nothing)
go !max_depth my_ty old_ty a = do
let monomorphic = not(isTyVarTy my_ty)
-- This ^^^ is a convention. The ancestor tests for
-- monomorphism and passes a type instead of a tv
- clos <- trIO $ getClosureData a
+ clos <- trIO $ GHCi.getClosure hsc_env a
case clos of
-- Thunks we may want to force
- t | isThunk t && force -> traceTR (text "Forcing a " <> text (show t)) >>
- seq a (go (pred max_depth) my_ty old_ty a)
+ t | isThunk t && force -> do
+ traceTR (text "Forcing a " <> text (show (fmap (const ()) t)))
+ liftIO $ GHCi.seqHValue hsc_env a
+ go (pred max_depth) my_ty old_ty a
-- Blackholes are indirections iff the payload is not TSO or BLOCKING_QUEUE. So we
-- treat them like indirections; if the payload is TSO or BLOCKING_QUEUE, we'll end up
-- showing '_' which is what we want.
BlackholeClosure{indirectee=ind} -> do
traceTR (text "Following a BLACKHOLE")
- (\(Box x) -> go max_depth my_ty old_ty (HValue x)) ind
+ go max_depth my_ty old_ty ind
-- We always follow indirections
IndClosure{indirectee=ind} -> do
traceTR (text "Following an indirection" )
- (\(Box x) -> go max_depth my_ty old_ty (HValue x)) ind
+ go max_depth my_ty old_ty ind
-- We also follow references
- MutVarClosure{}
+ MutVarClosure{var=contents}
| Just (tycon,[world,contents_ty]) <- tcSplitTyConApp_maybe old_ty
-> do
-- Deal with the MutVar# primitive
@@ -640,7 +710,6 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
-- MutVar# :: contents_ty -> MutVar# s contents_ty
traceTR (text "Following a MutVar")
contents_tv <- newVar liftedTypeKind
- contents <- trIO$ IO$ \w -> readMutVar# (unsafeCoerce# a) w
ASSERT(isUnliftedType my_ty) return ()
(mutvar_ty,_) <- instScheme $ quantifyType $ mkFunTy
contents_ty (mkTyConApp tycon [world,contents_ty])
@@ -649,8 +718,8 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
return (RefWrap my_ty x)
-- The interesting case
- ConstrClosure{ptrArgs=pArgs} -> do
- traceTR (text "entering a constructor " <>
+ ConstrClosure{ptrArgs=pArgs,dataArgs=dArgs} -> do
+ traceTR (text "entering a constructor " <> ppr dArgs <+>
if monomorphic
then parens (text "already monomorphic: " <> ppr my_ty)
else Ppr.empty)
@@ -667,8 +736,8 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
tag = showPpr dflags dcname
vars <- replicateM (length pArgs)
(newVar liftedTypeKind)
- subTerms <- sequence $ zipWith (\(Box x) tv ->
- go (pred max_depth) tv tv (HValue x)) pArgs vars
+ subTerms <- sequence $ zipWith (\x tv ->
+ go (pred max_depth) tv tv x) pArgs vars
return (Term my_ty (Left ('<' : tag ++ ">")) a subTerms)
Just dc -> do
traceTR (text "Is constructor" <+> (ppr dc $$ ppr my_ty))
@@ -676,9 +745,17 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
subTerms <- extractSubTerms (\ty -> go (pred max_depth) ty ty) clos subTtypes
return (Term my_ty (Right dc) a subTerms)
+ -- This is to support printing of Integers. It's not a general
+ -- mechanism by any means; in particular we lose the size in
+ -- bytes of the array.
+ ArrWordsClosure{bytes=b, arrWords=ws} -> do
+ traceTR (text "ByteArray# closure, size " <> ppr b)
+ return (Term my_ty (Left "ByteArray#") a [Prim my_ty ws])
+
-- The otherwise case: can be a Thunk,AP,PAP,etc.
_ -> do
- traceTR (text "Unknown closure:" <+> text (show clos))
+ traceTR (text "Unknown closure:" <+>
+ text (show (fmap (const ()) clos)))
return (Suspension (tipe (info clos)) my_ty a Nothing)
-- insert NewtypeWraps around newtypes
@@ -698,8 +775,8 @@ cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
worker ct ty hval n | isFunTy ty = Suspension ct (dictsView ty) hval n
| otherwise = Suspension ct ty hval n
-extractSubTerms :: (Type -> HValue -> TcM Term)
- -> Closure -> [Type] -> TcM [Term]
+extractSubTerms :: (Type -> ForeignHValue -> TcM Term)
+ -> GenClosure ForeignHValue -> [Type] -> TcM [Term]
extractSubTerms recurse clos = liftM thdOf3 . go 0 0
where
array = dataArgs clos
@@ -733,7 +810,7 @@ extractSubTerms recurse clos = liftM thdOf3 . go 0 0
go_rep ptr_i arr_i ty rep
| isGcPtrRep rep = do
- t <- (\(Box x) -> recurse ty (HValue x)) $ (ptrArgs clos)!!ptr_i
+ t <- recurse ty $ (ptrArgs clos)!!ptr_i
return (ptr_i + 1, arr_i, t)
| otherwise = do
-- This is a bit involved since we allow packing multiple fields
@@ -805,7 +882,7 @@ cvReconstructType
:: HscEnv
-> Int -- ^ How many times to recurse for subterms
-> GhciType -- ^ Type to refine
- -> HValue -- ^ Refine the type using this value
+ -> ForeignHValue -- ^ Refine the type using this value
-> IO (Maybe Type)
cvReconstructType hsc_env max_depth old_ty hval = runTR_maybe hsc_env $ do
traceTR (text "RTTI started with initial type " <> ppr old_ty)
@@ -845,15 +922,14 @@ cvReconstructType hsc_env max_depth old_ty hval = runTR_maybe hsc_env $ do
search stop expand (xx `mappend` Seq.fromList new) $! (pred d)
-- returns unification tasks,since we are going to want a breadth-first search
- go :: Type -> HValue -> TR [(Type, HValue)]
+ go :: Type -> ForeignHValue -> TR [(Type, ForeignHValue)]
go my_ty a = do
traceTR (text "go" <+> ppr my_ty)
- clos <- trIO $ getClosureData a
+ clos <- trIO $ GHCi.getClosure hsc_env a
case clos of
- BlackholeClosure{indirectee=ind} -> (\(Box x) -> go my_ty (HValue x)) ind
- IndClosure{indirectee=ind} -> (\(Box x) -> go my_ty (HValue x)) ind
- MutVarClosure{} -> do
- contents <- trIO$ IO$ \w -> readMutVar# (unsafeCoerce# a) w
+ BlackholeClosure{indirectee=ind} -> go my_ty ind
+ IndClosure{indirectee=ind} -> go my_ty ind
+ MutVarClosure{var=contents} -> do
tv' <- newVar liftedTypeKind
world <- newVar liftedTypeKind
addConstraint my_ty (mkTyConApp mutVarPrimTyCon [world,tv'])
@@ -864,15 +940,15 @@ cvReconstructType hsc_env max_depth old_ty hval = runTR_maybe hsc_env $ do
(_,mb_dc) <- tryTc (tcLookupDataCon dcname)
case mb_dc of
Nothing-> do
- forM pArgs $ \(Box x) -> do
+ forM pArgs $ \x -> do
tv <- newVar liftedTypeKind
- return (tv, HValue x)
+ return (tv, x)
Just dc -> do
arg_tys <- getDataConArgTys dc my_ty
(_, itys) <- findPtrTyss 0 arg_tys
traceTR (text "Constr2" <+> ppr dcname <+> ppr arg_tys)
- return $ zipWith (\(_,ty) (Box x) -> (ty, HValue x)) itys pArgs
+ return $ zipWith (\(_,ty) x -> (ty, x)) itys pArgs
_ -> return []
findPtrTys :: Int -- Current pointer index