or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apidiff.mdconstraints.mdebnf.mderrors.mdevent.mdgorelease.mdindex.mdio-i2c.mdio-spi.mdjsonrpc2.mdmaps.mdmmap.mdmodgraphviz.mdrand.mdshiny.mdslices.mdslog.mdstats.mdsumdb.mdtrace.mdtxtar.mdtypeparams.mdutf8string.md
tile.json

trace.mddocs/

golang.org/x/exp/trace

The golang.org/x/exp/trace package provides a mechanism to collect and retrieve the most recent execution data without keeping the complete execution tracing history. It offers a flight recorder pattern for runtime tracing, enabling efficient analysis of goroutine, processor, and thread execution behavior.

Package Information

  • Package Name: golang.org/x/exp/trace
  • Package Type: Go
  • Language: Go
  • Installation: go get golang.org/x/exp/trace

Core Imports

import (
    "golang.org/x/exp/trace"
    "io"
    "time"
    "iter"
)

Basic Usage

package main

import (
    "golang.org/x/exp/trace"
    "log"
    "os"
)

func main() {
    // Create a new flight recorder
    recorder := trace.NewFlightRecorder()

    // Set the buffer size and period
    recorder.SetSize(10 * 1024 * 1024)  // 10MB
    recorder.SetPeriod(30 * time.Second) // 30 seconds

    // Start recording
    if err := recorder.Start(); err != nil {
        log.Fatal(err)
    }
    defer recorder.Stop()

    // Perform some work...

    // Write trace data to a file
    f, _ := os.Create("trace.out")
    defer f.Close()
    recorder.WriteTo(f)

    // Later, read and analyze the trace
    f, _ = os.Open("trace.out")
    defer f.Close()

    reader, _ := trace.NewReader(f)
    for {
        event, err := reader.ReadEvent()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }

        // Process event
        _ = event.Kind()
    }
}

Architecture

The golang.org/x/exp/trace package provides a high-level API for analyzing Go runtime execution traces. The main components are:

  • Flight Recorder: A circular buffer that captures the most recent execution data
  • Event Reader: Parses trace data and produces structured event objects
  • Event Types: Categorized events representing different runtime activities (goroutine state changes, processor scheduling, synchronization points, etc.)
  • Resource IDs: Identifies goroutines, processors, threads, and other runtime resources
  • Clock Synchronization: Provides reference points to correlate trace timestamps with wall-clock and monotonic clock time

Capabilities

Flight Recording

The flight recorder captures runtime execution data in a circular buffer, storing only the most recent execution history. This enables continuous monitoring without the memory overhead of complete trace histories.

type FlightRecorder struct {
    // Has unexported fields.
}

func NewFlightRecorder() *FlightRecorder
    // NewFlightRecorder creates a new flight recording configuration.
    // Flight recording holds execution trace data in a circular buffer
    // representing the most recent execution data.
    // Only one flight recording may be active at any given time.

func (r *FlightRecorder) Start() error
    // Start begins flight recording. Only one flight recorder or one call to
    // runtime/trace.Start may be active at any given time. Returns an error if
    // starting the flight recorder would violate this rule.

func (r *FlightRecorder) Stop() error
    // Stop ends flight recording. It waits until any concurrent
    // FlightRecorder.WriteTo calls exit. Returns an error if the flight recorder
    // is inactive.

func (r *FlightRecorder) Enabled() bool
    // Enabled returns true if the flight recorder is active. Specifically,
    // it will return true if Start did not return an error, and Stop has not yet
    // been called. It is safe to call from multiple goroutines simultaneously.

func (r *FlightRecorder) SetSize(bytes int)
    // SetSize sets the approximate size of the flight recorder's circular buffer.
    // This generally takes precedence over the duration passed to SetPeriod.
    // However, it does not make any guarantees on the size of the data WriteTo
    // will write. This is just a hint to the runtime to enable some control over
    // the memory overheads of tracing.
    // The initial size is implementation defined.
    // Adjustments to this value will not apply to an active flight recorder, and
    // will not apply if tracing is already enabled via trace.Start. All tracing
    // must be stopped and started again to change this value.

