diff options
Diffstat (limited to 'internal/flamegraph')
| -rw-r--r-- | internal/flamegraph/iordata.go | 40 | ||||
| -rw-r--r-- | internal/flamegraph/iordata_test.go | 4 | ||||
| -rw-r--r-- | internal/flamegraph/livetrie.go | 18 | ||||
| -rw-r--r-- | internal/flamegraph/trie.go | 9 |
4 files changed, 34 insertions, 37 deletions
diff --git a/internal/flamegraph/iordata.go b/internal/flamegraph/iordata.go index 76e438a..36b5103 100644 --- a/internal/flamegraph/iordata.go +++ b/internal/flamegraph/iordata.go @@ -14,8 +14,7 @@ import ( "ior/internal/file" "ior/internal/types" - // Is there a zstd library part of Go 1.25 - "github.com/DataDog/zstd" + "github.com/DataDog/zstd" // Go stdlib does not include zstd; third-party dep required ) type pathType = string @@ -61,19 +60,13 @@ func LoadFromFile(filename string) (iter.Seq[IterRecord], error) { return iod.iter(), nil } -func cloneString(s string) string { - // Clone the string by creating a new string with the same content - // This is a workaround to avoid using unsafe package - return string([]byte(s)) -} - -func (iod iorData) addEventPair(ev *event.Pair) { +func (iod *iorData) addEventPair(ev *event.Pair) { cnt := Counter{Count: 1, Duration: ev.Duration, DurationToPrev: ev.DurationToPrev, Bytes: ev.Bytes} iod.add(ev.FileName(), ev.EnterEv.GetTraceId(), strings.TrimSpace(ev.Comm), ev.EnterEv.GetPid(), ev.EnterEv.GetTid(), ev.Flags(), cnt) } -func (iod iorData) add(path pathType, traceId traceIdType, comm commType, +func (iod *iorData) add(path pathType, traceId traceIdType, comm commType, pid pidType, tid tidType, flags flagsType, addCnt Counter) { key := recordKey{ @@ -92,14 +85,14 @@ func (iod iorData) add(path pathType, traceId traceIdType, comm commType, iod.records[key] = cnt.add(addCnt) } -func (iod iorData) merge(other iorData) iorData { +func (iod *iorData) merge(other iorData) *iorData { for key, cnt := range other.records { iod.add(key.Path, key.TraceID, key.Comm, key.Pid, key.Tid, key.Flags, cnt) } return iod } -func (iod iorData) serializeToFile(flamegraphName string) (retErr error) { +func (iod *iorData) serializeToFile(flamegraphName string) (retErr error) { hostname, err := hostnameFn() if err != nil { return fmt.Errorf("get hostname: %w", err) @@ -118,24 +111,25 @@ func (iod iorData) serializeToFile(flamegraphName string) (retErr error) { return fmt.Errorf("create temp file %s: %w", tmpFilename, err) } defer func() { - if err := file.Close(); err != nil { - retErr = errors.Join(retErr, fmt.Errorf("close temp file %s: %w", tmpFilename, err)) + // Close file on error paths; on success it is already closed before rename. + if retErr != nil { + file.Close() } }() encoder := zstd.NewWriter(file) - defer func() { - if err := encoder.Close(); err != nil { - retErr = errors.Join(retErr, fmt.Errorf("close zstd writer for %s: %w", tmpFilename, err)) - } - }() gobEncoder := gob.NewEncoder(encoder) if err := gobEncoder.Encode(iod.records); err != nil { return fmt.Errorf("encode ior records: %w", err) } - if err := encoder.Flush(); err != nil { - return fmt.Errorf("flush ior records: %w", err) + // Close encoder before file to flush the final zstd frame, then close + // the file to flush OS buffers. Both must complete before rename. + if err := encoder.Close(); err != nil { + return fmt.Errorf("close zstd writer for %s: %w", tmpFilename, err) + } + if err := file.Close(); err != nil { + return fmt.Errorf("close temp file %s: %w", tmpFilename, err) } if err := os.Rename(tmpFilename, filename); err != nil { @@ -173,7 +167,7 @@ func (iod *iorData) loadFromFile(filename string) (retErr error) { return nil } -func (iod iorData) serialize() ([]byte, error) { +func (iod *iorData) serialize() ([]byte, error) { var buf bytes.Buffer enc := gob.NewEncoder(&buf) err := enc.Encode(iod.records) @@ -224,7 +218,7 @@ func (ir IterRecord) StringByName(name string) (string, error) { } } -func (iod iorData) iter() iter.Seq[IterRecord] { +func (iod *iorData) iter() iter.Seq[IterRecord] { return func(yield func(IterRecord) bool) { for key, cnt := range iod.records { record := IterRecord{ diff --git a/internal/flamegraph/iordata_test.go b/internal/flamegraph/iordata_test.go index 722cad3..f805aab 100644 --- a/internal/flamegraph/iordata_test.go +++ b/internal/flamegraph/iordata_test.go @@ -90,7 +90,7 @@ func TestMerge(t *testing.T) { t.Log("iod2", iod2) t.Log("iod3", iod3) t.Log("iod4", iod4) - merged := iod1.merge(iod2).merge(iod3).merge(iod4) + merged := *iod1.merge(iod2).merge(iod3).merge(iod4) t.Log("merged", merged) t.Run("Merged correctly", func(t *testing.T) { @@ -217,7 +217,7 @@ func TestMergeEmpty(t *testing.T) { }) empty := newIorData() - merged := iod.merge(empty) + merged := *iod.merge(empty) if len(merged.records) != 1 { t.Errorf("Expected 1 record, got %d", len(merged.records)) diff --git a/internal/flamegraph/livetrie.go b/internal/flamegraph/livetrie.go index 600e404..51f3697 100644 --- a/internal/flamegraph/livetrie.go +++ b/internal/flamegraph/livetrie.go @@ -1,10 +1,10 @@ package flamegraph import ( + "cmp" "encoding/json" "fmt" "slices" - "sort" "strings" "sync" "sync/atomic" @@ -277,8 +277,8 @@ type childSnapshotState struct { func buildSnapshotWithTotal(node *trieNode, depth int, minFraction float64, rootTotal uint64, forceKeep bool) (*trieSnapshot, uint64) { total := node.value children := slices.Clone(node.children) - sort.Slice(children, func(i, j int) bool { - return children[i].name < children[j].name + slices.SortFunc(children, func(a, b *trieNode) int { + return cmp.Compare(a.name, b.name) }) childStates := make([]childSnapshotState, 0, len(children)) @@ -335,13 +335,13 @@ func ensureFallbackVisibleChildren(children []childSnapshotState, depth int, min candidates = append(candidates, idx) } } - sort.Slice(candidates, func(i, j int) bool { - left := children[candidates[i]] - right := children[candidates[j]] - if left.total == right.total { - return left.node.name < right.node.name + slices.SortFunc(candidates, func(a, b int) int { + left := children[a] + right := children[b] + if left.total != right.total { + return cmp.Compare(right.total, left.total) } - return left.total > right.total + return cmp.Compare(left.node.name, right.node.name) }) limit := liveTrieMinVisibleChildrenWhenPruned diff --git a/internal/flamegraph/trie.go b/internal/flamegraph/trie.go index 022b846..d7790c2 100644 --- a/internal/flamegraph/trie.go +++ b/internal/flamegraph/trie.go @@ -1,6 +1,9 @@ package flamegraph -import "sort" +import ( + "cmp" + "slices" +) type trieNode struct { name string @@ -35,8 +38,8 @@ func (t *trie) computeTotals() { t.maxDepth = depth } - sort.Slice(node.children, func(i, j int) bool { - return node.children[i].name < node.children[j].name + slices.SortFunc(node.children, func(a, b *trieNode) int { + return cmp.Compare(a.name, b.name) }) total := node.value |
