summaryrefslogtreecommitdiff
path: root/internal/flamegraph
diff options
context:
space:
mode:
Diffstat (limited to 'internal/flamegraph')
-rw-r--r--internal/flamegraph/iordata.go40
-rw-r--r--internal/flamegraph/iordata_test.go4
-rw-r--r--internal/flamegraph/livetrie.go18
-rw-r--r--internal/flamegraph/trie.go9
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