summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 12:57:21 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 12:57:21 +0200
commit6d53aeb16bee34eeee14b457cd0e7b0811bee873 (patch)
tree2c5a8fb6afb7999d11bbf5bdd41bd3545986fd87
parent6c4f68164df93ed70123db61fb32a9688f5b0623 (diff)
Handle close_range cleanup
Amp-Thread-ID: https://ampcode.com/threads/T-019c7faf-baaa-704f-af15-8aeba9df4628 Co-authored-by: Amp <amp@ampcode.com>
-rw-r--r--internal/eventloop.go12
-rw-r--r--internal/eventloop_test.go154
2 files changed, 166 insertions, 0 deletions
diff --git a/internal/eventloop.go b/internal/eventloop.go
index cf128d2..91af7c9 100644
--- a/internal/eventloop.go
+++ b/internal/eventloop.go
@@ -254,6 +254,18 @@ func (e *eventLoop) tracepointExited(exitEv event.Event, ch chan<- *event.Pair)
case *FdEvent:
fd := ep.EnterEv.(*FdEvent).Fd
+ if ep.Is(SYS_ENTER_CLOSE_RANGE) {
+ // close_range provides (first, last), but fd_event only carries the first
+ // argument, so we approximate by closing all tracked fds >= first.
+ retEv, ok := ep.ExitEv.(*types.RetEvent)
+ if ok && retEv.Ret == 0 {
+ for fdToClose := range e.files {
+ if fdToClose >= fd {
+ delete(e.files, fdToClose)
+ }
+ }
+ }
+ }
if file_, ok := e.files[fd]; ok {
ep.File = file_
if ep.Is(SYS_ENTER_CLOSE) {
diff --git a/internal/eventloop_test.go b/internal/eventloop_test.go
index dc92bda..f608eab 100644
--- a/internal/eventloop_test.go
+++ b/internal/eventloop_test.go
@@ -33,6 +33,8 @@ func TestEventloop(t *testing.T) {
"ReadEventTest": makeReadEventTestData(t),
"WriteEventTest": makeWriteEventTestData(t),
"CloseEventTest": makeCloseEventTestData(t),
+ "CloseRangeEventTest": makeCloseRangeEventTestData(t),
+ "CloseRangeFailureTest": makeCloseRangeFailureTestData(t),
"FsyncEventTest": makeFsyncEventTestData(t),
"FtruncateEventTest": makeFtruncateEventTestData(t),
// PathEvent tests
@@ -353,6 +355,158 @@ func makeCloseEventTestData(t *testing.T) (td testData) {
return td
}
+func makeCloseRangeEventTestData(t *testing.T) (td testData) {
+ fd1 := int32(41)
+ fd2 := int32(42)
+ fd3 := int32(43)
+ filename1 := "close_range_1.txt"
+ filename2 := "close_range_2.txt"
+ filename3 := "close_range_3.txt"
+
+ openEnterEv1, openEnterBytes1 := makeEnterOpenEvent(t, defaulTime, defaultPid, defaultTid)
+ copy(openEnterEv1.Filename[:], filename1)
+ openEnterBytes1, _ = openEnterEv1.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openEnterBytes1)
+
+ openExitEv1, openExitBytes1 := makeExitOpenEvent(t, defaulTime+100, defaultPid, defaultTid)
+ openExitEv1.Ret = int64(fd1)
+ openExitBytes1, _ = openExitEv1.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openExitBytes1)
+
+ openEnterEv2, openEnterBytes2 := makeEnterOpenEvent(t, defaulTime+200, defaultPid, defaultTid)
+ copy(openEnterEv2.Filename[:], filename2)
+ openEnterBytes2, _ = openEnterEv2.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openEnterBytes2)
+
+ openExitEv2, openExitBytes2 := makeExitOpenEvent(t, defaulTime+300, defaultPid, defaultTid)
+ openExitEv2.Ret = int64(fd2)
+ openExitBytes2, _ = openExitEv2.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openExitBytes2)
+
+ openEnterEv3, openEnterBytes3 := makeEnterOpenEvent(t, defaulTime+400, defaultPid, defaultTid)
+ copy(openEnterEv3.Filename[:], filename3)
+ openEnterBytes3, _ = openEnterEv3.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openEnterBytes3)
+
+ openExitEv3, openExitBytes3 := makeExitOpenEvent(t, defaulTime+500, defaultPid, defaultTid)
+ openExitEv3.Ret = int64(fd3)
+ openExitBytes3, _ = openExitEv3.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openExitBytes3)
+
+ enterCloseRange, enterCloseRangeBytes := makeEnterFdEvent(t, defaulTime+600, defaultPid, defaultTid, fd2, types.SYS_ENTER_CLOSE_RANGE)
+ td.rawTracepoints = append(td.rawTracepoints, enterCloseRangeBytes)
+
+ exitCloseRange, exitCloseRangeBytes := makeExitRetEvent(t, defaulTime+700, defaultPid, defaultTid, types.SYS_EXIT_CLOSE_RANGE, 0)
+ td.rawTracepoints = append(td.rawTracepoints, exitCloseRangeBytes)
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !openEnterEv1.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", openEnterEv1, ep.EnterEv)
+ }
+ if !openExitEv1.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", openExitEv1, ep.ExitEv)
+ }
+ })
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !openEnterEv2.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", openEnterEv2, ep.EnterEv)
+ }
+ if !openExitEv2.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", openExitEv2, ep.ExitEv)
+ }
+ })
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !openEnterEv3.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", openEnterEv3, ep.EnterEv)
+ }
+ if !openExitEv3.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", openExitEv3, ep.ExitEv)
+ }
+ })
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !enterCloseRange.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", enterCloseRange, ep.EnterEv)
+ }
+ if !exitCloseRange.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", exitCloseRange, ep.ExitEv)
+ }
+
+ verifyFileDescriptor(t, el, fd1, filename1)
+ verifyFdNotTracked(t, el, fd2)
+ verifyFdNotTracked(t, el, fd3)
+ })
+
+ return td
+}
+
+func makeCloseRangeFailureTestData(t *testing.T) (td testData) {
+ fd1 := int32(41)
+ fd2 := int32(42)
+ filename1 := "close_range_fail_1.txt"
+ filename2 := "close_range_fail_2.txt"
+
+ openEnterEv1, openEnterBytes1 := makeEnterOpenEvent(t, defaulTime, defaultPid, defaultTid)
+ copy(openEnterEv1.Filename[:], filename1)
+ openEnterBytes1, _ = openEnterEv1.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openEnterBytes1)
+
+ openExitEv1, openExitBytes1 := makeExitOpenEvent(t, defaulTime+100, defaultPid, defaultTid)
+ openExitEv1.Ret = int64(fd1)
+ openExitBytes1, _ = openExitEv1.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openExitBytes1)
+
+ openEnterEv2, openEnterBytes2 := makeEnterOpenEvent(t, defaulTime+200, defaultPid, defaultTid)
+ copy(openEnterEv2.Filename[:], filename2)
+ openEnterBytes2, _ = openEnterEv2.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openEnterBytes2)
+
+ openExitEv2, openExitBytes2 := makeExitOpenEvent(t, defaulTime+300, defaultPid, defaultTid)
+ openExitEv2.Ret = int64(fd2)
+ openExitBytes2, _ = openExitEv2.Bytes()
+ td.rawTracepoints = append(td.rawTracepoints, openExitBytes2)
+
+ enterCloseRange, enterCloseRangeBytes := makeEnterFdEvent(t, defaulTime+400, defaultPid, defaultTid, fd1, types.SYS_ENTER_CLOSE_RANGE)
+ td.rawTracepoints = append(td.rawTracepoints, enterCloseRangeBytes)
+
+ exitCloseRange, exitCloseRangeBytes := makeExitRetEvent(t, defaulTime+500, defaultPid, defaultTid, types.SYS_EXIT_CLOSE_RANGE, -1)
+ td.rawTracepoints = append(td.rawTracepoints, exitCloseRangeBytes)
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !openEnterEv1.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", openEnterEv1, ep.EnterEv)
+ }
+ if !openExitEv1.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", openExitEv1, ep.ExitEv)
+ }
+ })
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !openEnterEv2.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", openEnterEv2, ep.EnterEv)
+ }
+ if !openExitEv2.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", openExitEv2, ep.ExitEv)
+ }
+ })
+
+ td.validates = append(td.validates, func(t *testing.T, el *eventLoop, ep *event.Pair) {
+ if !enterCloseRange.Equals(ep.EnterEv) {
+ t.Errorf("Expected '%v' but got '%v'", enterCloseRange, ep.EnterEv)
+ }
+ if !exitCloseRange.Equals(ep.ExitEv) {
+ t.Errorf("Expected '%v' but got '%v'", exitCloseRange, ep.ExitEv)
+ }
+
+ verifyFileDescriptor(t, el, fd1, filename1)
+ verifyFileDescriptor(t, el, fd2, filename2)
+ })
+
+ return td
+}
+
func makeFsyncEventTestData(t *testing.T) (td testData) {
fd := int32(45)
enterEv, enterEvBytes := makeEnterFdEvent(t, defaulTime, defaultPid, defaultTid, fd, types.SYS_ENTER_FSYNC)