Skip to content

Commit a9b9bdc

Browse files
authored
Merge pull request #599 from input-output-hk/cad-2069-process-stats
CAD-2069: more efficient process stats collection
2 parents b5965e0 + 4bec799 commit a9b9bdc

File tree

7 files changed

+224
-35
lines changed

7 files changed

+224
-35
lines changed

iohk-monitoring/iohk-monitoring.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ library
3737
Cardano.BM.Counters.Common
3838
Cardano.BM.Counters.Dummy
3939

40+
Cardano.BM.Stats
41+
Cardano.BM.Stats.Resources
42+
4043
Cardano.BM.Data.Aggregated
4144
Cardano.BM.Data.AggregatedKind
4245
Cardano.BM.Data.Backend

iohk-monitoring/src/Cardano/BM/Counters/Darwin.hsc

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
{-# LANGUAGE ForeignFunctionInterface #-}
44

55
module Cardano.BM.Counters.Darwin
6-
(
7-
readCounters
6+
( readCounters
7+
, readResourceStats
88
, DiskInfo (..)
99
) where
1010

@@ -16,12 +16,14 @@ import Foreign.C.Types
1616
import Foreign.Marshal.Alloc
1717
import Foreign.Ptr
1818
import Foreign.Storable
19+
import qualified GHC.Stats as GhcStats
1920
import System.Posix.Process (getProcessID)
2021
import System.Posix.Types (ProcessID)
2122

2223
import Cardano.BM.Counters.Common (getMonoClock, readRTSStats)
2324
import Cardano.BM.Data.Observable
2425
import Cardano.BM.Data.Aggregated (Measurable(..))
26+
import Cardano.BM.Stats.Resources
2527
#endif
2628
import Cardano.BM.Data.Counter
2729
import Cardano.BM.Data.SubTrace
@@ -401,6 +403,31 @@ getMemoryInfo pid =
401403

402404

403405
#ifdef ENABLE_OBSERVABLES
406+
readResourceStats :: IO (Maybe ResourceStats)
407+
readResourceStats = getProcessID >>= \pid -> do
408+
cpu <- getCpuTimes pid
409+
rts <- GhcStats.getRTSStats
410+
mem <- getMemoryInfo pid
411+
pure . Just $
412+
Resources
413+
{ rCentiCpu = timeValToCenti (_user_time cpu)
414+
+ timeValToCenti (_system_time cpu)
415+
, rCentiGC = nsToCenti $ GhcStats.gc_cpu_ns rts
416+
, rCentiMut = nsToCenti $ GhcStats.mutator_cpu_ns rts
417+
, rGcsMajor = fromIntegral $ GhcStats.major_gcs rts
418+
, rGcsMinor = fromIntegral $ GhcStats.gcs rts - GhcStats.major_gcs rts
419+
, rAlloc = GhcStats.allocated_bytes rts
420+
, rLive = GhcStats.gcdetails_live_bytes $ GhcStats.gc rts
421+
, rRSS = fromIntegral (_resident_size mem)
422+
, rCentiBlkIO = 0
423+
, rThreads = 0
424+
}
425+
where
426+
nsToCenti :: GhcStats.RtsTime -> Word64
427+
nsToCenti = fromIntegral . (/ 10000000)
428+
timeValToCenti :: TIME_VALUE_T -> Word64
429+
timeValToCenti = fromIntegral . ceiling . (/ 10000) . usFromTimeValue
430+
404431
readSysStats :: ProcessID -> IO [Counter]
405432
readSysStats _pid = do
406433
-- sysinfo <- getSysInfo

iohk-monitoring/src/Cardano/BM/Counters/Dummy.lhs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ The only supported measurements are monotonic clock time and RTS statistics for
1111
\begin{code}
1212
{-# LANGUAGE CPP #-}
1313
module Cardano.BM.Counters.Dummy
14-
(
15-
readCounters
14+
( readCounters
15+
, readResourceStats
1616
) where
1717
1818
#ifdef ENABLE_OBSERVABLES
1919
import Cardano.BM.Counters.Common (getMonoClock, readRTSStats)
2020
import Cardano.BM.Data.Observable
21+
import Cardano.BM.Stats.Resources
2122
#endif
2223
import Cardano.BM.Data.Aggregated (Measurable(..))
2324
import Cardano.BM.Data.Counter
@@ -27,6 +28,9 @@ import Cardano.BM.Data.SubTrace
2728

2829
\label{code:Dummy.readCounters}\index{Counters!Dummy!readCounters}
2930
\begin{code}
31+
readResourceStats :: IO (Maybe ResourceStats)
32+
readResourceStats = pure . Just $ pure 0
33+
3034
readCounters :: SubTrace -> IO [Counter]
3135
readCounters NoTrace = return []
3236
readCounters Neutral = return []

iohk-monitoring/src/Cardano/BM/Counters/Linux.lhs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
%if style == newcode
66
\begin{code}
77
{-# LANGUAGE CPP #-}
8+
{-# LANGUAGE LambdaCase #-}
89
910
module Cardano.BM.Counters.Linux
10-
(
11-
readCounters
11+
( readCounters
12+
, readResourceStats
1213
) where
1314
1415
#ifdef ENABLE_OBSERVABLES
1516
import Data.Foldable (foldrM)
1617
import Data.Maybe (catMaybes)
1718
import Data.Text (Text, pack)
19+
import qualified GHC.Stats as GhcStats
1820
import System.FilePath.Posix ((</>))
1921
import System.Posix.Files (getFileStatus,fileMode,ownerReadMode,
2022
intersectFileModes)
@@ -27,6 +29,7 @@ import Text.Read (readMaybe)
2729
import Cardano.BM.Counters.Common (getMonoClock, readRTSStats)
2830
import Cardano.BM.Data.Observable
2931
import Cardano.BM.Data.Aggregated (Measurable(..))
32+
import Cardano.BM.Stats.Resources
3033
#endif
3134
import Cardano.BM.Data.Counter
3235
import Cardano.BM.Data.SubTrace
@@ -39,6 +42,34 @@ import Cardano.BM.Data.SubTrace
3942
\label{code:Linux.readCounters}\index{Counters!Linux!readCounters}
4043
\begin{code}
4144
45+
readResourceStats :: IO (Maybe ResourceStats)
46+
readResourceStats = do
47+
rts <- GhcStats.getRTSStats
48+
mkProcStats rts . fmap fromIntegral <$> readProcList "/proc/self/stat"
49+
where
50+
mkProcStats :: GhcStats.RTSStats -> [Word64] -> Maybe ResourceStats
51+
mkProcStats rts
52+
(_:_:_:_:_:_:_:_:_:_ -- 00-09
53+
:_:_:_:user:sys:_:_:_:_:threads -- 10-19
54+
:_:_:_:rss:_:_:_:_:_:_ -- 20-29
55+
:_:_:_:_:_:_:_:_:_:_ -- 30-39
56+
:_:blkio:_rest) = -- 40-42
57+
Just $ Resources
58+
{ rCentiCpu = user + sys
59+
, rCentiGC = nsToCenti $ GhcStats.gc_cpu_ns rts
60+
, rCentiMut = nsToCenti $ GhcStats.mutator_cpu_ns rts
61+
, rGcsMajor = fromIntegral $ GhcStats.major_gcs rts
62+
, rGcsMinor = fromIntegral $ GhcStats.gcs rts - GhcStats.major_gcs rts
63+
, rAlloc = GhcStats.allocated_bytes rts
64+
, rLive = GhcStats.gcdetails_live_bytes $ GhcStats.gc rts
65+
, rRSS = rss * 4096 -- TODO: this is really PAGE_SIZE.
66+
, rCentiBlkIO = blkio
67+
, rThreads = threads
68+
}
69+
mkProcStats _ _ = Nothing
70+
nsToCenti :: GhcStats.RtsTime -> Word64
71+
nsToCenti = floor . (/ (10000000 :: Double)) . fromIntegral
72+
4273
readCounters :: SubTrace -> IO [Counter]
4374
readCounters NoTrace = return []
4475
readCounters Neutral = return []
@@ -395,7 +426,17 @@ readProcStats pid = do
395426
let ticks = if length ps0 > 15 then (ps0 !! 13 + ps0 !! 14) else 0
396427
let ps1 = zip colnames ps0
397428
ps2 = [("cputicks",ticks)] <> filter (("unused" /=) . fst) ps1
398-
return $ map (\(n,i) -> Counter StatInfo n (PureI i)) ps2
429+
metricWanted = \case
430+
0 -> True -- cputicks
431+
20 -> True -- numthreads
432+
24 -> True -- rss
433+
42 -> True -- blkio
434+
_ -> False
435+
return $ catMaybes $ map (\((val,i), nr) ->
436+
if metricWanted nr
437+
then Just $ Counter StatInfo val (PureI i)
438+
else Nothing) $
439+
zip ps2 [0::Int ..]
399440
where
400441
colnames :: [Text]
401442
colnames = [ "pid","unused","unused","ppid","pgrp","session","ttynr","tpgid","flags","minflt"

iohk-monitoring/src/Cardano/BM/Counters/Windows.hsc

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
{-# LANGUAGE TypeApplications #-}
55

66
module Cardano.BM.Counters.Windows
7-
(
8-
readCounters
7+
( readCounters
8+
, readResourceStats
99
) where
1010

1111
#ifdef ENABLE_OBSERVABLES
@@ -16,12 +16,14 @@ import Foreign.C.Types
1616
import Foreign.Marshal.Alloc
1717
import Foreign.Ptr
1818
import Foreign.Storable
19+
import qualified GHC.Stats as GhcStats
1920
import System.Win32.Process (ProcessId, getCurrentProcessId)
2021
import System.Win32.Types
2122

2223
import Cardano.BM.Counters.Common (getMonoClock, readRTSStats)
2324
import Cardano.BM.Data.Observable
2425
import Cardano.BM.Data.Aggregated (Measurable(..))
26+
import Cardano.BM.Stats.Resources
2527
#endif
2628
import Cardano.BM.Data.Counter
2729
import Cardano.BM.Data.SubTrace
@@ -121,7 +123,7 @@ foreign import ccall unsafe c_get_io_counters :: Ptr IOCounters -> CInt -> IO CI
121123

122124

123125
{- system information -}
124-
{-
126+
{-
125127
typedef struct _SYSTEM_INFO {
126128
union {
127129
DWORD dwOemId; // Obsolete field...do not use
@@ -231,7 +233,7 @@ readCounters (ObservableTrace _ _) = return []
231233
#ifdef ENABLE_OBSERVABLES
232234
readProcMem :: ProcessId -> IO [Counter]
233235
readProcMem pid = do
234-
meminfo <- getMemoryInfo
236+
meminfo <- getMemoryInfo pid
235237
return [ Counter MemoryCounter "Pid" (PureI $ fromIntegral pid)
236238
, Counter MemoryCounter "WorkingSetSize" (PureI $ fromIntegral (_workingSetSize meminfo))
237239
, Counter MemoryCounter "PeakWorkingSetSize" (PureI $ fromIntegral (_peakWorkingSetSize meminfo))
@@ -243,25 +245,25 @@ readProcMem pid = do
243245
, Counter MemoryCounter "PeakPagefileUsage" (PureI $ fromIntegral (_peakPagefileUsage meminfo))
244246
, Counter MemoryCounter "PageFaultCount" (PureI $ fromIntegral (_pageFaultCount meminfo))
245247
]
246-
where
247-
getMemoryInfo :: IO ProcessMemoryCounters
248-
getMemoryInfo =
249-
allocaBytes 128 $ \ptr -> do
250-
res <- c_get_process_memory_info ptr (fromIntegral pid)
251-
if res <= 0
252-
then do
253-
putStrLn $ "c_get_process_memory_info: failure returned: " ++ (show res)
254-
return $ ProcessMemoryCounters 0 0 0 0 0 0 0 0 0 0
255-
else
256-
peek ptr
248+
249+
getMemoryInfo :: ProcessId -> IO ProcessMemoryCounters
250+
getMemoryInfo pid =
251+
allocaBytes 128 $ \ptr -> do
252+
res <- c_get_process_memory_info ptr (fromIntegral pid)
253+
if res <= 0
254+
then do
255+
putStrLn $ "c_get_process_memory_info: failure returned: " ++ (show res)
256+
return $ ProcessMemoryCounters 0 0 0 0 0 0 0 0 0 0
257+
else
258+
peek ptr
257259
#endif
258260

259261

260262
#ifdef ENABLE_OBSERVABLES
261263
readSysStats :: ProcessId -> IO [Counter]
262264
readSysStats pid = do
263265
sysinfo <- getSysInfo
264-
cputimes <- getCpuTimes
266+
cputimes <- getCpuTimes pid
265267
winbits <- getWinBits
266268
return [ Counter SysInfo "Pid" (PureI $ fromIntegral pid)
267269
, Counter SysInfo "Platform" (PureI $ fromIntegral $ fromEnum Windows)
@@ -275,7 +277,7 @@ readSysStats pid = do
275277
, Counter SysInfo "KernelTime" (Microseconds $ systime cputimes)
276278
, Counter SysInfo "CPUTime" (Microseconds $ (systime cputimes + usertime cputimes))
277279
, Counter SysInfo "IdleTime" (Microseconds $ idletime cputimes)
278-
, Counter SysInfo "WindowsPlatformBits" (PureI $ fromIntegral winbits)
280+
, Counter SysInfo "WindowsPlatformBits" (PureI $ fromIntegral winbits)
279281
]
280282
where
281283
getWinBits :: IO CInt
@@ -305,6 +307,30 @@ readSysStats pid = do
305307

306308

307309
#ifdef ENABLE_OBSERVABLES
310+
readResourceStats :: IO (Maybe ResourceStats)
311+
readResourceStats = getCurrentProcessId >>= \pid -> do
312+
cpu <- getCpuTimes pid
313+
mem <- getMemoryInfo pid
314+
rts <- GhcStats.getRTSStats
315+
pure . Just $
316+
Resources
317+
{ rCentiCpu = usecsToCenti $ usertime cpu + systime cpu
318+
, rCentiGC = nsToCenti $ GhcStats.gc_cpu_ns rts
319+
, rCentiMut = nsToCenti $ GhcStats.mutator_cpu_ns rts
320+
, rGcsMajor = fromIntegral $ GhcStats.major_gcs rts
321+
, rGcsMinor = fromIntegral $ GhcStats.gcs rts - GhcStats.major_gcs rts
322+
, rAlloc = GhcStats.allocated_bytes rts
323+
, rLive = GhcStats.gcdetails_live_bytes $ GhcStats.gc rts
324+
, rRSS = fromIntegral (_workingSetSize mem)
325+
, rCentiBlkIO = 0
326+
, rThreads = 0
327+
}
328+
where
329+
usecsToCenti :: ULONGLONG -> Word64
330+
usecsToCenti = ceiling . (/ 10000)
331+
nsToCenti :: GhcStats.RtsTime -> Word64
332+
nsToCenti = fromIntegral . (/ 10000000)
333+
308334
readProcStats :: ProcessId -> IO [Counter]
309335
readProcStats pid = do
310336
cputimes <- getCpuTimes
@@ -314,17 +340,17 @@ readProcStats pid = do
314340
, Counter StatInfo "StartTime" (Microseconds $ idletime cputimes)
315341
, Counter StatInfo "CPUTime" (Microseconds $ (systime cputimes + usertime cputimes))
316342
]
317-
where
318-
getCpuTimes :: IO CpuTimes
319-
getCpuTimes =
320-
allocaBytes 128 $ \ptr -> do
321-
res <- c_get_proc_cpu_times ptr (fromIntegral pid)
322-
if res <= 0
323-
then do
324-
putStrLn $ "c_get_proc_cpu_times: failure returned: " ++ (show res)
325-
return $ CpuTimes 0 0 0
326-
else
327-
peek ptr
343+
344+
getCpuTimes :: ProcessId -> IO CpuTimes
345+
getCpuTimes pid =
346+
allocaBytes 128 $ \ptr -> do
347+
res <- c_get_proc_cpu_times ptr (fromIntegral pid)
348+
if res <= 0
349+
then do
350+
putStrLn $ "c_get_proc_cpu_times: failure returned: " ++ (show res)
351+
return $ CpuTimes 0 0 0
352+
else
353+
peek ptr
328354

329355
#endif
330356

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{-# LANGUAGE CPP #-}
2+
module Cardano.BM.Stats
3+
( Resources
4+
, ResourceStats
5+
, Platform.readResourceStats
6+
)
7+
where
8+
9+
import Cardano.BM.Stats.Resources
10+
11+
#if defined(linux_HOST_OS)
12+
import qualified Cardano.BM.Counters.Linux as Platform
13+
#elif defined(mingw32_HOST_OS)
14+
import qualified Cardano.BM.Counters.Windows as Platform
15+
#elif defined(darwin_HOST_OS)
16+
import qualified Cardano.BM.Counters.Darwin as Platform
17+
#else
18+
import qualified Cardano.BM.Counters.Dummy as Platform
19+
#endif

0 commit comments

Comments
 (0)