Source File
flightrecorder.go
Belonging Package
runtime/trace
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package trace
import (
_ // added for go linkname usage
)
// FlightRecorder represents a single consumer of a Go execution
// trace.
// It tracks a moving window over the execution trace produced by
// the runtime, always containing the most recent trace data.
//
// At most one flight recorder may be active at any given time,
// though flight recording is allowed to be concurrently active
// with a trace consumer using trace.Start.
// This restriction of only a single flight recorder may be removed
// in the future.
type FlightRecorder struct {
err error
// State specific to the recorder.
header [16]byte
active rawGeneration
ringMu sync.Mutex
ring []rawGeneration
freq frequency // timestamp conversion factor, from the runtime
// Externally-set options.
targetSize uint64
targetPeriod time.Duration
enabled bool // whether the flight recorder is enabled.
writing sync.Mutex // protects concurrent calls to WriteTo
// The values of targetSize and targetPeriod we've committed to since the last Start.
wantSize uint64
wantDur time.Duration
}
// NewFlightRecorder creates a new flight recorder from the provided configuration.
func ( FlightRecorderConfig) *FlightRecorder {
:= new(FlightRecorder)
if .MaxBytes != 0 {
.targetSize = .MaxBytes
} else {
.targetSize = 10 << 20 // 10 MiB.
}
if .MinAge != 0 {
.targetPeriod = .MinAge
} else {
.targetPeriod = 10 * time.Second
}
return
}
// Start activates the flight recorder and begins recording trace data.
// Only one call to trace.Start may be active at any given time.
// In addition, currently only one flight recorder may be active in the program.
// Returns an error if the flight recorder cannot be started or is already started.
func ( *FlightRecorder) () error {
if .enabled {
return fmt.Errorf("cannot enable a enabled flight recorder")
}
.wantSize = .targetSize
.wantDur = .targetPeriod
.err = nil
.freq = frequency(1.0 / (float64(runtime_traceClockUnitsPerSecond()) / 1e9))
// Start tracing, data is sent to a recorder which forwards it to our own
// storage.
if := tracing.subscribeFlightRecorder(&recorder{r: }); != nil {
return
}
.enabled = true
return nil
}
// Stop ends recording of trace data. It blocks until any concurrent WriteTo calls complete.
func ( *FlightRecorder) () {
if !.enabled {
return
}
.enabled = false
tracing.unsubscribeFlightRecorder()
// Reset all state. No need to lock because the reader has already exited.
.active = rawGeneration{}
.ring = nil
}
// 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 ( *FlightRecorder) () bool { return .enabled }
// WriteTo snapshots the moving window tracked by the flight recorder.
// The snapshot is expected to contain data that is up-to-date as of when WriteTo is called,
// though this is not a hard guarantee.
// Only one goroutine may execute WriteTo at a time.
// An error is returned upon failure to write to w, if another WriteTo call is already in-progress,
// or if the flight recorder is inactive.
func ( *FlightRecorder) ( io.Writer) ( int64, error) {
if !.enabled {
return 0, fmt.Errorf("cannot snapshot a disabled flight recorder")
}
if !.writing.TryLock() {
// 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.
return 0, fmt.Errorf("call to WriteTo for trace.FlightRecorder already in progress")
}
defer .writing.Unlock()
// Force a global buffer flush.
runtime_traceAdvance(false)
// Now that everything has been flushed and written, grab whatever we have.
//
// N.B. traceAdvance blocks until the tracer goroutine has actually written everything
// out, which means the generation we just flushed must have been already been observed
// by the recorder goroutine. Because we flushed twice, the first flush is guaranteed to
// have been both completed *and* processed by the recorder goroutine.
.ringMu.Lock()
:= .ring
.ringMu.Unlock()
// Write the header.
, := .Write(.header[:])
if != nil {
return int64(),
}
+= int64()
// Write all the data.
for , := range {
for , := range .batches {
// Write batch data.
, = .Write(.data)
+= int64()
if != nil {
return ,
}
}
}
return , nil
}
type FlightRecorderConfig struct {
// MinAge is a lower bound on the age of an event in the flight recorder's window.
//
// The flight recorder will strive to promptly discard events older than the minimum age,
// but older events may appear in the window snapshot. The age setting will always be
// overridden by MaxSize.
//
// If this is 0, the minimum age is implementation defined, but can be assumed to be on the order
// of seconds.
MinAge time.Duration
// MaxBytes is an upper bound on the size of the window in bytes.
//
// This setting takes precedence over MinAge.
// However, it does not make any guarantees on the size of the data WriteTo will write,
// nor does it guarantee memory overheads will always stay below MaxBytes. Treat it
// as a hint.
//
// If this is 0, the maximum size is implementation defined.
MaxBytes uint64
}
//go:linkname runtime_traceClockUnitsPerSecond
func runtime_traceClockUnitsPerSecond() uint64
//go:linkname runtime_traceAdvance runtime.traceAdvance
func runtime_traceAdvance( bool)
![]() |
The pages are generated with Golds v0.7.7-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |