mirror of https://github.com/golang/go.git
internal/trace/tracev2: add guide for trace experiments
Change-Id: I6fb354a57f3e73bd6589570868c7d68369adcf3c Reviewed-on: https://go-review.googlesource.com/c/go/+/645136 Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
49eba8b15b
commit
34e8541d24
|
|
@ -0,0 +1,101 @@
|
|||
# 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.
|
||||
Loading…
Reference in New Issue