Skip to content

Speedscope export#49

Open
mjambon wants to merge 7 commits into
LexiFi:masterfrom
mjambon:speedscope-export
Open

Speedscope export#49
mjambon wants to merge 7 commits into
LexiFi:masterfrom
mjambon:speedscope-export

Conversation

@mjambon
Copy link
Copy Markdown

@mjambon mjambon commented May 19, 2026

Hello @mlasson,

On @nojb's suggestion, I added Speedscope export to Landmarks. Here's a PR that I created with AI assistance. It's ready for review. Please let me know what you think.

Supported format

Among the various formats supported by Speedscope, three of them are documented as generic, and one of them is Speedscope's own format. I decided to target it and it seems to work fine. I don't have big programs to test it on, though.

JSON support

The original implementation uses it own tiny implementation for exporting to JSON. I'm not sure why but if I assumed it's a matter of reducing external dependencies. Keeping this in mind, here's what I did:

  • I translated the original spec (commented TypeScript types that match the JSON Schema spec) to ATD types. This provides automatic mapping from OCaml types to JSON with well-defined rules to handling field renames, optional fields, etc. and ensures statically that the exported JSON complies with the type definitions.
  • Since this format is unlikely to change frequently if ever, the code generator atdml is not an Opam dependency of the landmarks package. A call to atdml speedscope_fmt.atd will regenerate speedscope_fmt.mli and speedscope_fmt.ml when the need arises. These two files are kept alongside source code in src/.
  • The generated files add a dependency on the Yojson library. If we want to avoid this dependency, I can add a simple translation layer (no need to vendor the whole Yojson project).

Example

Download tests/speedscope/profile.json and then upload it at https://www.speedscope.app/.

Code quality

I paid reasonable attention to the interfaces and to the tests. I did not review src/speedscope.ml closely. Trying it out on non-trivial example would be great.

mjambon and others added 7 commits May 19, 2026 00:11
Adds `format=speedscope` to `OCAML_LANDMARKS`, which writes a sampled
flame-graph profile openable at https://www.speedscope.app.

Implementation:
- `src/speedscope_fmt.atd` — ATD schema for the Speedscope file format
  (source of truth; regenerate with `atdml speedscope_fmt.atd`)
- `src/speedscope_fmt.ml[i]` — generated types + Yojson serialisers,
  tracked in git so the build has no atdml dependency
- `src/graph.ml` — `Graph.Speedscope` submodule: DFS over the call graph
  producing one sampled stack per node with positive self-time; uses
  `sys_time` (seconds) when collected, CPU cycles (`unit: none`) otherwise
- `src/landmark.ml[i]` — new `Speedscope` variant of `profile_format`,
  env-var parsing, at-exit hook wiring
- `src/dune` / `dune-project` — add `yojson` dependency
- `tests/speedscope/` — hand-instrumented example with pre-generated
  `profile.json` checked in for read-without-running convenience

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move Speedscope export to a standalone Speedscope module (src/speedscope.ml[i])
  instead of a submodule of Graph; expose it as Landmark.Speedscope
- Regenerate speedscope_fmt.ml[i] using atdml (fixed ATD syntax: annotations
  need '=', 'shared' is a reserved ATD keyword, 'None' shadows Stdlib.None)
- Rename ATD fields per review: unit_ → unit, dollar_schema → schema,
  shared → profile_shared; use None_ for the "none" value_unit variant
- Add link to the TypeScript spec in the ATD file header
- Wrap Speedscope doc comment in landmark.mli to ≤80 columns
- Rewrite example using PPX [@landmark] decorators with a 3-level call tree
  (main → prepare/run/summarise, run → phase_a/phase_b)
- Add deterministic test (test.ml builds a fixed Graph and exports it;
  test.out.expected is checked in and verified by dune runtest)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All applicable field and type comments from file-format-spec.ts are now
expressed as ATD doc annotations and flow through into the generated
speedscope_fmt.mli as OCaml doc comments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move long URLs in the file header onto their own indented lines
- Condense <doc text="..."> strings that exceeded the column limit
- Regenerate speedscope_fmt.ml[i]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant