summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-18 20:54:35 +0200
committerPaul Buetow <paul@buetow.org>2026-03-18 20:54:35 +0200
commitcd554b0af706b5f62b4e1bfde04091052b4aac61 (patch)
treee6d02f1c2a1da27da17386e8832c2d4a3e699cdf /internal
parentb421b2232351049277ee4ad5b31367bb2b6779bb (diff)
cleanup
Diffstat (limited to 'internal')
-rw-r--r--internal/c/generated_tracepoints.c46
-rw-r--r--internal/event/event.go2
-rw-r--r--internal/event/pair.go23
-rw-r--r--internal/file/file.go9
-rw-r--r--internal/flags/flags.go26
-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
-rw-r--r--internal/generate/codegen.go9
-rw-r--r--internal/generate/typesgo.go2
-rw-r--r--internal/globalfilter/filter.go20
-rw-r--r--internal/parquet/schema.go16
-rw-r--r--internal/probemanager/grouping.go14
-rw-r--r--internal/probemanager/manager.go5
-rw-r--r--internal/statsengine/bench_test.go4
-rw-r--r--internal/statsengine/filerank.go11
-rw-r--r--internal/statsengine/process.go25
-rw-r--r--internal/statsengine/syscall.go25
-rw-r--r--internal/statsengine/syscall_test.go12
-rw-r--r--internal/tui/dashboard/bubbles.go19
-rw-r--r--internal/tui/dashboard/files.go10
-rw-r--r--internal/tui/dashboard/icicle.go15
-rw-r--r--internal/tui/dashboard/treemap.go27
-rw-r--r--internal/tui/flamegraph/model.go20
-rw-r--r--internal/tui/flamegraph/renderer.go19
-rw-r--r--internal/tui/pidpicker/proclist.go19
-rw-r--r--internal/types/generated_types.go18
28 files changed, 265 insertions, 202 deletions
diff --git a/internal/c/generated_tracepoints.c b/internal/c/generated_tracepoints.c
index 06f8c39..b4d4e0f 100644
--- a/internal/c/generated_tracepoints.c
+++ b/internal/c/generated_tracepoints.c
@@ -1,7 +1,7 @@
// Code generated - don't change manually!
-/// Ignoring sys_enter_accept sys_exit_accept as possibly not file I/O related
/// Ignoring sys_enter_accept4 sys_exit_accept4 as possibly not file I/O related
+/// Ignoring sys_enter_accept sys_exit_accept as possibly not file I/O related
/// Ignoring sys_enter_acct sys_exit_acct as possibly not file I/O related
/// Ignoring sys_enter_add_key sys_exit_add_key as possibly not file I/O related
/// Ignoring sys_enter_adjtimex sys_exit_adjtimex as possibly not file I/O related
@@ -17,20 +17,20 @@
/// Ignoring sys_enter_clock_gettime sys_exit_clock_gettime as possibly not file I/O related
/// Ignoring sys_enter_clock_nanosleep sys_exit_clock_nanosleep as possibly not file I/O related
/// Ignoring sys_enter_clock_settime sys_exit_clock_settime as possibly not file I/O related
-/// Ignoring sys_enter_clone sys_exit_clone as possibly not file I/O related
/// Ignoring sys_enter_clone3 sys_exit_clone3 as possibly not file I/O related
+/// Ignoring sys_enter_clone sys_exit_clone as possibly not file I/O related
/// Ignoring sys_enter_connect sys_exit_connect as possibly not file I/O related
/// Ignoring sys_enter_delete_module sys_exit_delete_module as possibly not file I/O related
-/// Ignoring sys_enter_epoll_create sys_exit_epoll_create as possibly not file I/O related
/// Ignoring sys_enter_epoll_create1 sys_exit_epoll_create1 as possibly not file I/O related
+/// Ignoring sys_enter_epoll_create sys_exit_epoll_create as possibly not file I/O related
/// Ignoring sys_enter_epoll_ctl sys_exit_epoll_ctl as possibly not file I/O related
-/// Ignoring sys_enter_epoll_pwait sys_exit_epoll_pwait as possibly not file I/O related
/// Ignoring sys_enter_epoll_pwait2 sys_exit_epoll_pwait2 as possibly not file I/O related
+/// Ignoring sys_enter_epoll_pwait sys_exit_epoll_pwait as possibly not file I/O related
/// Ignoring sys_enter_epoll_wait sys_exit_epoll_wait as possibly not file I/O related
-/// Ignoring sys_enter_eventfd sys_exit_eventfd as possibly not file I/O related
/// Ignoring sys_enter_eventfd2 sys_exit_eventfd2 as possibly not file I/O related
-/// Ignoring sys_enter_execve sys_exit_execve as possibly not file I/O related
+/// Ignoring sys_enter_eventfd sys_exit_eventfd as possibly not file I/O related
/// Ignoring sys_enter_execveat sys_exit_execveat as possibly not file I/O related
+/// Ignoring sys_enter_execve sys_exit_execve as possibly not file I/O related
/// Ignoring sys_enter_exit sys_exit_exit as possibly not file I/O related
/// Ignoring sys_enter_exit_group sys_exit_exit_group as possibly not file I/O related
/// Ignoring sys_enter_fanotify_init sys_exit_fanotify_init as possibly not file I/O related
@@ -42,14 +42,13 @@
/// Ignoring sys_enter_futex_wait sys_exit_futex_wait as possibly not file I/O related
/// Ignoring sys_enter_futex_waitv sys_exit_futex_waitv as possibly not file I/O related
/// Ignoring sys_enter_futex_wake sys_exit_futex_wake as possibly not file I/O related
-/// Ignoring sys_enter_get_mempolicy sys_exit_get_mempolicy as possibly not file I/O related
-/// Ignoring sys_enter_get_robust_list sys_exit_get_robust_list as possibly not file I/O related
/// Ignoring sys_enter_getcpu sys_exit_getcpu as possibly not file I/O related
/// Ignoring sys_enter_getegid sys_exit_getegid as possibly not file I/O related
/// Ignoring sys_enter_geteuid sys_exit_geteuid as possibly not file I/O related
/// Ignoring sys_enter_getgid sys_exit_getgid as possibly not file I/O related
/// Ignoring sys_enter_getgroups sys_exit_getgroups as possibly not file I/O related
/// Ignoring sys_enter_getitimer sys_exit_getitimer as possibly not file I/O related
+/// Ignoring sys_enter_get_mempolicy sys_exit_get_mempolicy as possibly not file I/O related
/// Ignoring sys_enter_getpeername sys_exit_getpeername as possibly not file I/O related
/// Ignoring sys_enter_getpgid sys_exit_getpgid as possibly not file I/O related
/// Ignoring sys_enter_getpgrp sys_exit_getpgrp as possibly not file I/O related
@@ -60,6 +59,7 @@
/// Ignoring sys_enter_getresgid sys_exit_getresgid as possibly not file I/O related
/// Ignoring sys_enter_getresuid sys_exit_getresuid as possibly not file I/O related
/// Ignoring sys_enter_getrlimit sys_exit_getrlimit as possibly not file I/O related
+/// Ignoring sys_enter_get_robust_list sys_exit_get_robust_list as possibly not file I/O related
/// Ignoring sys_enter_getrusage sys_exit_getrusage as possibly not file I/O related
/// Ignoring sys_enter_getsid sys_exit_getsid as possibly not file I/O related
/// Ignoring sys_enter_getsockname sys_exit_getsockname as possibly not file I/O related
@@ -69,8 +69,8 @@
/// Ignoring sys_enter_getuid sys_exit_getuid as possibly not file I/O related
/// Ignoring sys_enter_init_module sys_exit_init_module as possibly not file I/O related
/// Ignoring sys_enter_inotify_add_watch sys_exit_inotify_add_watch as possibly not file I/O related
-/// Ignoring sys_enter_inotify_init sys_exit_inotify_init as possibly not file I/O related
/// Ignoring sys_enter_inotify_init1 sys_exit_inotify_init1 as possibly not file I/O related
+/// Ignoring sys_enter_inotify_init sys_exit_inotify_init as possibly not file I/O related
/// Ignoring sys_enter_inotify_rm_watch sys_exit_inotify_rm_watch as possibly not file I/O related
/// Ignoring sys_enter_ioperm sys_exit_ioperm as possibly not file I/O related
/// Ignoring sys_enter_iopl sys_exit_iopl as possibly not file I/O related
@@ -97,11 +97,11 @@
/// Ignoring sys_enter_memfd_secret sys_exit_memfd_secret as possibly not file I/O related
/// Ignoring sys_enter_migrate_pages sys_exit_migrate_pages as possibly not file I/O related
/// Ignoring sys_enter_mincore sys_exit_mincore as possibly not file I/O related
-/// Ignoring sys_enter_mknod sys_exit_mknod as possibly not file I/O related
/// Ignoring sys_enter_mknodat sys_exit_mknodat as possibly not file I/O related
-/// Ignoring sys_enter_mlock sys_exit_mlock as possibly not file I/O related
+/// Ignoring sys_enter_mknod sys_exit_mknod as possibly not file I/O related
/// Ignoring sys_enter_mlock2 sys_exit_mlock2 as possibly not file I/O related
/// Ignoring sys_enter_mlockall sys_exit_mlockall as possibly not file I/O related
+/// Ignoring sys_enter_mlock sys_exit_mlock as possibly not file I/O related
/// Ignoring sys_enter_modify_ldt sys_exit_modify_ldt as possibly not file I/O related
/// Ignoring sys_enter_mount sys_exit_mount as possibly not file I/O related
/// Ignoring sys_enter_move_mount sys_exit_move_mount as possibly not file I/O related
@@ -119,8 +119,8 @@
/// Ignoring sys_enter_msgget sys_exit_msgget as possibly not file I/O related
/// Ignoring sys_enter_msgrcv sys_exit_msgrcv as possibly not file I/O related
/// Ignoring sys_enter_msgsnd sys_exit_msgsnd as possibly not file I/O related
-/// Ignoring sys_enter_munlock sys_exit_munlock as possibly not file I/O related
/// Ignoring sys_enter_munlockall sys_exit_munlockall as possibly not file I/O related
+/// Ignoring sys_enter_munlock sys_exit_munlock as possibly not file I/O related
/// Ignoring sys_enter_munmap sys_exit_munmap as possibly not file I/O related
/// Ignoring sys_enter_nanosleep sys_exit_nanosleep as possibly not file I/O related
/// Ignoring sys_enter_newuname sys_exit_newuname as possibly not file I/O related
@@ -129,8 +129,8 @@
/// Ignoring sys_enter_personality sys_exit_personality as possibly not file I/O related
/// Ignoring sys_enter_pidfd_open sys_exit_pidfd_open as possibly not file I/O related
/// Ignoring sys_enter_pidfd_send_signal sys_exit_pidfd_send_signal as possibly not file I/O related
-/// Ignoring sys_enter_pipe sys_exit_pipe as possibly not file I/O related
/// Ignoring sys_enter_pipe2 sys_exit_pipe2 as possibly not file I/O related
+/// Ignoring sys_enter_pipe sys_exit_pipe as possibly not file I/O related
/// Ignoring sys_enter_pivot_root sys_exit_pivot_root as possibly not file I/O related
/// Ignoring sys_enter_pkey_alloc sys_exit_pkey_alloc as possibly not file I/O related
/// Ignoring sys_enter_pkey_free sys_exit_pkey_free as possibly not file I/O related
@@ -162,11 +162,11 @@
/// Ignoring sys_enter_rt_sigsuspend sys_exit_rt_sigsuspend as possibly not file I/O related
/// Ignoring sys_enter_rt_sigtimedwait sys_exit_rt_sigtimedwait as possibly not file I/O related
/// Ignoring sys_enter_rt_tgsigqueueinfo sys_exit_rt_tgsigqueueinfo as possibly not file I/O related
-/// Ignoring sys_enter_sched_get_priority_max sys_exit_sched_get_priority_max as possibly not file I/O related
-/// Ignoring sys_enter_sched_get_priority_min sys_exit_sched_get_priority_min as possibly not file I/O related
/// Ignoring sys_enter_sched_getaffinity sys_exit_sched_getaffinity as possibly not file I/O related
/// Ignoring sys_enter_sched_getattr sys_exit_sched_getattr as possibly not file I/O related
/// Ignoring sys_enter_sched_getparam sys_exit_sched_getparam as possibly not file I/O related
+/// Ignoring sys_enter_sched_get_priority_max sys_exit_sched_get_priority_max as possibly not file I/O related
+/// Ignoring sys_enter_sched_get_priority_min sys_exit_sched_get_priority_min as possibly not file I/O related
/// Ignoring sys_enter_sched_getscheduler sys_exit_sched_getscheduler as possibly not file I/O related
/// Ignoring sys_enter_sched_rr_get_interval sys_exit_sched_rr_get_interval as possibly not file I/O related
/// Ignoring sys_enter_sched_setaffinity sys_exit_sched_setaffinity as possibly not file I/O related
@@ -184,10 +184,6 @@
/// Ignoring sys_enter_sendmmsg sys_exit_sendmmsg as possibly not file I/O related
/// Ignoring sys_enter_sendmsg sys_exit_sendmsg as possibly not file I/O related
/// Ignoring sys_enter_sendto sys_exit_sendto as possibly not file I/O related
-/// Ignoring sys_enter_set_mempolicy sys_exit_set_mempolicy as possibly not file I/O related
-/// Ignoring sys_enter_set_mempolicy_home_node sys_exit_set_mempolicy_home_node as possibly not file I/O related
-/// Ignoring sys_enter_set_robust_list sys_exit_set_robust_list as possibly not file I/O related
-/// Ignoring sys_enter_set_tid_address sys_exit_set_tid_address as possibly not file I/O related
/// Ignoring sys_enter_setdomainname sys_exit_setdomainname as possibly not file I/O related
/// Ignoring sys_enter_setfsgid sys_exit_setfsgid as possibly not file I/O related
/// Ignoring sys_enter_setfsuid sys_exit_setfsuid as possibly not file I/O related
@@ -195,6 +191,8 @@
/// Ignoring sys_enter_setgroups sys_exit_setgroups as possibly not file I/O related
/// Ignoring sys_enter_sethostname sys_exit_sethostname as possibly not file I/O related
/// Ignoring sys_enter_setitimer sys_exit_setitimer as possibly not file I/O related
+/// Ignoring sys_enter_set_mempolicy sys_exit_set_mempolicy as possibly not file I/O related
+/// Ignoring sys_enter_set_mempolicy_home_node sys_exit_set_mempolicy_home_node as possibly not file I/O related
/// Ignoring sys_enter_setns sys_exit_setns as possibly not file I/O related
/// Ignoring sys_enter_setpgid sys_exit_setpgid as possibly not file I/O related
/// Ignoring sys_enter_setpriority sys_exit_setpriority as possibly not file I/O related
@@ -203,8 +201,10 @@
/// Ignoring sys_enter_setresuid sys_exit_setresuid as possibly not file I/O related
/// Ignoring sys_enter_setreuid sys_exit_setreuid as possibly not file I/O related
/// Ignoring sys_enter_setrlimit sys_exit_setrlimit as possibly not file I/O related
+/// Ignoring sys_enter_set_robust_list sys_exit_set_robust_list as possibly not file I/O related
/// Ignoring sys_enter_setsid sys_exit_setsid as possibly not file I/O related
/// Ignoring sys_enter_setsockopt sys_exit_setsockopt as possibly not file I/O related
+/// Ignoring sys_enter_set_tid_address sys_exit_set_tid_address as possibly not file I/O related
/// Ignoring sys_enter_settimeofday sys_exit_settimeofday as possibly not file I/O related
/// Ignoring sys_enter_setuid sys_exit_setuid as possibly not file I/O related
/// Ignoring sys_enter_shmat sys_exit_shmat as possibly not file I/O related
@@ -213,8 +213,8 @@
/// Ignoring sys_enter_shmget sys_exit_shmget as possibly not file I/O related
/// Ignoring sys_enter_shutdown sys_exit_shutdown as possibly not file I/O related
/// Ignoring sys_enter_sigaltstack sys_exit_sigaltstack as possibly not file I/O related
-/// Ignoring sys_enter_signalfd sys_exit_signalfd as possibly not file I/O related
/// Ignoring sys_enter_signalfd4 sys_exit_signalfd4 as possibly not file I/O related
+/// Ignoring sys_enter_signalfd sys_exit_signalfd as possibly not file I/O related
/// Ignoring sys_enter_socket sys_exit_socket as possibly not file I/O related
/// Ignoring sys_enter_socketpair sys_exit_socketpair as possibly not file I/O related
/// Ignoring sys_enter_splice sys_exit_splice as possibly not file I/O related
@@ -228,12 +228,12 @@
/// Ignoring sys_enter_time sys_exit_time as possibly not file I/O related
/// Ignoring sys_enter_timer_create sys_exit_timer_create as possibly not file I/O related
/// Ignoring sys_enter_timer_delete sys_exit_timer_delete as possibly not file I/O related
-/// Ignoring sys_enter_timer_getoverrun sys_exit_timer_getoverrun as possibly not file I/O related
-/// Ignoring sys_enter_timer_gettime sys_exit_timer_gettime as possibly not file I/O related
-/// Ignoring sys_enter_timer_settime sys_exit_timer_settime as possibly not file I/O related
/// Ignoring sys_enter_timerfd_create sys_exit_timerfd_create as possibly not file I/O related
/// Ignoring sys_enter_timerfd_gettime sys_exit_timerfd_gettime as possibly not file I/O related
/// Ignoring sys_enter_timerfd_settime sys_exit_timerfd_settime as possibly not file I/O related
+/// Ignoring sys_enter_timer_getoverrun sys_exit_timer_getoverrun as possibly not file I/O related
+/// Ignoring sys_enter_timer_gettime sys_exit_timer_gettime as possibly not file I/O related
+/// Ignoring sys_enter_timer_settime sys_exit_timer_settime as possibly not file I/O related
/// Ignoring sys_enter_times sys_exit_times as possibly not file I/O related
/// Ignoring sys_enter_tkill sys_exit_tkill as possibly not file I/O related
/// Ignoring sys_enter_umask sys_exit_umask as possibly not file I/O related
diff --git a/internal/event/event.go b/internal/event/event.go
index 48bde48..c846e04 100644
--- a/internal/event/event.go
+++ b/internal/event/event.go
@@ -7,7 +7,7 @@ import (
)
var poolOfEventPairs = sync.Pool{
- New: func() interface{} { return &Pair{} },
+ New: func() any { return &Pair{} },
}
// Event is the common contract implemented by decoded syscall trace events.
diff --git a/internal/event/pair.go b/internal/event/pair.go
index 3eb8a16..4d3f342 100644
--- a/internal/event/pair.go
+++ b/internal/event/pair.go
@@ -25,20 +25,13 @@ type Pair struct {
Comm string
Duration uint64
DurationToPrev uint64
- Bytes uint64 // Number of bytes transferred (read/write/transfer syscalls only)
- Equals bool
+ Bytes uint64 // Number of bytes transferred (read/write/transfer syscalls only)
}
func NewPair(enterEv Event) *Pair {
e := poolOfEventPairs.Get().(*Pair)
- e.EnterEv = enterEv
- e.ExitEv = nil
- e.File = nil
- e.Comm = ""
- e.Duration = 0
- e.DurationToPrev = 0
- e.Bytes = 0
- e.Equals = false
+ // Zero all fields via struct literal to prevent stale data from previous pool reuse.
+ *e = Pair{EnterEv: enterEv}
return e
}
@@ -126,13 +119,7 @@ func (e *Pair) Recycle() {
if e.ExitEv != nil {
e.ExitEv.Recycle()
}
- e.EnterEv = nil
- e.ExitEv = nil
- e.File = nil
- e.Comm = ""
- e.Duration = 0
- e.DurationToPrev = 0
- e.Bytes = 0
- e.Equals = false
+ // Zero all fields via struct literal to prevent stale data on pool reuse.
+ *e = Pair{}
poolOfEventPairs.Put(e)
}
diff --git a/internal/file/file.go b/internal/file/file.go
index ab67fe2..fc9c320 100644
--- a/internal/file/file.go
+++ b/internal/file/file.go
@@ -99,8 +99,11 @@ func parseFlagsFromFdInfo(data []byte) (Flags, error) {
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "flags:") {
- flagsStr := strings.Fields(line)[1]
- flags, err := strconv.ParseUint(flagsStr, 8, 32)
+ fields := strings.Fields(line)
+ if len(fields) < 2 {
+ return unknownFlag, fmt.Errorf("malformed flags line in fdinfo: %q", line)
+ }
+ flags, err := strconv.ParseUint(fields[1], 8, 32)
return Flags(flags), err
}
}
@@ -118,7 +121,7 @@ func (f *FdFile) String() string {
var sb strings.Builder
if len(f.name) == 0 {
- sb.WriteString("E:name") // Emtpy name string
+ sb.WriteString("E:name") // Empty name string
} else {
sb.WriteString(f.name)
}
diff --git a/internal/flags/flags.go b/internal/flags/flags.go
index 0baac9a..9b1c3cf 100644
--- a/internal/flags/flags.go
+++ b/internal/flags/flags.go
@@ -26,7 +26,7 @@ type Config struct {
PprofEnable bool
Duration int
- // Tracepints flags
+ // Tracepoints flags
TracepointsToAttach []*regexp.Regexp
TracepointsToExclude []*regexp.Regexp
@@ -234,6 +234,30 @@ func extractTracepointFlags(tracepoints string) (regexes []*regexp.Regexp, err e
return regexes, nil
}
+// TraceFilter builds a globalfilter.Filter from the config's filter fields.
+// If GlobalFilter is already active, it is returned as-is. Otherwise,
+// individual CLI-level filters (CommFilter, PathFilter, PidFilter, TidFilter)
+// are merged into a new filter.
+func (cfg Config) TraceFilter() globalfilter.Filter {
+ filter := cfg.GlobalFilter.Clone()
+ if filter.IsActive() {
+ return filter
+ }
+ if cfg.CommFilter != "" {
+ filter.Comm = &globalfilter.StringFilter{Pattern: cfg.CommFilter}
+ }
+ if cfg.PathFilter != "" {
+ filter.File = &globalfilter.StringFilter{Pattern: cfg.PathFilter}
+ }
+ if cfg.PidFilter > 0 {
+ filter.PID = globalfilter.NewEqFilter(int64(cfg.PidFilter))
+ }
+ if cfg.TidFilter > 0 {
+ filter.TID = globalfilter.NewEqFilter(int64(cfg.TidFilter))
+ }
+ return filter
+}
+
func (flags Config) ShouldIAttachTracepoint(tracepointName string) bool {
for _, re := range flags.TracepointsToExclude {
if re.MatchString(tracepointName) {
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
diff --git a/internal/generate/codegen.go b/internal/generate/codegen.go
index 9b9f52c..1c7a9b3 100644
--- a/internal/generate/codegen.go
+++ b/internal/generate/codegen.go
@@ -1,8 +1,9 @@
package generate
import (
+ "cmp"
"fmt"
- "sort"
+ "slices"
"strings"
)
@@ -37,8 +38,8 @@ func GenerateTracepointsC(formats []Format) string {
accepted = append(accepted, tracepoints...)
}
- sort.Slice(accepted, func(i, j int) bool {
- return accepted[i].Format.ID > accepted[j].Format.ID
+ slices.SortFunc(accepted, func(a, b GeneratedTracepoint) int {
+ return cmp.Compare(b.Format.ID, a.Format.ID)
})
b.WriteString("\n")
@@ -147,6 +148,6 @@ func syscallFormatNames(sc Syscall) []string {
if sc.Exit != nil {
names = append(names, sc.Exit.Name)
}
- sort.Strings(names)
+ slices.Sort(names)
return names
}
diff --git a/internal/generate/typesgo.go b/internal/generate/typesgo.go
index dca40fc..9f91981 100644
--- a/internal/generate/typesgo.go
+++ b/internal/generate/typesgo.go
@@ -295,7 +295,7 @@ func writeGetterMethods(b *strings.Builder, goName, selfRef string) {
}
func writeSyncPool(b *strings.Builder, goName, selfRef string) {
- fmt.Fprintf(b, "var poolOf%ss = sync.Pool{\n\tNew: func() interface{} { return &%s{} },\n}\n\n", goName, goName)
+ fmt.Fprintf(b, "var poolOf%ss = sync.Pool{\n\tNew: func() any { return &%s{} },\n}\n\n", goName, goName)
fmt.Fprintf(b, "func New%s(raw []byte) *%s {\n", goName, goName)
fmt.Fprintf(b, "\t%s := poolOf%ss.Get().(*%s)\n", selfRef, goName, goName)
fmt.Fprintf(b, "\tif err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, %s); err != nil {\n", selfRef)
diff --git a/internal/globalfilter/filter.go b/internal/globalfilter/filter.go
index 6d19207..d55c4c1 100644
--- a/internal/globalfilter/filter.go
+++ b/internal/globalfilter/filter.go
@@ -23,6 +23,26 @@ type NumericFilter struct {
Value int64
}
+// NewEqFilter creates an equality NumericFilter for a positive value.
+// Returns nil if value is not positive.
+func NewEqFilter(value int64) *NumericFilter {
+ if value <= 0 {
+ return nil
+ }
+ return &NumericFilter{Op: OpEq, Value: value}
+}
+
+// EqValue returns the filter's positive equality value if the filter
+// represents an exact-match constraint (Op == OpEq and Value > 0).
+// Returns (0, false) when the filter is nil, uses a different operator,
+// or has a non-positive value.
+func (f *NumericFilter) EqValue() (int, bool) {
+ if f == nil || f.Op != OpEq || f.Value <= 0 {
+ return 0, false
+ }
+ return int(f.Value), true
+}
+
type StringFilter struct {
Pattern string
}
diff --git a/internal/parquet/schema.go b/internal/parquet/schema.go
index 2ede444..62d448b 100644
--- a/internal/parquet/schema.go
+++ b/internal/parquet/schema.go
@@ -1,7 +1,9 @@
package parquet
import (
+ "os"
"strconv"
+ "time"
"ior/internal/flags"
"ior/internal/streamrow"
@@ -35,6 +37,20 @@ type FileMetadata struct {
IORVersion string
}
+// NewFileMetadata constructs file-level metadata for a parquet trace file,
+// populating the hostname, timestamp, version, and recording mode.
+func NewFileMetadata(mode string) FileMetadata {
+ meta := FileMetadata{
+ StartedAtUnixNano: uint64(time.Now().UnixNano()),
+ Mode: mode,
+ IORVersion: flags.Version,
+ }
+ if hostname, err := os.Hostname(); err == nil {
+ meta.Hostname = hostname
+ }
+ return meta
+}
+
// RecordFromStream converts one shared stream row into the persisted format.
func RecordFromStream(row streamrow.Row, filterEpoch uint64) Record {
return Record{
diff --git a/internal/probemanager/grouping.go b/internal/probemanager/grouping.go
index aa4f133..be55659 100644
--- a/internal/probemanager/grouping.go
+++ b/internal/probemanager/grouping.go
@@ -35,19 +35,17 @@ func GroupTracepoints(names []string) map[string]TracepointPair {
}
func parseSyscallTracepoint(name string) (base string, isEnter bool, ok bool) {
- if strings.HasPrefix(name, sysEnterPrefix) {
- base = strings.TrimPrefix(name, sysEnterPrefix)
- if base == "" {
+ if after, found := strings.CutPrefix(name, sysEnterPrefix); found {
+ if after == "" {
return "", false, false
}
- return base, true, true
+ return after, true, true
}
- if strings.HasPrefix(name, sysExitPrefix) {
- base = strings.TrimPrefix(name, sysExitPrefix)
- if base == "" {
+ if after, found := strings.CutPrefix(name, sysExitPrefix); found {
+ if after == "" {
return "", false, false
}
- return base, false, true
+ return after, false, true
}
return "", false, false
}
diff --git a/internal/probemanager/manager.go b/internal/probemanager/manager.go
index 4c9dcec..cad755e 100644
--- a/internal/probemanager/manager.go
+++ b/internal/probemanager/manager.go
@@ -1,9 +1,10 @@
package probemanager
import (
+ "cmp"
"errors"
"fmt"
- "sort"
+ "slices"
"strings"
"sync"
)
@@ -244,7 +245,7 @@ func (m *Manager) States() []ProbeState {
}
out = append(out, state)
}
- sort.Slice(out, func(i, j int) bool { return out[i].Syscall < out[j].Syscall })
+ slices.SortFunc(out, func(a, b ProbeState) int { return cmp.Compare(a.Syscall, b.Syscall) })
return out
}
diff --git a/internal/statsengine/bench_test.go b/internal/statsengine/bench_