func (r *FlightRecorder) SetPeriod(d time.Duration)
    // SetPeriod sets the approximate time duration that the flight recorder's
    // circular buffer represents.
    // Note that SetPeriod does not make any guarantees on the amount of time the
    // trace produced by WriteTo will represent. This is just a hint to the runtime
    // to enable some control the resulting trace.
    // The initial period is implementation defined, but can be assumed to be on
    // the order of seconds.
    // Adjustments to this value will not apply to an active flight recorder, and
    // will not apply if tracing is already enabled via trace.Start. All tracing
    // must be stopped and started again to change this value.

func (r *FlightRecorder) WriteTo(w io.Writer) (total int, err error)
    // WriteTo takes a snapshots of the circular buffer's contents and writes the
    // execution data to w. Returns the number of bytes written and an error.
    // An error is returned upon failure to write to w or if the flight recorder is
    // inactive. Only one goroutine may execute WriteTo at a time, but it is safe
    // to call from multiple goroutines. If a goroutine calls WriteTo while another
    // goroutine is currently executing it, WriteTo will return ErrSnapshotActive
    // to that goroutine.

Event Reading and Analysis

The event reader parses trace data streams and produces structured event objects that can be analyzed to understand runtime behavior.

type Reader struct {
    // Has unexported fields.
}

func NewReader(r io.Reader) (*Reader, error)
    // NewReader creates a new trace reader.
    // Reader reads a byte stream, validates it, and produces trace events.
    // Provided the trace is non-empty the Reader always produces a Sync event as
    // the first event, and a Sync event as the last event. (There may also be any
    // number of Sync events in the middle, too.)

func (r *Reader) ReadEvent() (e Event, err error)
    // ReadEvent reads a single event from the stream.
    // If the stream has been exhausted, it returns an invalid event and io.EOF.

Event Types and Categories

Events represent different categories of runtime activity, from goroutine state transitions to synchronization points.

type Event struct {
    // Has unexported fields.
}

func (e Event) Kind() EventKind
    // Kind returns the kind of event that this is.

func (e Event) String() string
    // String returns the event as a human-readable string.
    // The format of the string is intended for debugging and is subject to change.

func (e Event) Time() Time
    // Time returns the timestamp of the event.

func (e Event) Goroutine() GoID
    // Goroutine returns the ID of the goroutine that was executing when this event
    // happened. It describes part of the execution context for this event.
    // Note that for goroutine state transitions this always refers to the state
    // before the transition. For example, if a goroutine is just starting
    // to run on this thread and/or proc, then this will return NoGoroutine.
    // In this case, the goroutine starting to run will be can be found at
    // Event.StateTransition().Resource.

func (e Event) Proc() ProcID
    // Proc returns the ID of the proc this event event pertains to.
    // Note that for proc state transitions this always refers to the state before
    // the transition. For example, if a proc is just starting to run on this
    // thread, then this will return NoProc.

func (e Event) Thread() ThreadID
    // Thread returns the ID of the thread this event pertains to.
    // Note that for thread state transitions this always refers to the state
    // before the transition. For example, if a thread is just starting to run,
    // then this will return NoThread.
    // Note: tracking thread state is not currently supported, so this will always
    // return a valid thread ID. However thread state transitions may be tracked in
    // the future, and callers must be robust to this possibility.

type EventKind uint16
    // EventKind indicates the kind of event this is.
    // Use this information to obtain a more specific event that allows access to
    // more detailed information.

