hat-2.9.1: tools/BlackHat.hs
module Main where
import NonTermLib
import LowLevel
import SExp
import System.Process (system)
import System.Environment (getArgs,getProgName,getEnv)
import System.Exit (exitWith,ExitCode(..))
import Data.List (isPrefixOf,isSuffixOf,group,groupBy)
import Control.Monad (when)
import System.IO (hSetBuffering,BufferMode(..),stdin,stdout,stderr
,hPutStrLn,hFlush)
import Foreign.C.String (withCString)
import HighlightStyle (getTerminalSize)
main =
do args <- System.Environment.getArgs
prog <- System.Environment.getProgName
initialiseCount
checkHelp args
hatname <- getHatName args
hatfile <- return (rectify hatname)
withCString prog (\p-> withCString hatfile (openHatFile p))
errmsg <- getErrorMessage
(columns, lines) <- getTerminalSize
let state = (setOptions args) { progname=hatname, file=hatfile
, width=columns , height=lines }
checkError state errmsg
putStr ("---- Black Hat: " ++ hatname ++ " ----\n")
showPath state
if (showCount state) then
putStrLn ("No. reads from file: " ++ (show getCount) )
else return ()
where
showPath state
| bhpath == [] = do putStr (progname state)
putStrLn (": no black hole found")
exitWith (ExitFailure 1)
| (showNode state) = mapIO putStrLn
(inter (map (showSctx state) bhpath)
(map show bhpath) )
| otherwise = mapIO (putStrLn . showSctx state ) bhpath
where bhpath = blackHoleSearch state getRootNode
rectify :: FilePath -> FilePath
rectify f | ".hat" `isSuffixOf` f = f
| otherwise = f ++ ".hat"
checkError :: State -> String -> IO ()
checkError state msg
| msg `isPrefixOf` "<<loop>>" = return ()
| otherwise = do hPutStrLn stderr ((progname state)
++": error is not a loop")
exitWith (ExitFailure 1)
checkHelp args | member "--help" args = do showHelp
exitWith ExitSuccess
| otherwise = return ()
getHatName args =
case args of (f:_) -> return f
_ -> do hPutStrLn stderr
("black-hat: no trace file")
showHelpShort
exitWith (ExitFailure 1)
showHelpShort = putStrLn "Usage: black-hat [PROGNAME] [OPTIONS]..."
showHelp =
do showHelpShort
putStrLn " --showqual={t,f} Show function module names"
putStrLn " --showrt={t,f} Show all nodes from the root node"
putStrLn " --srcref={t,f} Show source references for nodes"
putStrLn " --cutoff=<int> Cutoff expression at depth <int>"
-- This function calls the function which implements the search process
-- proper. It also cuts off the head of the list, if the correct option
-- is set.
blackHoleSearch :: State -> FileNode -> [Sctx]
blackHoleSearch state startnode
| (showRoot state) = reverse (map reverse resultpath)
| otherwise = cutAt node (reverse (map reverse resultpath))
where
(node, resultpath) = blackHoleSearchAux state [startnode] []
cutAt _ [] = [] -- shouldn't happen
cutAt func (x:xs) | member node x = (x:xs)
| otherwise = cutAt func xs
-- This function looks for a black-hole path in the ART graph. It implements
-- the search process documented in the report. Basically, the function uses
-- a depth-first search of the ART graph.
--
-- From the root node, each node is considered in turn.
-- * if it is the wrong kind of node (ie a Module, SrcPos) the function
-- returns (nil, []), indicating failure
-- * if it has already appeared on the search path, the search returns the
-- search path, and the head node, indicating success
-- * otherwise, the function calls itself on the node's result. If
-- any of them return a search path, indicating success, this path
-- is returned
-- * otherwise, the function calls itself on the node's children, as
-- above
-- * otherwise, the function returns (nil, []) indicating failure
blackHoleSearchAux :: State -> Sctx -> [Sctx] -> (FileNode, [Sctx])
blackHoleSearchAux state curr@(currnode:stack) path
| haltAtNode currnode = (nil, [])
| nodeInPath currnode path = (currnode, (curr:path))
| resultpath /= [] = (resultnode, resultpath)
| otherwise = searchChildren (getNodeChildren currnode)
where
nodeInPath currnode [] = False
nodeInPath currnode (x:xs)
| incMember currnode x = True
| otherwise = nodeInPath currnode xs
incMember m [] = False
incMember m (y:ys) | (showCount state) &&
incrementCount 1 && m == y = True
| m == y = True
| otherwise = incMember m ys
-- call the function on the node's result
resultsctx = [(peekResultMod currnode)]
(resultnode, resultpath)
| (peekResult currnode) == currnode = (nil, [])
| otherwise = blackHoleSearchAux state resultsctx (curr:path)
-- call the function on all of the node's children
searchChildren [] = (nil, [])
searchChildren (ch:rest) | childpath /= [] = (childnode, childpath)
| otherwise = searchChildren rest
where
(childnode, childpath) = blackHoleSearchAux state chsctx path
chsctx = (ch:currnode:stack)
-- This function indicates if the search process should fail and
-- backtrack when it reaches a node.
haltAtNode node | node <= (FileNode 4) = True
| ntype == ExpApp = False
| ntype == ExpValueApp = False
| ntype == ExpValueUse = False
| ntype == ExpConstUse = False
| ntype == ExpConstDef = False
| ntype == ExpGuard = False
| ntype == ExpCase = False
| ntype == ExpIf = False
| ntype == ExpForward = False
| ntype == ExpProjection = False
| ntype == Module = True
| ntype == SrcPos = True
| ntype == ExpChar = True
| ntype == ExpInt = True
| ntype == ExpInteger = True
| ntype == ExpRat = True
| ntype == ExpRational = True
| ntype == ExpFloat = True
| ntype == ExpDouble = True
| ntype == ExpFieldUpdate = True
| ntype == ExpHidden = True
| ntype == ExpDoStmt = True
| ntype == AtomVariable = True
| ntype == AtomConstructor = True
| ntype == AtomAbstract = True
where ntype = nodeType node