Source file src/runtime/trace/recorder.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package trace
     6  
     7  import (
     8  	"fmt"
     9  	"slices"
    10  	"time"
    11  	_ "unsafe" // added for go linkname usage
    12  )
    13  
    14  // A recorder receives bytes from the runtime tracer, processes it.
    15  type recorder struct {
    16  	r *FlightRecorder
    17  
    18  	headerReceived bool
    19  }
    20  
    21  func (w *recorder) Write(b []byte) (n int, err error) {
    22  	r := w.r
    23  
    24  	defer func() {
    25  		if err != nil {
    26  			// Propagate errors to the flightrecorder.
    27  			if r.err == nil {
    28  				r.err = err
    29  			}
    30  		}
    31  	}()
    32  
    33  	if !w.headerReceived {
    34  		if len(b) < len(r.header) {
    35  			return 0, fmt.Errorf("expected at least %d bytes in the first write", len(r.header))
    36  		}
    37  		r.header = ([16]byte)(b[:16])
    38  		n += 16
    39  		w.headerReceived = true
    40  	}
    41  	if len(b) == n {
    42  		return 0, nil
    43  	}
    44  	ba, gen, nb, err := readBatch(b[n:]) // Every write from the runtime is guaranteed to be a complete batch.
    45  	if err != nil {
    46  		return len(b) - int(nb) - n, err
    47  	}
    48  	n += int(nb)
    49  
    50  	// Append the batch to the current generation.
    51  	if r.active.gen == 0 {
    52  		r.active.gen = gen
    53  	}
    54  	if r.active.minTime == 0 || r.active.minTime > r.freq.mul(ba.time) {
    55  		r.active.minTime = r.freq.mul(ba.time)
    56  	}
    57  	r.active.size += len(ba.data)
    58  	r.active.batches = append(r.active.batches, ba)
    59  
    60  	return len(b), nil
    61  }
    62  
    63  func (w *recorder) endGeneration() {
    64  	r := w.r
    65  
    66  	// Check if we're entering a new generation.
    67  	r.ringMu.Lock()
    68  
    69  	// Get the current trace clock time.
    70  	now := traceTimeNow(r.freq)
    71  
    72  	// Add the current generation to the ring. Make sure we always have at least one
    73  	// complete generation by putting the active generation onto the new list, regardless
    74  	// of whatever our settings are.
    75  	//
    76  	// N.B. Let's completely replace the ring here, so that WriteTo can just make a copy
    77  	// and not worry about aliasing. This creates allocations, but at a very low rate.
    78  	newRing := []rawGeneration{r.active}
    79  	size := r.active.size
    80  	for i := len(r.ring) - 1; i >= 0; i-- {
    81  		// Stop adding older generations if the new ring already exceeds the thresholds.
    82  		// This ensures we keep generations that cross a threshold, but not any that lie
    83  		// entirely outside it.
    84  		if uint64(size) > r.wantSize || now.Sub(newRing[len(newRing)-1].minTime) > r.wantDur {
    85  			break
    86  		}
    87  		size += r.ring[i].size
    88  		newRing = append(newRing, r.ring[i])
    89  	}
    90  	slices.Reverse(newRing)
    91  	r.ring = newRing
    92  	r.ringMu.Unlock()
    93  
    94  	// Start a new active generation.
    95  	r.active = rawGeneration{}
    96  }
    97  
    98  type rawGeneration struct {
    99  	gen     uint64
   100  	size    int
   101  	minTime eventTime
   102  	batches []batch
   103  }
   104  
   105  func traceTimeNow(freq frequency) eventTime {
   106  	return freq.mul(timestamp(runtime_traceClockNow()))
   107  }
   108  
   109  //go:linkname runtime_traceClockNow runtime.traceClockNow
   110  func runtime_traceClockNow() uint64
   111  
   112  // frequency is nanoseconds per timestamp unit.
   113  type frequency float64
   114  
   115  // mul multiplies an unprocessed to produce a time in nanoseconds.
   116  func (f frequency) mul(t timestamp) eventTime {
   117  	return eventTime(float64(t) * float64(f))
   118  }
   119  
   120  // eventTime is a timestamp in nanoseconds.
   121  //
   122  // It corresponds to the monotonic clock on the platform that the
   123  // trace was taken, and so is possible to correlate with timestamps
   124  // for other traces taken on the same machine using the same clock
   125  // (i.e. no reboots in between).
   126  //
   127  // The actual absolute value of the timestamp is only meaningful in
   128  // relation to other timestamps from the same clock.
   129  //
   130  // BUG: Timestamps coming from traces on Windows platforms are
   131  // only comparable with timestamps from the same trace. Timestamps
   132  // across traces cannot be compared, because the system clock is
   133  // not used as of Go 1.22.
   134  //
   135  // BUG: Traces produced by Go versions 1.21 and earlier cannot be
   136  // compared with timestamps from other traces taken on the same
   137  // machine. This is because the system clock was not used at all
   138  // to collect those timestamps.
   139  type eventTime int64
   140  
   141  // Sub subtracts t0 from t, returning the duration in nanoseconds.
   142  func (t eventTime) Sub(t0 eventTime) time.Duration {
   143  	return time.Duration(int64(t) - int64(t0))
   144  }
   145  

View as plain text