Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

heapster-saw: Export block entry-point and implication error information for IDE ingestion #1442

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3a9c13b
add basic IDE logging, start error cleanup
Jun 28, 2021
bfd0566
clean up additional implication errors
Jun 28, 2021
49f72e4
add structure to stmt fail in line with impl fail
Jun 29, 2021
716a2e1
redesign error message types, pipe through to log
Jul 7, 2021
9283f0e
fix default heapster environments missing ioref
Jul 8, 2021
1e427a4
carry more information through on most common implication error
Jul 9, 2021
059ab7b
Merge branch 'heapster-ide-info' of github.com:GaloisInc/saw-script i…
Jul 9, 2021
6fef67a
remove Some from error constructor
Jul 9, 2021
f3b76bb
Merge branch 'master' into heapster-ide-info
glguy Jul 26, 2021
e2c9cd6
Export valueperms in full json detail
glguy Jul 28, 2021
b59adb9
Avoid generating orphan ToJSON instances and using Given
glguy Jul 28, 2021
65ae7dd
refine jsonexport instances
glguy Jul 28, 2021
931d802
Document JSONExport
glguy Jul 28, 2021
2ad2225
Remove need for passing undefined
glguy Jul 30, 2021
ce3313b
JSONExport support for PermImpls
glguy Jul 30, 2021
df114c1
export entrypoint and caller ID information
Aug 12, 2021
8875708
cleanup imports, 80 char columns
Aug 12, 2021
5f12cc4
heapster: export function name for IDE
Aug 12, 2021
cb7c57c
heapster-saw: LogEntry with names and structure
glguy Aug 19, 2021
e768f1f
Incorporate names from bindings in JsonExport
glguy Aug 20, 2021
f60affd
Merge remote-tracking branch 'origin/master' into heapster-ide-info
glguy Aug 20, 2021
51665d7
Update PPInfo while exporting
glguy Aug 23, 2021
dc70b93
checkpoint
glguy Aug 27, 2021
f0c7747
Use types to generate names for pretty permissions where possible
glguy Aug 27, 2021
e634949
Checkpoint
glguy Aug 28, 2021
b284fc4
Checkpoint
glguy Aug 28, 2021
491c38f
Update cabal file
glguy Aug 30, 2021
4454faf
Merge branch 'master' into glguy/heapster-ide-info
Aug 30, 2021
3d0ae00
user the error prefix when logging errors for IDE
Aug 31, 2021
4e4cc4b
revert commented error prefix code
Aug 31, 2021
f60e26b
Change Mb' to NMb
glguy Aug 31, 2021
f34cabb
Merge branch 'glguy/heapster-ide-info' of github.com:GaloisInc/saw-sc…
glguy Aug 31, 2021
24d7122
Comments and more consistent naming
glguy Aug 31, 2021
2ac547b
Merge branch 'master' of github.com:GaloisInc/saw-script into heapste…
glguy Aug 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
heapster-saw: LogEntry with names and structure
  • Loading branch information
glguy committed Aug 19, 2021
commit cb7c57c1a028de660c88480e1eaad26bc57f7ba3
10 changes: 10 additions & 0 deletions heapster-saw/src/Verifier/SAW/Heapster/CruUtil.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Data.Text (Text)
import qualified Data.Text as Text
import Data.Reflection
import Data.List.NonEmpty (NonEmpty(..))
import Data.Functor.Constant
import Data.ByteString
import Numeric
import Numeric.Natural
Expand Down Expand Up @@ -564,6 +565,15 @@ instance Liftable (KnownReprObj f a) where
instance LiftableAny1 (KnownReprObj f) where
mbLiftAny1 = mbLift

instance Liftable a => LiftableAny1 (Constant a) where
mbLiftAny1 = mbLift

instance Liftable a => Liftable (Constant a b) where
mbLift (mbMatch -> [nuMP| Data.Functor.Constant.Constant x |]) = Data.Functor.Constant.Constant (mbLift x)

instance (Liftable a, Liftable b, Liftable c) => Liftable (a,b,c) where
mbLift (mbMatch -> [nuMP| (x,y,z) |]) = (mbLift x, mbLift y, mbLift z)

