content: {-# language OverloadedStrings #-}
  {-# language CPP #-}
  {-# language NumDecimals #-}
  
  -- | This module defines a metrics that exposes statistics from the GHC runtime
  -- system ("GHC.Conc", "GHC.Stats").
  --
  -- To use these metrics, the monitored executable should run with the `+RTS -T`
  -- command line flags and the following must be added somewhere near the
  -- beginning of the main method:
  --
  -- >>> register ghcMetrics
  module Prometheus.Metric.GHC (
      GHCMetrics
  ,   ghcMetrics
  ,   ghcMetricsWithLabels
  ) where
  
  #if __GLASGOW_HASKELL__ < 710
  import Control.Applicative ((<$>))
  #endif
  import qualified Data.ByteString.UTF8 as BS
  import Data.Text (Text)
  import Data.Fixed (Fixed, E9)
  #if __GLASGOW_HASKELL__ < 804
  import GHC.Conc (numSparks, getNumCapabilities)
  import GHC.Stats (GCStats(..), getGCStatsEnabled, getGCStats)
  #else
  import GHC.Stats (RTSStats(..), GCDetails(..), getRTSStatsEnabled, getRTSStats)
  #endif
  import qualified GHC.Stats as Stats
  import Prometheus
  
  
  data GHCMetrics = GHCMetrics
  
  ghcMetrics :: Metric GHCMetrics
  ghcMetrics = ghcMetricsWithLabels []
  
  ghcMetricsWithLabels :: LabelPairs -> Metric GHCMetrics
  ghcMetricsWithLabels labels = Metric (do
    statsEnabled <-
  #if __GLASGOW_HASKELL__ < 804
      getGCStatsEnabled
  #else
      getRTSStatsEnabled
  #endif
    if statsEnabled
    then return (GHCMetrics, do
          stats <-
  #if __GLASGOW_HASKELL__ < 804
              getGCStats
  #else
              getRTSStats
  #endif
          concat <$> mapM (\f -> f labels stats) ghcCollectors
      )
    else return (GHCMetrics, return [])
    )
  
  #if __GLASGOW_HASKELL__ < 804
  ghcCollectors :: [LabelPairs -> GCStats -> IO [SampleGroup]]
  ghcCollectors = [
          \labelpairs gcstats -> do
            sparkCount <- numSparks
            showCollector
              "ghc_sparks"
              "The number of sparks in the local spark pool."
              GaugeType
              sparkCount
              labelpairs
      ,   \labelpairs gcstats -> do
            numCapabilities <- getNumCapabilities
            showCollector
              "ghc_capabilities"
              "The number of threads that can run truly simultaneously."
              GaugeType
              numCapabilities
              labelpairs
      ,   statsCollector
              "ghc_allocated_bytes_total"
              "Total number of bytes allocated."
              CounterType
              bytesAllocated
      ,   statsCollector
              "ghc_num_gcs"
              "The number of garbage collections performed."
              CounterType
              numGcs
      ,   statsCollector
              "ghc_max_used_bytes"
              "The maximum number of live bytes seen so far."
              GaugeType
              maxBytesUsed
      ,   statsCollector
              "ghc_cumulative_used_bytes_total"
              "The cumulative total bytes used."
              CounterType
              cumulativeBytesUsed
      ,   statsCollector
              "ghc_copied_bytes_total"
              "The number of bytes copied during garbage collection."
              CounterType
              bytesCopied
      ,   statsCollector
              "ghc_current_used_bytes"
              "The number of current live bytes."
              GaugeType
              currentBytesUsed
      ,   statsCollector
              "ghc_current_slop_bytes"
              "The current number of bytes lost to slop."
              GaugeType
              currentBytesSlop
      ,   statsCollector
              "ghc_max_slop_bytes"
              "The maximum number of bytes lost to slop so far."
              GaugeType
              maxBytesSlop
      ,   statsCollector
              "ghc_peak_allocated_megabytes" -- XXX: export as bytes?
              "The maximum number of megabytes allocated."
              GaugeType
              peakMegabytesAllocated
      ,   statsCollector
              "ghc_mutator_cpu_seconds_total"
              "The CPU time spent running mutator threads."
              CounterType
              mutatorCpuSeconds
      ,   statsCollector
              "ghc_mutator_wall_seconds_total"
              "The wall clock time spent running mutator threads."
              CounterType
              mutatorCpuSeconds
      ,   statsCollector
              "ghc_gc_cpu_seconds_total"
              "The CPU time spent running GC."
              CounterType
              gcCpuSeconds
      ,   statsCollector
              "ghc_gc_wall_seconds_total"
              "The wall clock time spent running GC."
              CounterType
              gcWallSeconds
      ,   statsCollector
              "ghc_cpu_seconds_total"
              "Total CPU time elapsed since program start."
              CounterType
              cpuSeconds
      ,   statsCollector
              "ghc_wall_seconds_total"
              "Total wall clock time elapsed since start."
              CounterType
              wallSeconds
      ,   statsCollector
              "ghc_parallel_copied_bytes_total"
              "Number of bytes copied during GC, minus space held by mutable lists held by the capabilities."
              CounterType
              parTotBytesCopied
      ,   statsCollector
              "ghc_parallel_max_copied_bytes_total"
              "Sum of number of bytes copied each GC by the most active GC thread each GC."
              CounterType
              parMaxBytesCopied
      ]
  
  #else
  
  ghcCollectors :: [LabelPairs -> RTSStats -> IO [SampleGroup]]
  ghcCollectors = [
        statsCollector
              "ghc_gcs_total"
              "Total number of GCs"
              CounterType
              gcs
      , statsCollector
              "ghc_major_gcs_total"
              "Total number of major (oldest generation) GCs"
              CounterType
              major_gcs
      , statsCollector
              "ghc_allocated_bytes_total"
              "Total bytes allocated"
              CounterType
              allocated_bytes
      , statsCollector
              "ghc_max_live_bytes"
              "Maximum live data (including large objects + compact regions)"
              GaugeType
              max_live_bytes
      , statsCollector
              "ghc_max_large_objects_bytes"
              "Maximum live data in large objects"
              GaugeType
              max_large_objects_bytes
      , statsCollector
              "ghc_max_compact_bytes"
              "Maximum live data in compact regions"
              GaugeType
              max_compact_bytes
      , statsCollector
              "ghc_max_slop_bytes"
              "Maximum slop"
              GaugeType
              max_slop_bytes
      , statsCollector
              "ghc_max_mem_in_use_bytes"
              "Maximum memory in use by the RTS"
              GaugeType
              max_mem_in_use_bytes
      , statsCollector
              "ghc_cumulative_live_bytes_total"
              "Sum of live bytes across all major GCs. Divided by major_gcs gives the average live data over the lifetime of the program."
              CounterType
              cumulative_live_bytes
      , statsCollector
              "ghc_copied_bytes_total"
              "Sum of copied_bytes across all GCs"
              CounterType
              copied_bytes
      , statsCollector
              "ghc_par_copied_bytes_total"
              "Sum of copied_bytes across all parallel GCs"
              CounterType
              par_copied_bytes
      , statsCollector
              "ghc_cumulative_par_max_copied_bytes_total"
              "Sum of par_max_copied_bytes across all parallel GCs"
              CounterType
              cumulative_par_max_copied_bytes
      , statsCollector
              "ghc_mutator_cpu_seconds_total"
              "Total CPU time used by the mutator"
              CounterType
              (rtsTimeToSeconds . mutator_cpu_ns)
      , statsCollector
              "ghc_mutator_elapsed_seconds_total"
              "Total elapsed time used by the mutator"
              CounterType
              (rtsTimeToSeconds . mutator_elapsed_ns)
      , statsCollector
              "ghc_gc_cpu_seconds_total"
              "Total CPU time used by the GC"
              CounterType
              (rtsTimeToSeconds . gc_cpu_ns)
      , statsCollector
              "ghc_gc_elapsed_seconds_total"
              "Total elapsed time used by the GC"
              CounterType
              (rtsTimeToSeconds . gc_elapsed_ns)
      , statsCollector
              "ghc_cpu_seconds_total"
              "Total CPU time (at the previous GC)"
              CounterType
              (rtsTimeToSeconds . cpu_ns)
      , statsCollector
              "ghc_elapsed_seconds_total"
              "Total elapsed time (at the previous GC)"
              CounterType
              (rtsTimeToSeconds . elapsed_ns)
  
      , statsCollector
              "ghc_gcdetails_gen"
              "The generation number of this GC"
              HistogramType -- TODO: is this correct?
                            -- Gauge makes little sense here.
                            -- With Histogram we'll be able to see which
                            -- generations are collected most often.
              (gcdetails_gen . gc)
      , statsCollector
              "ghc_gcdetails_threads"
              "Number of threads used in this GC"
              GaugeType
              (gcdetails_threads . gc)
      , statsCollector
              "ghc_gcdetails_allocated_bytes"
              "Number of bytes allocated since the previous GC"
              GaugeType -- TODO: this doesn't seem very meaningful.
              (gcdetails_allocated_bytes . gc)
      , statsCollector
              "ghc_gcdetails_live_bytes"
              "Total amount of live data in the heap (including large + compact data)"
              GaugeType
              (gcdetails_live_bytes . gc)
      , statsCollector
              "ghc_gcdetails_large_objects_bytes"
              "Total amount of live data in large objects"
              GaugeType
              (gcdetails_large_objects_bytes . gc)
      , statsCollector
              "ghc_gcdetails_compact_bytes"
              "Total amount of live data in compact regions"
              GaugeType
              (gcdetails_compact_bytes . gc)
      , statsCollector
              "ghc_gcdetails_slop_bytes"
              "Total amount of slop (wasted memory)"
              GaugeType
              (gcdetails_slop_bytes . gc)
      , statsCollector
              "ghc_gcdetails_mem_in_use_bytes"
              "Total amount of memory in use by the RTS"
              GaugeType
              (gcdetails_mem_in_use_bytes . gc)
      , statsCollector
              "ghc_gcdetails_copied_bytes"
              "Total amount of data copied during this GC"
              GaugeType -- TODO: this will also vary wildly between GCs of different generations.
              (gcdetails_copied_bytes . gc)
      , statsCollector
              "ghc_gcdetails_par_max_copied_bytes"
              "In parallel GC, the max amount of data copied by any one thread"
              GaugeType
              (gcdetails_par_max_copied_bytes . gc)
      , statsCollector
              "ghc_gcdetails_sync_elapsed_seconds"
              "The time elapsed during synchronisation before GC"
              GaugeType
              (rtsTimeToSeconds . gcdetails_sync_elapsed_ns . gc)
      , statsCollector
              "ghc_gcdetails_cpu_seconds"
              "The CPU time used during GC itself"
              GaugeType
              (rtsTimeToSeconds . gcdetails_cpu_ns . gc)
      , statsCollector
              "ghc_gcdetails_elapsed_seconds"
              "The time elapsed during GC itself"
              GaugeType
              (rtsTimeToSeconds . gcdetails_elapsed_ns . gc)
    ]
  
  -- | Convert from 'RtsTime' (nanoseconds) to seconds with nanosecond precision.
  rtsTimeToSeconds :: Stats.RtsTime -> Fixed E9
  rtsTimeToSeconds = (/ 1e9) . fromIntegral
  #endif
  
  #if __GLASGOW_HASKELL__ < 804
  statsCollector :: Show a
                 => Text -> Text -> SampleType -> (GCStats -> a) -> LabelPairs -> GCStats -> IO [SampleGroup]
  statsCollector name help sampleType stat labels gcstats =
      showCollector name help sampleType (stat gcstats) labels
  #else
  statsCollector :: Show a
                 => Text -> Text -> SampleType -> (RTSStats -> a) -> LabelPairs -> RTSStats -> IO [SampleGroup]
  statsCollector name help sampleType stat labels rtsStats =
      showCollector name help sampleType (stat rtsStats) labels
  #endif
  
  showCollector :: Show a => Text -> Text -> SampleType -> a -> LabelPairs -> IO [SampleGroup]
  showCollector name help sampleType value labels = do
      let info = Info name help
      let valueBS = BS.fromString $ show value
      return [SampleGroup info sampleType [Sample name labels valueBS]]
  