summaryrefslogtreecommitdiff
path: root/internal/flamegraph/nativesvg.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-24 20:20:42 +0200
committerPaul Buetow <paul@buetow.org>2026-02-24 20:20:42 +0200
commit8361fd22d45e4fbf6b24309aaa1b6d49d9010759 (patch)
tree1aef478a8bc096acee794802b8dc7e29b32ecdb9 /internal/flamegraph/nativesvg.go
parent81735bb46a75dce67a06e383f0703871e23b29d4 (diff)
flamegraph: add native svg pipeline and tests
Diffstat (limited to 'internal/flamegraph/nativesvg.go')
-rw-r--r--internal/flamegraph/nativesvg.go74
1 files changed, 74 insertions, 0 deletions
diff --git a/internal/flamegraph/nativesvg.go b/internal/flamegraph/nativesvg.go
new file mode 100644
index 0000000..2c76a7d
--- /dev/null
+++ b/internal/flamegraph/nativesvg.go
@@ -0,0 +1,74 @@
+package flamegraph
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+
+ "iter"
+)
+
+type NativeSVG struct {
+ fields []string
+ countField string
+ config SVGConfig
+}
+
+func NewNativeSVG(fields []string, countField string) NativeSVG {
+ return NativeSVG{
+ fields: fields,
+ countField: countField,
+ config: defaultSVGConfig(),
+ }
+}
+
+func (n NativeSVG) WriteSVGFromFile(iorDataFile string) error {
+ outFile := fmt.Sprintf("%s.%s-by-%s.svg",
+ strings.TrimSuffix(iorDataFile, ".ior.zst"),
+ strings.Join(n.fields, ":"),
+ n.countField,
+ )
+
+ iod, err := newIorDataFromFile(iorDataFile)
+ if err != nil {
+ return fmt.Errorf("read ior data: %w", err)
+ }
+
+ fd, err := os.Create(outFile)
+ if err != nil {
+ return fmt.Errorf("create output %s: %w", outFile, err)
+ }
+ defer fd.Close()
+
+ return n.WriteSVGFromIter(iod.iter(), fd)
+}
+
+func (n NativeSVG) WriteSVGFromIter(records iter.Seq[IterRecord], w io.Writer) error {
+ tr := newTrie()
+ for record := range records {
+ frames, err := n.recordFrames(record)
+ if err != nil {
+ return err
+ }
+ tr.add(frames, record.Cnt.ValueByName(n.countField))
+ }
+ tr.computeTotals()
+ return WriteSVG(w, tr, n.config)
+}
+
+func (n NativeSVG) recordFrames(record IterRecord) ([]string, error) {
+ var frames []string
+ for _, fieldName := range n.fields {
+ value, err := record.StringByName(fieldName)
+ if err != nil {
+ return nil, fmt.Errorf("field %s: %w", fieldName, err)
+ }
+ for _, part := range strings.Split(value, ";") {
+ if part != "" {
+ frames = append(frames, part)
+ }
+ }
+ }
+ return frames, nil
+}