-- FIXME: this change for issue #28 requires ClosableAny1 to be exported from
-- Hobbits
{-
Expand Down
76 changes: 42 additions & 34 deletions heapster-saw/src/Verifier/SAW/Heapster/IDESupport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE PolyKinds #-}
module Verifier.SAW.Heapster.IDESupport where

import Control.Monad.Reader
Expand Down Expand Up @@ -51,7 +52,10 @@ import Verifier.SAW.Heapster.Implication
import Verifier.SAW.Heapster.Permissions
import Verifier.SAW.Heapster.TypedCrucible
import Verifier.SAW.Heapster.JSONExport(ppToJson)

import Data.Aeson (Value)
import Data.Type.RList (mapRAssign)
import Data.Functor.Constant
import Control.Monad.Writer

-- | The entry point for dumping a Heapster environment to a file for IDE
-- consumption.
Expand All @@ -65,6 +69,9 @@ type ExtractionM = ReaderT (PPInfo, ProgramLoc, String) (Writer [LogEntry])
emit :: LogEntry -> ExtractionM ()
emit entry = tell [entry]

gather :: ExtractionM () -> ExtractionM [LogEntry]
gather m = snd <$> listen m

-- | A single entry in the IDE info dump log. At a bare minimum, this must
-- include a location and corresponding permission. Once the basics are
-- working, we can enrich the information we log.
Expand All @@ -74,26 +81,31 @@ data LogEntry
, leEntryId :: LogEntryID
, leCallers :: [LogEntryID]
, leFunctionName :: String
, leExport :: Value
, lePermissions :: String
, lePermissions :: [(String, String, Value)]
}
| LogError
{ lerrLocation :: String
, lerrError :: String
, lerrFunctionName :: String
}
| LogImpl
{ limplLocation :: String
, limplExport :: Value
, limplFunctionName :: String
}

deriving (Generic, Show)
instance ToJSON LogEntry
instance NuMatching LogEntry where
nuMatchingProof = unsafeMbTypeRepr
instance Liftable LogEntry where
mbLift mb = case mbMatch mb of
[nuMP| LogEntry u v w x y z |] ->
LogEntry (mbLift u) (mbLift v) (mbLift w)
(mbLift x) (mbLift y) (mbLift z)
[nuMP| LogEntry v w x y z |] ->
LogEntry (mbLift v) (mbLift w) (mbLift x) (mbLift y) (mbLift z)
[nuMP| LogError x y z |] ->
LogError (mbLift x) (mbLift y) (mbLift z)

[nuMP| LogImpl x y z |] ->
LogImpl (mbLift x) (mbLift y) (mbLift z)

