| # 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. |