Skip to content

Commit d3837ea

Browse files
authored
Merge pull request #39 from talex5/gc-stats
Add gc-stats
2 parents 4071040 + 0cd00f7 commit d3837ea

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,27 @@ eio-trace render trace.fxt
8686

8787
You can also use `--format=png` for PNG output.
8888

89+
To calculate time spent in GC:
90+
91+
```
92+
eio-trace gc-stats trace.fxt
93+
94+
./trace.fxt:
95+
96+
Ring GC/s App/s Total/s %GC
97+
0 0.273 2.258 2.530 10.78
98+
1 0.304 0.855 1.159 26.22
99+
2 0.301 0.859 1.160 25.98
100+
3 0.302 0.859 1.161 26.02
101+
4 0.301 0.858 1.159 25.96
102+
5 0.300 0.866 1.165 25.72
103+
6 0.297 0.873 1.170 25.36
104+
105+
All 2.077 7.427 9.504 21.86
106+
107+
Note: all times are wall-clock and so include time spent blocking.
108+
```
109+
89110
## Reading traces
90111

91112
Eio fibers are shown as horizontal bars.

src/gc_stats.ml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
open Eio.Std
2+
3+
module Read = Fxt.Read
4+
module Rings = Map.Make(Int)
5+
6+
let ( ++ ) = Int64.add
7+
let ( -- ) = Int64.sub
8+
9+
type ring_stats = {
10+
mutable last_event : int64;
11+
mutable gc_depth : int;
12+
mutable app_time : int64;
13+
mutable gc_time : int64;
14+
}
15+
16+
type stats = {
17+
mutable rings : ring_stats Rings.t;
18+
}
19+
20+
let pp_header f =
21+
Fmt.pf f "Ring GC/s App/s Total/s %%GC@,"
22+
23+
let pp_times f ~gc ~app =
24+
let gc = Int64.to_float gc /. 1e9 in
25+
let app = Int64.to_float app /. 1e9 in
26+
Fmt.pf f "%8.3f %8.3f %8.3f %8.2f" gc app (gc +. app)
27+
(100. *. (gc /. (gc +. app)))
28+
29+
let pp_ring f (id, stats) =
30+
Fmt.pf f "%3d " id; pp_times f ~gc:stats.gc_time ~app:stats.app_time
31+
32+
let pp_stats f { rings } =
33+
pp_header f;
34+
Fmt.(list ~sep:cut) pp_ring f (Rings.bindings rings);
35+
let gc_time = Rings.fold (fun _ ring acc -> acc ++ ring.gc_time) rings 0L in
36+
let app_time = Rings.fold (fun _ ring acc -> acc ++ ring.app_time) rings 0L in
37+
Fmt.pf f "@,@,All "; pp_times f ~gc:gc_time ~app:app_time
38+
39+
let i64 = Int64.to_int
40+
41+
let get_ring ~timestamp t ring =
42+
match Rings.find_opt ring t.rings with
43+
| Some x -> x
44+
| None ->
45+
let x = { last_event = timestamp; gc_time = 0L; app_time = 0L; gc_depth = 0 } in
46+
t.rings <- Rings.add ring x t.rings;
47+
x
48+
49+
let ring_of_thread ~timestamp t (thread : Read.thread) =
50+
let id = i64 thread.tid in
51+
if id land 3 = 1 then (
52+
let ring = id lsr 2 in
53+
Some (get_ring t ring ~timestamp)
54+
) else None
55+
56+
let process_event stats e =
57+
let { Read.Event.ty; timestamp; thread; category; name = _; args = _ } = e in
58+
match ring_of_thread stats thread ~timestamp with
59+
| None -> ()
60+
| Some ring ->
61+
let used = timestamp -- ring.last_event in
62+
if ring.gc_depth = 0 then
63+
ring.app_time <- ring.app_time ++ used
64+
else
65+
ring.gc_time <- ring.gc_time ++ used;
66+
ring.last_event <- timestamp;
67+
match category, ty with
68+
| "gc", Duration_begin -> ring.gc_depth <- ring.gc_depth + 1
69+
| "gc", Duration_end ->
70+
(* todo: we could notice if the event is a top-level one and
71+
reset the depth to 0 in that case, to cope with missed events. *)
72+
if ring.gc_depth > 0 then
73+
ring.gc_depth <- ring.gc_depth - 1
74+
else
75+
traceln "Warning: unexpected GC end event."
76+
| _ -> ()
77+
78+
let analyse reader =
79+
let stats = { rings = Rings.empty } in
80+
Read.records reader |> Seq.iter (fun (r : Read.record) ->
81+
match r with
82+
| Event e -> process_event stats e
83+
| User _ -> ()
84+
| Scheduling _ -> ()
85+
| Metadata -> ()
86+
| Kernel _ -> ()
87+
| Unknown _ -> ()
88+
);
89+
stats
90+
91+
let main out tracefiles =
92+
tracefiles |> List.iter (fun tracefile ->
93+
Eio.Path.with_open_in tracefile @@
94+
Eio.Buf_read.parse_exn ~max_size:max_int @@ fun r ->
95+
let stats = analyse r in
96+
Fmt.pf out "@[<v>%s:@,@,%a@]@." (Eio.Path.native_exn tracefile) pp_stats stats;
97+
Fmt.pf out "@.Note: all times are wall-clock and so include time spent blocking.@.";
98+
);
99+
Ok ()

src/gc_stats.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
val main :
2+
Format.formatter ->
3+
_ Eio.Path.t list ->
4+
(unit, 'a) result

src/main.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ let cmd env =
109109
"run", run ~fs ~proc_mgr $$ freq $ child_args;
110110
"show", show $$ tracefiles;
111111
"render", render $$ tracefiles $ imagefile $ fmt $ start_time $ duration;
112+
"gc-stats", Gc_stats.main Format.std_formatter $$ (List.map path $$ tracefiles);
112113
]
113114

114115
let () =

src/record.ml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ let callbacks t =
6363
~category:"gc"
6464
~ts:(Runtime_events.Timestamp.to_int64 ts)
6565
)
66+
~lifecycle:(fun ring_id ts event _data ->
67+
let ts = Runtime_events.Timestamp.to_int64 ts in
68+
let thread = ring_thread t ring_id in
69+
match event with
70+
| EV_DOMAIN_SPAWN ->
71+
Write.instant_event t.fxt ~thread ~ts ~name:"domain-spawn" ~category:"ocaml"
72+
| EV_DOMAIN_TERMINATE ->
73+
Write.instant_event t.fxt ~thread ~ts ~name:"domain-terminate" ~category:"ocaml"
74+
| _ -> ()
75+
)
6676
~lost_events:(fun ring n -> traceln "Warning: ring %d lost %d events" ring n)
6777
|> Eio_runtime_events.add_callbacks
6878
(fun ring_id ts e ->

0 commit comments

Comments
 (0)