Skip to content

OxCaml Stack Switching - #376

Open
TheNumbat wants to merge 7 commits into
masterfrom
oxcaml-stack-switching-rebased
Open

OxCaml Stack Switching#376
TheNumbat wants to merge 7 commits into
masterfrom
oxcaml-stack-switching-rebased

Conversation

@TheNumbat

@TheNumbat TheNumbat commented Jun 15, 2026

Copy link
Copy Markdown
Member

Adds special handling for OCaml 5 effect primitives.

In OxCaml, if the ptwrite instruction is supported, the runtime will emit events that record the fiber ID upon perform and resume. Magic trace can use this ID to save and restore the fiber's callstack and exception handling state. We now also generate flow events connecting the perform and resume pair. Support for ptwrite events comes from #328.

If ptwrite events are not present, we still try to track state for the current fiber, which means programs that perform and resume with a stack discipline will work correctly. Otherwise, we still use the most recent state, so callstacks may be incorrect, but the trace still mostly works (e.g. the migrate test hits unknown return warnings, but still produces something coherent).

Adds several sample programs that exercise effects and exceptions. They all give the expected structure with ptwrite events and something coherent without.

There are currently a bunch of hardcoded offsets to detect the effect operations - I'll add these to the compiler output (alongside the exn handler info) and read them in another pr.

@TheNumbat TheNumbat mentioned this pull request Jun 15, 2026
@TheNumbat
TheNumbat marked this pull request as ready for review June 15, 2026 19:46
@TheNumbat
TheNumbat requested a review from Svetlitski June 17, 2026 18:42
@glittershark

Copy link
Copy Markdown
Member

@TheNumbat this will need to be updated for oxcaml/oxcaml#6440

Comment thread src/event.ml
}
| Ptwrite of
{ location : Location.t
; data : string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is data of type string? The ptwrite instruction can only hold a 32-bit/64-bit payload, and judging by perf_ptwrite_event_re in src/perf_decode.ml it appears perf script renders it as a number.

Comment thread src/trace_segment.ml
}
end

module Fiber = struct

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could really use a doc-comment explaining things like:

  • What does a Fiber.t represent?
  • Why do we need special handling for them?
  • etc.

Comment thread src/trace_segment.ml
; symbolizer : Symbolizer.t
; ocaml_exception_info : Ocaml_exception_info.t or_null
; exception_handlers : Frame.t Vec.t
; effect_handlers : (Frame.t * exn_depth:int) Vec.t

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc-comments explaining these fields would be great.

Comment thread src/trace_segment.ml
match Vec.last t.exception_handlers with
| This dst_frame ->
Vec.pop_back_unit_exn t.exception_handlers;
if not (Symbol.equal dst_frame.location.symbol dst.symbol)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you weaken this from an assert to a log? I'd prefer to keep this strict, getting this wrong is generally evidence of a correctness issue.

Comment thread src/trace_segment.ml
| #(maybe_frame, ~distance, ..) ->
(* We are probably raising into an exception handler much further up the stack that we never saw the entrance into. *)
let distance =
(* - Add 1 to the distance for the [_phantom_frame] we are injecting.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please restore the comment indentation to what it was previously? It looks like is an issue in most of the existing comments you moved around.

Comment thread src/trace_segment.ml
end

(* The [location], [parent], and [kind] fields are actually **immutable** except for on [Sentinel.t] instances. *)
(* The [location], [parent], and [kind] fields are actually **immutable** except for on [Sentinel.t] instances.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parent being immutable save for on Sentinel.ts (when parent is Null) was vital for correctness, because the invariant that all Frame.t chains in a trace-segment end with the same Sentinel.t is what grants you the ability to do an O(1) update to place a new parent frame at the top of stack and know that it applies to all the frames in the trace-segment. You don't get this guarantee with non-sentinel frames. Are you sure this is not broken?

Comment thread src/trace_writer_intf.ml
-> time:Time_ns.Span.t
-> unit

type flow

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: module Flow : sig type t end would be preferable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants