Source file src/runtime/trace2time.go

     1  // Copyright 2023 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  //go:build goexperiment.exectracer2
     6  
     7  // Trace time and clock.
     8  
     9  package runtime
    10  
    11  import "internal/goarch"
    12  
    13  // Timestamps in trace are produced through either nanotime or cputicks
    14  // and divided by traceTimeDiv. nanotime is used everywhere except on
    15  // platforms where osHasLowResClock is true, because the system clock
    16  // isn't granular enough to get useful information out of a trace in
    17  // many cases.
    18  //
    19  // This makes absolute values of timestamp diffs smaller, and so they are
    20  // encoded in fewer bytes.
    21  //
    22  // The target resolution in all cases is 64 nanoseconds.
    23  // This is based on the fact that fundamentally the execution tracer won't emit
    24  // events more frequently than roughly every 200 ns or so, because that's roughly
    25  // how long it takes to call through the scheduler.
    26  // We could be more aggressive and bump this up to 128 ns while still getting
    27  // useful data, but the extra bit doesn't save us that much and the headroom is
    28  // nice to have.
    29  //
    30  // Hitting this target resolution is easy in the nanotime case: just pick a
    31  // division of 64. In the cputicks case it's a bit more complex.
    32  //
    33  // For x86, on a 3 GHz machine, we'd want to divide by 3*64 to hit our target.
    34  // To keep the division operation efficient, we round that up to 4*64, or 256.
    35  // Given what cputicks represents, we use this on all other platforms except
    36  // for PowerPC.
    37  // The suggested increment frequency for PowerPC's time base register is
    38  // 512 MHz according to Power ISA v2.07 section 6.2, so we use 32 on ppc64
    39  // and ppc64le.
    40  const traceTimeDiv = (1-osHasLowResClockInt)*64 + osHasLowResClockInt*(256-224*(goarch.IsPpc64|goarch.IsPpc64le))
    41  
    42  // traceTime represents a timestamp for the trace.
    43  type traceTime uint64
    44  
    45  // traceClockNow returns a monotonic timestamp. The clock this function gets
    46  // the timestamp from is specific to tracing, and shouldn't be mixed with other
    47  // clock sources.
    48  //
    49  // nosplit because it's called from exitsyscall, which is nosplit.
    50  //
    51  //go:nosplit
    52  func traceClockNow() traceTime {
    53  	if osHasLowResClock {
    54  		return traceTime(cputicks() / traceTimeDiv)
    55  	}
    56  	return traceTime(nanotime() / traceTimeDiv)
    57  }
    58  
    59  // traceClockUnitsPerSecond estimates the number of trace clock units per
    60  // second that elapse.
    61  func traceClockUnitsPerSecond() uint64 {
    62  	if osHasLowResClock {
    63  		// We're using cputicks as our clock, so we need a real estimate.
    64  		return uint64(ticksPerSecond() / traceTimeDiv)
    65  	}
    66  	// Our clock is nanotime, so it's just the constant time division.
    67  	// (trace clock units / nanoseconds) * (1e9 nanoseconds / 1 second)
    68  	return uint64(1.0 / float64(traceTimeDiv) * 1e9)
    69  }
    70  
    71  // traceFrequency writes a batch with a single EvFrequency event.
    72  //
    73  // freq is the number of trace clock units per second.
    74  func traceFrequency(gen uintptr) {
    75  	w := unsafeTraceWriter(gen, nil)
    76  
    77  	// Ensure we have a place to write to.
    78  	w, _ = w.ensure(1 + traceBytesPerNumber /* traceEvFrequency + frequency */)
    79  
    80  	// Write out the string.
    81  	w.byte(byte(traceEvFrequency))
    82  	w.varint(traceClockUnitsPerSecond())
    83  
    84  	// Immediately flush the buffer.
    85  	systemstack(func() {
    86  		lock(&trace.lock)
    87  		traceBufFlush(w.traceBuf, gen)
    88  		unlock(&trace.lock)
    89  	})
    90  }
    91  

View as plain text