Skip to content

Commit 10e6d96

Browse files
authored
Additional data extraction from simulation traces (#399)
* Implemented extraction of CPU info from traces * Implemented resource extraction from traces * Implemented receipts analysis for simulation tracing * Updated logbook * Tidied haskell code formatting. * Added trace processor to CI
1 parent 71198dd commit 10e6d96

File tree

10 files changed

+716
-60
lines changed

10 files changed

+716
-60
lines changed

.github/workflows/simulation.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
- "data/**"
88
- "simulation/**"
99
- "leios-trace-hs/**"
10+
- "analysis/sims/trace-processor/**"
1011
push:
1112
branches:
1213
- main
@@ -15,6 +16,7 @@ on:
1516
- "data/**"
1617
- "simulation/**"
1718
- "leios-trace-hs/**"
19+
- "analysis/sims/trace-processor/**"
1820

1921
jobs:
2022
simulation-test:

Logbook.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
11
# Leios logbook
22

3+
## 2025-06-11
4+
5+
### Additional data analyses in Leios trace processor
6+
7+
The [`leios-trace-processor`](analysis/sims/trace-processor/) now extracts CPU, resource, and message-receipt data from simulation trace files. This eliminates the need for using the old, lower performance, scripts for analyzing simulation results.
8+
9+
```console
10+
$ cabal run exe:leios-trace-processor -- --help
11+
12+
Process Leios trace logs into CSV file abstracts
13+
14+
Usage: leios-trace-processor [--trace-file FILE] --lifecycle-file FILE
15+
--cpu-file FILE --resource-file FILE
16+
--receipt-file FILE
17+
18+
Leios trace processor
19+
20+
Available options:
21+
--trace-file FILE Input Leios simulation trace log file
22+
--lifecycle-file FILE Output CSV file for transaction lifecycle data
23+
--cpu-file FILE Output CSV file for CPU data
24+
--resource-file FILE Output CSV file for resource data
25+
--receipt-file FILE Output CSV file for receipt data
26+
-h,--help Show this help text
27+
```
28+
329
## 2025-06-09
430

531
### Fixes to CI

analysis/sims/trace-processor/app/Main.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ main :: IO ()
1313
main =
1414
do
1515
Command{..} <- O.execParser commandParser
16-
process logFile lifecycleFile
16+
process logFile lifecycleFile cpuFile resourceFile receiptFile
1717

1818
data Command = Command
1919
{ logFile :: FilePath
2020
, lifecycleFile :: FilePath
21+
, cpuFile :: FilePath
22+
, resourceFile :: FilePath
23+
, receiptFile :: FilePath
2124
}
2225
deriving (Eq, Ord, Read, Show)
2326

@@ -32,3 +35,6 @@ commandParser =
3235
Command
3336
<$> O.strOption (O.long "trace-file" <> O.metavar "FILE" <> O.value "/dev/stdin" <> O.help "Input Leios simulation trace log file")
3437
<*> O.strOption (O.long "lifecycle-file" <> O.metavar "FILE" <> O.help "Output CSV file for transaction lifecycle data")
38+
<*> O.strOption (O.long "cpu-file" <> O.metavar "FILE" <> O.help "Output CSV file for CPU data")
39+
<*> O.strOption (O.long "resource-file" <> O.metavar "FILE" <> O.help "Output CSV file for resource data")
40+
<*> O.strOption (O.long "receipt-file" <> O.metavar "FILE" <> O.help "Output CSV file for receipt data")
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{-# LANGUAGE DeriveGeneric #-}
2+
{-# LANGUAGE LambdaCase #-}
3+
{-# LANGUAGE NamedFieldPuns #-}
4+
{-# LANGUAGE OverloadedStrings #-}
5+
{-# LANGUAGE RecordWildCards #-}
6+
{-# LANGUAGE StrictData #-}
7+
{-# LANGUAGE TupleSections #-}
8+
9+
module Leios.Tracing.Cpu (
10+
cpu,
11+
) where
12+
13+
import Control.Concurrent.Chan (Chan, readChan)
14+
import Control.Monad.IO.Class (liftIO)
15+
import Control.Monad.State.Strict (StateT, execStateT, modify')
16+
import Data.Aeson (Value (Object), withObject, (.:))
17+
import Data.Aeson.Types (Parser, parseMaybe)
18+
import Data.Function (on)
19+
import Data.List (intercalate)
20+
import Data.Map.Strict (Map)
21+
import Data.Monoid (Sum (..))
22+
import Data.Text (Text)
23+
24+
import qualified Data.Map.Strict as M (insertWith, toList)
25+
import qualified Data.Text as T (unpack)
26+
27+
data ItemKey
28+
= ItemKey
29+
{ slot :: Int
30+
, node :: Text
31+
, task :: Text
32+
}
33+
deriving (Eq, Ord, Show)
34+
35+
newtype ItemInfo = ItemInfo {duration :: Sum Double}
36+
deriving (Show)
37+
38+
instance Semigroup ItemInfo where
39+
x <> y =
40+
ItemInfo
41+
{ duration = on (<>) duration x y
42+
}
43+
44+
instance Monoid ItemInfo where
45+
mempty = ItemInfo{duration = mempty}
46+
47+
toCSV :: ItemKey -> ItemInfo -> String
48+
toCSV ItemKey{..} ItemInfo{..} =
49+
intercalate
50+
sep
51+
[ show slot
52+
, T.unpack node
53+
, T.unpack task
54+
, show $ getSum duration
55+
]
56+
57+
itemHeader :: String
58+
itemHeader =
59+
intercalate
60+
sep
61+
[ "Slot"
62+
, "Node"
63+
, "Task"
64+
, "Duration [s]"
65+
]
66+
67+
sep :: String
68+
sep = ","
69+
70+
parseEvent :: Value -> Parser (ItemKey, ItemInfo)
71+
parseEvent =
72+
withObject "TraceEvent" $ \event ->
73+
do
74+
time <- event .: "time_s"
75+
message <- event .: "message"
76+
typ <- message .: "type"
77+
parseMessage typ time $ Object message
78+
79+
parseMessage :: Text -> Double -> Value -> Parser (ItemKey, ItemInfo)
80+
parseMessage "Cpu" created =
81+
withObject "Cpu" $ \message ->
82+
do
83+
let slot = floor created
84+
node <- message .: "node"
85+
task <- message .: "task_type"
86+
duration <- message .: "cpu_time_s"
87+
pure (ItemKey{..}, mempty{duration})
88+
parseMessage _ _ =
89+
const $ fail "Ignore"
90+
91+
type Index = Map ItemKey ItemInfo
92+
93+
tally :: Monad m => Value -> StateT Index m ()
94+
tally event =
95+
case parseMaybe parseEvent event of
96+
Just (itemKey, itemInfo) ->
97+
do
98+
-- Insert the generated items.
99+
modify' $ M.insertWith (<>) itemKey itemInfo
100+
Nothing -> pure ()
101+
102+
cpu :: FilePath -> Chan (Maybe Value) -> IO ()
103+
cpu cpuFile events =
104+
do
105+
let
106+
go =
107+
do
108+
liftIO (readChan events)
109+
>>= \case
110+
Nothing -> pure ()
111+
Just event -> tally event >> go
112+
index <- go `execStateT` mempty
113+
writeFile cpuFile . unlines . (itemHeader :) $
114+
uncurry toCSV <$> M.toList index

analysis/sims/trace-processor/src/Leios/Tracing/Lifecycle.hs

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE DeriveGeneric #-}
2+
{-# LANGUAGE LambdaCase #-}
23
{-# LANGUAGE NamedFieldPuns #-}
34
{-# LANGUAGE OverloadedStrings #-}
45
{-# LANGUAGE RecordWildCards #-}
@@ -9,44 +10,24 @@ module Leios.Tracing.Lifecycle (
910
lifecycle,
1011
) where
1112

13+
import Control.Concurrent.Chan (Chan, readChan)
1214
import Control.Monad ((<=<))
13-
import Control.Monad.State.Strict (State, execState, gets, modify')
14-
import Data.Aeson (FromJSON (..), Value (Object), withObject, (.:))
15+
import Control.Monad.IO.Class (liftIO)
16+
import Control.Monad.State.Strict (StateT, execStateT, gets, modify')
17+
import Data.Aeson (Value (Object), withObject, (.:))
1518
import Data.Aeson.Types (Parser, parseMaybe)
1619
import Data.Function (on)
1720
import Data.List (intercalate)
1821
import Data.Map.Strict (Map)
1922
import Data.Monoid (Sum (..))
2023
import Data.Set (Set)
2124
import Data.Text (Text)
25+
import Leios.Tracing.Util (Minimum (..))
2226

2327
import qualified Data.Map.Strict as M (elems, fromList, insertWith, restrictKeys, toList, unionWith)
2428
import qualified Data.Set as S (map, singleton)
2529
import qualified Data.Text as T (unpack)
2630

27-
newtype Earliest a = Earliest {getEarliest :: Maybe a}
28-
deriving (Show)
29-
30-
instance Eq a => Eq (Earliest a) where
31-
Earliest (Just x) == Earliest (Just y) = x == y
32-
Earliest Nothing == Earliest Nothing = True
33-
_ == _ = False
34-
35-
instance Ord a => Ord (Earliest a) where
36-
Earliest (Just x) `compare` Earliest (Just y) = x `compare` y
37-
Earliest (Just _) `compare` Earliest Nothing = LT
38-
Earliest Nothing `compare` Earliest (Just _) = GT
39-
Earliest Nothing `compare` Earliest Nothing = EQ
40-
41-
instance Ord a => Semigroup (Earliest a) where
42-
x <> y = if x < y then x else y
43-
44-
instance Ord a => Monoid (Earliest a) where
45-
mempty = Earliest Nothing
46-
47-
instance FromJSON a => FromJSON (Earliest a) where
48-
parseJSON = fmap Earliest . parseJSON
49-
5031
data ItemKey
5132
= ItemKey
5233
{ kind :: Text
@@ -56,13 +37,13 @@ data ItemKey
5637

5738
data ItemInfo
5839
= ItemInfo
59-
{ size :: Earliest Int
40+
{ size :: Minimum Int
6041
, references :: Sum Int
61-
, created :: Earliest Double
62-
, toIB :: Earliest Double
63-
, toEB :: Earliest Double
64-
, toRB :: Earliest Double
65-
, inRB :: Earliest Double
42+
, created :: Minimum Double
43+
, toIB :: Minimum Double
44+
, toEB :: Minimum Double
45+
, toRB :: Minimum Double
46+
, inRB :: Minimum Double
6647
, inIBs :: Set Text
6748
, inEBs :: Set Text
6849
}
@@ -102,13 +83,13 @@ toCSV ItemKey{..} ItemInfo{..} =
10283
sep
10384
[ T.unpack kind
10485
, T.unpack item
105-
, maybe "NA" show $ getEarliest size
86+
, show size
10687
, show $ getSum references
107-
, maybe "NA" show $ getEarliest created
108-
, maybe "NA" show $ getEarliest toIB
109-
, maybe "NA" show $ getEarliest toEB
110-
, maybe "NA" show $ getEarliest toRB
111-
, maybe "NA" show $ getEarliest inRB
88+
, show created
89+
, show toIB
90+
, show toEB
91+
, show toRB
92+
, show inRB
11293
]
11394

11495
itemHeader :: String
@@ -133,13 +114,13 @@ parseEvent :: Value -> Parser (ItemKey, ItemInfo, Index)
133114
parseEvent =
134115
withObject "TraceEvent" $ \event ->
135116
do
136-
time <- Earliest <$> event .: "time_s"
117+
time <- Minimum <$> event .: "time_s"
137118
message <- event .: "message"
138119
typ <- message .: "type"
139120
ident <- message .: "id"
140121
parseMessage typ ident time $ Object message
141122

142-
parseMessage :: Text -> Text -> Earliest Double -> Value -> Parser (ItemKey, ItemInfo, Index)
123+
parseMessage :: Text -> Text -> Minimum Double -> Value -> Parser (ItemKey, ItemInfo, Index)
143124
parseMessage "TXGenerated" item created =
144125
withObject "TXGenerated" $ \message ->
145126
do
@@ -174,7 +155,7 @@ parseMessage _ _ _ =
174155

175156
type Index = Map ItemKey ItemInfo
176157

177-
tally :: Value -> State Index ()
158+
tally :: Monad m => Value -> StateT Index m ()
178159
tally event =
179160
case parseMaybe parseEvent event of
180161
Just (itemKey, itemInfo, updates) ->
@@ -185,7 +166,7 @@ tally event =
185166
modify' $ M.unionWith (<>) updates
186167
Nothing -> pure ()
187168

188-
updateInclusions :: Text -> ItemKey -> Set Text -> State Index ()
169+
updateInclusions :: Monad m => Text -> ItemKey -> Set Text -> StateT Index m ()
189170
updateInclusions kind itemKey includers =
190171
do
191172
includers' <- gets $ M.elems . (`M.restrictKeys` S.map (ItemKey kind) includers)
@@ -198,24 +179,30 @@ updateInclusions kind itemKey includers =
198179
, toRB = mconcat $ toRB <$> includers'
199180
}
200181

201-
updateEBs :: ItemKey -> ItemInfo -> State Index ()
182+
updateEBs :: Monad m => ItemKey -> ItemInfo -> StateT Index m ()
202183
updateEBs itemKey = updateInclusions "EB" itemKey . inEBs
203184

204-
updateIBs :: ItemKey -> ItemInfo -> State Index ()
185+
updateIBs :: Monad m => ItemKey -> ItemInfo -> StateT Index m ()
205186
updateIBs itemKey = updateInclusions "IB" itemKey . inIBs
206187

207-
lifecycle :: FilePath -> [Value] -> IO ()
188+
lifecycle :: FilePath -> Chan (Maybe Value) -> IO ()
208189
lifecycle lifecycleFile events =
209-
let
210-
index =
211-
(`execState` mempty) $
190+
do
191+
let
192+
go =
193+
do
194+
liftIO (readChan events)
195+
>>= \case
196+
Nothing -> pure ()
197+
Just event -> tally event >> go
198+
index <-
199+
(`execStateT` mempty) $
212200
do
213201
-- Compute the direct metrics from the traces.
214-
mapM_ tally events
202+
go
215203
-- Update arrival in EBs and RBs for IBs.
216204
mapM_ (uncurry updateEBs) =<< gets M.toList
217205
-- Update arrival in EBs and RBs for TXs.
218206
mapM_ (uncurry updateIBs) =<< gets M.toList
219-
in
220207
writeFile lifecycleFile . unlines . (itemHeader :) $
221208
uncurry toCSV <$> M.toList index

0 commit comments

Comments
 (0)