// 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) }