]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't write unique string to trace if it's length zero
authorMichael Anthony Knyszek <mknyszek@google.com>
Wed, 19 Nov 2025 03:51:20 +0000 (03:51 +0000)
committerGopher Robot <gobot@golang.org>
Mon, 24 Nov 2025 21:20:09 +0000 (13:20 -0800)
While we're here, document that ID 0 is implicitly assigned to an empty
set of data for both stacks and strings.

Change-Id: Ic52ff3a1132abc5a8f6f6c4e4357e31e6e7799fc
Reviewed-on: https://go-review.googlesource.com/c/go/+/723061
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Michael Knyszek <mknyszek@google.com>

src/runtime/tracemap.go
src/runtime/tracestack.go
src/runtime/tracestring.go

index 9efa325c112984a6bd08514202c319d45e163dcb..1d6aabb4310ef346024b3f4332eeb4c3117fa5ed 100644 (file)
@@ -23,6 +23,13 @@ import (
        "unsafe"
 )
 
+// traceMap is a map of a variable-sized array of bytes to a unique ID.
+//
+// Because traceMap just operates on raw bytes, this type is used as the
+// backing store for both the trace string table and trace stack table,
+// the latter of which is just an array of PCs.
+//
+// ID 0 is reserved for arrays of bytes of size zero.
 type traceMap struct {
        root atomic.UnsafePointer // *traceMapNode (can't use generics because it's notinheap)
        _    cpu.CacheLinePad
index 51f3c29445c5a74c6a279ffea04a1c3d3ee14b1b..0da217fba98f003e89997dae3565352814c6e396 100644 (file)
@@ -136,8 +136,9 @@ func traceStack(skip int, gp *g, tab *traceStackTable) uint64 {
        return id
 }
 
-// traceStackTable maps stack traces (arrays of PC's) to unique uint32 ids.
-// It is lock-free for reading.
+// traceStackTable maps stack traces (arrays of PC's) to unique IDs.
+//
+// ID 0 is reserved for a zero-length stack.
 type traceStackTable struct {
        tab traceMap
 }
@@ -145,8 +146,10 @@ type traceStackTable struct {
 // put returns a unique id for the stack trace pcs and caches it in the table,
 // if it sees the trace for the first time.
 func (t *traceStackTable) put(pcs []uintptr) uint64 {
+       // Even though put will handle this for us, taking the address of pcs forces a bounds check
+       // that will fail if len(pcs) == 0.
        if len(pcs) == 0 {
-               return 0
+               return 0 // ID 0 is reserved for zero-length stacks.
        }
        id, _ := t.tab.put(noescape(unsafe.Pointer(&pcs[0])), uintptr(len(pcs))*unsafe.Sizeof(uintptr(0)))
        return id
index d486f9efbdf8b5cb65c8fe8699479922f8162402..bd31f06a6708ee061b9242c667fe2867cffc41df 100644 (file)
@@ -12,6 +12,8 @@ import "internal/trace/tracev2"
 
 // traceStringTable is map of string -> unique ID that also manages
 // writing strings out into the trace.
+//
+// ID 0 is reserved for the empty string.
 type traceStringTable struct {
        // lock protects buf.
        lock mutex
@@ -37,6 +39,9 @@ func (t *traceStringTable) put(gen uintptr, s string) uint64 {
 
 // 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 {
+       if len(s) == 0 {
+               return 0 // Empty strings are implicitly assigned ID 0 already.
+       }
        // Grab an ID and write the string to the buffer.
        id := t.tab.stealID()
        systemstack(func() {