const (
    EventBad EventKind = iota
    // EventKindSync is an event that indicates a global synchronization
    // point in the trace. At the point of a sync event, the
    // trace reader can be certain that all resources (e.g. threads,
    // goroutines) that have existed until that point have been enumerated.
    EventSync
    // EventMetric is an event that represents the value of a metric at
    // a particular point in time.
    EventMetric
    // EventLabel attaches a label to a resource.
    EventLabel
    // EventStackSample represents an execution sample, indicating what a
    // thread/proc/goroutine was doing at a particular point in time via
    // its backtrace.
    // Note: Samples should be considered a close approximation of
    // what a thread/proc/goroutine was executing at a given point in time.
    // These events may slightly contradict the situation StateTransitions
    // describe, so they should only be treated as a best-effort annotation.
    EventStackSample
    // EventRangeBegin and EventRangeEnd are a pair of generic events representing
    // a special range of time. Ranges are named and scoped to some resource
    // (identified via ResourceKind). A range that has begun but has not ended
    // is considered active.
    // EvRangeBegin and EvRangeEnd will share the same name, and an End will always
    // follow a Begin on the same instance of the resource. The associated
    // resource ID can be obtained from the Event. ResourceNone indicates the
    // range is globally scoped. That is, any goroutine/proc/thread can start or
    // stop, but only one such range may be active at any given time.
    // EventRangeActive is like EventRangeBegin, but indicates that the range was
    // already active. In this case, the resource referenced may not be in the current
    // context.
    EventRangeBegin
    EventRangeActive
    EventRangeEnd
    // EvTaskBegin and EvTaskEnd are a pair of events representing a runtime/trace.Task.
    EventTaskBegin
    EventTaskEnd
    // EventRegionBegin and EventRegionEnd are a pair of events represent a runtime/trace.Region.
    EventRegionBegin
    EventRegionEnd
    // EventLog represents a runtime/trace.Log call.
    EventLog
    // EventStateTransition represents a state change for some resource.
    EventStateTransition
    // EventExperimental is an experimental event that is unvalidated and exposed in a raw form.
    // Users are expected to understand the format and perform their own validation. These events
    // may always be safely ignored.
    EventExperimental
)

func (e EventKind) String() string
    // String returns a string form of the EventKind.

Event Details Extraction

Methods to extract detailed information from specific event types.

func (e Event) StateTransition() StateTransition
    // StateTransition returns details about a StateTransition event.
    // Panics if Kind != EventStateTransition.

func (e Event) Task() Task
    // Task returns details about a TaskBegin or TaskEnd event.
    // Panics if Kind != EventTaskBegin and Kind != EventTaskEnd.

func (e Event) Region() Region
    // Region returns details about a RegionBegin or RegionEnd event.
    // Panics if Kind != EventRegionBegin and Kind != EventRegionEnd.

func (e Event) Label() Label
    // Label returns details about a Label event.
    // Panics if Kind != EventLabel.

func (e Event) Range() Range
    // Range returns details about an EventRangeBegin, EventRangeActive,
    // or EventRangeEnd event.
    // Panics if Kind != EventRangeBegin, Kind != EventRangeActive, and Kind !=
    // EventRangeEnd.

func (e Event) RangeAttributes() []RangeAttribute
    // RangeAttributes returns attributes for a completed range.
    // Panics if Kind != EventRangeEnd.

func (e Event) Metric() Metric
    // Metric returns details about a Metric event.
    // Panics if Kind != EventMetric.

func (e Event) Log() Log
    // Log returns details about a Log event.
    // Panics if Kind != EventLog.

func (e Event) Sync() Sync
    // Sync returns details that are relevant for the following events, up to but
    // excluding the next EventSync event.

func (e Event) Experimental() ExperimentalEvent
    // Experimental returns a view of the raw event for an experimental event.
    // Panics if Kind != EventExperimental.

func (e Event) Stack() Stack
    // Stack returns a handle to a stack associated with the event.
    // This represents a stack trace at the current moment in time for the current
    // execution context.

Goroutine State Management

Types and functions for tracking goroutine states and execution context.

type GoID int64
    // GoID is the runtime-internal G structure's goid field. This is unique for
    // each goroutine.

type GoState uint8
    // GoState represents the state of a goroutine.
    // New GoStates may be added in the future. Users of this type must be robust
    // to that possibility.

const (
    GoUndetermined GoState = iota
    // No information is known about the goroutine.
    GoNotExist
    // Goroutine does not exist.
    GoRunnable
    // Goroutine is runnable but not running.
    GoRunning
    // Goroutine is running.
    GoWaiting
    // Goroutine is waiting on something to happen.
    GoSyscall
    // Goroutine is in a system call.
)

func (s GoState) Executing() bool
    // Executing returns true if the state indicates that the goroutine is
    // executing and bound to its thread.

func (s GoState) String() string
    // String returns a human-readable representation of a GoState.
    // The format of the returned string is for debugging purposes and is subject
    // to change.

const NoGoroutine = GoID(-1)
    // NoGoroutine indicates that the relevant events don't correspond to any
    // goroutine in particular.

Processor State Management

Types and functions for tracking processor states in the Go runtime.

type ProcID int64
    // ProcID is the runtime-internal G structure's id field. This is unique for
    // each P.

type ProcState uint8
    // ProcState represents the state of a proc.
    // New ProcStates may be added in the future. Users of this type must be robust
    // to that possibility.

