{-# Language OverloadedStrings, CPP #-}

{-|
Module      : RtsStats
Description : Compatibility layer for GHC RTS statistics across versions
Copyright   : (c) Eric Mertens, 2017
License     : ISC
Maintainer  : emertens@gmail.com

-}

module RtsStats
  ( Stats
  , getStats
  , statsToEntries
  ) where

import           Data.Int
import           Data.List (intercalate)
import           Data.List.Split (chunksOf)
import           Data.Text (Text)
import qualified Data.Text as Text
import           Data.Word
import           GHC.Stats

class    Render a      where render :: a -> Text
instance Render Word32 where render = prettyNum
instance Render Word64 where render = prettyNum
instance Render Int64  where render = prettyNum
instance Render Double where render = Text.pack . show

prettyNum :: Show a => Num a => Ord a => a -> Text
prettyNum n = Text.pack (addSign (addCommas (show (abs n))))
  where
    addSign = if n < 0 then (:) '-' else id

addCommas :: String -> String
addCommas = reverse . intercalate "," . chunksOf 3 . reverse

#if MIN_VERSION_base(4,10,0)

newtype Stats = Stats RTSStats

getStats :: IO (Maybe Stats)
getStats =
  do enabled <- getRTSStatsEnabled
     if enabled then Just . Stats <$> getRTSStats
                else pure Nothing

statsToEntries :: Stats -> [(Text, Text)]
statsToEntries (Stats rts) = seq rgc
  [ ("GCs"                                 , render $ gcs                             rts)
  , ("Major GCs"                           , render $ major_gcs                       rts)
  , ("Allocated bytes"                     , render $ allocated_bytes                 rts)
  , ("Max live bytes"                      , render $ max_live_bytes                  rts)
  , ("Max large objects bytes"             , render $ max_large_objects_bytes         rts)
  , ("Max compact bytes"                   , render $ max_compact_bytes               rts)
  , ("Max slop bytes"                      , render $ max_slop_bytes                  rts)
  , ("Max memory in use bytes"             , render $ max_mem_in_use_bytes            rts)
  , ("Cumulative live bytes"               , render $ cumulative_live_bytes           rts)
  , ("Copied bytes"                        , render $ copied_bytes                    rts)
  , ("Parallel copied bytes"               , render $ par_copied_bytes                rts)
  , ("Cumulative parallel max copied bytes", render $ cumulative_par_max_copied_bytes rts)
  , ("Mutator CPU ns"                      , render $ mutator_cpu_ns                  rts)
  , ("Mutator elapsed ns"                  , render $ mutator_elapsed_ns              rts)
  , ("GC CPU ns"                           , render $ gc_cpu_ns                       rts)
  , ("GC elapsed ns"                       , render $ gc_elapsed_ns                   rts)
  , ("CPU ns"                              , render $ cpu_ns                          rts)
  , ("Elapsed ns"                          , render $ elapsed_ns                      rts)
  , ("==[Totals]=="                        , ""                                          )
  , (""                                    , ""                                          )
  , ("Generation"                          , render $ gcdetails_gen                   rgc)
  , ("Threads"                             , render $ gcdetails_threads               rgc)
  , ("Allocated bytes"                     , render $ gcdetails_allocated_bytes       rgc)
  , ("Live bytes"                          , render $ gcdetails_live_bytes            rgc)
  , ("Large objects bytes"                 , render $ gcdetails_large_objects_bytes   rgc)
  , ("Compact bytes"                       , render $ gcdetails_compact_bytes         rgc)
  , ("Slop bytes"                          , render $ gcdetails_slop_bytes            rgc)
  , ("Memory in use bytes"                 , render $ gcdetails_mem_in_use_bytes      rgc)
  , ("Copied bytes"                        , render $ gcdetails_copied_bytes          rgc)
  , ("Parallel max copied bytes"           , render $ gcdetails_par_max_copied_bytes  rgc)
  , ("Sync elapsed ns"                     , render $ gcdetails_sync_elapsed_ns       rgc)
  , ("CPU ns"                              , render $ gcdetails_cpu_ns                rgc)
  , ("Elapsed ns"                          , render $ gcdetails_elapsed_ns            rgc)
  , ("==[Last GC]=="                       , ""                                          )
  ]
  where
    rgc = gc rts

#else

statsToEntries :: Stats -> [(Text, Text)]
statsToEntries (Stats rts) = seq rts
  [ ("Allocated bytes"            , render $ bytesAllocated         rts)
  , ("GCs"                        , render $ numGcs                 rts)
  , ("Major GCs"                  , render $ numByteUsageSamples    rts)
  , ("Cumulative allocated bytes" , render $ maxBytesUsed           rts)
  , ("Cumulative live bytes"      , render $ cumulativeBytesUsed    rts)
  , ("Cumulative copied  bytes"   , render $ bytesCopied            rts)
  , ("Max slop bytes"             , render $ maxBytesSlop           rts)
  , ("Maximum allocated megabytes", render $ peakMegabytesAllocated rts)
  , ("Mutator CPU seconds"        , render $ mutatorCpuSeconds      rts)
  , ("Mutator elapsed seconds"    , render $ mutatorWallSeconds     rts)
  , ("GC CPU seconds"             , render $ gcCpuSeconds           rts)
  , ("GC elapsed seconds"         , render $ gcWallSeconds          rts)
  , ("CPU seconds"                , render $ cpuSeconds             rts)
  , ("Elapsed seconds"            , render $ wallSeconds            rts)
  , ("Total bytes copied"         , render $ parTotBytesCopied      rts)
  , ("Max parallel bytes copied"  , render $ parMaxBytesCopied      rts)
  , ("==[Totals]=="               , ""                                 )
  , (""                           , ""                                 )
  , ("(last) Live bytes"          , render $ currentBytesUsed       rts)
  , ("(last) Slop bytes"          , render $ currentBytesSlop       rts)
  , ("==[Last GC]=="              , ""                                 )
  ]

newtype Stats = Stats GCStats

getStats :: IO (Maybe Stats)
getStats =
  do enabled <- getGCStatsEnabled
     if enabled then Just . Stats <$> getGCStats
                else pure Nothing

#endif