Action Tracing
Copy MarkdownMLIR dispatches compiler work — pass execution, greedy rewrite iterations,
tiling, and other transformations — through mlir::Action. Action Tracing
observes and optionally controls those dispatches at a granularity that
pass-level IR dumps cannot reach: you see the decision to run a pass or
pattern, not just the IR before and after it.
Beaver exposes this through Beaver.MLIR.ActionTracing: attach a
context-scoped session, run a pass pipeline, drain structured events, and
optionally skip or limit actions by tag.
Comparison with existing debug output
mlir-opt --debug prints IR around pass execution and stops for human
interaction. It is useful interactively, but it serializes full IR text and is
not a data source: there is no structured record of which action ran, how deep
it was nested, or how long it took.
Beaver's pass instrumentation (MLIR.Pass timing, --mlir-timing style
output) aggregates wall time per pass. That answers how long, but not what
was attempted: an iteration that found no rewrites, a pattern that was tried
and failed, or a tiling decision that was made are all invisible.
Action Tracing sits between the two:
- Events carry
tag,depth,description, the affected IR units (operation name + location, without full IR text), and a nanosecond timestamp. start/stoppairs are correlated on the BEAM side sostopcarries adurationmeasurement.- Filters select tags or source locations up front, so unobserved actions never leave the native side.
- Skip/limit controls change what executes, enabling deterministic bisection: skip the first N occurrences of a tag and observe whether the failure reproduces.
Quick start
ctx = MLIR.Context.create()
session =
MLIR.ActionTracing.attach(ctx,
tags: ["pass-execution"],
skip: %{"pass-execution" => 1}
)
module = MLIR.Module.create!("module {}", ctx: ctx)
module |> Beaver.Composer.append("canonicalize") |> Beaver.Composer.run!()
MLIR.ActionTracing.drain(session)
#=> [%{"phase" => "before", "tag" => "pass-execution", ...}, ...]
MLIR.ActionTracing.detach(session)drain/1 returns the decoded events and also emits them as telemetry:
[:beaver, :mlir, :compilation, :action, :start][:beaver, :mlir, :compilation, :action, :stop]
Pass drain_interval_ms: to have the session drain on a timer and emit
telemetry automatically.
Filtering and control
:tags— list of action tags to observe.:locations— list of source location substrings; an action is observed when one of its context IR units carries a matching location.:skip— map of tag to a skip count; the first N occurrences are not executed.:limit— map of tag to an execution limit; further occurrences are skipped once the limit is reached.
Threading and teardown
Native observers run on MLIR worker threads and never call BEAM APIs. Events
are queued under a mutex and drained by the BEAM, so multithreaded pipelines
are safe by construction. The session is owned by the native context and is
detached automatically when the context is destroyed; detach/1 is idempotent
and safe to call from any process.
Limitations and future work
- Event pairing keys on
(tag, depth); concurrent actions at the same tag and depth from different threads may be paired out of order. - Stepping and breakpoint-based control (
Step,Next,Finish,Rerun) are not exposed yet; the observer path is intended to stabilize before those controls are added. - The supplemental C ABI (
mlir-c/Beaver/ActionTracing.h) is a private Beaver interface, not a stable public MLIR ABI.