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