{-# Language TransformListComp, MonadComprehensions #-}
module Text.LLVM.DebugUtils
(
Info(..), computeFunctionTypes, valMdToInfo
, localVariableNameDeclarations
, mkMdMap
, derefInfo
, fieldIndexByPosition
, fieldIndexByName
, guessAliasInfo
) where
import Control.Applicative ((<|>))
import Control.Monad ((<=<))
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.List (elemIndex, tails, stripPrefix)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe, listToMaybe, maybeToList, mapMaybe)
import Data.Word (Word16, Word64)
import Text.LLVM.AST
dbgKind :: String
dbgKind = "dbg"
llvmDbgCuKey :: String
llvmDbgCuKey = "llvm.dbg.cu"
dwarfPointer, dwarfStruct, dwarfTypedef, dwarfUnion, dwarfBasetype,
dwarfConst, dwarfArray :: Word16
dwarfPointer = 0x0f
dwarfStruct = 0x13
dwarfTypedef = 0x16
dwarfArray = 0x01
dwarfUnion = 0x17
dwarfBasetype = 0x24
dwarfConst = 0x26
type MdMap = IntMap ValMd
data Info
= Pointer Info
| Structure [(String,Word64,Info)]
| Union [(String,Info)]
| ArrInfo Info
| BaseType String
| Unknown
deriving Show
mkMdMap :: Module -> IntMap ValMd
mkMdMap m = IntMap.fromList [ (umIndex md, umValues md) | md <- modUnnamedMd m ]
getDebugInfo :: MdMap -> ValMd -> Maybe DebugInfo
getDebugInfo mdMap (ValMdRef i) = getDebugInfo mdMap =<< IntMap.lookup i mdMap
getDebugInfo _ (ValMdDebugInfo di) = Just di
getDebugInfo _ _ = Nothing
getList :: MdMap -> ValMd -> Maybe [Maybe ValMd]
getList mdMap (ValMdRef i) = getList mdMap =<< IntMap.lookup i mdMap
getList _ (ValMdNode di) = Just di
getList _ _ = Nothing
valMdToInfo :: MdMap -> ValMd -> Info
valMdToInfo mdMap val =
maybe Unknown (debugInfoToInfo mdMap) (getDebugInfo mdMap val)
valMdToInfo' :: MdMap -> Maybe ValMd -> Info
valMdToInfo' = maybe Unknown . valMdToInfo
debugInfoToInfo :: MdMap -> DebugInfo -> Info
debugInfoToInfo mdMap (DebugInfoDerivedType dt)
| didtTag dt == dwarfPointer = Pointer (valMdToInfo' mdMap (didtBaseType dt))
| didtTag dt == dwarfTypedef = valMdToInfo' mdMap (didtBaseType dt)
| didtTag dt == dwarfConst = valMdToInfo' mdMap (didtBaseType dt)
debugInfoToInfo _ (DebugInfoBasicType bt)
| dibtTag bt == dwarfBasetype = BaseType (dibtName bt)
debugInfoToInfo mdMap (DebugInfoCompositeType ct)
| dictTag ct == dwarfStruct = maybe Unknown Structure (getStructFields mdMap ct)
| dictTag ct == dwarfUnion = maybe Unknown Union (getUnionFields mdMap ct)
| dictTag ct == dwarfArray = ArrInfo (valMdToInfo' mdMap (dictBaseType ct))
debugInfoToInfo _ _ = Unknown
getFieldDIs :: MdMap -> DICompositeType -> Maybe [DebugInfo]
getFieldDIs mdMap =
traverse (getDebugInfo mdMap) <=< sequence <=< getList mdMap <=< dictElements
getStructFields :: MdMap -> DICompositeType -> Maybe [(String, Word64, Info)]
getStructFields mdMap = traverse (debugInfoToStructField mdMap) <=< getFieldDIs mdMap
debugInfoToStructField :: MdMap -> DebugInfo -> Maybe (String, Word64, Info)
debugInfoToStructField mdMap di =
do DebugInfoDerivedType dt <- Just di
fieldName <- didtName dt
Just (fieldName, didtOffset dt, valMdToInfo' mdMap (didtBaseType dt))
getUnionFields :: MdMap -> DICompositeType -> Maybe [(String, Info)]
getUnionFields mdMap = traverse (debugInfoToUnionField mdMap) <=< getFieldDIs mdMap
debugInfoToUnionField :: MdMap -> DebugInfo -> Maybe (String, Info)
debugInfoToUnionField mdMap di =
do DebugInfoDerivedType dt <- Just di
fieldName <- didtName dt
Just (fieldName, valMdToInfo' mdMap (didtBaseType dt))
computeFunctionTypes ::
Module ->
Symbol ->
Maybe [Info]
computeFunctionTypes m sym =
[ maybe (BaseType "void") (valMdToInfo mdMap) <$> types
| let mdMap = mkMdMap m
, sp <- findSubprogramViaDefine mdMap m sym
<|> findSubprogramViaCu mdMap m sym
, DebugInfoSubroutineType st <- getDebugInfo mdMap =<< dispType sp
, types <- getList mdMap =<< distTypeArray st
]
findSubprogramViaDefine ::
IntMap ValMd ->
Module ->
Symbol ->
Maybe DISubprogram
findSubprogramViaDefine mdMap m sym =
[ sp
| def <- modDefines m
, defName def == sym
, then listToMaybe
, dbgMd <- Map.lookup dbgKind (defMetadata def)
, DebugInfoSubprogram sp <- getDebugInfo mdMap dbgMd
]
findSubprogramViaCu ::
MdMap ->
Module ->
Symbol ->
Maybe DISubprogram
findSubprogramViaCu mdMap m (Symbol sym) = listToMaybe
[ sp
| md <- modNamedMd m
, nmName md == llvmDbgCuKey
, ref <- nmValues md
, DebugInfoCompileUnit cu <- maybeToList $ getDebugInfo mdMap $ ValMdRef ref
, Just entry <- fromMaybe [] $ getList mdMap =<< dicuSubprograms cu
, DebugInfoSubprogram sp <- maybeToList $ getDebugInfo mdMap entry
, dispName sp == Just sym
]
derefInfo ::
Info ->
Info
derefInfo (Pointer x) = x
derefInfo (ArrInfo x) = x
derefInfo _ = Unknown
fieldIndexByPosition ::
Int ->
Info ->
Info
fieldIndexByPosition i info =
case info of
Structure xs -> go [ x | (_,_,x) <- xs ]
Union xs -> go [ x | (_,x) <- xs ]
_ -> Unknown
where
go xs = case drop i xs of
[] -> Unknown
x:_ -> x
fieldIndexByName ::
String ->
Info ->
Maybe Int
fieldIndexByName n info =
case info of
Structure xs -> go [ x | (x,_,_) <- xs ]
Union xs -> go [ x | (x,_) <- xs ]
_ -> Nothing
where
go = elemIndex n
localVariableNameDeclarations ::
IntMap ValMd ->
Define ->
Map Ident Ident
localVariableNameDeclarations mdMap def =
case defBody def of
blk1 : _ -> foldr aux Map.empty (tails (bbStmts blk1))
_ -> Map.empty
where
aux :: [Stmt] -> Map Ident Ident -> Map Ident Ident
aux ( Effect (Store src dst _ _) _
: Effect (Call _ _ (ValSymbol (Symbol what)) [var,md,_]) _
: _) sofar
| what == "llvm.dbg.declare"
, Just dstIdent <- extractIdent dst
, Just srcIdent <- extractIdent src
, Just varIdent <- extractIdent var
, dstIdent == varIdent
, Just name <- extractLvName md
= Map.insert name srcIdent sofar
aux ( Effect (Call _ _ (ValSymbol (Symbol what)) [var,_,md,_]) _
: _) sofar
| what == "llvm.dbg.value"
, Just key <- extractIdent var
, Just name <- extractLvName md
= Map.insert name key sofar
aux _ sofar = sofar
extractIdent :: Typed Value -> Maybe Ident
extractIdent (Typed _ (ValIdent i)) = Just i
extractIdent _ = Nothing
extractLvName :: Typed Value -> Maybe Ident
extractLvName mdArg =
do ValMd md <- Just (typedValue mdArg)
DebugInfoLocalVariable dilv <- getDebugInfo mdMap md
Ident <$> dilvName dilv
guessAliasInfo ::
IntMap ValMd ->
Ident ->
Info
guessAliasInfo mdMap (Ident name) =
case stripPrefix "struct." name of
Nothing -> Unknown
Just pfx -> guessStructInfo mdMap pfx
guessStructInfo ::
IntMap ValMd ->
String ->
Info
guessStructInfo mdMap name =
case mapMaybe (go <=< getDebugInfo mdMap) (IntMap.elems mdMap) of
[] -> Unknown
x:_ -> x
where
go di | DebugInfoDerivedType didt <- di
, Just name == didtName didt
= Just (debugInfoToInfo mdMap di)
go di | DebugInfoCompositeType dict <- di
, Just name == dictName dict
= Just (debugInfoToInfo mdMap di)
go _ = Nothing