1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Trace string management.
6
7package runtime
8
9// Trace strings.
10
11const maxTraceStringLen = 1024
12
13// traceStringTable is map of string -> unique ID that also manages
14// writing strings out into the trace.
15type traceStringTable struct {
16	// lock protects buf.
17	lock mutex
18	buf  *traceBuf // string batches to write out to the trace.
19
20	// tab is a mapping of string -> unique ID.
21	tab traceMap
22}
23
24// put adds a string to the table, emits it, and returns a unique ID for it.
25func (t *traceStringTable) put(gen uintptr, s string) uint64 {
26	// Put the string in the table.
27	ss := stringStructOf(&s)
28	id, added := t.tab.put(ss.str, uintptr(ss.len))
29	if added {
30		// Write the string to the buffer.
31		systemstack(func() {
32			t.writeString(gen, id, s)
33		})
34	}
35	return id
36}
37
38// emit emits a string and creates an ID for it, but doesn't add it to the table. Returns the ID.
39func (t *traceStringTable) emit(gen uintptr, s string) uint64 {
40	// Grab an ID and write the string to the buffer.
41	id := t.tab.stealID()
42	systemstack(func() {
43		t.writeString(gen, id, s)
44	})
45	return id
46}
47
48// writeString writes the string to t.buf.
49//
50// Must run on the systemstack because it acquires t.lock.
51//
52//go:systemstack
53func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) {
54	// Truncate the string if necessary.
55	if len(s) > maxTraceStringLen {
56		s = s[:maxTraceStringLen]
57	}
58
59	lock(&t.lock)
60	w := unsafeTraceWriter(gen, t.buf)
61
62	// Ensure we have a place to write to.
63	var flushed bool
64	w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* traceEvStrings + traceEvString + ID + len + string data */)
65	if flushed {
66		// Annotate the batch as containing strings.
67		w.byte(byte(traceEvStrings))
68	}
69
70	// Write out the string.
71	w.byte(byte(traceEvString))
72	w.varint(id)
73	w.varint(uint64(len(s)))
74	w.stringData(s)
75
76	// Store back buf in case it was updated during ensure.
77	t.buf = w.traceBuf
78	unlock(&t.lock)
79}
80
81// reset clears the string table and flushes any buffers it has.
82//
83// Must be called only once the caller is certain nothing else will be
84// added to this table.
85func (t *traceStringTable) reset(gen uintptr) {
86	if t.buf != nil {
87		systemstack(func() {
88			lock(&trace.lock)
89			traceBufFlush(t.buf, gen)
90			unlock(&trace.lock)
91		})
92		t.buf = nil
93	}
94
95	// Reset the table.
96	t.tab.reset()
97}
98