ghc-prof 1.0.0 → 1.0.1
raw patch · 6 files changed
+69/−39 lines, 6 filesdep ~containersPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: containers
API changes (from Hackage documentation)
+ GHC.Prof: decode :: Text -> Either String Profile
+ GHC.Prof.CostCentreTree: buildAggregateCostCentresOrderBy :: Ord a => (AggregateCostCentre -> a) -> CostCentreTree -> [AggregateCostCentre]
+ GHC.Prof.Types: instance GHC.Classes.Eq GHC.Prof.Types.AggregateCostCentre
+ GHC.Prof.Types: instance GHC.Classes.Eq GHC.Prof.Types.CostCentre
+ GHC.Prof.Types: instance GHC.Classes.Ord GHC.Prof.Types.AggregateCostCentre
+ GHC.Prof.Types: instance GHC.Classes.Ord GHC.Prof.Types.CostCentre
- GHC.Prof: CostCentre :: !Text -> !Text -> !(Maybe Text) -> !CostCentreNo -> !Integer -> !Scientific -> !Scientific -> !Scientific -> !Scientific -> !(Maybe Integer) -> !(Maybe Integer) -> CostCentre
+ GHC.Prof: CostCentre :: !CostCentreNo -> !Text -> !Text -> !(Maybe Text) -> !Integer -> !Scientific -> !Scientific -> !Scientific -> !Scientific -> !(Maybe Integer) -> !(Maybe Integer) -> CostCentre
- GHC.Prof: callSites :: Text -> Text -> Profile -> Maybe (Callee, Seq CallSite)
+ GHC.Prof: callSites :: Text -> Text -> Profile -> Maybe (Callee, [CallSite])
- GHC.Prof: callSitesOrderBy :: Ord a => (CostCentre -> a) -> Text -> Text -> Profile -> Maybe (Callee, Seq CallSite)
+ GHC.Prof: callSitesOrderBy :: Ord a => (CostCentre -> a) -> Text -> Text -> Profile -> Maybe (Callee, [CallSite])
- GHC.Prof.CostCentreTree: buildCallSitesOrderBy :: Ord a => (CostCentre -> a) -> Text -> Text -> CostCentreTree -> Maybe (Callee, Seq CallSite)
+ GHC.Prof.CostCentreTree: buildCallSitesOrderBy :: Ord a => (CostCentre -> a) -> Text -> Text -> CostCentreTree -> Maybe (Callee, [CallSite])
- GHC.Prof.CostCentreTree: callSites :: Text -> Text -> Profile -> Maybe (Callee, Seq CallSite)
+ GHC.Prof.CostCentreTree: callSites :: Text -> Text -> Profile -> Maybe (Callee, [CallSite])
- GHC.Prof.CostCentreTree: callSitesOrderBy :: Ord a => (CostCentre -> a) -> Text -> Text -> Profile -> Maybe (Callee, Seq CallSite)
+ GHC.Prof.CostCentreTree: callSitesOrderBy :: Ord a => (CostCentre -> a) -> Text -> Text -> Profile -> Maybe (Callee, [CallSite])
- GHC.Prof.Types: CostCentre :: !Text -> !Text -> !(Maybe Text) -> !CostCentreNo -> !Integer -> !Scientific -> !Scientific -> !Scientific -> !Scientific -> !(Maybe Integer) -> !(Maybe Integer) -> CostCentre
+ GHC.Prof.Types: CostCentre :: !CostCentreNo -> !Text -> !Text -> !(Maybe Text) -> !Integer -> !Scientific -> !Scientific -> !Scientific -> !Scientific -> !(Maybe Integer) -> !(Maybe Integer) -> CostCentre
- GHC.Prof.Types: CostCentreTree :: !(IntMap CostCentre) -> !(IntMap CostCentreNo) -> !(IntMap (Seq CostCentre)) -> !(Map (Text, Text) (Seq CostCentre)) -> !(Map (Text, Text) AggregateCostCentre) -> CostCentreTree
+ GHC.Prof.Types: CostCentreTree :: !(IntMap CostCentre) -> !(IntMap CostCentreNo) -> !(IntMap (Set CostCentre)) -> !(Map (Text, Text) (Set CostCentre)) -> !(Map (Text, Text) AggregateCostCentre) -> CostCentreTree
- GHC.Prof.Types: [costCentreCallSites] :: CostCentreTree -> !(Map (Text, Text) (Seq CostCentre))
+ GHC.Prof.Types: [costCentreCallSites] :: CostCentreTree -> !(Map (Text, Text) (Set CostCentre))
- GHC.Prof.Types: [costCentreChildren] :: CostCentreTree -> !(IntMap (Seq CostCentre))
+ GHC.Prof.Types: [costCentreChildren] :: CostCentreTree -> !(IntMap (Set CostCentre))
Files
- ghc-prof.cabal +2/−1
- src/GHC/Prof.hs +20/−8
- src/GHC/Prof/CostCentreTree.hs +18/−13
- src/GHC/Prof/Parser.hs +7/−6
- src/GHC/Prof/Types.hs +8/−8
- tests/Regression.hs +14/−3
ghc-prof.cabal view
@@ -1,5 +1,5 @@ name: ghc-prof-version: 1.0.0+version: 1.0.1 synopsis: Library for parsing GHC time and allocation profiling reports description: Library for parsing GHC time and allocation profiling reports homepage: https://github.com/maoe/ghc-prof@@ -59,6 +59,7 @@ build-depends: attoparsec >= 0.10 && < 0.14 , base+ , containers , directory , filepath , ghc-prof
src/GHC/Prof.hs view
@@ -1,12 +1,5 @@ module GHC.Prof- ( Profile(..)- , TotalTime(..)- , TotalAlloc(..)- , AggregateCostCentre(..)- , CostCentre(..)- , CostCentreNo- , Callee(..)- , CallSite(..)+ ( decode -- * Parser , profile@@ -19,8 +12,27 @@ , costCentresOrderBy , callSites , callSitesOrderBy++ -- * Types+ , Profile(..)+ , TotalTime(..)+ , TotalAlloc(..)+ , AggregateCostCentre(..)+ , CostCentre(..)+ , CostCentreNo+ , Callee(..)+ , CallSite(..) ) where +import qualified Data.Attoparsec.Text.Lazy as ATL+import qualified Data.Text.Lazy as TL+ import GHC.Prof.CostCentreTree import GHC.Prof.Parser (profile) import GHC.Prof.Types++-- | Decode a GHC time allocation profiling report from a lazy 'ATL.Text'+decode :: TL.Text -> Either String Profile+decode text = case ATL.parse profile text of+ ATL.Fail _unconsumed _contexts reason -> Left reason+ ATL.Done _unconsumed prof -> Right prof
src/GHC/Prof/CostCentreTree.hs view
@@ -12,6 +12,7 @@ , callSites , callSitesOrderBy + , buildAggregateCostCentresOrderBy , buildCostCentresOrderBy , buildCallSitesOrderBy ) where@@ -24,11 +25,11 @@ import Data.Traversable (mapM) import Prelude hiding (mapM) import qualified Data.Foldable as Fold-import qualified Data.Sequence as Seq -import Data.Sequence (Seq)+import Data.Set (Set) import Data.Text (Text) import Data.Tree (Tree)+import qualified Data.Set as Set import qualified Data.Tree as Tree import GHC.Prof.Types@@ -41,11 +42,15 @@ import qualified Data.Map as Map #endif +-- | Build a list of cost-centres from a profiling report ordered by the time+-- spent and the amount of allocation. aggregateCostCentres :: Profile -> [AggregateCostCentre] aggregateCostCentres = aggregateCostCentresOrderBy sortKey where sortKey = aggregateCostCentreTime &&& aggregateCostCentreAlloc +-- | Build a list of cost-centres from a profling report ordered by the given+-- key. aggregateCostCentresOrderBy :: Ord a => (AggregateCostCentre -> a)@@ -83,7 +88,7 @@ -> Text -- ^ Module name -> Profile- -> Maybe (Callee, Seq CallSite)+ -> Maybe (Callee, [CallSite]) callSites = callSitesOrderBy sortKey where sortKey =@@ -102,7 +107,7 @@ -> Text -- ^ Module name -> Profile- -> Maybe (Callee, Seq CallSite)+ -> Maybe (Callee, [CallSite]) callSitesOrderBy sortKey name modName = buildCallSitesOrderBy sortKey name modName . profileCostCentreTree @@ -135,7 +140,7 @@ !children = maybe [] Fold.toList $ do nodes <- IntMap.lookup key costCentreChildren return $ costCentreNo- <$> Seq.unstableSortBy (flip compare `on` sortKey) nodes+ <$> sortBy (flip compare `on` sortKey) (Set.toList nodes) buildCallSitesOrderBy :: Ord a@@ -146,7 +151,7 @@ -> Text -- ^ Module name -> CostCentreTree- -> Maybe (Callee, Seq CallSite)+ -> Maybe (Callee, [CallSite]) buildCallSitesOrderBy sortKey name modName tree@CostCentreTree {..} = (,) <$> callee <*> callers where@@ -157,17 +162,17 @@ callers = do callees <- lookupCallees mapM (buildCallSite tree) $- Seq.unstableSortBy (flip compare `on` sortKey) callees+ sortBy (flip compare `on` sortKey) $ Set.toList callees -buildCallee :: Text -> Text -> Seq CostCentre -> Callee+buildCallee :: Text -> Text -> Set CostCentre -> Callee buildCallee name modName callees = Callee { calleeName = name , calleeModule = modName- , calleeEntries = Fold.sum $ costCentreEntries <$> callees- , calleeTime = Fold.sum $ costCentreIndTime <$> callees- , calleeAlloc = Fold.sum $ costCentreIndAlloc <$> callees- , calleeTicks = asum $ costCentreTicks <$> callees- , calleeBytes = asum $ costCentreBytes <$> callees+ , calleeEntries = Fold.sum $ Set.map costCentreEntries callees+ , calleeTime = Fold.sum $ Set.map costCentreIndTime callees+ , calleeAlloc = Fold.sum $ Set.map costCentreIndAlloc callees+ , calleeTicks = asum $ Set.map costCentreTicks callees+ , calleeBytes = asum $ Set.map costCentreBytes callees } buildCallSite :: CostCentreTree -> CostCentre -> Maybe CallSite
src/GHC/Prof/Parser.hs view
@@ -20,11 +20,10 @@ import Control.Monad import Data.Char (isSpace) import Data.Foldable (asum, foldl')-import Data.Sequence ((><)) import Data.Maybe import Data.Text (Text) import Data.Time-import qualified Data.Sequence as Seq+import qualified Data.Set as Set import Data.Attoparsec.Text as A @@ -38,6 +37,7 @@ import qualified Data.Map as Map #endif +-- | Parse a GHC time-allocation profiling report profile :: Parser Profile profile = do skipHorizontalSpace@@ -51,6 +51,7 @@ endOfInput return $! Profile {..} +-- | Parse the timestamp in a header as local time timestamp :: Parser LocalTime timestamp = do parseDayOfTheWeek >> skipSpace@@ -264,13 +265,13 @@ (\parent -> IntMap.insert ccNo parent costCentreParents) parentNo , costCentreChildren = maybe costCentreChildren- (\parent -> IntMap.insertWith (><) parent- (Seq.singleton node)+ (\parent -> IntMap.insertWith Set.union parent+ (Set.singleton node) costCentreChildren) parentNo- , costCentreCallSites = Map.insertWith (><)+ , costCentreCallSites = Map.insertWith Set.union (costCentreName node, costCentreModule node)- (Seq.singleton node)+ (Set.singleton node) costCentreCallSites , costCentreAggregate = Map.insertWith addCostCentre (costCentreName node, costCentreModule node)
src/GHC/Prof/Types.hs view
@@ -6,7 +6,7 @@ import Data.IntMap (IntMap) import Data.Map (Map) import Data.Scientific (Scientific)-import Data.Sequence (Seq)+import Data.Set (Set) import Data.Text (Text) import Data.Time (DiffTime, LocalTime) @@ -55,18 +55,18 @@ , aggregateCostCentreBytes :: !(Maybe Integer) -- ^ Total memory allocation in the cost-centre. This number -- exists only if @-P@ or @-Pa@ option is given at run-time.- } deriving Show+ } deriving (Show, Eq, Ord) -- | Cost-centre node data CostCentre = CostCentre- { costCentreName :: !Text+ { costCentreNo :: !CostCentreNo+ -- ^ Identifier of the cost-centre+ , costCentreName :: !Text -- ^ Name of the cost-centre , costCentreModule :: !Text -- ^ Module name of the cost-centre , costCentreSrc :: !(Maybe Text) -- ^ Source location of the cost-centre- , costCentreNo :: !CostCentreNo- -- ^ Identifier of the cost-centre , costCentreEntries :: !Integer -- ^ Number of entries to the cost-centre , costCentreIndTime :: !Scientific@@ -81,15 +81,15 @@ -- ^ Number of ticks in the cost-centre. , costCentreBytes :: !(Maybe Integer) -- ^ Number of allocated bytes in the cost-centre.- } deriving Show+ } deriving (Show, Eq, Ord) type CostCentreNo = Int data CostCentreTree = CostCentreTree { costCentreNodes :: !(IntMap CostCentre) , costCentreParents :: !(IntMap CostCentreNo)- , costCentreChildren :: !(IntMap (Seq CostCentre))- , costCentreCallSites :: !(Map (Text, Text) (Seq CostCentre))+ , costCentreChildren :: !(IntMap (Set CostCentre))+ , costCentreCallSites :: !(Map (Text, Text) (Set CostCentre)) , costCentreAggregate :: !(Map (Text, Text) AggregateCostCentre) } deriving Show
tests/Regression.hs view
@@ -14,8 +14,9 @@ import System.Process import Test.Tasty import Test.Tasty.HUnit-import qualified Data.Text.Lazy.IO as TL import qualified Data.Attoparsec.Text.Lazy as ATL+import qualified Data.Set as Set+import qualified Data.Text.Lazy.IO as TL import GHC.Prof @@ -35,7 +36,12 @@ generateProfiles :: IO [FilePath] generateProfiles = do withFile "hello.hs" WriteMode $ \h ->- hPutStrLn h "main = putStrLn \"Hello, World!\""+ hPutStrLn h $ unlines+ [ "import Control.Exception"+ , "main = evaluate $ fib 100000"+ , "fib n = fibs !! n"+ , "fibs = 0:1:zipWith (+) fibs (tail fibs)"+ ] void $ readProcess "ghc" ["-prof", "-rtsopts", "-fforce-recomp", "hello.hs"] "" for profilingFlags $ \(name, flag) -> do void $ readProcess "./hello" ["+RTS", flag, "-RTS"] ""@@ -54,7 +60,12 @@ assertProfile path = do text <- TL.readFile path case ATL.parse profile text of- ATL.Done {} -> return ()+ ATL.Done _ prof -> do+ let actual = Set.fromList $ aggregateCostCentres prof+ expected = Set.fromList $ profileTopCostCentres prof+ assertBool+ ("Missing cost centre(s): " ++ show (Set.difference expected actual)) $+ Set.isSubsetOf expected actual ATL.Fail _ _ reason -> assertFailure reason #if !MIN_VERSION_directory(1, 2, 3)