// 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. // Trace stack table and acquisition. package runtime import ( "internal/abi" "internal/goarch" "unsafe" ) // traceTypeTable maps stack traces (arrays of PC's) to unique uint32 ids. // It is lock-free for reading. type traceTypeTable struct { tab traceMap } // put returns a unique id for the type typ and caches it in the table, // if it's seeing it for the first time. // // N.B. typ must be kept alive forever for this to work correctly. func (t *traceTypeTable) put(typ *abi.Type) uint64 { if typ == nil { return 0 } // Insert the pointer to the type itself. id, _ := t.tab.put(noescape(unsafe.Pointer(&typ)), goarch.PtrSize) return id } // dump writes all previously cached types to trace buffers and // releases all memory and resets state. It must only be called once the caller // can guarantee that there are no more writers to the table. func (t *traceTypeTable) dump(gen uintptr) { w := unsafeTraceExpWriter(gen, nil, traceExperimentAllocFree) if root := (*traceMapNode)(t.tab.root.Load()); root != nil { w = dumpTypesRec(root, w) } w.flush().end() t.tab.reset() } func dumpTypesRec(node *traceMapNode, w traceExpWriter) traceExpWriter { typ := (*abi.Type)(*(*unsafe.Pointer)(unsafe.Pointer(&node.data[0]))) typName := toRType(typ).string() // The maximum number of bytes required to hold the encoded type. maxBytes := 1 + 5*traceBytesPerNumber + len(typName) // Estimate the size of this record. This // bound is pretty loose, but avoids counting // lots of varint sizes. // // Add 1 because we might also write a traceAllocFreeTypesBatch byte. var flushed bool w, flushed = w.ensure(1 + maxBytes) if flushed { // Annotate the batch as containing types. w.byte(byte(traceAllocFreeTypesBatch)) } // Emit type. w.varint(uint64(node.id)) w.varint(uint64(uintptr(unsafe.Pointer(typ)))) w.varint(uint64(typ.Size())) w.varint(uint64(typ.PtrBytes)) w.varint(uint64(len(typName))) w.stringData(typName) // Recursively walk all child nodes. for i := range node.children { child := node.children[i].Load() if child == nil { continue } w = dumpTypesRec((*traceMapNode)(child), w) } return w }