blob: fedf7bdbdc8db74f9f564bafe9708cfa563046e4 [file] [log] [blame] [view]
# Trace experiments
Execution traces allow for trialing new events on an experimental basis via
trace experiments.
This document is a guide that explains how you can define your own trace
experiments.
Note that if you're just trying to do some debugging or perform some light
instrumentation, then a trace experiment is way overkill.
Use `runtime/trace.Log` instead.
Even if you're just trying to create a proof-of-concept for a low-frequency
event, `runtime/trace.Log` will probably be easier overall if you can make
it work.
Consider a trace experiment if:
- The volume of new trace events will be relatively high, and so the events
would benefit from a more compact representation (creating new tables to
deduplicate data, taking advantage of the varint representation, etc.).
- It's not safe to call `runtime/trace.Log` (or its runtime equivalent) in
the contexts you want to generate an event (for example, for events about
timers).
## Defining a new experiment
To define a new experiment, modify `internal/trace/tracev2` to define a
new `Experiment` enum value.
An experiment consists of two parts: timed events and experimental batches.
Timed events are events like any other and follow the same format.
They are easier to order and require less work to make use of.
Experimental batches are essentially bags of bytes that correspond to
an entire trace generation.
What they contain and how they're interpreted is totally up to you, but
they're most often useful for tables that your other events can refer into.
For example, the AllocFree experiment uses them to store type information
that allocation events can refer to.
### Defining new events
1. Define your new experiment event types (by convention, experimental events
types start at ID 127, so look for the `const` block defining events
starting there).
2. Describe your new events in `specs`.
Use the documentation for `Spec` to write your new specs, and check your
work by running the tests in the `internal/trace/tracev2` package.
If you wish for your event argument to be interpreted in a particular
way, follow the naming convention in
`src/internal/trace/tracev2/spec.go`.
For example, if you intend to emit a string argument, make sure the
argument name has the suffix `string`.
3. Add ordering and validation logic for your new events to
`src/internal/trace/order.go` by listing handlers for those events in
the `orderingDispatch` table.
If your events are always emitted in a regular user goroutine context,
then the handler should be trivial and just validate the scheduling
context to match userGoReqs.
If it's more complicated, see `(*ordering).advanceAllocFree` for a
slightly more complicated example that handles events from a larger
variety of execution environments.
If you need to encode a partial ordering, look toward the scheduler
events (names beginning with `Go`) or just ask someone for help.
4. Add your new events to the `tracev2Type2Kind` table in
`src/internal/trace/event.go`.
## Emitting data
### Emitting your new events
1. Define helper methods on `runtime.traceEventWriter` for emitting your
events.
2. Instrument the runtime with calls to these helper methods.
Make sure to call `traceAcquire` and `traceRelease` around the operation
your event represents, otherwise it will not be emitted atomically with
that operation completing, resulting in a potentially misleading trace.
### Emitting experimental batches
To emit experimental batches, use the `runtime.unsafeTraceExpWriter` to
write experimental batches associated with your experiment.
Heed the warnings and make sure that while you write them, the trace
generation cannot advance.
Note that each experiment can only have one distinguishable set of
batches.
## Recovering experimental data
### Recovering experimental events from the trace
Experimental events will appear in the event stream as an event with the
`EventExperimental` `Kind`.
Use the `Experimental` method to collect the raw data inserted into the
trace.
It's essentially up to you to interpret the event from here.
I recommend writing a thin wrapper API to present a cleaner interface if you
so desire.
### Recovering experimental batches
Parse out all the experimental batches from `Sync` events as they come.
These experimental batches are all for the same generation as all the
experimental events up until the next `Sync` event.