const (
    ProcUndetermined ProcState = iota
    // No information is known about the proc.
    ProcNotExist
    // Proc does not exist.
    ProcRunning
    // Proc is running.
    ProcIdle
    // Proc is idle.
)

func (s ProcState) Executing() bool
    // Executing returns true if the state indicates that the proc is executing and
    // bound to its thread.

func (s ProcState) String() string
    // String returns a human-readable representation of a ProcState.
    // The format of the returned string is for debugging purposes and is subject
    // to change.

const NoProc = ProcID(-1)
    // NoProc indicates that the relevant events don't correspond to any P in
    // particular.

Thread Management

Types for identifying and working with OS threads in the runtime.

type ThreadID int64
    // ThreadID is the runtime-internal M structure's ID. This is unique for each
    // OS thread.

const NoThread = ThreadID(-1)
    // NoThread indicates that the relevant events don't correspond to any thread
    // in particular.

Resource Identification and State Transitions

Unified mechanisms for identifying and tracking state changes of runtime resources.

type ResourceID struct {
    // Kind is the kind of resource this ID is for.
    Kind ResourceKind
    // Has unexported fields.
}
    // ResourceID represents a generic resource ID.

func MakeResourceID[T interface{ GoID | ProcID | ThreadID }](id T) ResourceID
    // MakeResourceID creates a general resource ID from a specific resource's ID.

func (r ResourceID) Goroutine() GoID
    // Goroutine obtains a GoID from the resource ID.
    // r.Kind must be ResourceGoroutine or this function will panic.

func (r ResourceID) Proc() ProcID
    // Proc obtains a ProcID from the resource ID.
    // r.Kind must be ResourceProc or this function will panic.

func (r ResourceID) Thread() ThreadID
    // Thread obtains a ThreadID from the resource ID.
    // r.Kind must be ResourceThread or this function will panic.

func (r ResourceID) String() string
    // String returns a human-readable string representation of the ResourceID.
    // This representation is subject to change and is intended primarily for
    // debugging.

type ResourceKind uint8
    // ResourceKind indicates a kind of resource that has a state machine.
    // New ResourceKinds may be added in the future. Users of this type must be
    // robust to that possibility.

const (
    ResourceNone ResourceKind = iota
    // No resource.
    ResourceGoroutine
    // Goroutine.
    ResourceProc
    // Proc.
    ResourceThread
    // Thread.
)

func (r ResourceKind) String() string
    // String returns a human-readable representation of a ResourceKind.
    // The format of the returned string is for debugging purposes and is subject
    // to change.

type StateTransition struct {
    // Resource is the resource this state transition is for.
    Resource ResourceID
    // Reason is a human-readable reason for the state transition.
    Reason string
    // Stack is the stack trace of the resource making the state transition.
    // This is distinct from the result (Event).Stack because it pertains to
    // the transitioning resource, not any of the ones executing the event
    // this StateTransition came from.
    // An example of this difference is the NotExist -> Runnable transition for
    // goroutines, which indicates goroutine creation. In this particular case,
    // a Stack here would refer to the starting stack of the new goroutine, and
    // an (Event).Stack would refer to the stack trace of whoever created the
    // goroutine.
    Stack Stack
    // Has unexported fields.
}
    // StateTransition provides details about a StateTransition event.

func (d StateTransition) Goroutine() (from, to GoState)
    // Goroutine returns the state transition for a goroutine.
    // Transitions to and from states that are Executing are special in that they
    // change the future execution context. In other words, future events on the
    // same thread will feature the same goroutine until it stops running.
    // Panics if d.Resource.Kind is not ResourceGoroutine.

func (d StateTransition) Proc() (from, to ProcState)
    // Proc returns the state transition for a proc.
    // Transitions to and from states that are Executing are special in that they
    // change the future execution context. In other words, future events on the
    // same thread will feature the same goroutine until it stops running.
    // Panics if d.Resource.Kind is not ResourceProc.

Task and Region Tracking

Types for tracking user-defined tasks and regions in the trace.

type TaskID uint64
    // TaskID is the internal ID of a task used to disambiguate tasks (even if they
    // are of the same type).

const (
    NoTask = TaskID(^uint64(0))
    // NoTask indicates the lack of a task.
    BackgroundTask = TaskID(0)
    // BackgroundTask is the global task that events are attached to if there was
    // no other task in the context at the point the event was emitted.
)

