packages feed

ghc-debug-client-0.6.0.0: src/GHC/Debug/CostCentres.hs

{-# LANGUAGE BangPatterns #-}
module GHC.Debug.CostCentres
  ( findAllChildrenOfCC
  , findExactlyByCC
  , findAllCCSPayloads
  , traverseCCSPayloads
  -- * Helper functions for working with `IndexTable`'s
  , flattenIndexTable
  , traverseIndexTable
  , foldIndexTable
  -- * Efficient representation of CCSPtr sets
  , CCSSet(..)
  , memberCCSSet
  ) where

import qualified Data.Set as Set
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet

import GHC.Debug.Client
import GHC.Debug.Types.Ptr (CCSPtr(..))
import Data.Coerce (coerce)

newtype CCSSet = CCSSet IntSet

memberCCSSet :: CCSPtr -> CCSSet -> Bool
memberCCSSet (CCSPtr k) set = IntSet.member (fromIntegral k) (coerce set)

-- | Find all Cost Centre Stacks that reference precisely the cost centre with the given id.
findExactlyByCC :: (CCPayload -> Bool) -> DebugM (Set.Set CCSPtr)
findExactlyByCC isRelevantCC = do
  ccsMain <- requestCCSMain
  collectNode Set.empty ccsMain
  where
    collectNode :: Set.Set CCSPtr -> CCSPtr -> DebugM (Set.Set CCSPtr)
    collectNode !seen !ccsPtr = do
      ccsPl <- dereferenceCCS ccsPtr
      ccPl <- dereferenceCC (ccsCc ccsPl)
      let newSeen = if isRelevantCC ccPl
            then ccsPtr `Set.insert` seen
            else seen
      foldIndexTable (\_ ptr backEdge !seen' -> do
        if backEdge
          then pure seen'
          else collectNode seen' ptr)
        newSeen
        (ccsIndexTable ccsPl)

-- | Find all cost centre stack parts that are transitively children of the cost
-- centre with the given id.
findAllChildrenOfCC :: (CCPayload -> Bool) -> DebugM (Set.Set CCSPtr)
findAllChildrenOfCC isRelevantCC = do
  ccsMain <- requestCCSMain
  findCostCentre Set.empty ccsMain
  where
    findCostCentre :: Set.Set CCSPtr -> CCSPtr -> DebugM (Set.Set CCSPtr)
    findCostCentre !seen !ccsPtr = do
      ccsPl <- dereferenceCCS ccsPtr
      ccPl <- dereferenceCC (ccsCc ccsPl)
      if isRelevantCC ccPl
        then collectNodes seen ccsPtr
        else
          foldIndexTable  (\_ ptr backEdge !seen' -> do
            if backEdge
              then pure seen'
              else findCostCentre seen' ptr)
            seen
            (ccsIndexTable ccsPl)

    collectNodes :: Set.Set CCSPtr -> CCSPtr -> DebugM (Set.Set CCSPtr)
    collectNodes !seen !ccsPtr = do
      ccsPl <- dereferenceCCS ccsPtr
      foldIndexTable  (\_ ptr backEdge !seen' -> do
        let seen'' = ptr `Set.insert` seen'
        if backEdge
          then pure seen''
          else collectNodes seen'' ptr)
        (ccsPtr `Set.insert` seen)
        (ccsIndexTable ccsPl)

findAllCCSPayloads :: DebugM CCSSet
findAllCCSPayloads = do
  ccsMain <- requestCCSMain
  CCSSet <$> collectNodes IntSet.empty ccsMain
  where
    collectNodes :: IntSet.IntSet -> CCSPtr -> DebugM (IntSet.IntSet)
    collectNodes !seen ccsPtr@(CCSPtr w_) = do
      ccsPl <- dereferenceCCS ccsPtr
      foldIndexTable  (\_ ptr@(CCSPtr w) backEdge !seen' -> do
        let seen'' = fromIntegral w `IntSet.insert` seen'
        if backEdge
          then pure seen''
          else collectNodes seen'' ptr)
        (fromIntegral w_ `IntSet.insert` seen)
        (ccsIndexTable ccsPl)

traverseCCSPayloads :: DebugM ()
traverseCCSPayloads = do
  ccsMain <- requestCCSMain
  collectNodes ccsMain
  where
    collectNodes :: CCSPtr -> DebugM ()
    collectNodes ccsPtr = do
      ccsPl <- dereferenceCCS ccsPtr
      foldIndexTable  (\_ ptr backEdge () -> do
        if backEdge
          then pure ()
          else collectNodes ptr)
        ()
        (ccsIndexTable ccsPl)

traverseIndexTable :: Maybe IndexTablePtr -> (CCPtr -> CCSPtr -> Bool -> DebugM a) -> DebugM [a]
traverseIndexTable Nothing _ = pure []
traverseIndexTable (Just ptr) f = do
  idxTable <- dereferenceIndexTable ptr
  x <- f (itCostCentre idxTable) (itCostCentreStack idxTable) (itBackEdge idxTable)
  rest <- traverseIndexTable (itNext idxTable) f
  pure $ x:rest

foldIndexTable :: (CCPtr -> CCSPtr -> Bool -> a -> DebugM a) -> a -> Maybe IndexTablePtr -> DebugM a
foldIndexTable _f acc Nothing = pure acc
foldIndexTable f acc (Just ptr) = do
  idxTable <- dereferenceIndexTable ptr
  acc' <- f (itCostCentre idxTable) (itCostCentreStack idxTable) (itBackEdge idxTable) acc
  foldIndexTable f acc' (itNext idxTable)

-- | Flatten an optional index table pointer into a list of CCS Payloads.
flattenIndexTable :: Maybe IndexTablePtr -> DebugM [CCSPayload]
flattenIndexTable root = traverseIndexTable root (\_ ccsPtr _ -> dereferenceCCS ccsPtr)