Skip to content

Commit fe4d2bb

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 haskell#1493
1 parent c4b667e commit fe4d2bb

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-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
Development.IDE
140140
Development.IDE.Main
141141
Development.IDE.Core.Actions
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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ import Development.IDE.Types.Options (IdeGhcSession,
6464
import Development.IDE.Types.Shake (Key (Key))
6565
import GHC.IO.Encoding (setLocaleEncoding)
6666
import GHC.IO.Handle (hDuplicate)
67+
import Development.IDE.Main.HeapStats (withHeapStats)
68+
import Development.Shake (action)
6769
import HIE.Bios.Cradle (findCradle)
6870
import qualified HieDb.Run as HieDb
6971
import Ide.Plugin.Config (CheckParents (NeverCheck),
@@ -178,7 +180,7 @@ stderrLogger = do
178180
T.hPutStrLn stderr $ "[" <> T.pack (show p) <> "] " <> m
179181

180182
defaultMain :: Arguments -> IO ()
181-
defaultMain Arguments{..} = do
183+
defaultMain Arguments{..} = withHeapStats argsLogger $ do
182184
setLocaleEncoding utf8
183185
pid <- T.pack . show <$> getProcessID
184186
logger <- argsLogger
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
@@ -311,7 +311,8 @@ executable haskell-language-server
311311
-rtsopts
312312
-- disable idle GC
313313
-- increase nursery size
314-
"-with-rtsopts=-I0 -A128M"
314+
-- Enable collection of heap statistics
315+
"-with-rtsopts=-I0 -A128M -T"
315316
-Wno-unticked-promoted-constructors
316317
if flag(pedantic)
317318
ghc-options: -Werror

0 commit comments

Comments
 (0)