summaryrefslogtreecommitdiff
path: root/internal/flamegraph/collapsed.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-03-18 22:25:27 +0200
committerPaul Buetow <paul@buetow.org>2025-03-18 22:25:27 +0200
commit6010e057e2d0593a6c6b50f4c7aee301a86a478a (patch)
treee8018323e426477fb41e938538f25c4e7df4fce3 /internal/flamegraph/collapsed.go
parent000891ad475c89d88874d4290728857ecbe53a7d (diff)
workers are parallelized now
Diffstat (limited to 'internal/flamegraph/collapsed.go')
-rw-r--r--internal/flamegraph/collapsed.go95
1 files changed, 95 insertions, 0 deletions
diff --git a/internal/flamegraph/collapsed.go b/internal/flamegraph/collapsed.go
new file mode 100644
index 0000000..f61d917
--- /dev/null
+++ b/internal/flamegraph/collapsed.go
@@ -0,0 +1,95 @@
+package flamegraph
+
+import (
+ "fmt"
+ "ior/internal/types"
+ "os"
+ "strings"
+ "sync"
+)
+
+type counter struct {
+ count uint64
+ duration uint64
+}
+
+func (c *counter) merge(other counter) {
+ c.count += other.count
+ c.duration += other.duration
+}
+
+type collapsed map[string]map[types.TraceId]counter
+
+// TODO: Unit test this
+func (c collapsed) merge(other collapsed) (merged int) {
+ for k, v := range other {
+ if _, ok := c[k]; !ok {
+ c[k] = make(map[types.TraceId]counter)
+ }
+ for traceId, cnt := range v {
+ if existingCnt, ok := c[k][traceId]; ok {
+ existingCnt.merge(cnt)
+ merged++
+ c[k][traceId] = existingCnt
+ continue
+ }
+ c[k][traceId] = cnt
+ }
+ }
+ return
+}
+
+func (c collapsed) dump() {
+ var wg sync.WaitGroup
+ wg.Add(4)
+
+ go c.dumpBy(&wg, "ior-by-path-count-flamegraph.collapsed", true, func(cnt counter) uint64 {
+ return cnt.count
+ })
+ go c.dumpBy(&wg, "ior-by-path-duration-flamegraph.collapsed", true, func(cnt counter) uint64 {
+ return cnt.duration
+ })
+ go c.dumpBy(&wg, "ior-by-syscall-count-flamegraph.collapsed", false, func(cnt counter) uint64 {
+ return cnt.count
+ })
+ go c.dumpBy(&wg, "ior-by-syscall-duration-flamegraph.collapsed", false, func(cnt counter) uint64 {
+ return cnt.duration
+ })
+
+ wg.Wait()
+}
+
+func (c collapsed) dumpBy(wg *sync.WaitGroup, outfile string, syscallAtTop bool, by func(counter) uint64) {
+ defer wg.Done()
+
+ fmt.Println("Dumping", outfile)
+ file, err := os.Create(outfile)
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
+
+ for path, value := range c {
+ var sb strings.Builder
+
+ for i, part := range strings.Split(path, "/") {
+ if i > 1 {
+ sb.WriteString(";")
+ sb.WriteString("/")
+ }
+ sb.WriteString(part)
+ }
+
+ for traceId, cnt := range value {
+ var err error
+ if syscallAtTop {
+ _, err = fmt.Fprintf(file, "%s;syscall`%s %v\n", sb.String(), traceId.Name(), by(cnt))
+ } else {
+ _, err = fmt.Fprintf(file, "syscall`%s;%s %v\n", traceId.Name(), sb.String(), by(cnt))
+ }
+ if err != nil {
+ panic(err)
+ }
+ }
+ }
+}