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