type Task struct {
    // ID is a unique identifier for the task.
    // This can be used to associate the beginning of a task with its end.
    ID TaskID
    // ParentID is the ID of the parent task.
    Parent TaskID
    // Type is the taskType that was passed to runtime/trace.NewTask.
    // May be "" if a task's TaskBegin event isn't present in the trace.
    Type string
}
    // Task provides details about a Task event.

type Region struct {
    // Task is the ID of the task this region is associated with.
    Task TaskID
    // Type is the regionType that was passed to runtime/trace.StartRegion or runtime/trace.WithRegion.
    Type string
}
    // Region provides details about a Region event.

type Range struct {
    // Name is a human-readable name for the range.
    // This name can be used to identify the end of the range for the resource
    // its scoped to, because only one of each type of range may be active on
    // a particular resource. The relevant resource should be obtained from the
    // Event that produced these details. The corresponding RangeEnd will have
    // an identical name.
    Name string
    // Scope is the resource that the range is scoped to.
    // For example, a ResourceGoroutine scope means that the same goroutine
    // must have a start and end for the range, and that goroutine can only
    // have one range of a particular name active at any given time. The
    // ID that this range is scoped to may be obtained via Event.Goroutine.
    // The ResourceNone scope means that the range is globally scoped. As a
    // result, any goroutine/proc/thread may start or end the range, and only
    // one such named range may be active globally at any given time.
    // For RangeBegin and RangeEnd events, this will always reference some
    // resource ID in the current execution context. For RangeActive events,
    // this may reference a resource not in the current context. Prefer Scope
    // over the current execution context.
    Scope ResourceID
}
    // Range provides details about a Range event.

type RangeAttribute struct {
    // Name is the human-readable name for the range.
    Name string
    // Value is the value of the attribute.
    Value Value
}
    // RangeAttributes provides attributes about a completed Range.

Logging and Labels

Types for tracking logging calls and resource labels in the trace.

type Label struct {
    // Label is the label applied to some resource.
    Label string
    // Resource is the resource to which this label should be applied.
    Resource ResourceID
}
    // Label provides details about a Label event.

type Log struct {
    // Task is the ID of the task this region is associated with.
    Task TaskID
    // Category is the category that was passed to runtime/trace.Log or runtime/trace.Logf.
    Category string
    // Message is the message that was passed to runtime/trace.Log or runtime/trace.Logf.
    Message string
}
    // Log provides details about a Log event.

Metrics and Performance Monitoring

Types for tracking metrics and performance data in the trace.

type Metric struct {
    // Name is the name of the sampled metric.
    // Names follow the same convention as metric names in the
    // runtime/metrics package, meaning they include the unit.
    // Names that match with the runtime/metrics package represent
    // the same quantity. Note that this corresponds to the
    // runtime/metrics package for the Go version this trace was
    // collected for.
    Name string
    // Value is the sampled value of the metric.
    // The Value's Kind is tied to the name of the metric, and so is
    // guaranteed to be the same for metric samples for the same metric.
    Value Value
}
    // Metric provides details about a Metric event.

type Value struct {
    // Has unexported fields.
}
    // Value is a dynamically-typed value obtained from a trace.

func (v Value) Kind() ValueKind
    // Kind returns the ValueKind of the value.
    // It represents the underlying structure of the value.
    // New ValueKinds may be added in the future. Users of this type must be robust
    // to that possibility.

func (v Value) String() string
    // String returns the string value for a ValueString, and otherwise a string
    // representation of the value for other kinds of values.

func (v Value) Uint64() uint64
    // Uint64 returns the uint64 value for a ValueUint64.
    // Panics if this Value's Kind is not ValueUint64.

type ValueKind uint8
    // ValueKind is the type of a dynamically-typed value from a trace.

const (
    ValueBad ValueKind = iota
    ValueUint64
    ValueString
)

Stack Traces

Types for accessing and inspecting stack frames and traces.

type Stack struct {
    // Has unexported fields.
}
    // Stack represents a stack. It's really a handle to a stack and it's trivially
    // comparable.
    // If two Stacks are equal then their Frames are guaranteed to be identical.
    // If they are not equal, however, their Frames may still be equal.

func (s Stack) Frames() iter.Seq[StackFrame]
    // Frames is an iterator over the frames in a Stack.

