diff options
| author | Paul Buetow <paul@buetow.org> | 2025-03-11 22:42:57 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-03-11 22:42:57 +0200 |
| commit | 2cf6f74135b830885bad25e4f6aba3e0e31581b8 (patch) | |
| tree | 4bb0249a01cd78ca4385e865f0ecd0c2d1601156 | |
| parent | 41e2d2661faa4d72ab6f03e7f1ba702593754d97 (diff) | |
more on tree
| -rw-r--r-- | internal/event/event.go | 15 | ||||
| -rw-r--r-- | internal/eventloop.go | 35 | ||||
| -rw-r--r-- | internal/ior.go | 1 | ||||
| -rw-r--r-- | internal/tree/tree.go | 82 |
4 files changed, 121 insertions, 12 deletions
diff --git a/internal/event/event.go b/internal/event/event.go index db8028c..680ee62 100644 --- a/internal/event/event.go +++ b/internal/event/event.go @@ -27,7 +27,7 @@ type Pair struct { EnterEv, ExitEv Event File file.File Comm string - duration uint64 + Duration uint64 TracepointMismatch bool // To calculate the time difference from the previoud event. @@ -42,7 +42,7 @@ func NewPair(enterEv Event) *Pair { } func (e *Pair) CalculateDurations() { - e.duration = e.ExitEv.GetTime() - e.EnterEv.GetTime() + e.Duration = e.ExitEv.GetTime() - e.EnterEv.GetTime() if e.PrevPair != nil { e.durationToPrev = e.EnterEv.GetTime() - e.PrevPair.ExitEv.GetTime() @@ -58,7 +58,7 @@ const EventStreamHeader = "durationToPrevNs,durationNs,comm,pid.tid,name,ret,not func (e *Pair) String() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("%08d,%08d", e.durationToPrev, e.duration)) + sb.WriteString(fmt.Sprintf("%08d,%08d", e.durationToPrev, e.Duration)) sb.WriteString(",") sb.WriteString(e.Comm) @@ -95,3 +95,12 @@ func (e *Pair) Recycle() { e.PrevPair = nil poolOfEventPairs.Put(e) } + +// Only recycle the previous event, as the current event is the previous event of the next event! +// And the previous event is required for calculation of durationToPrev! +func (e *Pair) RecyclePrev() { + if e.PrevPair == nil { + return + } + e.PrevPair.Recycle() +} diff --git a/internal/eventloop.go b/internal/eventloop.go index 7a322a7..f6cd410 100644 --- a/internal/eventloop.go +++ b/internal/eventloop.go @@ -24,6 +24,7 @@ type eventLoop struct { comms map[uint32]string // Program or thread name of the current Tid. prevPairs map[uint32]*event.Pair // Previous event (to calculate time differences between two events) tree tree.Tree // Storing all paths in a tree structure for analysis + done chan struct{} // Statistics numTracepoints uint @@ -36,6 +37,7 @@ type eventLoop struct { func newEventLoop(flags flags.Flags) *eventLoop { return &eventLoop{ flags: flags, + done: make(chan struct{}), filter: newEventFilter(flags), enterEvs: make(map[uint32]*event.Pair), files: make(map[int32]file.File), @@ -58,24 +60,39 @@ func (e *eventLoop) stats() string { e.numSyscallsAfterFilter, float64(e.numSyscallsAfterFilter)/duration.Seconds()) } +func (e *eventLoop) stop() { + close(e.done) + if e.flags.TreeEnable { + fmt.Println("Waiting for tree to finish") + <-e.tree.Finished + } +} + func (e *eventLoop) run(rawCh <-chan []byte) { - e.startTime = time.Now() + var recycle bool + + if e.flags.TreeEnable { + e.tree.Start() + } if e.flags.PprofEnable { fmt.Println("Profiling, press Ctrl+C to stop") fmt.Println(event.EventStreamHeader) } + + e.startTime = time.Now() for ev := range e.events(rawCh) { switch { case e.flags.TreeEnable: - // e.tree.Add(ev) + e.tree.Add(ev) + recycle = false // tree needs to recycle by itself case e.flags.PprofEnable: + recycle = true default: + recycle = true fmt.Println(ev.String()) } - if ev.PrevPair != nil { - // Only recycle the previous event, as the current event is the previous event of the next event! - ev.PrevPair.Recycle() - continue + if recycle { + ev.RecyclePrev() } e.numSyscallsAfterFilter++ } @@ -87,6 +104,12 @@ func (e *eventLoop) events(rawCh <-chan []byte) <-chan *event.Pair { go func() { defer close(ch) for raw := range rawCh { + select { + case <-e.done: + return + default: + } + e.numTracepoints++ switch EventType(raw[0]) { case ENTER_OPEN_EVENT: diff --git a/internal/ior.go b/internal/ior.go index 6d73011..76cf17d 100644 --- a/internal/ior.go +++ b/internal/ior.go @@ -88,6 +88,7 @@ func Run(flags flags.Flags) { pprof.StopCPUProfile() pprof.WriteHeapProfile(memProfile) } + loop.stop() fmt.Println("Good bye... (unloading BPF tracepoints will take a few seconds...)") os.Exit(0) }() diff --git a/internal/tree/tree.go b/internal/tree/tree.go index 462ba19..a7c5c20 100644 --- a/internal/tree/tree.go +++ b/internal/tree/tree.go @@ -1,12 +1,88 @@ package tree +import ( + "fmt" + "ior/internal/event" + "ior/internal/generated/types" + "os" + "path" + "strings" +) + +type counter struct { + count uint64 + duration uint64 +} + +// It's a "flat tree" stored in a map, one key per directory type Tree struct { + // Collapsed flamegraph stats collector + collapsed map[string]map[types.TraceId]counter + inCh chan *event.Pair + Finished chan struct{} } func New() Tree { - return Tree{} + return Tree{ + collapsed: make(map[string]map[types.TraceId]counter), + inCh: make(chan *event.Pair, 4096), + Finished: make(chan struct{}), + } +} + +func (t Tree) Add(ev *event.Pair) { + t.inCh <- ev +} + +func (t Tree) Start() { + go func() { + for ev := range t.inCh { + pathname := path.Dir(ev.File.Name()) + pathMap, ok := t.collapsed[pathname] + if !ok { + pathMap = make(map[types.TraceId]counter) + } + + traceId := ev.EnterEv.GetTraceId() + cnt := pathMap[traceId] + cnt.count++ + cnt.duration += ev.Duration + pathMap[traceId] = cnt + + t.collapsed[pathname] = pathMap + ev.RecyclePrev() + } + fmt.Println("Tree processed last event") + t.mustDump("ior.collapsed") + close(t.Finished) + fmt.Println("DEBUG") + }() } -// func (t Tree) Add(ev *) { +// TODO: By count and by duration +// TODO: Reverse by syscall vs path +// TODO: Allow full path? +func (t Tree) mustDump(outfile string) { + fmt.Println("Writing", outfile) + file, err := os.Create(outfile) + if err != nil { + panic(err) + } + defer file.Close() -// } + for path, value := range t.collapsed { + var sb strings.Builder + + for _, part := range strings.Split(path, "/") { + sb.WriteString("/") + sb.WriteString(part) + } + + for traceId, cnt := range value { + _, err := fmt.Fprintf(file, "%s;%s %v\n", sb.String(), traceId, cnt.count) + if err != nil { + panic(err) + } + } + } +} |
