Skip to content

Commit e1949dd

Browse files
mpickeringpepeiborrajhrcekjneirawz1000
authored
Log live_bytes and heap_size as reported by GHC.Stats (#1508)
* Log live_bytes and heap_size as reported by GHC.Stats A thread is spawned which at a prespecified interval will report the live bytes and heap size at the last major collection. Live bytes corresponds to how much live data a program has and should match closely the value reported during heap profiling. Heap size reports the total amount of memory the RTS is using, which corresponds more closely to OS memory usage. ``` [INFO] Live bytes: 367.45MB Heap size: 676.33MB ``` Closes #1493 * Update ghcide/ghcide.cabal Co-authored-by: Jan Hrcek <[email protected]> Co-authored-by: Pepe Iborra <[email protected]> Co-authored-by: Jan Hrcek <[email protected]> Co-authored-by: Javier Neira <[email protected]> Co-authored-by: wz1000 <[email protected]> Co-authored-by: Anton Latukha <[email protected]>
1 parent acdb82e commit e1949dd

File tree

4 files changed

+67
-4
lines changed

4 files changed

+67
-4
lines changed

ghcide/ghcide.cabal

+3-1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ library
146146
Development.IDE
147147
Development.IDE.Main
148148
Development.IDE.Core.Actions
149+
Development.IDE.Main.HeapStats
149150
Development.IDE.Core.Debouncer
150151
Development.IDE.Core.FileStore
151152
Development.IDE.Core.IdeConfiguration
@@ -300,7 +301,8 @@ executable ghcide
300301
-rtsopts
301302
-- disable idle GC
302303
-- increase nursery size
303-
"-with-rtsopts=-I0 -A128M"
304+
-- Enable collection of heap statistics
305+
"-with-rtsopts=-I0 -A128M -T"
304306
main-is: Main.hs
305307
build-depends:
306308
hiedb,

ghcide/src/Development/IDE/Main.hs

+6-2
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@ import Development.IDE.Types.Options (IdeGhcSession,
7777
defaultIdeOptions,
7878
optModifyDynFlags,
7979
optTesting)
80-
import Development.IDE.Types.Shake (fromKeyType)
80+
import Development.IDE.Types.Shake (Key(Key),
81+
fromKeyType)
8182
import GHC.Conc (getNumProcessors)
8283
import GHC.IO.Encoding (setLocaleEncoding)
8384
import GHC.IO.Handle (hDuplicate)
85+
import Development.IDE.Main.HeapStats (withHeapStats)
8486
import HIE.Bios.Cradle (findCradle)
8587
import qualified HieDb.Run as HieDb
8688
import Ide.Plugin.Config (CheckParents (NeverCheck),
@@ -240,7 +242,9 @@ stderrLogger logLevel = do
240242
T.hPutStrLn stderr $ "[" <> T.pack (show p) <> "] " <> m
241243

242244
defaultMain :: Arguments -> IO ()
243-
defaultMain Arguments{..} = do
245+
defaultMain Arguments{..} = flip withHeapStats fun =<< argsLogger
246+
where
247+
fun = do
244248
setLocaleEncoding utf8
245249
pid <- T.pack . show <$> getProcessID
246250
logger <- argsLogger
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{-# LANGUAGE NumericUnderscores #-}
2+
-- | Logging utilities for reporting heap statistics
3+
module Development.IDE.Main.HeapStats ( withHeapStats ) where
4+
5+
import GHC.Stats
6+
import Development.IDE.Types.Logger (Logger, logInfo)
7+
import Control.Concurrent.Async
8+
import qualified Data.Text as T
9+
import Data.Word
10+
import Control.Monad
11+
import Control.Concurrent
12+
import Text.Printf (printf)
13+
14+
-- | Interval at which to report the latest heap statistics.
15+
heapStatsInterval :: Int
16+
heapStatsInterval = 60_000_000 -- 60s
17+
18+
-- | Report the live bytes and heap size at the last major collection.
19+
logHeapStats :: Logger -> IO ()
20+
logHeapStats l = do
21+
stats <- getRTSStats
22+
-- live_bytes is the total amount of live memory in a program
23+
-- (corresponding to the amount on a heap profile)
24+
let live_bytes = gcdetails_live_bytes (gc stats)
25+
-- heap_size is the total amount of memory the RTS is using
26+
-- this corresponds closer to OS memory usage
27+
heap_size = gcdetails_mem_in_use_bytes (gc stats)
28+
format :: Word64 -> T.Text
29+
format m = T.pack (printf "%.2fMB" (fromIntegral @Word64 @Double m / 1e6))
30+
message = "Live bytes: " <> format live_bytes <> " " <>
31+
"Heap size: " <> format heap_size
32+
logInfo l message
33+
34+
-- | An action which logs heap statistics at the 'heapStatsInterval'
35+
heapStatsThread :: Logger -> IO r
36+
heapStatsThread l = forever $ do
37+
threadDelay heapStatsInterval
38+
logHeapStats l
39+
40+
-- | A helper function which lauches the 'heapStatsThread' and kills it
41+
-- appropiately when the inner action finishes. It also checks to see
42+
-- if `-T` is enabled.
43+
withHeapStats :: Logger -> IO r -> IO r
44+
withHeapStats l k = do
45+
enabled <- getRTSStatsEnabled
46+
if enabled
47+
then do
48+
logInfo l ("Logging heap statistics every "
49+
<> T.pack (printf "%.2fs" (fromIntegral @Int @Double heapStatsInterval / 1e6)))
50+
withAsync (heapStatsThread l) (const k)
51+
else do
52+
logInfo l "Heap statistics are not enabled (RTS option -T is needed)"
53+
k

haskell-language-server.cabal

+5-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,11 @@ executable haskell-language-server
345345
-rtsopts
346346
-- disable idle GC
347347
-- increase nursery size
348-
"-with-rtsopts=-I0 -A128M"
348+
-- Enable collection of heap statistics
349+
"-with-rtsopts=-I0 -A128M -T"
350+
-Wno-unticked-promoted-constructors
351+
if flag(pedantic)
352+
ghc-options: -Werror
349353

350354
build-depends:
351355
, aeson

0 commit comments

Comments
 (0)