Skip to content

Commit 2188224

Browse files
aclementspull[bot]
authored andcommitted
runtime: allow experimental trace batches to be reused
Currently, we can only cache regular trace event buffers on each M. As a result, calling unsafeTraceExpWriter will, in effect, always return a new trace batch, with all of the overhead that entails. This extends that cache to support buffers for experimental trace data. This way, unsafeTraceExpWriter can return a partially used buffer, which the caller can continue to extend. This gives the caller control over when these buffers get flushed and reuses all of the existing trace buffering mechanism. This also has the consequence of simplifying the experimental batch infrastructure a bit. Now, traceWriter needs to know the experiment ID anyway, which means there's no need for a separate traceExpWriter type. Change-Id: Idc2100176c5d02e0fbb229dc8aa4aea2b1cf5231 Reviewed-on: https://go-review.googlesource.com/c/go/+/594595 Auto-Submit: Austin Clements <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 214489b commit 2188224

File tree

5 files changed

+38
-48
lines changed

5 files changed

+38
-48
lines changed

src/runtime/trace.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -524,10 +524,11 @@ func traceAdvance(stopTrace bool) {
524524
// trace.lock needed for traceBufFlush, but also to synchronize
525525
// with traceThreadDestroy, which flushes both buffers unconditionally.
526526
lock(&trace.lock)
527-
bufp := &mp.trace.buf[gen%2]
528-
if *bufp != nil {
529-
traceBufFlush(*bufp, gen)
530-
*bufp = nil
527+
for exp, buf := range mp.trace.buf[gen%2] {
528+
if buf != nil {
529+
traceBufFlush(buf, gen)
530+
mp.trace.buf[gen%2][exp] = nil
531+
}
531532
}
532533
unlock(&trace.lock)
533534

src/runtime/tracebuf.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const traceBytesPerNumber = 10
2424
// we can change it if it's deemed too error-prone.
2525
type traceWriter struct {
2626
traceLocker
27+
exp traceExperiment
2728
*traceBuf
2829
}
2930

@@ -47,7 +48,7 @@ func (tl traceLocker) writer() traceWriter {
4748
gp.throwsplit = true
4849
}
4950
}
50-
return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2]}
51+
return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][traceNoExperiment]}
5152
}
5253

