Skip to content

Commit 816cd26

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 ffd5ced commit 816cd26

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
@@ -138,6 +138,7 @@ library
138138
exposed-modules:
139139
Development.IDE
140140
Development.IDE.Main
141+
Development.IDE.Main.HeapStats
141142
Development.IDE.Core.Debouncer
142143
Development.IDE.Core.FileStore
143144
Development.IDE.Core.IdeConfiguration
@@ -258,7 +259,8 @@ executable ghcide
258259
-rtsopts
259260
-- disable idle GC
260261
-- increase nursery size
261-
"-with-rtsopts=-I0 -A128M"
262+
-- Enable collection of heap statistics
263+
"-with-rtsopts="-I0 -A128M -T"
262264
main-is: Main.hs
263265
build-depends:
264266
hiedb,

ghcide/src/Development/IDE/Main.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import Development.IDE.Core.Shake (IdeState (shakeExtras),
2727
ShakeExtras (state), uses)
2828
import Development.IDE.Core.Tracing (measureMemory)
2929
import Development.IDE.Core.IdeConfiguration (registerIdeConfiguration, IdeConfiguration(..))
30+
import Development.IDE.Main.HeapStats (withHeapStats)
3031
import Development.IDE.LSP.LanguageServer (runLanguageServer)
3132
import Development.IDE.Plugin (Plugin (pluginHandlers, pluginRules))
3233
import Development.IDE.Plugin.HLS (asGhcIdePlugin)
@@ -94,7 +95,7 @@ instance Default Arguments where
9495
}
9596

9697
defaultMain :: Arguments -> IO ()
97-
defaultMain Arguments{..} = do
98+
defaultMain Arguments{..} = withHeapStats argsLogger $ do
9899
pid <- T.pack . show <$> getProcessID
99100

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

haskell-language-server.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ executable haskell-language-server
306306
-rtsopts
307307
-- disable idle GC
308308
-- increase nursery size
309-
"-with-rtsopts=-I0 -A128M"
309+
-- Enable collection of heap statistics
310+
"-with-rtsopts=-I0 -A128M -T"
310311
-Wno-unticked-promoted-constructors
311312
if flag(pedantic)
312313
ghc-options: -Werror

0 commit comments

Comments
 (0)