summaryrefslogtreecommitdiff
path: root/internal/flamegraph
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-04-06 21:59:54 +0300
committerPaul Buetow <paul@buetow.org>2025-04-06 21:59:54 +0300
commit95e3477875c0e31e0c6907ab6afe4ce48fb37391 (patch)
treea4965cd66b377fbe18d54d66243227d59399fa3e /internal/flamegraph
parent6ca3491f421e8506fa3ff832a51b3a7d8a5c7ef6 (diff)
more refined iod format
Diffstat (limited to 'internal/flamegraph')
-rw-r--r--internal/flamegraph/counter.go3
-rw-r--r--internal/flamegraph/iordata.go40
-rw-r--r--internal/flamegraph/iordata_test.go74
3 files changed, 95 insertions, 22 deletions
diff --git a/internal/flamegraph/counter.go b/internal/flamegraph/counter.go
index 98ac035..bc30df0 100644
--- a/internal/flamegraph/counter.go
+++ b/internal/flamegraph/counter.go
@@ -4,12 +4,13 @@ type counter struct {
count uint64
duration uint64
durationToPrev uint64
- // bytes uint64 TODO implement
+ bytes uint64 // TODO: implement
}
func (c counter) add(other counter) counter {
c.count += other.count
c.duration += other.duration
c.durationToPrev += other.durationToPrev
+ c.bytes += other.bytes
return c
}
diff --git a/internal/flamegraph/iordata.go b/internal/flamegraph/iordata.go
index 1cf1f0a..2d4b46b 100644
--- a/internal/flamegraph/iordata.go
+++ b/internal/flamegraph/iordata.go
@@ -1,17 +1,20 @@
package flamegraph
import (
- "encoding/json"
"fmt"
"ior/internal/event"
"ior/internal/flags"
"ior/internal/types"
+ "iter"
"os"
+ "strings"
"time"
"github.com/DataDog/zstd"
)
+const recordSeparator = " ␞ "
+
type pathType = string
type traceIdType = types.TraceId
type commType = string
@@ -123,6 +126,37 @@ func (iod iorData) commit() error {
encoder := zstd.NewWriter(file)
defer encoder.Close()
- jsonEncoder := json.NewEncoder(encoder)
- return jsonEncoder.Encode(iod.paths)
+ // Write the data to a .txt file one line one entry and with a separator ␞, don't use JSON
+ return nil
+}
+
+func (iod iorData) lines() iter.Seq[string] {
+ return func(yield func(string) bool) {
+ for path, traceIdMap := range iod.paths {
+ for traceId, commMap := range traceIdMap {
+ for comm, pidMap := range commMap {
+ for pid, tidMap := range pidMap {
+ for tid, flagsMap := range tidMap {
+ for flags, cnt := range flagsMap {
+ joinedStr := strings.Join([]string{
+ path,
+ traceId.String(),
+ comm,
+ fmt.Sprint(pid),
+ fmt.Sprint(tid),
+ flags,
+ fmt.Sprintf("%d %d %d %d", cnt.count, cnt.duration, cnt.durationToPrev, cnt.bytes),
+ },
+ recordSeparator)
+ if !yield(joinedStr) {
+ // Stop iteration if yield returns false
+ return
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/internal/flamegraph/iordata_test.go b/internal/flamegraph/iordata_test.go
index f938a49..7496067 100644
--- a/internal/flamegraph/iordata_test.go
+++ b/internal/flamegraph/iordata_test.go
@@ -1,17 +1,14 @@
package flamegraph
import (
+ "ior/internal/types"
"testing"
)
-func TestAdd(t *testring.T) {
-
-}
-
func TestAddPath(t *testing.T) {
iod := newIorData()
path := pathType("testPath")
- traceId := traceIdType(1)
+ traceId := types.SYS_ENTER_OPENAT
comm := commType("testComm")
pid := pidType(1234)
tid := tidType(5678)
@@ -36,28 +33,29 @@ func TestAddPath(t *testing.T) {
func TestMerge(t *testing.T) {
rdwrFlag := flagsType("O_RDWR")
roFlag := flagsType("O_RDONLY")
+ traceId := types.SYS_ENTER_OPENAT
// Initialize two iorData instances with sample data
iod1 := iorData{paths: pathMap{
- "path1": {1: {"comm1": {100: {1000: {rdwrFlag: counter{
+ "path1": {traceId: {"comm1": {100: {1000: {rdwrFlag: counter{
count: 10,
duration: 1000,
durationToPrev: 100,
}}}}}}}}
iod2 := iorData{paths: pathMap{
- "path1": {1: {"comm1": {100: {1000: {roFlag: counter{
+ "path1": {traceId: {"comm1": {100: {1000: {roFlag: counter{
count: 20,
duration: 2000,
durationToPrev: 200,
}}}}}}}}
iod3 := iorData{paths: pathMap{
- "path2": {1: {"comm2": {101: {1000: {roFlag: counter{
+ "path2": {traceId: {"comm2": {101: {1000: {roFlag: counter{
count: 20,
duration: 2000,
durationToPrev: 200,
}}}}}}}}
iod4 := iorData{paths: pathMap{
- "path2": {1: {"comm2": {101: {1000: {roFlag: counter{
+ "path2": {traceId: {"comm2": {101: {1000: {roFlag: counter{
count: 40,
duration: 4000,
durationToPrev: 400,
@@ -69,16 +67,56 @@ func TestMerge(t *testing.T) {
merged := iod1.merge(iod2).merge(iod3).merge(iod4)
t.Log(merged)
- // Check if the merged data contains data from both iod1 and iod2
- if len(merged.paths) != 2 {
- t.Errorf("Expected 2 paths, got %d", len(merged.paths))
- }
+ t.Run("Merged correctly", func(t *testing.T) {
+ if len(merged.paths) != 2 {
+ t.Errorf("Expected 2 paths, got %d", len(merged.paths))
+ }
+ if merged.paths["path1"][traceId]["comm1"][100][1000][flagsType("O_RDWR")].count != 10 {
+ t.Errorf("Expected counter 10, got %d", merged.paths["path1"][1]["comm1"][100][1000][flagsType("O_RDWR")].count)
+ }
+ if merged.paths["path2"][traceId]["comm2"][101][1000][roFlag].count != 60 {
+ t.Errorf("Expected counter 60, got %d", merged.paths["path2"][1]["comm2"][101][1000][roFlag].count)
+ }
+ })
- if merged.paths["path1"][1]["comm1"][100][1000][flagsType("O_RDWR")].count != 10 {
- t.Errorf("Expected counter 10, got %d", merged.paths["path1"][1]["comm1"][100][1000][flagsType("O_RDWR")].count)
- }
+ t.Run("Iterate over lines", func(t *testing.T) {
+ expectedLines := []string{
+ "path1 ␞ enter_openat ␞ comm1 ␞ 100 ␞ 1000 ␞ O_RDWR ␞ 10 1000 100 0",
+ "path1 ␞ enter_openat ␞ comm1 ␞ 100 ␞ 1000 ␞ O_RDONLY ␞ 20 2000 200 0",
+ "path2 ␞ enter_openat ␞ comm2 ␞ 101 ␞ 1000 ␞ O_RDONLY ␞ 60 6000 600 0",
+ }
+ var lines []string
- if merged.paths["path2"][1]["comm2"][101][1000][roFlag].count != 60 {
- t.Errorf("Expected counter 60, got %d", merged.paths["path2"][1]["comm2"][101][1000][roFlag].count)
+ for line := range merged.lines() {
+ t.Log(line)
+ lines = append(lines, line)
+ }
+
+ if len(lines) != len(expectedLines) {
+ t.Errorf("Expected %d lines, got %d", len(expectedLines), len(lines))
+ }
+
+ if !bothArraysHaveSameElements(lines, expectedLines) {
+ t.Errorf("Expected lines %v, got %v", expectedLines, lines)
+ }
+ })
+}
+
+func bothArraysHaveSameElements(a, b []string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for _, v1 := range a {
+ found := false
+ for _, v2 := range b {
+ if v1 == v2 {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
}
+ return true
}