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 traceimport (_ // 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]byteactive rawGenerationringMu sync.Mutexring []rawGenerationfreq frequency // timestamp conversion factor, from the runtime// Externally-set options.targetSize uint64targetPeriod time.Durationenabled 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 uint64wantDur 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 = truereturn nil}// Stop ends recording of trace data. It blocks until any concurrent WriteTo calls complete.func ( *FlightRecorder) () {if !.enabled {return}.enabled = falsetracing.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_traceClockUnitsPerSecondfunc runtime_traceClockUnitsPerSecond() uint64//go:linkname runtime_traceAdvance runtime.traceAdvancefunc runtime_traceAdvance( bool)
![]() |
The pages are generated with Golds v0.7.9-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. |