module Language.Pck.Tool.Debugger (
runDbg
, runDbgIO
, evalProgDbg
, TrcLog
, DbgTrc(..)
, DbgBrk(..)
, DbgOrd(..)
)where
import Control.Monad.State
import qualified Data.ByteString.Char8 as B
import Text.Printf (printf)
import Data.List (intercalate)
import Language.Pck.Cpu.Instruction
import Language.Pck.Cpu.Memory
import Language.Pck.Cpu.Register
import Language.Pck.Cpu.State
import Language.Pck.Cpu.Execution
runDbg :: [DbgTrc] -> [DbgBrk] -> InstImage -> DataImage -> (TrcLog, CpuState)
runDbg dbgtrc dbgbrk insts vals = runState (evalProgDbg dbgtrc dbgbrk)
(initCpuStateMem insts vals)
runDbgIO :: [DbgTrc] -> [DbgBrk] -> InstImage -> DataImage -> IO ()
runDbgIO dbgtrc dbgbrk insts vals =
let (trc, _) = runDbg dbgtrc dbgbrk insts vals
in putStr $ B.unpack trc
evalProgDbg :: [DbgTrc] -> [DbgBrk] -> EvalCpu TrcLog
evalProgDbg dbgtrc dbgbrk = loop B.empty 0
where loop trclog cnt = do trclog' <- tracePre dbgtrc trclog
res <- evalProg True
res' <- checkBreak dbgbrk res
trclog'' <- tracePost dbgtrc trclog'
checkRunLimit cnt
case res' of
RsNormal -> loop trclog'' (cnt+1)
RsErr e -> error $ show trclog'' ++ "\n\n"
++ e ++ "\n"
_ -> return trclog''
dbgRunLimit :: Int
dbgRunLimit = 1000000
checkRunLimit :: Int -> EvalCpu ()
checkRunLimit n
| n < dbgRunLimit = return ()
| otherwise = do s <- get
error $ "RUN COUNT OVER!\n" ++ show s
type TrcLog = B.ByteString
data DbgTrc = TrcInst
| TrcReg
| TrcPc
| TrcCall
| TrcBranch
| TrcLoad
| TrcStore
deriving (Show, Eq)
tracePre, tracePost :: [DbgTrc] -> TrcLog -> EvalCpu TrcLog
tracePre = traceMany [TrcInst, TrcPc, TrcBranch, TrcCall, TrcLoad, TrcStore]
tracePost = traceMany [TrcReg]
traceMany :: [DbgTrc] -> [DbgTrc] -> TrcLog -> EvalCpu TrcLog
traceMany target dbgtrc trclog = do let list = filter (`elem` target) dbgtrc
l <- mapM traceOne list
return $ B.append trclog (B.concat l)
traceOne :: DbgTrc -> EvalCpu TrcLog
traceOne TrcPc = tracePc
traceOne TrcInst = traceInst
traceOne TrcReg = traceReg
traceOne TrcCall = traceCall
traceOne TrcBranch = traceBranch
traceOne TrcLoad = traceLoad
traceOne TrcStore = traceStore
tracePc :: EvalCpu TrcLog
tracePc = do pc <- readPc
return $ B.pack $ concat ["TrcPc:\tpc : ", (pprHex pc), "\n"]
traceInst :: EvalCpu TrcLog
traceInst = do pc <- readPc
inst <- fetchInst
return $ B.pack $ concat [ "TrcInst:\tpc : ", (pprHex pc), "\t"
, (show inst), "\n\n"]
traceReg :: EvalCpu TrcLog
traceReg = do stat <- get
return $ B.pack $ concat
[ "TrcReg:\n"
, "pc : ", pprHex (pcFromCpuState stat)
, "\ngr : ", pprHexList (grFromCpuState stat)
, "\nfl : ", show (flFromCpuState stat), "\n\n"]
traceLoad :: EvalCpu TrcLog
traceLoad = traceAddress isLoadInst "TrcLoad:\tload-ad : "
traceStore :: EvalCpu TrcLog
traceStore = traceAddress isStoreInst "TrcStore:\tstore-ad : "
traceCall :: EvalCpu TrcLog
traceCall = traceAddress isCallInst "TrcCall:\ttarget : "
traceAddress :: (Inst -> Maybe GReg) -> String -> EvalCpu TrcLog
traceAddress prd str = do pc <- readPc
inst <- fetchInst
case (prd inst) of
Just reg -> do ad <- readGReg reg
return $ pprSIIInst str ad pc inst
_ -> return ""
isLoadInst :: Inst -> Maybe GReg
isLoadInst (LD _ reg) = Just reg
isLoadInst _ = Nothing
isStoreInst :: Inst -> Maybe GReg
isStoreInst (ST reg _) = Just reg
isStoreInst _ = Nothing
isCallInst :: Inst -> Maybe GReg
isCallInst (CALL reg) = Just reg
isCallInst _ = Nothing
traceBranch :: EvalCpu TrcLog
traceBranch = do pc <- readPc
inst <- fetchInst
case inst of
BRI cond imm -> do flag <- readFlags
let strTaken = if (judgeFCond flag cond)
then "Taken" else "Not"
return $ pprTrcBranch
(pc+imm) strTaken pc inst
JRI imm -> return $ pprTrcBranch
(pc+imm) "Taken" pc inst
J reg -> do ad <- readGReg reg
return $ pprTrcBranch ad "Taken" pc inst
CALL reg -> do ad <- readGReg reg
return $ pprTrcBranch ad "Taken" pc inst
_ -> return ""
pprHex :: Int -> String
pprHex = printf "0x%x"
pprHexList :: [Int] -> String
pprHexList xs = "[" ++ (intercalate "," (map pprHex xs)) ++ "]"
pprSIIInst :: String -> Int -> Int -> Inst -> TrcLog
pprSIIInst str n pc inst = B.pack $ concat
[ str, (show n), "\t -- "
, "pc : " , (show pc), "\t"
, (show inst), "\n\n" ]
pprTrcBranch :: Int -> String -> Int -> Inst -> TrcLog
pprTrcBranch ad str pc inst = B.pack $ concat
[ "TrcBranch:\ttarget : ", (show ad), "\t"
, str, "\t -- "
, "pc : ", (show pc), "\t"
, (show inst) , "\n\n" ]
data DbgBrk = BrkNon
| BrkOne
| BrkPc DbgOrd Int
| BrkGReg GReg DbgOrd Int
| BrkDmem Int DbgOrd Int
deriving (Eq)
data DbgOrd = BEQ
| BNE
| BLT
| BLE
| BGT
| BGE
deriving (Eq, Show)
checkBreak :: [DbgBrk] -> ResultStat -> EvalCpu ResultStat
checkBreak [] res = return res
checkBreak dbgbrk res = do b <- mapM breakOne dbgbrk
return $ if (RsDbgBrk `elem` b)
then RsDbgBrk else res
breakOne :: DbgBrk -> EvalCpu ResultStat
breakOne (BrkNon) = return RsNormal
breakOne (BrkOne) = return RsDbgBrk
breakOne (BrkPc o v) = do pc <- readPc
return $ if (ordFunc o) pc v
then RsDbgBrk else RsNormal
breakOne (BrkGReg reg o v) = do reg' <- readGReg reg
return $ if (ordFunc o) reg' v
then RsDbgBrk else RsNormal
breakOne (BrkDmem ad o v) = do mem <- readDmem ad
return $ if (ordFunc o) mem v
then RsDbgBrk else RsNormal
ordFunc :: DbgOrd -> (Int -> Int -> Bool)
ordFunc BEQ = (==)
ordFunc BNE = (/=)
ordFunc BLT = (<)
ordFunc BLE = (<=)
ordFunc BGT = (>)
ordFunc BGE = (>=)