"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
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
}
// 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
// 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
// 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() {