module LLVM.Analysis.CallGraph (
CallGraph,
CG,
CallEdge(..),
CallNode(..),
mkCallGraph,
callGraphRepr,
callValueTargets,
callSiteTargets,
callGraphFunctions,
functionCallees,
allFunctionCallees,
functionCallers,
allFunctionCallers,
cgGraphvizRepr
) where
import Data.GraphViz
import Data.Maybe ( mapMaybe )
import Data.Hashable
import Data.HashSet ( HashSet )
import qualified Data.HashSet as HS
import Data.HashMap.Strict ( HashMap )
import qualified Data.HashMap.Strict as HM
import Data.Monoid
import Data.Graph.Interface
import Data.Graph.MutableDigraph
import Data.Graph.Algorithms.DFS ( dfs, rdfs, scc )
import LLVM.Analysis
import LLVM.Analysis.PointsTo
type CG = SparseDigraph CallNode CallEdge
data CallNode = DefinedFunction Function
| ExtFunction ExternalFunction
| UnknownFunction
deriving (Eq)
instance Show CallNode where
show (DefinedFunction v) = show $ functionName v
show (ExtFunction v) = "extern " ++ show (externalFunctionName v)
show UnknownFunction = "unknown"
instance Labellable CallNode where
toLabelValue = toLabelValue . show
data CallEdge = DirectCall
| IndirectCall
| UnknownCall
deriving (Ord, Eq)
instance Hashable CallEdge where
hashWithSalt s DirectCall = s `hashWithSalt` (1 :: Int)
hashWithSalt s IndirectCall = s `hashWithSalt` (2 :: Int)
hashWithSalt s UnknownCall = s `hashWithSalt` (3 :: Int)
instance Show CallEdge where
show DirectCall = ""
show IndirectCall = "?"
show UnknownCall = "??"
instance Labellable CallEdge where
toLabelValue = toLabelValue . show
data CallGraph = forall pta . (PointsToAnalysis pta) => CallGraph CG pta
callGraphFunctions :: CallGraph -> [Function]
callGraphFunctions (CallGraph cg _) =
mapMaybe extractDefinedFunction (labeledVertices cg)
where
extractDefinedFunction (_, DefinedFunction f) = Just f
extractDefinedFunction _ = Nothing
callGraphRepr :: CallGraph -> CG
callGraphRepr (CallGraph g _) = g
callSiteTargets :: CallGraph -> Instruction -> [Value]
callSiteTargets cg (CallInst { callFunction = f }) =
callValueTargets cg f
callSiteTargets cg (InvokeInst { invokeFunction = f}) =
callValueTargets cg f
callSiteTargets _ i =
error ("LLVM.Analysis.CallGraph.callSiteTargets: Expected a Call or Invoke instruction: " ++ show i)
callValueTargets :: CallGraph -> Value -> [Value]
callValueTargets (CallGraph _ pta) v =
let v' = stripBitcasts v
in case valueContent v' of
FunctionC _ -> [v']
ExternalFunctionC _ -> [v']
_ -> pointsTo pta v
functionCallees :: CallGraph -> Function -> [Value]
functionCallees (CallGraph g _) =
mapMaybe (toCallValue g) . suc g . functionUniqueId
allFunctionCallees :: CallGraph -> Function -> [Value]
allFunctionCallees (CallGraph g _) =
mapMaybe (toCallValue g) . flip dfs g . (:[]) . functionUniqueId
functionCallers :: CallGraph -> Function -> [Value]
functionCallers (CallGraph g _) =
mapMaybe (toCallValue g) . pre g . functionUniqueId
allFunctionCallers :: CallGraph -> Function -> [Value]
allFunctionCallers (CallGraph g _) =
mapMaybe (toCallValue g) . flip rdfs g . (:[]) . functionUniqueId
toCallValue :: CG -> Vertex -> Maybe Value
toCallValue g v = do
l <- lab g v
case l of
DefinedFunction f -> return (toValue f)
ExtFunction ef -> return (toValue ef)
_ -> Nothing
mkCallGraph :: (PointsToAnalysis a) => Module
-> a
-> [Function]
-> CallGraph
mkCallGraph m pta _ =
CallGraph (mkGraph allNodes (unique allEdges)) pta
where
allNodes = concat [ knownNodes, unknownNodes, externNodes ]
(allEdges, unknownNodes) = buildEdges pta funcs
knownNodes = map (\f -> (valueUniqueId f, DefinedFunction f)) funcs
externNodes = map mkExternFunc $ moduleExternalFunctions m
funcs = moduleDefinedFunctions m
unique :: (Hashable a, Eq a) => [a] -> [a]
unique = HS.toList . HS.fromList
unknownNodeId :: Vertex
unknownNodeId = 100
mkExternFunc :: ExternalFunction -> (Vertex, VertexLabel CG)
mkExternFunc v = (valueUniqueId v, ExtFunction v)
buildEdges :: (PointsToAnalysis a) => a -> [Function] -> ([Edge CG], [(Vertex, VertexLabel CG)])
buildEdges pta funcs = do
let es = map (buildFuncEdges pta) funcs
unknownNodes = [(unknownNodeId, UnknownFunction)]
(concat es, unknownNodes)
isCall :: Instruction -> Bool
isCall CallInst {} = True
isCall InvokeInst {} = True
isCall _ = False
buildFuncEdges :: (PointsToAnalysis a) => a -> Function -> [Edge CG]
buildFuncEdges pta f = concat es
where
insts = concatMap basicBlockInstructions $ functionBody f
calls = filter isCall insts
es = map (buildCallEdges pta f) calls
getCallee :: Instruction -> Value
getCallee CallInst { callFunction = f } = f
getCallee InvokeInst { invokeFunction = f } = f
getCallee i = error ("LLVM.Analysis.CallGraph.getCallee: Expected a function in getCallee: " ++ show i)
buildCallEdges :: (PointsToAnalysis a) => a -> Function -> Instruction -> [Edge CG]
buildCallEdges pta caller callInst = build' (getCallee callInst)
where
callerId = valueUniqueId caller
build' calledFunc =
case valueContent' calledFunc of
FunctionC f ->
[Edge callerId (valueUniqueId f) DirectCall]
GlobalAliasC GlobalAlias { globalAliasTarget = aliasee } ->
[Edge callerId (valueUniqueId aliasee) DirectCall]
ExternalFunctionC ef ->
[Edge callerId (valueUniqueId ef) DirectCall]
InstructionC BitcastInst { castedValue = bcv } -> build' bcv
_ ->
let targets = resolveIndirectCall pta callInst
indirectEdges = map (\t -> Edge callerId (valueUniqueId t) IndirectCall) targets
unknownEdge = Edge callerId unknownNodeId UnknownCall
in unknownEdge : indirectEdges
cgGraphvizParams :: HashMap Int Int -> HashSet Int -> GraphvizParams Int CallNode CallEdge Int CallNode
cgGraphvizParams compMap singletons =
defaultParams { fmtNode = \(_,l) -> [toLabel l]
, fmtEdge = \(_,_,l) -> [toLabel l]
, clusterBy = clusterByFunc
, clusterID = clusterIDFunc
}
where
clusterIDFunc cid =
case cid `HS.member` singletons of
True -> Str ""
False -> Int cid
clusterByFunc n@(nid, _) =
let cid = HM.lookupDefault (1) nid compMap
in case cid `HS.member` singletons of
True -> N n
False -> C cid (N n)
cgGraphvizRepr :: CallGraph -> DotGraph Int
cgGraphvizRepr (CallGraph g _) =
graphElemsToDot (cgGraphvizParams compMap singletons) ns es
where
ns = labeledVertices g
es = map (\(Edge s d l) -> (s, d, l)) $ edges g
comps = zip [0..] $ scc g
singletons = HS.fromList $ map fst $ filter ((==0) . length . snd) comps
compMap = foldr assignComponent mempty comps
assignComponent :: (Int, [Int]) -> HashMap Int Int -> HashMap Int Int
assignComponent (compId, nodeIds) acc =
foldr (\nid -> HM.insert nid compId) acc nodeIds