type StackFrame struct {
    // PC is the program counter of the function call if this
    // is not a leaf frame. If it's a leaf frame, it's the point
    // at which the stack trace was taken.
    PC uint64
    // Func is the name of the function this frame maps to.
    Func string
    // File is the file which contains the source code of Func.
    File string
    // Line is the line number within File which maps to PC.
    Line uint64
}
    // StackFrame represents a single frame of a stack.

var NoStack = Stack{}
    // NoStack is a sentinel value that can be compared against any Stack value,
    // indicating a lack of a stack trace.

Synchronization and Clock Management

Types for clock synchronization and coordination in traces.

type Sync struct {
    // N indicates that this is the Nth sync event in the trace.
    N int
    // ClockSnapshot represents a near-simultaneous clock reading of several
    // different system clocks. The snapshot can be used as a reference to
    // convert timestamps to different clocks, which is helpful for correlating
    // timestamps with data captured by other tools. The value is nil for traces
    // before go1.25.
    ClockSnapshot *ClockSnapshot
    // ExperimentalBatches contain all the unparsed batches of data for a given experiment.
    ExperimentalBatches map[string][]ExperimentalBatch
}
    // Sync contains details potentially relevant to all the following events,
    // up to but excluding the next EventSync event.

type ClockSnapshot struct {
    // Trace is a snapshot of the trace clock.
    Trace Time
    // Wall is a snapshot of the system's wall clock.
    Wall time.Time
    // Mono is a snapshot of the system's monotonic clock.
    Mono uint64
}
    // ClockSnapshot represents a near-simultaneous clock reading of several
    // different system clocks. The snapshot can be used as a reference to convert
    // timestamps to different clocks, which is helpful for correlating timestamps
    // with data captured by other tools.

type Time int64
    // Time is a timestamp in nanoseconds.
    // It corresponds to the monotonic clock on the platform that the trace was
    // taken, and so is possible to correlate with timestamps for other traces
    // taken on the same machine using the same clock (i.e. no reboots in between).
    // The actual absolute value of the timestamp is only meaningful in relation to
    // other timestamps from the same clock.
    // BUG: Timestamps coming from traces on Windows platforms are only comparable
    // with timestamps from the same trace. Timestamps across traces cannot be
    // compared, because the system clock is not used as of Go 1.22.
    // BUG: Traces produced by Go versions 1.21 and earlier cannot be compared with
    // timestamps from other traces taken on the same machine. This is because the
    // system clock was not used at all to collect those timestamps.

func (t Time) Sub(t0 Time) time.Duration
    // Sub subtracts t0 from t, returning the duration in nanoseconds.

Experimental Events

Types for handling raw experimental trace events.

type ExperimentalEvent struct {
    // Name is the name of the event.
    Name string
    // Experiment is the name of the experiment this event is a part of.
    Experiment string
    // Args lists the names of the event's arguments in order.
    Args []string
    // Has unexported fields.
}
    // ExperimentalEvent presents a raw view of an experimental event's arguments
    // and their names.

func (e ExperimentalEvent) ArgValue(i int) Value
    // ArgValue returns a typed Value for the i'th argument in the experimental
    // event.

type ExperimentalBatch struct {
    // Thread is the ID of the thread that produced a packet of data.
    Thread ThreadID
    // Data is a packet of unparsed data all produced by one thread.
    Data []byte
}
    // ExperimentalBatch represents a packet of unparsed data along with metadata
    // about that packet.

Error Handling

Error constants and conditions specific to trace operations.

var ErrSnapshotActive = fmt.Errorf("call to WriteTo for trace.FlightRecorder already in progress")
    // ErrSnapshotActive indicates that a call to WriteTo was made while one was
    // already in progress. If the caller of WriteTo sees this error, they should
    // use the result from the other call to WriteTo.

Usage Examples

Example 1: Basic Flight Recording and Reading

package main

import (
    "golang.org/x/exp/trace"
    "io"
    "log"
    "os"
    "time"
)

