2023-05-08 16:29:52 -06:00
|
|
|
// Copyright 2023 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.
|
|
|
|
|
|
|
|
//go:build goexperiment.exectracer2
|
|
|
|
|
|
|
|
// Trace time and clock.
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
|
|
|
import "internal/goarch"
|
|
|
|
|
|
|
|
// Timestamps in trace are produced through either nanotime or cputicks
|
|
|
|
// and divided by traceTimeDiv. nanotime is used everywhere except on
|
|
|
|
// platforms where osHasLowResClock is true, because the system clock
|
|
|
|
// isn't granular enough to get useful information out of a trace in
|
|
|
|
// many cases.
|
|
|
|
//
|
|
|
|
// This makes absolute values of timestamp diffs smaller, and so they are
|
|
|
|
// encoded in fewer bytes.
|
|
|
|
//
|
|
|
|
// The target resolution in all cases is 64 nanoseconds.
|
|
|
|
// This is based on the fact that fundamentally the execution tracer won't emit
|
|
|
|
// events more frequently than roughly every 200 ns or so, because that's roughly
|
|
|
|
// how long it takes to call through the scheduler.
|
|
|
|
// We could be more aggressive and bump this up to 128 ns while still getting
|
|
|
|
// useful data, but the extra bit doesn't save us that much and the headroom is
|
|
|
|
// nice to have.
|
|
|
|
//
|
|
|
|
// Hitting this target resolution is easy in the nanotime case: just pick a
|
|
|
|
// division of 64. In the cputicks case it's a bit more complex.
|
|
|
|
//
|
|
|
|
// For x86, on a 3 GHz machine, we'd want to divide by 3*64 to hit our target.
|
|
|
|
// To keep the division operation efficient, we round that up to 4*64, or 256.
|
|
|
|
// Given what cputicks represents, we use this on all other platforms except
|
|
|
|
// for PowerPC.
|
|
|
|
// The suggested increment frequency for PowerPC's time base register is
|
|
|
|
// 512 MHz according to Power ISA v2.07 section 6.2, so we use 32 on ppc64
|
|
|
|
// and ppc64le.
|
|
|
|
const traceTimeDiv = (1-osHasLowResClockInt)*64 + osHasLowResClockInt*(256-224*(goarch.IsPpc64|goarch.IsPpc64le))
|
|
|
|
|
|
|
|
// traceTime represents a timestamp for the trace.
|
|
|
|
type traceTime uint64
|
|
|
|
|
|
|
|
// traceClockNow returns a monotonic timestamp. The clock this function gets
|
|
|
|
// the timestamp from is specific to tracing, and shouldn't be mixed with other
|
|
|
|
// clock sources.
|
|
|
|
//
|
|
|
|
// nosplit because it's called from exitsyscall, which is nosplit.
|
|
|
|
//
|
|
|
|
//go:nosplit
|
|
|
|
func traceClockNow() traceTime {
|
|
|
|
if osHasLowResClock {
|
|
|
|
return traceTime(cputicks() / traceTimeDiv)
|
|
|
|
}
|
|
|
|
return traceTime(nanotime() / traceTimeDiv)
|
|
|
|
}
|
|
|
|
|
|
|
|
// traceClockUnitsPerSecond estimates the number of trace clock units per
|
|
|
|
// second that elapse.
|
|
|
|
func traceClockUnitsPerSecond() uint64 {
|
|
|
|
if osHasLowResClock {
|
|
|
|
// We're using cputicks as our clock, so we need a real estimate.
|
2024-02-28 15:24:48 -07:00
|
|
|
return uint64(ticksPerSecond() / traceTimeDiv)
|
2023-05-08 16:29:52 -06:00
|
|
|
}
|
|
|
|
// Our clock is nanotime, so it's just the constant time division.
|
|
|
|
// (trace clock units / nanoseconds) * (1e9 nanoseconds / 1 second)
|
|
|
|
return uint64(1.0 / float64(traceTimeDiv) * 1e9)
|
|
|
|
}
|
|
|
|
|
|
|
|
// traceFrequency writes a batch with a single EvFrequency event.
|
|
|
|
//
|
|
|
|
// freq is the number of trace clock units per second.
|
|
|
|
func traceFrequency(gen uintptr) {
|
|
|
|
w := unsafeTraceWriter(gen, nil)
|
|
|
|
|
|
|
|
// Ensure we have a place to write to.
|
|
|
|
w, _ = w.ensure(1 + traceBytesPerNumber /* traceEvFrequency + frequency */)
|
|
|
|
|
|
|
|
// Write out the string.
|
|
|
|
w.byte(byte(traceEvFrequency))
|
|
|
|
w.varint(traceClockUnitsPerSecond())
|
|
|
|
|
|
|
|
// Immediately flush the buffer.
|
|
|
|
systemstack(func() {
|
|
|
|
lock(&trace.lock)
|
|
|
|
traceBufFlush(w.traceBuf, gen)
|
|
|
|
unlock(&trace.lock)
|
|
|
|
})
|
|
|
|
}
|