From f609cda61e42bafb343186673e086c96045258ac Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 27 Feb 2026 17:03:04 +0200 Subject: flamegraph: add live trie scaffold --- internal/flamegraph/livetrie.go | 61 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 internal/flamegraph/livetrie.go (limited to 'internal/flamegraph') diff --git a/internal/flamegraph/livetrie.go b/internal/flamegraph/livetrie.go new file mode 100644 index 0000000..11d5c1b --- /dev/null +++ b/internal/flamegraph/livetrie.go @@ -0,0 +1,61 @@ +package flamegraph + +import ( + "slices" + "sync" + "sync/atomic" +) + +// LiveTrie is a thread-safe, append-only trie used for live flamegraph snapshots. +type LiveTrie struct { + mu sync.RWMutex + root *trieNode + maxDepth int + version atomic.Uint64 + fields []string + countField string + + // Snapshot cache avoids recomputing JSON when version is unchanged. + cacheMu sync.Mutex + cacheVersion uint64 + cacheJSON []byte +} + +// NewLiveTrie constructs an empty live trie with the configured frame/count fields. +func NewLiveTrie(fields []string, countField string) *LiveTrie { + return &LiveTrie{ + root: &trieNode{ + childMap: make(map[string]*trieNode), + }, + fields: slices.Clone(fields), + countField: countField, + } +} + +func (lt *LiveTrie) addLocked(frames []string, value uint64) { + node := lt.root + for _, frame := range frames { + if node.childMap == nil { + node.childMap = make(map[string]*trieNode) + } + child, ok := node.childMap[frame] + if !ok { + child = &trieNode{ + name: frame, + childMap: make(map[string]*trieNode), + } + node.children = append(node.children, child) + node.childMap[frame] = child + } + node = child + } + node.value += value + if len(frames) > lt.maxDepth { + lt.maxDepth = len(frames) + } +} + +// Version returns the current ingest version of the trie. +func (lt *LiveTrie) Version() uint64 { + return lt.version.Load() +} -- cgit v1.2.3