summaryrefslogtreecommitdiffstats
path: root/src/runtime/trace2string.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/runtime/trace2string.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/runtime/trace2string.go b/src/runtime/trace2string.go
new file mode 100644
index 0000000..cbb0ecf
--- /dev/null
+++ b/src/runtime/trace2string.go
@@ -0,0 +1,104 @@
+// 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)
+}