module Ecluse.Rts (
applyRuntimePosture,
RtsPosture (..),
CgroupLimits (..),
RuntimeOverrides (..),
Provenance (..),
RuntimePlan (..),
provenanceClause,
resolveRuntimePlan,
currentRtsPosture,
readCgroupLimits,
deriveMaxHeapBytes,
deriveAllocAreaBytes,
requiredRtsFlags,
EffectiveAxis (..),
EffectiveRuntimePlan (..),
axEnforced,
reconcileRuntimePlan,
appliedRuntimePlan,
effectiveCapabilities,
effectiveHeapCeiling,
renderEffectivePosture,
renderPostureWarnings,
parseCpuMax,
parseMemoryMax,
readIfExists,
parseInactiveFile,
usePermille,
cgroupMemoryUse,
) where
import Data.Ord (clamp)
import Data.Text qualified as T
import GHC.Conc (getNumCapabilities, getNumProcessors, setNumCapabilities)
import GHC.RTS.Flags (GCFlags (maxHeapSize, minAllocAreaSize, nurseryChunkSize), getGCFlags)
import System.Environment (getEnvironment, getExecutablePath)
import System.IO.Error (isDoesNotExistError)
import System.Posix.Process (executeFile)
import UnliftIO (tryIO, tryJust)
data RtsPosture = RtsPosture
{ RtsPosture -> Int
rpCapabilities :: Int
, RtsPosture -> Int
rpProcessors :: Int
, RtsPosture -> Int
rpAllocAreaBytes :: Int
, RtsPosture -> Maybe Int
rpNurseryChunkBytes :: Maybe Int
, RtsPosture -> Maybe Int
rpMaxHeapBytes :: Maybe Int
}
deriving stock (RtsPosture -> RtsPosture -> Bool
(RtsPosture -> RtsPosture -> Bool)
-> (RtsPosture -> RtsPosture -> Bool) -> Eq RtsPosture
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RtsPosture -> RtsPosture -> Bool
== :: RtsPosture -> RtsPosture -> Bool
$c/= :: RtsPosture -> RtsPosture -> Bool
/= :: RtsPosture -> RtsPosture -> Bool
Eq, Int -> RtsPosture -> ShowS
[RtsPosture] -> ShowS
RtsPosture -> FilePath
(Int -> RtsPosture -> ShowS)
-> (RtsPosture -> FilePath)
-> ([RtsPosture] -> ShowS)
-> Show RtsPosture
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RtsPosture -> ShowS
showsPrec :: Int -> RtsPosture -> ShowS
$cshow :: RtsPosture -> FilePath
show :: RtsPosture -> FilePath
$cshowList :: [RtsPosture] -> ShowS
showList :: [RtsPosture] -> ShowS
Show)
data CgroupLimits = CgroupLimits
{ CgroupLimits -> Maybe Double
cgCpuCores :: Maybe Double
, CgroupLimits -> Maybe Int
cgMemoryMaxBytes :: Maybe Int
}
deriving stock (CgroupLimits -> CgroupLimits -> Bool
(CgroupLimits -> CgroupLimits -> Bool)
-> (CgroupLimits -> CgroupLimits -> Bool) -> Eq CgroupLimits
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CgroupLimits -> CgroupLimits -> Bool
== :: CgroupLimits -> CgroupLimits -> Bool
$c/= :: CgroupLimits -> CgroupLimits -> Bool
/= :: CgroupLimits -> CgroupLimits -> Bool
Eq, Int -> CgroupLimits -> ShowS
[CgroupLimits] -> ShowS
CgroupLimits -> FilePath
(Int -> CgroupLimits -> ShowS)
-> (CgroupLimits -> FilePath)
-> ([CgroupLimits] -> ShowS)
-> Show CgroupLimits
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CgroupLimits -> ShowS
showsPrec :: Int -> CgroupLimits -> ShowS
$cshow :: CgroupLimits -> FilePath
show :: CgroupLimits -> FilePath
$cshowList :: [CgroupLimits] -> ShowS
showList :: [CgroupLimits] -> ShowS
Show)
data RuntimeOverrides = RuntimeOverrides
{ RuntimeOverrides -> Maybe Int
roCores :: Maybe Int
, RuntimeOverrides -> Maybe Int
roCoresCeiling :: Maybe Int
, RuntimeOverrides -> Maybe Int
roMaxHeapBytes :: Maybe Int
}
deriving stock (RuntimeOverrides -> RuntimeOverrides -> Bool
(RuntimeOverrides -> RuntimeOverrides -> Bool)
-> (RuntimeOverrides -> RuntimeOverrides -> Bool)
-> Eq RuntimeOverrides
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RuntimeOverrides -> RuntimeOverrides -> Bool
== :: RuntimeOverrides -> RuntimeOverrides -> Bool
$c/= :: RuntimeOverrides -> RuntimeOverrides -> Bool
/= :: RuntimeOverrides -> RuntimeOverrides -> Bool
Eq, Int -> RuntimeOverrides -> ShowS
[RuntimeOverrides] -> ShowS
RuntimeOverrides -> FilePath
(Int -> RuntimeOverrides -> ShowS)
-> (RuntimeOverrides -> FilePath)
-> ([RuntimeOverrides] -> ShowS)
-> Show RuntimeOverrides
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RuntimeOverrides -> ShowS
showsPrec :: Int -> RuntimeOverrides -> ShowS
$cshow :: RuntimeOverrides -> FilePath
show :: RuntimeOverrides -> FilePath
$cshowList :: [RuntimeOverrides] -> ShowS
showList :: [RuntimeOverrides] -> ShowS
Show)
data Provenance
=
FromConfig
|
FromCgroup
|
FromCgroupMemory
|
FromCoresCeiling
|
FromHeapCeiling
|
FromRts
deriving stock (Provenance -> Provenance -> Bool
(Provenance -> Provenance -> Bool)
-> (Provenance -> Provenance -> Bool) -> Eq Provenance
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Provenance -> Provenance -> Bool
== :: Provenance -> Provenance -> Bool
$c/= :: Provenance -> Provenance -> Bool
/= :: Provenance -> Provenance -> Bool
Eq, Int -> Provenance -> ShowS
[Provenance] -> ShowS
Provenance -> FilePath
(Int -> Provenance -> ShowS)
-> (Provenance -> FilePath)
-> ([Provenance] -> ShowS)
-> Show Provenance
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Provenance -> ShowS
showsPrec :: Int -> Provenance -> ShowS
$cshow :: Provenance -> FilePath
show :: Provenance -> FilePath
$cshowList :: [Provenance] -> ShowS
showList :: [Provenance] -> ShowS
Show)
data RuntimePlan = RuntimePlan
{ RuntimePlan -> (Int, Provenance)
planCapabilities :: (Int, Provenance)
, RuntimePlan -> (Int, Provenance)
planAllocAreaBytes :: (Int, Provenance)
, RuntimePlan -> (Maybe Int, Provenance)
planMaxHeapBytes :: (Maybe Int, Provenance)
}
deriving stock (RuntimePlan -> RuntimePlan -> Bool
(RuntimePlan -> RuntimePlan -> Bool)
-> (RuntimePlan -> RuntimePlan -> Bool) -> Eq RuntimePlan
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RuntimePlan -> RuntimePlan -> Bool
== :: RuntimePlan -> RuntimePlan -> Bool
$c/= :: RuntimePlan -> RuntimePlan -> Bool
/= :: RuntimePlan -> RuntimePlan -> Bool
Eq, Int -> RuntimePlan -> ShowS
[RuntimePlan] -> ShowS
RuntimePlan -> FilePath
(Int -> RuntimePlan -> ShowS)
-> (RuntimePlan -> FilePath)
-> ([RuntimePlan] -> ShowS)
-> Show RuntimePlan
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RuntimePlan -> ShowS
showsPrec :: Int -> RuntimePlan -> ShowS
$cshow :: RuntimePlan -> FilePath
show :: RuntimePlan -> FilePath
$cshowList :: [RuntimePlan] -> ShowS
showList :: [RuntimePlan] -> ShowS
Show)
resolveRuntimePlan :: RuntimeOverrides -> CgroupLimits -> RtsPosture -> RuntimePlan
resolveRuntimePlan :: RuntimeOverrides -> CgroupLimits -> RtsPosture -> RuntimePlan
resolveRuntimePlan RuntimeOverrides
overrides CgroupLimits
cgroup RtsPosture
rts =
RuntimePlan
{ planCapabilities :: (Int, Provenance)
planCapabilities = (Int, Provenance)
capabilities
, planAllocAreaBytes :: (Int, Provenance)
planAllocAreaBytes = (Int, Provenance)
allocArea
, planMaxHeapBytes :: (Maybe Int, Provenance)
planMaxHeapBytes = (Maybe Int, Provenance)
maxHeap
}
where
capabilities :: (Int, Provenance)
capabilities = case (RuntimeOverrides -> Maybe Int
roCores RuntimeOverrides
overrides, CgroupLimits -> Maybe Double
cgCpuCores CgroupLimits
cgroup, CgroupLimits -> Maybe Int
cgMemoryMaxBytes CgroupLimits
cgroup) of
(Just Int
n, Maybe Double
_, Maybe Int
_) -> (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
n, Provenance
FromConfig)
(Maybe Int
Nothing, Just Double
quota, Maybe Int
_) -> (Int -> Int
visible (Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
quota), Provenance
FromCgroup)
(Maybe Int
Nothing, Maybe Double
Nothing, Just Int
memMax) ->
(Int -> Int
visible (Int -> Int -> Int
nurseryFittedCapabilities Int
memMax Int
shippedAllocAreaBytes), Provenance
FromCgroupMemory)
(Maybe Int
Nothing, Maybe Double
Nothing, Maybe Int
Nothing) ->
(Int -> Int
visible (Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
defaultCoresCeiling (RuntimeOverrides -> Maybe Int
roCoresCeiling RuntimeOverrides
overrides)), Provenance
FromCoresCeiling)
visible :: Int -> Int
visible = (Int, Int) -> Int -> Int
forall a. Ord a => (a, a) -> a -> a
clamp (Int
1, RtsPosture -> Int
rpProcessors RtsPosture
rts)
allocArea :: (Int, Provenance)
allocArea = case (CgroupLimits -> Maybe Int
cgMemoryMaxBytes CgroupLimits
cgroup, RuntimeOverrides -> Maybe Int
roMaxHeapBytes RuntimeOverrides
overrides) of
(Just Int
memMax, Just Int
ceiling') | Int
ceiling' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
memMax -> Int -> Provenance -> (Int, Provenance)
fitted Int
ceiling' Provenance
FromHeapCeiling
(Just Int
memMax, Maybe Int
_) -> Int -> Provenance -> (Int, Provenance)
fitted Int
memMax Provenance
FromCgroup
(Maybe Int
Nothing, Maybe Int
configured) -> (Int, Provenance)
-> (Int -> (Int, Provenance)) -> Maybe Int -> (Int, Provenance)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (RtsPosture -> Int
rpAllocAreaBytes RtsPosture
rts, Provenance
FromRts) (Int -> Provenance -> (Int, Provenance)
`fitted` Provenance
FromHeapCeiling) (Maybe Int
configured Maybe Int -> Maybe Int -> Maybe Int
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> RtsPosture -> Maybe Int
rpMaxHeapBytes RtsPosture
rts)
fitted :: Int -> Provenance -> (Int, Provenance)
fitted Int
bound Provenance
provenance
| RtsPosture -> Int
rpAllocAreaBytes RtsPosture
rts Int -> [Int] -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [Int
shippedAllocAreaBytes, Int
derivedArea] = (Int
derivedArea, Provenance
provenance)
| Bool
otherwise = (RtsPosture -> Int
rpAllocAreaBytes RtsPosture
rts, Provenance
FromRts)
where
derivedArea :: Int
derivedArea = Int -> Int -> Int
deriveAllocAreaBytes Int
bound ((Int, Provenance) -> Int
forall a b. (a, b) -> a
fst (Int, Provenance)
capabilities)
maxHeap :: (Maybe Int, Provenance)
maxHeap = case (RuntimeOverrides -> Maybe Int
roMaxHeapBytes RuntimeOverrides
overrides, CgroupLimits -> Maybe Int
cgMemoryMaxBytes CgroupLimits
cgroup) of
(Just Int
bytes, Maybe Int
_) -> (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Int
alignToBlock Int
bytes), Provenance
FromConfig)
(Maybe Int
Nothing, Just Int
memMax) -> (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Int -> Int
deriveMaxHeapBytes Int
memMax ((Int, Provenance) -> Int
forall a b. (a, b) -> a
fst (Int, Provenance)
allocArea)), Provenance
FromCgroup)
(Maybe Int
Nothing, Maybe Int
Nothing) -> (RtsPosture -> Maybe Int
rpMaxHeapBytes RtsPosture
rts, Provenance
FromRts)
defaultCoresCeiling :: Int
defaultCoresCeiling :: Int
defaultCoresCeiling = Int
8
nurseryFittedCapabilities :: Int -> Int -> Int
nurseryFittedCapabilities :: Int -> Int -> Int
nurseryFittedCapabilities Int
budgetBytes Int
allocAreaBytes =
Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 (Int
budgetBytes Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
nurseryCeilingShareDiv Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
allocAreaBytes)
nurseryCeilingShareDiv :: Int
nurseryCeilingShareDiv :: Int
nurseryCeilingShareDiv = Int
4
deriveAllocAreaBytes :: Int -> Int -> Int
deriveAllocAreaBytes :: Int -> Int -> Int
deriveAllocAreaBytes Int
memMax Int
capabilities =
(Int, Int) -> Int -> Int
forall a. Ord a => (a, a) -> a -> a
clamp (Int
4 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
mebibyte, Int
shippedAllocAreaBytes) Int
share Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
mebibyte Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
mebibyte
where
share :: Int
share = Int
memMax Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` (Int
nurseryShareDiv Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
capabilities)
nurseryShareDiv :: Int
nurseryShareDiv :: Int
nurseryShareDiv = Int
8
shippedAllocAreaBytes :: Int
shippedAllocAreaBytes :: Int
shippedAllocAreaBytes = Int
64 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
mebibyte
mebibyte :: Int
mebibyte :: Int
mebibyte = Int
1024 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1024
deriveMaxHeapBytes :: Int -> Int -> Int
deriveMaxHeapBytes :: Int -> Int -> Int
deriveMaxHeapBytes Int
memMax Int
allocAreaBytes =
Int -> Int
alignToBlock (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Int
memMax Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
overshoot Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
offHeap) (Int
memMax Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2))
where
overshoot :: Int
overshoot = Int
allocAreaBytes
offHeap :: Int
offHeap = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Int
32 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
mebibyte) (Int
memMax Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
8)
alignToBlock :: Int -> Int
alignToBlock :: Int -> Int
alignToBlock Int
bytes = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
rtsBlockBytes (Int
bytes Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
bytes Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
rtsBlockBytes)
data EffectiveAxis a = EffectiveAxis
{ forall a. EffectiveAxis a -> a
axDesired :: a
, forall a. EffectiveAxis a -> a
axObserved :: a
, forall a. EffectiveAxis a -> Provenance
axProvenance :: Provenance
}
deriving stock (EffectiveAxis a -> EffectiveAxis a -> Bool
(EffectiveAxis a -> EffectiveAxis a -> Bool)
-> (EffectiveAxis a -> EffectiveAxis a -> Bool)
-> Eq (EffectiveAxis a)
forall a. Eq a => EffectiveAxis a -> EffectiveAxis a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. Eq a => EffectiveAxis a -> EffectiveAxis a -> Bool
== :: EffectiveAxis a -> EffectiveAxis a -> Bool
$c/= :: forall a. Eq a => EffectiveAxis a -> EffectiveAxis a -> Bool
/= :: EffectiveAxis a -> EffectiveAxis a -> Bool
Eq, Int -> EffectiveAxis a -> ShowS
[EffectiveAxis a] -> ShowS
EffectiveAxis a -> FilePath
(Int -> EffectiveAxis a -> ShowS)
-> (EffectiveAxis a -> FilePath)
-> ([EffectiveAxis a] -> ShowS)
-> Show (EffectiveAxis a)
forall a. Show a => Int -> EffectiveAxis a -> ShowS
forall a. Show a => [EffectiveAxis a] -> ShowS
forall a. Show a => EffectiveAxis a -> FilePath
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> EffectiveAxis a -> ShowS
showsPrec :: Int -> EffectiveAxis a -> ShowS
$cshow :: forall a. Show a => EffectiveAxis a -> FilePath
show :: EffectiveAxis a -> FilePath
$cshowList :: forall a. Show a => [EffectiveAxis a] -> ShowS
showList :: [EffectiveAxis a] -> ShowS
Show)
axEnforced :: (Eq a) => EffectiveAxis a -> Bool
axEnforced :: forall a. Eq a => EffectiveAxis a -> Bool
axEnforced EffectiveAxis a
ax = EffectiveAxis a -> a
forall a. EffectiveAxis a -> a
axDesired EffectiveAxis a
ax a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== EffectiveAxis a -> a
forall a. EffectiveAxis a -> a
axObserved EffectiveAxis a
ax
data EffectiveRuntimePlan = EffectiveRuntimePlan
{ EffectiveRuntimePlan -> EffectiveAxis Int
erpCapabilities :: EffectiveAxis Int
, EffectiveRuntimePlan -> EffectiveAxis (Maybe Int)
erpMaxHeapBytes :: EffectiveAxis (Maybe Int)
, EffectiveRuntimePlan -> Int
erpAllocAreaBytes :: Int
, EffectiveRuntimePlan -> Provenance
erpAllocAreaProvenance :: Provenance
, EffectiveRuntimePlan -> Maybe Int
erpNurseryChunkBytes :: Maybe Int
, EffectiveRuntimePlan -> Maybe Int
erpContainerMemoryBytes :: Maybe Int
}
deriving stock (EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool
(EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool)
-> (EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool)
-> Eq EffectiveRuntimePlan
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool
== :: EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool
$c/= :: EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool
/= :: EffectiveRuntimePlan -> EffectiveRuntimePlan -> Bool
Eq, Int -> EffectiveRuntimePlan -> ShowS
[EffectiveRuntimePlan] -> ShowS
EffectiveRuntimePlan -> FilePath
(Int -> EffectiveRuntimePlan -> ShowS)
-> (EffectiveRuntimePlan -> FilePath)
-> ([EffectiveRuntimePlan] -> ShowS)
-> Show EffectiveRuntimePlan
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EffectiveRuntimePlan -> ShowS
showsPrec :: Int -> EffectiveRuntimePlan -> ShowS
$cshow :: EffectiveRuntimePlan -> FilePath
show :: EffectiveRuntimePlan -> FilePath
$cshowList :: [EffectiveRuntimePlan] -> ShowS
showList :: [EffectiveRuntimePlan] -> ShowS
Show)
reconcileRuntimePlan :: CgroupLimits -> RuntimePlan -> RtsPosture -> EffectiveRuntimePlan
reconcileRuntimePlan :: CgroupLimits -> RuntimePlan -> RtsPosture -> EffectiveRuntimePlan
reconcileRuntimePlan CgroupLimits
cgroup RuntimePlan
plan RtsPosture
posture =
EffectiveRuntimePlan
{ erpCapabilities :: EffectiveAxis Int
erpCapabilities =
EffectiveAxis
{ axDesired :: Int
axDesired = (Int, Provenance) -> Int
forall a b. (a, b) -> a
fst (RuntimePlan -> (Int, Provenance)
planCapabilities RuntimePlan
plan)
, axObserved :: Int
axObserved = RtsPosture -> Int
rpCapabilities RtsPosture
posture
, axProvenance :: Provenance
axProvenance = (Int, Provenance) -> Provenance
forall a b. (a, b) -> b
snd (RuntimePlan -> (Int, Provenance)
planCapabilities RuntimePlan
plan)
}
, erpMaxHeapBytes :: EffectiveAxis (Maybe Int)
erpMaxHeapBytes =
EffectiveAxis
{ axDesired :: Maybe Int
axDesired = (Maybe Int, Provenance) -> Maybe Int
forall a b. (a, b) -> a
fst (RuntimePlan -> (Maybe Int, Provenance)
planMaxHeapBytes RuntimePlan
plan)
, axObserved :: Maybe Int
axObserved = RtsPosture -> Maybe Int
rpMaxHeapBytes RtsPosture
posture
, axProvenance :: Provenance
axProvenance = (Maybe Int, Provenance) -> Provenance
forall a b. (a, b) -> b
snd (RuntimePlan -> (Maybe Int, Provenance)
planMaxHeapBytes RuntimePlan
plan)
}
, erpAllocAreaBytes :: Int
erpAllocAreaBytes = RtsPosture -> Int
rpAllocAreaBytes RtsPosture
posture
, erpAllocAreaProvenance :: Provenance
erpAllocAreaProvenance =
if RtsPosture -> Int
rpAllocAreaBytes RtsPosture
posture Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== (Int, Provenance) -> Int
forall a b. (a, b) -> a
fst (RuntimePlan -> (Int, Provenance)
planAllocAreaBytes RuntimePlan
plan) then (Int, Provenance) -> Provenance
forall a b. (a, b) -> b
snd (RuntimePlan -> (Int, Provenance)
planAllocAreaBytes RuntimePlan
plan) else Provenance
FromRts
, erpNurseryChunkBytes :: Maybe Int
erpNurseryChunkBytes = RtsPosture -> Maybe Int
rpNurseryChunkBytes RtsPosture
posture
, erpContainerMemoryBytes :: Maybe Int
erpContainerMemoryBytes = CgroupLimits -> Maybe Int
cgMemoryMaxBytes CgroupLimits
cgroup
}
appliedRuntimePlan :: CgroupLimits -> RuntimePlan -> RtsPosture -> EffectiveRuntimePlan
appliedRuntimePlan :: CgroupLimits -> RuntimePlan -> RtsPosture -> EffectiveRuntimePlan
appliedRuntimePlan CgroupLimits
cgroup RuntimePlan
plan RtsPosture
posture =
(CgroupLimits -> RuntimePlan -> RtsPosture -> EffectiveRuntimePlan
reconcileRuntimePlan CgroupLimits
cgroup RuntimePlan
plan RtsPosture
posture)
{ erpCapabilities = enforced (planCapabilities plan)
, erpMaxHeapBytes = enforced (planMaxHeapBytes plan)
, erpAllocAreaBytes = fst (planAllocAreaBytes plan)
, erpAllocAreaProvenance = snd (planAllocAreaBytes plan)
}
where
enforced :: (a, Provenance) -> EffectiveAxis a
enforced (a
v, Provenance
prov) = EffectiveAxis{axDesired :: a
axDesired = a
v, axObserved :: a
axObserved = a
v, axProvenance :: Provenance
axProvenance = Provenance
prov}
effectiveCapabilities :: EffectiveRuntimePlan -> (Int, Provenance)
effectiveCapabilities :: EffectiveRuntimePlan -> (Int, Provenance)
effectiveCapabilities EffectiveRuntimePlan
p =
let ax :: EffectiveAxis Int
ax = EffectiveRuntimePlan -> EffectiveAxis Int
erpCapabilities EffectiveRuntimePlan
p
in (EffectiveAxis Int -> Int
forall a. EffectiveAxis a -> a
axObserved EffectiveAxis Int
ax, if EffectiveAxis Int -> Bool
forall a. Eq a => EffectiveAxis a -> Bool
axEnforced EffectiveAxis Int
ax then EffectiveAxis Int -> Provenance
forall a. EffectiveAxis a -> Provenance
axProvenance EffectiveAxis Int
ax else Provenance
FromRts)
effectiveHeapCeiling :: EffectiveRuntimePlan -> (Maybe Int, Provenance)
effectiveHeapCeiling :: EffectiveRuntimePlan -> (Maybe Int, Provenance)
effectiveHeapCeiling EffectiveRuntimePlan
p =
let ax :: EffectiveAxis (Maybe Int)
ax = EffectiveRuntimePlan -> EffectiveAxis (Maybe Int)
erpMaxHeapBytes EffectiveRuntimePlan
p
in case (EffectiveAxis (Maybe Int) -> Maybe Int
forall a. EffectiveAxis a -> a
axDesired EffectiveAxis (Maybe Int)
ax, EffectiveAxis (Maybe Int) -> Maybe Int
forall a. EffectiveAxis a -> a
axObserved EffectiveAxis (Maybe Int)
ax) of
(Just Int
desired, Just Int
observed)
| Int
observed Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
desired -> (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
observed, Provenance
FromRts)
(Maybe Int
Nothing, Just Int
observed) -> (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
observed, Provenance
FromRts)
(Maybe Int
desired, Maybe Int
_) -> (Maybe Int
desired, EffectiveAxis (Maybe Int) -> Provenance
forall a. EffectiveAxis a -> Provenance
axProvenance EffectiveAxis (Maybe Int)
ax)
requiredRtsFlags :: RtsPosture -> RuntimePlan -> [Text]
requiredRtsFlags :: RtsPosture -> RuntimePlan -> [Text]
requiredRtsFlags RtsPosture
rts RuntimePlan
plan =
[Maybe Text] -> [Text]
forall a. [Maybe a] -> [a]
catMaybes [Maybe Text
capsFlag, Maybe Text
allocFlag, Maybe Text
heapFlag]
where
capsFlag :: Maybe Text
capsFlag = case RuntimePlan -> (Int, Provenance)
planCapabilities RuntimePlan
plan of
(Int
_, Provenance
FromRts) -> Maybe Text
forall a. Maybe a
Nothing
(Int
n, Provenance
_)
| Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== RtsPosture -> Int
rpCapabilities RtsPosture
rts -> Maybe Text
forall a. Maybe a
Nothing
| Bool
otherwise -> Text -> Maybe Text
forall a. a -> Maybe a
Just (Text
"-N" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
n)
allocFlag :: Maybe Text
allocFlag = case RuntimePlan -> (Int, Provenance)
planAllocAreaBytes RuntimePlan
plan of
(Int
_, Provenance
FromRts) -> Maybe Text
forall a. Maybe a
Nothing
(Int
bytes, Provenance
_)
| Int
bytes Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== RtsPosture -> Int
rpAllocAreaBytes RtsPosture
rts -> Maybe Text
forall a. Maybe a
Nothing
| Bool
otherwise -> Text -> Maybe Text
forall a. a -> Maybe a
Just (Text
"-A" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
bytes)
heapFlag :: Maybe Text
heapFlag = case RuntimePlan -> (Maybe Int, Provenance)
planMaxHeapBytes RuntimePlan
plan of
(Maybe Int
_, Provenance
FromRts) -> Maybe Text
forall a. Maybe a
Nothing
(Maybe Int
Nothing, Provenance
_) -> Maybe Text
forall a. Maybe a
Nothing
(Just Int
bytes, Provenance
_)
| Int -> Maybe Int
forall a. a -> Maybe a
Just Int
bytes Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
== RtsPosture -> Maybe Int
rpMaxHeapBytes RtsPosture
rts -> Maybe Text
forall a. Maybe a
Nothing
| Bool
otherwise -> Text -> Maybe Text
forall a. a -> Maybe a
Just (Text
"-M" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
bytes)
renderEffectivePosture :: EffectiveRuntimePlan -> [Text]
renderEffectivePosture :: EffectiveRuntimePlan -> [Text]
renderEffectivePosture EffectiveRuntimePlan
p =
[ Text
"runtime: capabilities " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
capabilities Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Provenance -> Text
renderProvenance Provenance
capsProvenance
, case EffectiveRuntimePlan -> (Maybe Int, Provenance)
effectiveHeapCeiling EffectiveRuntimePlan
p of
(Just Int
bytes, Provenance
prov) -> Text
"runtime: max heap " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
renderMiB Int
bytes Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Provenance -> Text
renderProvenance Provenance
prov
(Maybe Int
Nothing, Provenance
_) -> Text
"runtime: max heap unbounded (found no cgroup memory limit and no maxHeapBytes or -M, set runtime.maxHeapBytes (ECLUSE_RUNTIME__MAX_HEAP_BYTES) for a ceiling)"
, Text
"runtime: allocation area "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
renderMiB (EffectiveRuntimePlan -> Int
erpAllocAreaBytes EffectiveRuntimePlan
p)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/capability"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> (Int -> Text) -> Maybe Int -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" (\Int
c -> Text
", nursery chunks " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
renderMiB Int
c) (EffectiveRuntimePlan -> Maybe Int
erpNurseryChunkBytes EffectiveRuntimePlan
p)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Provenance -> Text
renderProvenance (EffectiveRuntimePlan -> Provenance
erpAllocAreaProvenance EffectiveRuntimePlan
p)
]
where
(Int
capabilities, Provenance
capsProvenance) = EffectiveRuntimePlan -> (Int, Provenance)
effectiveCapabilities EffectiveRuntimePlan
p
renderPostureWarnings :: EffectiveRuntimePlan -> [Text]
renderPostureWarnings :: EffectiveRuntimePlan -> [Text]
renderPostureWarnings EffectiveRuntimePlan
p = EffectiveRuntimePlan -> [Text]
unenforcedWarnings EffectiveRuntimePlan
p [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> EffectiveRuntimePlan -> [Text]
capabilityAdvice EffectiveRuntimePlan
p
capabilityAdvice :: EffectiveRuntimePlan -> [Text]
capabilityAdvice :: EffectiveRuntimePlan -> [Text]
capabilityAdvice EffectiveRuntimePlan
p = case EffectiveRuntimePlan -> (Int, Provenance)
effectiveCapabilities EffectiveRuntimePlan
p of
(Int
n, Provenance
FromCgroupMemory) ->
[ Text -> Text -> Text
forall {a}. (Semigroup a, IsString a) => a -> a -> a
advice
(Text
"no cgroup CPU quota binds this process, so capabilities are bounded at " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
n Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" by what the cgroup memory limit can feed")
Text
"If this container has a CPU request but no limit, set runtime.cores (ECLUSE_RUNTIME__CORES) to the whole cores requested."
]
(Int
n, Provenance
FromCoresCeiling) ->
[ Text -> Text -> Text
forall {a}. (Semigroup a, IsString a) => a -> a -> a
advice
(Text
"found no cgroup CPU or memory limit, so capabilities are capped at " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
n Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" by runtime.coresCeiling")
Text
"If a limit exists but Écluse cannot read it, set runtime.cores (ECLUSE_RUNTIME__CORES) and the heap ceiling runtime.maxHeapBytes (ECLUSE_RUNTIME__MAX_HEAP_BYTES) by hand."
]
(Int
_, Provenance
FromConfig) -> []
(Int
_, Provenance
FromCgroup) -> []
(Int
_, Provenance
FromHeapCeiling) -> []
(Int
_, Provenance
FromRts) -> []
where
advice :: a -> a -> a
advice a
reason a
suffix = a
"runtime: " a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
reason a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
". " a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
suffix
unenforcedWarnings :: EffectiveRuntimePlan -> [Text]
unenforcedWarnings :: EffectiveRuntimePlan -> [Text]
unenforcedWarnings EffectiveRuntimePlan
p =
[Maybe Text] -> [Text]
forall a. [Maybe a] -> [a]
catMaybes
[ Text -> (Int -> Text) -> EffectiveAxis Int -> Maybe Text
forall a.
Eq a =>
Text -> (a -> Text) -> EffectiveAxis a -> Maybe Text
warnAxis Text
"capabilities" Int -> Text
forall b a. (Show a, IsString b) => a -> b
show (EffectiveRuntimePlan -> EffectiveAxis Int
erpCapabilities EffectiveRuntimePlan
p)
, Text
-> (Maybe Int -> Text) -> EffectiveAxis (Maybe Int) -> Maybe Text
forall a.
Eq a =>
Text -> (a -> Text) -> EffectiveAxis a -> Maybe Text
warnAxis Text
"max heap" (Text -> (Int -> Text) -> Maybe Int -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"unbounded" Int -> Text
renderMiB) (EffectiveRuntimePlan -> EffectiveAxis (Maybe Int)
erpMaxHeapBytes EffectiveRuntimePlan
p)
]
where
warnAxis :: (Eq a) => Text -> (a -> Text) -> EffectiveAxis a -> Maybe Text
warnAxis :: forall a.
Eq a =>
Text -> (a -> Text) -> EffectiveAxis a -> Maybe Text
warnAxis Text
name a -> Text
render EffectiveAxis a
ax
| EffectiveAxis a -> Bool
forall a. Eq a => EffectiveAxis a -> Bool
axEnforced EffectiveAxis a
ax = Maybe Text
forall a. Maybe a
Nothing
| Bool
otherwise =
Text -> Maybe Text
forall a. a -> Maybe a
Just
( Text
"runtime: "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
name
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" desired "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> a -> Text
render (EffectiveAxis a -> a
forall a. EffectiveAxis a -> a
axDesired EffectiveAxis a
ax)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" but the RTS is running with "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> a -> Text
render (EffectiveAxis a -> a
forall a. EffectiveAxis a -> a
axObserved EffectiveAxis a
ax)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"; budgets use the effective value"
)
renderProvenance :: Provenance -> Text
renderProvenance :: Provenance -> Text
renderProvenance Provenance
prov = Text
" (" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Provenance -> Text
provenanceClause Provenance
prov Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")"
provenanceClause :: Provenance -> Text
provenanceClause :: Provenance -> Text
provenanceClause = \case
Provenance
FromConfig -> Text
"from config"
Provenance
FromCgroup -> Text
"derived from the cgroup limit"
Provenance
FromCgroupMemory -> Text
"bounded by the cgroup memory limit, no CPU quota set"
Provenance
FromCoresCeiling -> Text
"no cgroup CPU or memory limit found, capped at runtime.coresCeiling"
Provenance
FromHeapCeiling -> Text
"fitted to the configured heap ceiling"
Provenance
FromRts -> Text
"as the RTS resolved it"
renderMiB :: Int -> Text
renderMiB :: Int -> Text
renderMiB Int
bytes =
let mib :: Double
mib = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bytes Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double
1024 Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
1024) :: Double
in if Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
round Double
mib :: Int) Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
mib
then Int -> Text
forall b a. (Show a, IsString b) => a -> b
show (Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
round Double
mib :: Int) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" MiB"
else FilePath -> Text
forall a. ToText a => a -> Text
toText (Double -> FilePath
showRounded Double
mib) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" MiB"
showRounded :: Double -> String
showRounded :: Double -> FilePath
showRounded Double
x = Double -> FilePath
forall b a. (Show a, IsString b) => a -> b
show (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
round (Double
x Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
10) :: Int) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
10 :: Double)
parseCpuMax :: Text -> Maybe Double
parseCpuMax :: Text -> Maybe Double
parseCpuMax Text
body = case Text -> [Text]
T.words (Text -> Text
T.strip Text
body) of
[Text
quota, Text
period] -> do
q <- FilePath -> Maybe Double
forall a. Read a => FilePath -> Maybe a
readMaybe (Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
quota) :: Maybe Double
p <- readMaybe (toString period) :: Maybe Double
guard (q > 0 && p > 0)
pure (q / p)
[Text]
_ -> Maybe Double
forall a. Maybe a
Nothing
parseMemoryMax :: Text -> Maybe Int
parseMemoryMax :: Text -> Maybe Int
parseMemoryMax Text
body = do
n <- FilePath -> Maybe Int
forall a. Read a => FilePath -> Maybe a
readMaybe (Text -> FilePath
forall a. ToString a => a -> FilePath
toString (Text -> Text
T.strip Text
body)) :: Maybe Int
guard (n > 0)
pure n
parseInactiveFile :: Text -> Maybe Int
parseInactiveFile :: Text -> Maybe Int
parseInactiveFile Text
body =
[Int] -> Maybe Int
forall a. [a] -> Maybe a
listToMaybe
[ Int
n
| Text
line <- Text -> [Text]
forall t. IsText t "lines" => t -> [t]
lines Text
body
, [Text
"inactive_file", Text
value] <- [Text -> [Text]
T.words Text
line]
, Just Int
n <- [FilePath -> Maybe Int
forall a. Read a => FilePath -> Maybe a
readMaybe (Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
value)]
]
cgroupMemoryUse :: IO (IO (Maybe Int))
cgroupMemoryUse :: IO (IO (Maybe Int))
cgroupMemoryUse = do
selfCgroup <- FilePath -> IO (Maybe Text)
readIfExists FilePath
"/proc/self/cgroup"
let relative = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"/" (Maybe Text
selfCgroup Maybe Text -> (Text -> Maybe Text) -> Maybe Text
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Text
parseCgroupSelfPath)
dirs = [FilePath
cgroupRoot FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
suffix | Text
suffix <- Text -> [Text]
ancestorPaths Text
relative]
limits <- traverse (\FilePath
dir -> (Int -> (FilePath, Int)) -> Maybe Int -> Maybe (FilePath, Int)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (FilePath
dir,) (Maybe Int -> Maybe (FilePath, Int))
-> IO (Maybe Int) -> IO (Maybe (FilePath, Int))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Text -> Maybe Int) -> FilePath -> FilePath -> IO (Maybe Int)
forall a. (Text -> Maybe a) -> FilePath -> FilePath -> IO (Maybe a)
limitAt Text -> Maybe Int
parseMemoryMax FilePath
"/memory.max" FilePath
dir) dirs
pure $ case sortOn snd (catMaybes limits) of
[] -> Maybe Int -> IO (Maybe Int)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Int
forall a. Maybe a
Nothing
(FilePath
dir, Int
limit) : [(FilePath, Int)]
_ -> FilePath -> Int -> IO (Maybe Int)
readUse FilePath
dir Int
limit
readUse :: FilePath -> Int -> IO (Maybe Int)
readUse :: FilePath -> Int -> IO (Maybe Int)
readUse FilePath
dir Int
limit = do
current <- Maybe Int -> Either IOError (Maybe Int) -> Maybe Int
forall b a. b -> Either a b -> b
fromRight Maybe Int
forall a. Maybe a
Nothing (Either IOError (Maybe Int) -> Maybe Int)
-> IO (Either IOError (Maybe Int)) -> IO (Maybe Int)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO (Maybe Int) -> IO (Either IOError (Maybe Int))
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either IOError a)
tryIO ((Text -> Maybe Int) -> FilePath -> FilePath -> IO (Maybe Int)
forall a. (Text -> Maybe a) -> FilePath -> FilePath -> IO (Maybe a)
limitAt Text -> Maybe Int
parseMemoryMax FilePath
"/memory.current" FilePath
dir)
inactive <- fromRight Nothing <$> tryIO ((>>= parseInactiveFile) <$> readIfExists (dir <> "/memory.stat"))
pure (usePermille limit inactive <$> current)
usePermille :: Int -> Maybe Int -> Int -> Int
usePermille :: Int -> Maybe Int -> Int -> Int
usePermille Int
limit Maybe Int
inactive Int
current = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
current Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
0 Maybe Int
inactive) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1000 Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
limit
applyRuntimePosture :: (Text -> IO ()) -> (Text -> IO ()) -> RuntimeOverrides -> IO EffectiveRuntimePlan
applyRuntimePosture :: (Text -> IO ())
-> (Text -> IO ()) -> RuntimeOverrides -> IO EffectiveRuntimePlan
applyRuntimePosture Text -> IO ()
logInfo Text -> IO ()
logWarning RuntimeOverrides
overrides = do
rts <- IO RtsPosture
currentRtsPosture
cgroup <- readCgroupLimits
let plan = RuntimeOverrides -> CgroupLimits -> RtsPosture -> RuntimePlan
resolveRuntimePlan RuntimeOverrides
overrides CgroupLimits
cgroup RtsPosture
rts
flags = RtsPosture -> RuntimePlan -> [Text]
requiredRtsFlags RtsPosture
rts RuntimePlan
plan
alreadyApplied <- isJust <$> lookupEnv reexecMarker
case flags of
[] -> IO ()
forall (f :: * -> *). Applicative f => f ()
pass
[Text]
_ | Bool
alreadyApplied -> (Text -> IO ()) -> [Text] -> IO ()
warnStillDivergent Text -> IO ()
logWarning [Text]
flags
[Text
capsOnly]
| Text
"-N" Text -> Text -> Bool
`T.isPrefixOf` Text
capsOnly ->
Int -> IO ()
setNumCapabilities ((Int, Provenance) -> Int
forall a b. (a, b) -> a
fst (RuntimePlan -> (Int, Provenance)
planCapabilities RuntimePlan
plan))
[Text]
_ -> (Text -> IO ()) -> (Text -> IO ()) -> [Text] -> IO ()
reexecOrWarn Text -> IO ()
logInfo Text -> IO ()
logWarning [Text]
flags
applied <- currentRtsPosture
let effective = CgroupLimits -> RuntimePlan -> RtsPosture -> EffectiveRuntimePlan
reconcileRuntimePlan CgroupLimits
cgroup RuntimePlan
plan RtsPosture
applied
traverse_ logInfo (renderEffectivePosture effective)
traverse_ logWarning (renderPostureWarnings effective)
pure effective
warnStillDivergent :: (Text -> IO ()) -> [Text] -> IO ()
warnStillDivergent :: (Text -> IO ()) -> [Text] -> IO ()
warnStillDivergent Text -> IO ()
logWarning [Text]
flags =
Text -> IO ()
logWarning
( Text
"runtime: the resolved plan still requires "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
" " [Text]
flags
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" after re-launch; an operator GHCRTS may be overriding the configuration, or the RTS rejected a flag. Continuing with the live posture."
)
reexecOrWarn :: (Text -> IO ()) -> (Text -> IO ()) -> [Text] -> IO ()
reexecOrWarn :: (Text -> IO ()) -> (Text -> IO ()) -> [Text] -> IO ()
reexecOrWarn Text -> IO ()
logInfo Text -> IO ()
logWarning [Text]
flags =
IO () -> IO (Either IOError ())
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> m (Either IOError a)
tryIO ((Text -> IO ()) -> [Text] -> IO ()
reexecWith Text -> IO ()
logInfo [Text]
flags) IO (Either IOError ()) -> (Either IOError () -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left IOError
err ->
Text -> IO ()
logWarning
( Text
"runtime: re-launching to apply "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
" " [Text]
flags
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" failed ("
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> IOError -> Text
forall b a. (Show a, IsString b) => a -> b
show IOError
err
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"); continuing with the live posture, unenforced."
)
Right () -> IO ()
forall (f :: * -> *). Applicative f => f ()
pass
currentRtsPosture :: IO RtsPosture
currentRtsPosture :: IO RtsPosture
currentRtsPosture = do
capabilities <- IO Int
getNumCapabilities
processors <- getNumProcessors
gc <- getGCFlags
let blocks a
n = a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
n Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
rtsBlockBytes
pure
RtsPosture
{ rpCapabilities = capabilities
, rpProcessors = processors
, rpAllocAreaBytes = blocks (minAllocAreaSize gc)
, rpNurseryChunkBytes = nonZero (blocks (nurseryChunkSize gc))
, rpMaxHeapBytes = nonZero (blocks (maxHeapSize gc))
}
where
nonZero :: a -> Maybe a
nonZero a
n = if a
n a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= a
0 then Maybe a
forall a. Maybe a
Nothing else a -> Maybe a
forall a. a -> Maybe a
Just a
n
rtsBlockBytes :: Int
rtsBlockBytes :: Int
rtsBlockBytes = Int
4096
readCgroupLimits :: IO CgroupLimits
readCgroupLimits :: IO CgroupLimits
readCgroupLimits = do
selfCgroup <- FilePath -> IO (Maybe Text)
readIfExists FilePath
"/proc/self/cgroup"
let relative = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"/" (Maybe Text
selfCgroup Maybe Text -> (Text -> Maybe Text) -> Maybe Text
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Text
parseCgroupSelfPath)
dirs = [FilePath
cgroupRoot FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
suffix | Text
suffix <- Text -> [Text]
ancestorPaths Text
relative]
cpus <- traverse (limitAt parseCpuMax "/cpu.max") dirs
memories <- traverse (limitAt parseMemoryMax "/memory.max") dirs
pure
CgroupLimits
{ cgCpuCores = tightest cpus
, cgMemoryMaxBytes = tightest memories
}
cgroupRoot :: FilePath
cgroupRoot :: FilePath
cgroupRoot = FilePath
"/sys/fs/cgroup"
limitAt :: (Text -> Maybe a) -> String -> FilePath -> IO (Maybe a)
limitAt :: forall a. (Text -> Maybe a) -> FilePath -> FilePath -> IO (Maybe a)
limitAt Text -> Maybe a
parse FilePath
file FilePath
dir = (Maybe Text -> (Text -> Maybe a) -> Maybe a
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe a
parse) (Maybe Text -> Maybe a) -> IO (Maybe Text) -> IO (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO (Maybe Text)
readIfExists (FilePath
dir FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
file)
tightest :: (Ord a) => [Maybe a] -> Maybe a
tightest :: forall a. Ord a => [Maybe a] -> Maybe a
tightest [Maybe a]
found = case [Maybe a] -> [a]
forall a. [Maybe a] -> [a]
catMaybes [Maybe a]
found of
[] -> Maybe a
forall a. Maybe a
Nothing
(a
x : [a]
xs) -> a -> Maybe a
forall a. a -> Maybe a
Just ((a -> a -> a) -> a -> [a] -> a
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' a -> a -> a
forall a. Ord a => a -> a -> a
min a
x [a]
xs)
readIfExists :: FilePath -> IO (Maybe Text)
readIfExists :: FilePath -> IO (Maybe Text)
readIfExists FilePath
path =
Either () Text -> Maybe Text
forall l r. Either l r -> Maybe r
rightToMaybe (Either () Text -> Maybe Text)
-> IO (Either () Text) -> IO (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (IOError -> Maybe ()) -> IO Text -> IO (Either () Text)
forall (m :: * -> *) e b a.
(MonadUnliftIO m, Exception e) =>
(e -> Maybe b) -> m a -> m (Either b a)
tryJust (Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (IOError -> Bool) -> IOError -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IOError -> Bool
isDoesNotExistError) (ByteString -> Text
forall a b. ConvertUtf8 a b => b -> a
decodeUtf8 (ByteString -> Text) -> IO ByteString -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO ByteString
forall (m :: * -> *). MonadIO m => FilePath -> m ByteString
readFileBS FilePath
path)
parseCgroupSelfPath :: Text -> Maybe Text
parseCgroupSelfPath :: Text -> Maybe Text
parseCgroupSelfPath Text
body =
[Text] -> Maybe Text
forall a. [a] -> Maybe a
listToMaybe ((Text -> Maybe Text) -> [Text] -> [Text]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (Text -> Text -> Maybe Text
T.stripPrefix Text
"0::") (Text -> [Text]
forall t. IsText t "lines" => t -> [t]
lines (Text -> Text
T.strip Text
body)))
ancestorPaths :: Text -> [Text]
ancestorPaths :: Text -> [Text]
ancestorPaths Text
path = case (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null) (HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
"/" (Text -> Text
T.strip Text
path)) of
[] -> [Text
""]
[Text]
segments ->
[[Text] -> Text
T.concat [Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
seg | Text
seg <- Int -> [Text] -> [Text]
forall a. Int -> [a] -> [a]
take Int
n [Text]
segments] | Int
n <- [[Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
segments, [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
segments Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 .. Int
1]] [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> [Text
""]
reexecMarker :: String
reexecMarker :: FilePath
reexecMarker = FilePath
"__ECLUSE_RUNTIME_RTS_APPLIED"
reexecWith :: (Text -> IO ()) -> [Text] -> IO ()
reexecWith :: (Text -> IO ()) -> [Text] -> IO ()
reexecWith Text -> IO ()
logInfo [Text]
flags = do
self <- IO FilePath
getExecutablePath
args <- getArgs
env <- getEnvironment
let prior = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> b
snd ((FilePath, FilePath) -> FilePath)
-> Maybe (FilePath, FilePath) -> Maybe FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((FilePath, FilePath) -> Bool)
-> [(FilePath, FilePath)] -> Maybe (FilePath, FilePath)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
"GHCRTS") (FilePath -> Bool)
-> ((FilePath, FilePath) -> FilePath)
-> (FilePath, FilePath)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> a
fst) [(FilePath, FilePath)]
env
appended = Text -> (FilePath -> Text) -> Maybe FilePath -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
newFlags (\FilePath
p -> FilePath -> Text
forall a. ToText a => a -> Text
toText FilePath
p Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
newFlags) Maybe FilePath
prior
env' =
((FilePath
"GHCRTS", Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
appended) (FilePath, FilePath)
-> [(FilePath, FilePath)] -> [(FilePath, FilePath)]
forall a. a -> [a] -> [a]
:)
([(FilePath, FilePath)] -> [(FilePath, FilePath)])
-> ([(FilePath, FilePath)] -> [(FilePath, FilePath)])
-> [(FilePath, FilePath)]
-> [(FilePath, FilePath)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((FilePath
reexecMarker, FilePath
"1") (FilePath, FilePath)
-> [(FilePath, FilePath)] -> [(FilePath, FilePath)]
forall a. a -> [a] -> [a]
:)
([(FilePath, FilePath)] -> [(FilePath, FilePath)])
-> ([(FilePath, FilePath)] -> [(FilePath, FilePath)])
-> [(FilePath, FilePath)]
-> [(FilePath, FilePath)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((FilePath, FilePath) -> Bool)
-> [(FilePath, FilePath)] -> [(FilePath, FilePath)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(FilePath
k, FilePath
_) -> FilePath
k FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
/= FilePath
"GHCRTS" Bool -> Bool -> Bool
&& FilePath
k FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
/= FilePath
reexecMarker)
([(FilePath, FilePath)] -> [(FilePath, FilePath)])
-> [(FilePath, FilePath)] -> [(FilePath, FilePath)]
forall a b. (a -> b) -> a -> b
$ [(FilePath, FilePath)]
env
logInfo ("runtime: re-launching with GHCRTS " <> appended <> " to apply the resolved plan (same process, exec in place)")
executeFile self False args (Just env')
where
newFlags :: Text
newFlags = Text -> [Text] -> Text
T.intercalate Text
" " [Text]
flags