data LogEntryID = LogEntryID
{ leIdBlock :: Int
Expand Down Expand Up @@ -126,7 +138,15 @@ instance (PermCheckExtC ext)
withLoc loc (mbExtractLogEntries (typedEntryBody te))
let entryId = mkLogEntryID $ typedEntryID te
let callers = callerIDs $ typedEntryCallers te
mbValPermEntries entryId callers (typedEntryPermsIn te)
(ppi, _, fname) <- ask
let loc' = snd (ppLoc loc)
let f ::
(Pair (Constant String) ValuePerm) x ->
Constant (String, String, Value) x
f (Pair (Constant name) vp) = Constant (name, permPrettyString ppi vp, ppToJson ppi vp)
let inputs = mbLift
$ fmap (RL.toList . mapRAssign f . zipRAssign (typedEntryNames te)) (typedEntryPermsIn te)
tell [LogEntry loc' entryId callers fname inputs]

mkLogEntryID :: TypedEntryID blocks args -> LogEntryID
mkLogEntryID = uncurry LogEntryID . entryIDIndices
Expand All @@ -135,6 +155,12 @@ callerIDs :: [Some (TypedCallSite phase blocks tops args ghosts)] -> [LogEntryID
callerIDs = map $ \(Some tcs) -> case typedCallSiteID tcs of
TypedCallSiteID tei _ _ _ -> mkLogEntryID tei

data Pair f g x = Pair (f x) (g x)

zipRAssign :: RL.RAssign f x -> RL.RAssign g x -> RL.RAssign (Pair f g) x
zipRAssign RL.MNil RL.MNil = RL.MNil
zipRAssign (xs RL.:>: x) (ys RL.:>: y) = zipRAssign xs ys RL.:>: Pair x y

instance ExtractLogEntries (TypedStmtSeq ext blocks tops ret ps_in) where
extractLogEntries (TypedImplStmt (AnnotPermImpl _str pimpl)) =
-- fmap (setErrorMsg str) <$> extractLogEntries pimpl
Expand All @@ -155,7 +181,10 @@ instance ExtractLogEntries (PermImpl1 ps_in ps_outs) where
extractLogEntries (Impl1_Fail err) =
do (_, loc, fname) <- ask
emit (LogError (snd (ppLoc loc)) (ppError err) fname)
extractLogEntries _ = pure ()
-- The error message is available further up the stack, so we just leave it
extractLogEntries impl =
do (ppi, loc, fname) <- ask
emit (LogImpl (snd (ppLoc loc)) (ppToJson ppi impl) fname)

instance ExtractLogEntries
(MbPermImpls (TypedStmtSeq ext blocks tops ret) ps_outs) where
Expand Down Expand Up @@ -186,30 +215,6 @@ mbExtractLogEntries mb_a =
execWriter $ runReaderT (extractLogEntries x)
(ppInfoAddExprNames "x" ns ppi, loc, fname)

-- TODO: The next two functions are a hack, and we should probably rethink how
-- this is architected a bit. They don't fit into the type signature of
-- `ExtractLogEntries` currently because we push down the extra information
-- about the entrypoint IDs which we need wherever `LogEntry`s are created.

mbValPermEntries
:: LogEntryID
-> [LogEntryID]
-> Mb ctx (ValuePerms ctx)
-> ExtractionM ()
mbValPermEntries entryId callers mb_vp =
ReaderT $ \(ppi, loc, fname) ->
tell $ mbLift $ flip nuMultiWithElim1 mb_vp $ \ns vp ->
execWriter $ runReaderT (valPermEntries entryId callers vp)
(ppInfoAddExprNames "x" ns ppi, loc, fname)

valPermEntries :: LogEntryID -> [LogEntryID] -> ValuePerms ctx -> ExtractionM ()
valPermEntries entryId callers vps = do
(ppi, loc, fname) <- ask
let loc' = snd (ppLoc loc)
let strs = foldValuePerms (\xs vp ->
(ppToJson ppi vp, permPrettyString ppi vp) : xs) [] vps
tell [LogEntry loc' entryId callers fname export str | (export, str) <- strs]

typedStmtOutCtx :: TypedStmt ext rets ps_in ps_next -> CruCtx rets
typedStmtOutCtx = error "FIXME: write typedStmtOutCtx"

Expand All @@ -218,13 +223,16 @@ withLoc loc = local (\(ppinfo, _, fname) -> (ppinfo, loc, fname))

setErrorMsg :: String -> LogEntry -> LogEntry
setErrorMsg msg le@LogError {} = le { lerrError = msg }
setErrorMsg msg le@LogImpl {} =
LogError { lerrError = msg
, lerrLocation = limplLocation le
, lerrFunctionName = limplFunctionName le}
setErrorMsg msg le@LogEntry {} =
LogError { lerrError = msg
, lerrLocation = leLocation le
, lerrFunctionName = leFunctionName le
}


runWithLoc :: PPInfo -> [Some SomeTypedCFG] -> [LogEntry]
runWithLoc ppi =
concatMap (runWithLocHelper ppi)
Expand Down
96 changes: 63 additions & 33 deletions heapster-saw/src/Verifier/SAW/Heapster/TypedCrucible.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import Verifier.SAW.Heapster.Permissions
import Verifier.SAW.Heapster.Widening

import Debug.Trace
import Data.Type.RList (mapRAssign)


----------------------------------------------------------------------
Expand Down Expand Up @@ -339,7 +340,6 @@ instance Closable (TypedCallSiteID blocks args vars) where
instance Liftable (TypedCallSiteID blocks args vars) where
mbLift = unClosed . mbLift . fmap toClosed


----------------------------------------------------------------------
-- * Typed Crucible Statements
----------------------------------------------------------------------
Expand Down Expand Up @@ -1175,6 +1175,8 @@ data TypedEntry phase ext blocks tops ret args ghosts =
typedEntryCallers :: ![Some (TypedCallSite phase blocks tops args ghosts)],
-- | The ghost variables for this entrypoint
typedEntryGhosts :: !(CruCtx ghosts),
--
typedEntryNames :: !(TransData phase (RAssign (Constant String) ((tops :++: args) :++: ghosts))),
-- | The input permissions for this entrypoint
typedEntryPermsIn :: !(MbValuePerms ((tops :++: args) :++: ghosts)),
-- | The output permissions for the function (cached locally)
Expand Down Expand Up @@ -1205,23 +1207,26 @@ completeTypedEntry ::
completeTypedEntry (TypedEntry { .. })
| Just body <- typedEntryBody
, Just callers <- mapM (traverseF completeTypedCallSite) typedEntryCallers
= Just $ TypedEntry { typedEntryBody = body, typedEntryCallers = callers, .. }
, Just names <- typedEntryNames
= Just $ TypedEntry { typedEntryBody = body, typedEntryCallers = callers, typedEntryNames = names, .. }
completeTypedEntry _ = Nothing

-- | Build an entrypoint from a call site, using that call site's permissions as
-- the entyrpoint input permissions
singleCallSiteEntry :: TypedCallSiteID blocks args vars ->
CruCtx tops -> CruCtx args -> TypeRepr ret ->
MbValuePerms ((tops :++: args) :++: vars) ->
MbValuePerms (tops :> ret) ->
TypedEntry TCPhase ext blocks tops ret args vars
singleCallSiteEntry ::
TypedCallSiteID blocks args vars ->
CruCtx tops -> CruCtx args -> TypeRepr ret ->
MbValuePerms ((tops :++: args) :++: vars) ->
MbValuePerms (tops :> ret) ->
TypedEntry TCPhase ext blocks tops ret args vars
singleCallSiteEntry siteID tops args ret perms_in perms_out =
TypedEntry
{
typedEntryID = callSiteDest siteID, typedEntryTops = tops,
typedEntryArgs = args, typedEntryRet = ret,
typedEntryCallers = [Some $ idTypedCallSite siteID tops args perms_in],
typedEntryGhosts = callSiteVars siteID,
typedEntryNames = Nothing,
typedEntryPermsIn = perms_in, typedEntryPermsOut = perms_out,
typedEntryBody = Nothing
}
Expand Down Expand Up @@ -1345,6 +1350,7 @@ emptyBlockForPerms names cblocks blk tops ret ghosts perms_in perms_out
typedEntryID = TypedEntryID blockID 0, typedEntryTops = tops,
typedEntryArgs = args, typedEntryRet = ret,
typedEntryCallers = [], typedEntryGhosts = ghosts,
typedEntryNames = Nothing,
typedEntryPermsIn = perms_in, typedEntryPermsOut = perms_out,
typedEntryBody = Nothing }]
names
Expand Down Expand Up @@ -3867,27 +3873,46 @@ tcBlockEntryBody ::
Block ext cblocks ret args ->
TypedEntry TCPhase ext blocks tops ret (CtxToRList args) ghosts ->
TopPermCheckM ext cblocks blocks tops ret
(Mb ((tops :++: CtxToRList args) :++: ghosts)
(RAssign (Constant String) ((tops :++: CtxToRList args) :++: ghosts),
Mb ((tops :++: CtxToRList args) :++: ghosts)
(TypedStmtSeq ext blocks tops ret ((tops :++: CtxToRList args) :++: ghosts)))
tcBlockEntryBody names blk entry@(TypedEntry {..}) =
runPermCheckM names typedEntryID typedEntryArgs typedEntryGhosts typedEntryPermsIn $
\tops_ns args_ns ghosts_ns perms ->
let ctx = mkCtxTrans (blockInputs blk) args_ns
ns = RL.append (RL.append tops_ns args_ns) ghosts_ns in
stmtTraceM (\i ->
pretty "Type-checking block" <+> pretty (blockID blk) <>
comma <+> pretty "entrypoint" <+> pretty (entryIndex typedEntryID)
<> line <>
pretty "Input types:"
<> align (permPretty i $
RL.map2 VarAndType ns $ cruCtxToTypes $
typedEntryAllArgs entry)
<> line <>
pretty "Input perms:"
<> align (permPretty i perms)) >>>
stmtRecombinePerms >>>
tcEmitStmtSeq names ctx (blk ^. blockStmts)
do mbNs <- mkNs
stmts <- mkStmts
pure (mbLift mbNs,stmts)
where
mkNs =
runPermCheckM names typedEntryID typedEntryArgs typedEntryGhosts typedEntryPermsIn $
\tops_ns args_ns ghosts_ns _ ->
permGetPPInfo >>>= \ppi ->
let tops = mapRAssign (Constant . show . permPretty ppi) tops_ns
args = mapRAssign (Constant . show . permPretty ppi) args_ns
ghosts = mapRAssign (Constant . show . permPretty ppi) ghosts_ns
ns = rappend (rappend tops args) ghosts
in traceShow ("ns", RL.toList ns) $ gmapRet (>> pure ns)

mkStmts =
runPermCheckM names typedEntryID typedEntryArgs typedEntryGhosts typedEntryPermsIn $
\tops_ns args_ns ghosts_ns perms ->
let ctx = mkCtxTrans (blockInputs blk) args_ns
ns = RL.append (RL.append tops_ns args_ns) ghosts_ns in
stmtTraceM (\i ->
pretty "Type-checking block" <+> pretty (blockID blk) <>
comma <+> pretty "entrypoint" <+> pretty (entryIndex typedEntryID)
<> line <>
pretty "Input types:"
<> align (permPretty i $
RL.map2 VarAndType ns $ cruCtxToTypes $
typedEntryAllArgs entry)
<> line <>
pretty "Input perms:"
<> align (permPretty i perms)) >>>
stmtRecombinePerms >>>
tcEmitStmtSeq names ctx (blk ^. blockStmts)

rappend :: RAssign f x -> RAssign f y -> RAssign f (x :++: y)
rappend xs (ys :>: y) = rappend xs ys :>: y
rappend xs MNil = xs

-- | Prove that the permissions held at a call site from the given source
-- entrypoint imply the supplied input permissions of the current entrypoint
Expand Down Expand Up @@ -3949,18 +3974,22 @@ visitCallSite (TypedEntry {..}) site@(TypedCallSite {..})

-- | Widen the permissions held by all callers of an entrypoint to compute new,
-- weaker input permissions that can hopefully be satisfied by them
widenEntry :: PermCheckExtC ext =>
TypedEntry TCPhase ext blocks tops ret args ghosts ->
Some (TypedEntry TCPhase ext blocks tops ret args)
widenEntry ::
forall ext blocks tops ret args ghosts.
PermCheckExtC ext =>
TypedEntry TCPhase ext blocks tops ret args ghosts ->
Some (TypedEntry TCPhase ext blocks tops ret args)
widenEntry (TypedEntry {..}) =
case foldl1' (widen typedEntryTops typedEntryArgs) $
map (fmapF typedCallSiteArgVarPerms) typedEntryCallers of
Some (ArgVarPerms ghosts perms_in) ->
Some (ArgVarPerms (ghosts :: CruCtx x) perms_in) ->
let callers =
map (fmapF (callSiteSetGhosts ghosts)) typedEntryCallers in
map (fmapF (callSiteSetGhosts ghosts)) typedEntryCallers
in
Some $
TypedEntry { typedEntryCallers = callers, typedEntryGhosts = ghosts,
typedEntryPermsIn = perms_in, typedEntryBody = Nothing, .. }
typedEntryPermsIn = perms_in, typedEntryBody = Nothing,
typedEntryNames = Nothing, .. }

-- | Visit an entrypoint, by first proving the required implications at each
-- call site, meaning that the permissions held at the call site imply the input
Expand Down Expand Up @@ -4003,9 +4032,10 @@ visitEntry names can_widen blk entry =
-- is no reason to re-type-check the body, so just update the callers
return $ Some $ entry { typedEntryCallers = callers }
else
do body <- maybe (tcBlockEntryBody names blk entry) return (typedEntryBody entry)
do (ns, body) <- maybe (tcBlockEntryBody names blk entry) return ((,) <$> typedEntryNames entry <*> typedEntryBody entry)
return $ Some $ entry { typedEntryCallers = callers,
typedEntryBody = Just body }
typedEntryBody = Just body,
typedEntryNames = Just ns }


-- | Visit a block by visiting all its entrypoints
Expand Down