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

View as plain text