blob: cbb0ecfb37438001c64a582e3a905f02714930a9 [file] [log] [blame]
// 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 string management.
package runtime
// Trace strings.
const maxTraceStringLen = 1024
// traceStringTable is map of string -> unique ID that also manages
// writing strings out into the trace.
type traceStringTable struct {
// lock protects buf.
lock mutex
buf *traceBuf // string batches to write out to the trace.
// tab is a mapping of string -> unique ID.
tab traceMap
}
// put adds a string to the table, emits it, and returns a unique ID for it.
func (t *traceStringTable) put(gen uintptr, s string) uint64 {
// Put the string in the table.
ss := stringStructOf(&s)
id, added := t.tab.put(ss.str, uintptr(ss.len))
if added {
// Write the string to the buffer.
systemstack(func() {
t.writeString(gen, id, s)
})
}
return id
}
// emit emits a string and creates an ID for it, but doesn't add it to the table. Returns the ID.
func (t *traceStringTable) emit(gen uintptr, s string) uint64 {
// Grab an ID and write the string to the buffer.
id := t.tab.stealID()
systemstack(func() {
t.writeString(gen, id, s)
})
return id
}
// writeString writes the string to t.buf.
//
// Must run on the systemstack because it may flush buffers and thus could acquire trace.lock.
//
//go:systemstack
func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) {
// Truncate the string if necessary.
if len(s) > maxTraceStringLen {
s = s[:maxTraceStringLen]
}
lock(&t.lock)
w := unsafeTraceWriter(gen, t.buf)
// Ensure we have a place to write to.
var flushed bool
w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* traceEvStrings + traceEvString + ID + len + string data */)
if flushed {
// Annotate the batch as containing strings.
w.byte(byte(traceEvStrings))
}
// Write out the string.
w.byte(byte(traceEvString))
w.varint(id)
w.varint(uint64(len(s)))
w.stringData(s)
// Store back buf if it was updated during ensure.
t.buf = w.traceBuf
unlock(&t.lock)
}
// reset clears the string table and flushes any buffers it has.
//
// Must be called only once the caller is certain nothing else will be
// added to this table.
//
// Because it flushes buffers, this may acquire trace.lock and thus
// must run on the systemstack.
//
//go:systemstack
func (t *traceStringTable) reset(gen uintptr) {
if t.buf != nil {
lock(&trace.lock)
traceBufFlush(t.buf, gen)
unlock(&trace.lock)
t.buf = nil
}
// Reset the table.
lock(&t.tab.lock)
t.tab.reset()
unlock(&t.tab.lock)
}