Skip to content

Commit a705897

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 9bd0928 commit a705897

File tree

4 files changed

+60
-3
lines changed

4 files changed

+60
-3
lines changed

ghcide/ghcide.cabal

+3-1
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

+2-1
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
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

+2-1
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)