5354
// unsafeTraceWriter produces a traceWriter that doesn't lock the trace.
@@ -105,7 +106,7 @@ func (w traceWriter) end() {
105106
// less error-prone.
106107
return
107108
}
108-
w.mp.trace.buf[w.gen%2] = w.traceBuf
109+
w.mp.trace.buf[w.gen%2][w.exp] = w.traceBuf
109110
if debugTraceReentrancy {
110111
// The writer is no longer live, we can drop throwsplit (if it wasn't
111112
// already set upon entry).
@@ -127,7 +128,7 @@ func (w traceWriter) end() {
127128
func (w traceWriter) ensure(maxSize int) (traceWriter, bool) {
128129
refill := w.traceBuf == nil || !w.available(maxSize)
129130
if refill {
130-
w = w.refill(traceNoExperiment)
131+
w = w.refill()
131132
}
132133
return w, refill
133134
}
@@ -151,14 +152,7 @@ func (w traceWriter) flush() traceWriter {
151152
}
152153

153154
// refill puts w.traceBuf on the queue of full buffers and refresh's w's buffer.
154-
//
155-
// exp indicates whether the refilled batch should be EvExperimentalBatch.
156-
//
157-
// nosplit because it's part of writing an event for an M, which must not
158-
// have any stack growth.
159-
//
160-
//go:nosplit
161-
func (w traceWriter) refill(exp traceExperiment) traceWriter {
155+
func (w traceWriter) refill() traceWriter {
162156
systemstack(func() {
163157
lock(&trace.lock)
164158
if w.traceBuf != nil {
@@ -192,11 +186,11 @@ func (w traceWriter) refill(exp traceExperiment) traceWriter {
192186
}
193187

194188
// Write the buffer's header.
195-
if exp == traceNoExperiment {
189+
if w.exp == traceNoExperiment {
196190
w.byte(byte(traceEvEventBatch))
197191
} else {
198192
w.byte(byte(traceEvExperimentalBatch))
199-
w.byte(byte(exp))
193+
w.byte(byte(w.exp))
200194
}
201195
w.varint(uint64(w.gen))
202196
w.varint(uint64(mID))

src/runtime/traceexp.go

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44

55
package runtime
66

7-
// traceExpWriter is a wrapper around trace writer that produces traceEvExperimentalBatch
8-
// batches. This means that the data written to the writer need not conform to the standard
9-
// trace format.
10-
type traceExpWriter struct {
11-
traceWriter
12-
exp traceExperiment
7+
// expWriter returns a traceWriter that writes into the current M's stream for
8+
// the given experiment.
9+
func (tl traceLocker) expWriter(exp traceExperiment) traceWriter {
10+
return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][exp], exp: exp}
1311
}
1412

15-
// unsafeTraceExpWriter produces a traceExpWriter that doesn't lock the trace.
13+
// unsafeTraceExpWriter produces a traceWriter for experimental trace batches
14+
// that doesn't lock the trace. Data written to experimental batches need not
15+
// conform to the standard trace format.
1616
//
1717
// It should only be used in contexts where either:
1818
// - Another traceLocker is held.
@@ -21,19 +21,8 @@ type traceExpWriter struct {
2121
// This does not have the same stack growth restrictions as traceLocker.writer.
2222
//
2323
// buf may be nil.
24-
func unsafeTraceExpWriter(gen uintptr, buf *traceBuf, exp traceExperiment) traceExpWriter {
25-
return traceExpWriter{traceWriter{traceLocker: traceLocker{gen: gen}, traceBuf: buf}, exp}
26-
}
27-
28-
// ensure makes sure that at least maxSize bytes are available to write.
29-
//
30-
// Returns whether the buffer was flushed.
31-
func (w traceExpWriter) ensure(maxSize int) (traceExpWriter, bool) {
32-
refill := w.traceBuf == nil || !w.available(maxSize)
33-
if refill {
34-
w.traceWriter = w.traceWriter.refill(w.exp)
35-
}
36-
return w, refill
24+
func unsafeTraceExpWriter(gen uintptr, buf *traceBuf, exp traceExperiment) traceWriter {
25+
return traceWriter{traceLocker: traceLocker{gen: gen}, traceBuf: buf, exp: exp}
3726
}
3827

3928
// traceExperiment is an enumeration of the different kinds of experiments supported for tracing.
@@ -45,6 +34,10 @@ const (
4534

4635
// traceExperimentAllocFree is an experiment to add alloc/free events to the trace.
4736
traceExperimentAllocFree
37+
38+
// traceNumExperiments is the number of trace experiments (and 1 higher than
39+
// the highest numbered experiment).
40+
traceNumExperiments
4841
)
4942

5043
// Experimental events.

src/runtime/traceruntime.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ func (s *gTraceState) reset() {
2424

2525
// mTraceState is per-M state for the tracer.
2626
type mTraceState struct {
27-
seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer.
28-
buf [2]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.
29-
link *m // Snapshot of alllink or freelink.
30-
reentered uint32 // Whether we've reentered tracing from within tracing.
31-
oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging.
27+
seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer.
28+
buf [2][traceNumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.
29+
link *m // Snapshot of alllink or freelink.
30+
reentered uint32 // Whether we've reentered tracing from within tracing.
31+
oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging.
3232
}
3333

3434
// pTraceState is per-P state for the tracer.
@@ -691,11 +691,13 @@ func traceThreadDestroy(mp *m) {
691691
systemstack(func() {
692692
lock(&trace.lock)
693693
for i := range mp.trace.buf {
694-
if mp.trace.buf[i] != nil {
695-
// N.B. traceBufFlush accepts a generation, but it
696-
// really just cares about gen%2.
697-
traceBufFlush(mp.trace.buf[i], uintptr(i))
698-
mp.trace.buf[i] = nil
694+
for exp, buf := range mp.trace.buf[i] {
695+
if buf != nil {
696+
// N.B. traceBufFlush accepts a generation, but it
697+
// really just cares about gen%2.
698+
traceBufFlush(buf, uintptr(i))
699+
mp.trace.buf[i][exp] = nil
700+
}
699701
}
700702
}
701703
unlock(&trace.lock)

src/runtime/tracetype.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (t *traceTypeTable) dump(gen uintptr) {
4343
t.tab.reset()
4444
}
4545

46-
func dumpTypesRec(node *traceMapNode, w traceExpWriter) traceExpWriter {
46+
func dumpTypesRec(node *traceMapNode, w traceWriter) traceWriter {
4747
typ := (*abi.Type)(*(*unsafe.Pointer)(unsafe.Pointer(&node.data[0])))
4848
typName := toRType(typ).string()
4949

0 commit comments

Comments
 (0)