func main() {
    // Create and configure the flight recorder
    recorder := trace.NewFlightRecorder()
    recorder.SetSize(5 * 1024 * 1024)  // 5MB buffer
    recorder.SetPeriod(10 * time.Second)

    // Start recording
    if err := recorder.Start(); err != nil {
        log.Fatal("Failed to start recorder:", err)
    }

    // Simulate some work
    time.Sleep(2 * time.Second)

    // Write trace to file
    f, err := os.Create("trace.out")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    _, err = recorder.WriteTo(f)
    if err != nil {
        log.Fatal("Failed to write trace:", err)
    }

    if err := recorder.Stop(); err != nil {
        log.Fatal("Failed to stop recorder:", err)
    }
}

Example 2: Analyzing Trace Events

package main

import (
    "golang.org/x/exp/trace"
    "io"
    "log"
    "os"
)

func main() {
    // Open trace file
    f, err := os.Open("trace.out")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    // Create reader
    reader, err := trace.NewReader(f)
    if err != nil {
        log.Fatal("Failed to create reader:", err)
    }

    eventCount := 0
    stateTransitionCount := 0

    // Process all events
    for {
        event, err := reader.ReadEvent()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal("Failed to read event:", err)
        }

        eventCount++

        // Handle different event types
        switch event.Kind() {
        case trace.EventStateTransition:
            stateTransitionCount++
            st := event.StateTransition()
            log.Printf("State transition: %s (resource: %v)\n", st.Reason, st.Resource)

        case trace.EventSync:
            sync := event.Sync()
            log.Printf("Sync point %d\n", sync.N)

        case trace.EventMetric:
            metric := event.Metric()
            log.Printf("Metric: %s = %v\n", metric.Name, metric.Value)

        case trace.EventLog:
            logEvent := event.Log()
            log.Printf("[%s] %s\n", logEvent.Category, logEvent.Message)

        case trace.EventStackSample:
            stack := event.Stack()
            log.Printf("Stack sample from goroutine %d\n", event.Goroutine())
            for frame := range stack.Frames() {
                log.Printf("  %s at %s:%d\n", frame.Func, frame.File, frame.Line)
            }
        }
    }

    log.Printf("Total events: %d, State transitions: %d\n", eventCount, stateTransitionCount)
}

Example 3: Goroutine State Analysis

package main

import (
    "golang.org/x/exp/trace"
    "io"
    "log"
    "os"
)

func main() {
    f, _ := os.Open("trace.out")
    defer f.Close()

    reader, _ := trace.NewReader(f)

    goroutineStates := make(map[trace.GoID]trace.GoState)

    for {
        event, err := reader.ReadEvent()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }

        // Track goroutine state transitions
        if event.Kind() == trace.EventStateTransition {
            st := event.StateTransition()
            if st.Resource.Kind == trace.ResourceGoroutine {
                goID := st.Resource.Goroutine()
                fromState, toState := st.Goroutine()

                log.Printf("Goroutine %d: %s -> %s\n", goID, fromState, toState)
                goroutineStates[goID] = toState
            }
        }
    }

    log.Printf("Final goroutine states:\n")
    for goID, state := range goroutineStates {
        log.Printf("  G%d: %s\n", goID, state)
    }
}

Example 4: Handling Concurrent WriteTo Calls

package main

import (
    "golang.org/x/exp/trace"
    "io"
    "log"
    "os"
    "sync"
)

func main() {
    recorder := trace.NewFlightRecorder()
    recorder.Start()
    defer recorder.Stop()

    var wg sync.WaitGroup

    // Multiple goroutines trying to write trace
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(index int) {
            defer wg.Done()

            filename := os.Sprintf("trace_%d.out", index)
            f, _ := os.Create(filename)
            defer f.Close()

            _, err := recorder.WriteTo(f)
            if err == trace.ErrSnapshotActive {
                log.Printf("Snapshot %d: already in progress, skipping\n", index)
            } else if err != nil {
                log.Printf("Snapshot %d: error %v\n", index, err)
            } else {
                log.Printf("Snapshot %d: success\n", index)
            }
        }(i)
    }

    wg.Wait()
}

Notes

  • The flight recorder was integrated into Go 1.25. The integrated flight recorder should be used when possible rather than this experimental package.
  • Only one flight recorder or one call to runtime/trace.Start may be active at any given time.
  • Clock snapshots are only available in traces from Go 1.25 onwards.
  • Windows platform timestamps are only comparable within the same trace.
  • Goroutine and processor state transitions that are marked as "Executing" change the future execution context on the thread.
  • Stack samples should be considered approximations and may not perfectly match state transitions.