Skip to content

Commit 8030cda

Browse files
authored
Merge pull request #30 from talex5/gc-colours
Show GC running in red
2 parents f264b40 + 0cfac9b commit 8030cda

File tree

7 files changed

+1125
-1533
lines changed

7 files changed

+1125
-1533
lines changed

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,29 @@ Switches can be named using `Switch.run ~name`. Unnamed switches are shown as "s
101101
A context being cancelled is indicated by a vertical red line.
102102

103103
Domain-wide events, such as garbage collection and waiting for events,
104-
are shown as yellow regions behind all the fibers in the domain.
105-
Nested events get progressively darker yellow.
104+
are shown as coloured regions behind all the fibers in the domain.
105+
GC periods are shown in shades of red when running, or yellow when waiting.
106+
Yellow is also used when the domain is waiting for events outside of GC.
106107
When there are multiple domains, they are shown stacked vertically:
107108

108109
<p align='center'>
109110
<img src="./doc/gc.svg"/>
110111
</p>
111112

112113
In the above trace, the upper domain performed GC while suspended
113-
(the dark "minor" region in the top right, inside the "suspend-domain" region).
114+
(the red "minor" region in the top right, inside the "suspend-domain" region).
114115
This is possible because each domain has a "backup" thread that handles GC while the domain is suspended.
115116

117+
For minor GCs:
118+
1. The domain initiating the GC enters a "stw_leader" (stop-the-world) phase and waits for the other domains to stop.
119+
2. One by one, the other domains stop and enter "stw_api_barrier" until all domains have stopped.
120+
3. All domains perform a minor GC, clearing their minor heaps.
121+
4. They then enter a "minor_leave_barrier" phase, waiting until all domains have finished.
122+
5. Each domain returns to running application code (including GC finalizers).
123+
124+
Phases that usually involve sleeping are shown with a yellow background, but sometimes they do perform work
125+
(the trace events don't give us enough information to know in all cases).
126+
116127
## Controls
117128

118129
- F5 : reload the trace file
@@ -123,7 +134,7 @@ This is possible because each domain has a "backup" thread that handles GC while
123134

124135
## Limitations
125136

126-
- OCaml 5.1 can [deadlock when tracing multiple domains](https://github.com/ocaml/ocaml/issues/12897). This should be fixed in OCaml 5.2.
137+
- OCaml 5.1 can [deadlock when tracing multiple domains](https://github.com/ocaml/ocaml/issues/12897). This was fixed in OCaml 5.2.
127138
- Events are reported per-domain, but not per-systhread.
128139
Events generated in systhreads will get mixed up and cannot be shown correctly.
129140
They will either appear attached to whatever fiber happens to be running, or shown as domain-level events.

doc/gc.svg

Lines changed: 1053 additions & 1522 deletions
Loading

examples/domains/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(executable
2+
(name main)
3+
(libraries eio_main))

examples/domains/main.ml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(* Run jobs using an executor pool, to get a trace with multiple domains showing GC. *)
2+
3+
open Eio.Std
4+
5+
let n_jobs = 10
6+
let domain_count = 2
7+
8+
let run_job () =
9+
let items = ref [] in
10+
for _ = 1 to 1000 do
11+
items := Bytes.create 1000 :: !items
12+
done
13+
14+
let () =
15+
Eio_main.run @@ fun env ->
16+
Switch.run @@ fun sw ->
17+
let pool = Eio.Executor_pool.create ~sw ~domain_count env#domain_mgr in
18+
let jobs = List.init n_jobs (fun _ -> Eio.Executor_pool.submit_fork ~sw ~weight:1. pool run_job) in
19+
List.iter Promise.await_exn jobs

lib/layout.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ module Ring = struct
6969
}
7070

7171
type t = {
72-
events : (timestamp * string list) array;
72+
events : (timestamp * Trace.Ring.event list) array;
7373
mutable y : int;
7474
mutable height : int;
7575
mutable roots : root list;

lib/render.ml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,26 @@ module Make (C : CANVAS) = struct
239239
let w = x1 -. x0 in
240240
begin match stack with
241241
| [] -> ()
242-
| op :: p ->
243-
let g = 1.0 -. min 1.0 (0.1 *. float (List.length stack)) in
242+
| Suspend op :: p ->
243+
begin match layer with
244+
| `Bg ->
245+
let g = 0.9 in
246+
C.set_source_rgb cr ~r:g ~g:g ~b:(g /. 2.);
247+
C.rectangle cr ~x:x0 ~y ~w ~h;
248+
C.fill cr
249+
| `Fg ->
250+
if p == !prev_stack then (
251+
let clip_area = (w -. 0.2, v.height) in
252+
C.set_source_rgb cr ~r:0.0 ~g:0.0 ~b:0.0;
253+
C.paint_text cr ~x:(x0 +. 2.) ~y:(y +. 12.) op
254+
~clip_area
255+
)
256+
end
257+
| Gc op :: p ->
258+
let g = max 0.1 (0.1 *. float (List.length stack)) in
244259
match layer with
245260
| `Bg ->
246-
C.set_source_rgb cr ~r:g ~g:g ~b:(g /. 2.);
261+
C.set_source_rgb cr ~r:1.0 ~g:g ~b:(g /. 2.);
247262
C.rectangle cr ~x:x0 ~y ~w ~h;
248263
C.fill cr
249264
| `Fg ->

lib/trace.ml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,13 @@ module Ring = struct
3838
mutable cc : (timestamp * item) option;
3939
}
4040

41+
type event =
42+
| Suspend of string
43+
| Gc of string
44+
4145
type t = {
4246
mutable current_fiber : int option;
43-
mutable events : (timestamp * string list) list;
47+
mutable events : (timestamp * event list) list;
4448
mutable roots : root list;
4549
}
4650

@@ -161,7 +165,7 @@ let process_event t e =
161165
| "eio.span", name, Duration_begin ->
162166
begin match fiber_of_thread t thread with
163167
| Some fiber -> add_activation fiber timestamp (`Enter_span name)
164-
| None -> ring_of_thread t thread |> Option.iter (fun ring -> Ring.push ring timestamp name)
168+
| None -> ring_of_thread t thread |> Option.iter (fun ring -> Ring.push ring timestamp (Suspend name))
165169
end
166170
| "eio.span", _name, Duration_end ->
167171
begin match fiber_of_thread t thread with
@@ -201,7 +205,16 @@ let process_event t e =
201205
| "eio", ("suspend-domain" as phase), Duration_begin
202206
| "gc", phase, Duration_begin ->
203207
let r = ring_of_thread t thread |> Option.get in
204-
Ring.push r timestamp phase
208+
Ring.push r timestamp (
209+
match phase with
210+
| "suspend-domain"
211+
| "major_gc_stw"
212+
| "major_gc_phase_change"
213+
| "stw_api_barrier"
214+
| "minor_leave_barrier"
215+
| "stw_leader" -> Ring.Suspend phase
216+
| _ -> Ring.Gc phase
217+
)
205218
| "eio", "suspend-domain", Duration_end
206219
| "gc", _, Duration_end ->
207220
let r = ring_of_thread t thread |> Option.get in

0 commit comments

Comments
 (0)