Skip to content

Commit c460f78

Browse files
committed
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
1 parent 7cb4ab7 commit c460f78

File tree

4 files changed

+60
-3
lines changed

4 files changed

+60
-3
lines changed

ghcide/ghcide.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ library
139139
Control.Concurrent.Strict
140140
Development.IDE
141141
Development.IDE.Main
142+
Development.IDE.Main.HeapStats
142143
Development.IDE.Core.Debouncer
143144
Development.IDE.Core.FileStore
144145
Development.IDE.Core.IdeConfiguration
@@ -260,7 +261,8 @@ executable ghcide
260261
-rtsopts
261262
-- disable idle GC
262263
-- increase nursery size
263-
"-with-rtsopts=-I0 -A128M"
264+
-- Enable collection of heap statistics
265+
"-with-rtsopts="-I0 -A128M -T"
264266
main-is: Main.hs
265267
build-depends:
266268
hiedb,

ghcide/src/Development/IDE/Main.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import Development.IDE.Types.Options (IdeGhcSession,
5252
clientSupportsProgress,
5353
defaultIdeOptions)
5454
import Development.IDE.Types.Shake (Key (Key))
55+
import Development.IDE.Main.HeapStats (withHeapStats)
5556
import Development.Shake (action)
5657
import HIE.Bios.Cradle (findCradle)
5758
import Ide.Plugin.Config (CheckParents (NeverCheck),
@@ -115,7 +116,7 @@ stderrLogger = do
115116
T.hPutStrLn stderr $ "[" <> T.pack (show p) <> "] " <> m
116117

117118
defaultMain :: Arguments -> IO ()
118-
defaultMain Arguments{..} = do
119+
defaultMain Arguments{..} = withHeapStats argsLogger $ do
119120
pid <- T.pack . show <$> getProcessID
120121
logger <- argsLogger
121122
hSetBuffering stderr LineBuffering
Lines changed: 53 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@ executable haskell-language-server
294294
-rtsopts
295295
-- disable idle GC
296296
-- increase nursery size
297-
"-with-rtsopts=-I0 -A128M"
297+
-- Enable collection of heap statistics
298+
"-with-rtsopts=-I0 -A128M -T"
298299
-Wno-unticked-promoted-constructors
299300
if flag(pedantic)
300301
ghc-options: -Werror

0 commit comments

Comments
 (0)