crucible-debug-0.1.0: src/Lang/Crucible/Debug/Trace.hs
{-|
Copyright : (c) Galois, Inc. 2025
Maintainer : Langston Barrett <langston@galois.com>
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Lang.Crucible.Debug.Trace
( TraceEntry(..)
, Trace
, empty
, append
, latest
) where
import Control.Lens qualified as Lens
import Data.Parameterized.Classes (ixF')
import Data.Parameterized.Context qualified as Ctx
import Data.Parameterized.NatRepr qualified as NatRepr
import Data.Parameterized.NatRepr (type (<=), NatRepr)
import Data.Parameterized.Some (Some(Some))
import Data.RingBuffer qualified as RB
import Data.Vector qualified as V
import Lang.Crucible.CFG.Core qualified as C
import Lang.Crucible.CFG.Extension qualified as C
import Prettyprinter qualified as PP
import What4.ProgramLoc qualified as W4
data TraceEntry ext
= forall blocks init ret.
TraceEntry
{ traceCfg :: C.CFG ext blocks init ret
, traceBlock :: Some (C.BlockID blocks)
}
instance C.PrettyExt ext => PP.Pretty (TraceEntry ext) where
pretty (TraceEntry cfg (Some blkId)) =
let blk = C.cfgBlockMap cfg Lens.^. ixF' (C.blockIDIndex blkId) in
ppStmtSeq True (Ctx.size (C.blockInputs blk)) (C._blockStmts blk)
-- TODO: Taken from Crucible. Export upstream?
where
prefixLineNum :: Bool -> W4.ProgramLoc -> PP.Doc ann -> PP.Doc ann
prefixLineNum True pl d = PP.vcat [PP.pretty "%" PP.<+> W4.ppNoFileName (W4.plSourceLoc pl), d]
prefixLineNum False _ d = d
ppStmtSeq :: C.PrettyExt ext => Bool -> Ctx.Size ctx -> C.StmtSeq ext blocks ret ctx -> PP.Doc ann
ppStmtSeq ppLineNum h (C.ConsStmt pl s r) =
PP.vcat
[ prefixLineNum ppLineNum pl (C.ppStmt h s)
, ppStmtSeq ppLineNum (C.nextStmtHeight h s) r
]
ppStmtSeq ppLineNum _ (C.TermStmt pl s) =
prefixLineNum ppLineNum pl (PP.pretty s)
newtype Trace ext = Trace (RB.RingBuffer V.Vector (TraceEntry ext))
-- 'RB.new' requires a nonzero input, which we enforce with types
empty :: (1 <= n) => NatRepr n -> IO (Trace ext)
empty n = Trace <$> RB.new (fromIntegral (NatRepr.natValue n))
-- | O(1).
append :: Trace ext -> TraceEntry ext -> IO ()
append (Trace b) e = RB.append e b
{-# INLINEABLE append #-}
-- | Get up to the @N@ latest entries
latest :: Trace ext -> Int -> IO [TraceEntry ext]
latest (Trace b) n = go n
where
go x | x <= 0 = pure []
go x = do
mEnt <- RB.latest b (n - x)
case mEnt of
Nothing -> pure []
Just ent -> (ent :) <$> go (x - 1)