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