Skip to content

Commit 95e41ef

Browse files
committed
Remove the separate io_uring trace stream; keep fs mirror
io_uring file read/writes are already mirrored into the fs/VFS trace (_mirror_io_uring_to_fs), so the standalone io_uring/ output duplicated that data. Drop the separate stream entirely: - IOTracer._print_event_io_uring now only resolves file identity and mirrors completed READ/WRITE ops into the fs trace; it no longer builds the full io_uring row or calls append_io_uring_log. The BPF io_uring probe/perf buffer stay (they feed the mirror). - WriterManager: remove the io_uring output file/dir, buffer, handle, lock, stream-registry entry, thresholds, dynamic_limits, adaptive-sizing branch, should_flush/append/flush methods, write_to_disk thread, force_flush compress, and close_handles entry. Note: non-file io_uring ops (network/poll), the submit lifecycle, and read/writes whose inode didn't resolve are no longer recorded, matching the intent that io_uring is represented via the fs trace.
1 parent 22eaf68 commit 95e41ef

3 files changed

Lines changed: 22 additions & 156 deletions

File tree

src/tracer/IOTracer.py

Lines changed: 21 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -792,14 +792,14 @@ def _print_event_pagefault(self, cpu, data, size):
792792

793793
def _print_event_io_uring(self, cpu, data, size):
794794
"""
795-
Callback for processing io_uring events from the perf buffer.
796-
797-
Captures io_uring async I/O operations including:
798-
- ENTER: io_uring_enter syscall
799-
- SUBMIT: Individual SQE submissions
800-
- COMPLETE: Request completions with latency
801-
- WORKER: Async worker executions
802-
795+
Callback for io_uring perf events.
796+
797+
The standalone io_uring trace stream has been removed. io_uring file
798+
activity is surfaced by mirroring completed READ/WRITE operations into
799+
the fs/VFS trace (they call ->read_iter/->write_iter directly, bypass
800+
vfs_read/vfs_write, and would otherwise be invisible). Other io_uring
801+
events (submit lifecycle, network/poll ops) are no longer recorded.
802+
803803
Args:
804804
cpu: CPU number where the event was captured
805805
data: Raw event data pointer
@@ -808,58 +808,23 @@ def _print_event_io_uring(self, cpu, data, size):
808808
self._tick_maintenance()
809809

810810
e = self.b["io_uring_events"].event(data)
811+
812+
# Only completed ops (event_type == 2) carry a result/latency and feed
813+
# the fs mirror; nothing else needs recording now that the separate
814+
# io_uring stream is gone.
815+
if e.event_type != 2:
816+
return
817+
811818
ts = datetime.today()
812-
813819
comm = e.comm.decode("utf-8", errors="replace").strip("\x00")
814-
820+
815821
if self._should_filter_process(comm):
816822
return
817-
818-
event_type = self.flag_mapper.format_io_uring_event_type(e.event_type)
819-
opcode = self.flag_mapper.format_io_uring_opcode(e.opcode) if e.opcode else ""
820-
enter_flags = self.flag_mapper.format_io_uring_enter_flags(e.enter_flags) if e.enter_flags else ""
821-
sqe_flags = self.flag_mapper.format_io_uring_sqe_flags(e.sqe_flags) if e.sqe_flags else ""
822-
823-
# Format fields based on event type
824-
ring_fd = str(e.ring_fd) if e.ring_fd else ""
825-
ring_ptr = hex(e.ring_ptr) if e.ring_ptr else ""
826-
to_submit = str(e.to_submit) if e.to_submit else ""
827-
min_complete = str(e.min_complete) if e.min_complete else ""
828-
829-
req_ptr = hex(e.req_ptr) if e.req_ptr else ""
830-
user_data = str(e.user_data) if e.user_data else ""
831-
fd = str(e.fd) if e.fd != 0 and e.fd != -1 else ""
832-
length = str(e.len) if e.len else ""
833-
offset = str(e.offset) if e.offset else ""
834-
ioprio = str(e.ioprio) if e.ioprio else ""
835-
buf_index = str(e.buf_index) if e.buf_index else ""
836-
personality = str(e.personality) if e.personality else ""
837-
838-
result = str(e.result) if e.result != 0 or e.event_type == 2 else ""
839-
is_error = "1" if e.is_error else ""
840-
cqe_errno = str(e.cqe_errno) if e.cqe_errno else ""
841-
842-
submit_ts = str(e.submit_ts_ns) if e.submit_ts_ns else ""
843-
complete_ts = str(e.complete_ts_ns) if e.complete_ts_ns else ""
844-
latency_ns = str(e.latency_ns) if e.latency_ns else ""
845-
846-
worker_pid = str(e.worker_pid) if e.worker_pid else ""
847-
worker_tid = str(e.worker_tid) if e.worker_tid else ""
848-
worker_cpu = str(e.worker_cpu) if e.worker_cpu else ""
849-
is_async = "1" if e.is_async else ""
850-
851-
sq_head = str(e.sq_head) if e.sq_head else ""
852-
sq_tail = str(e.sq_tail) if e.sq_tail else ""
853-
cq_head = str(e.cq_head) if e.cq_head else ""
854-
cq_tail = str(e.cq_tail) if e.cq_tail else ""
855-
sq_depth = str(e.sq_depth) if e.sq_depth else ""
856-
cq_depth = str(e.cq_depth) if e.cq_depth else ""
857823

858824
# File correlation — the prep probe records the backing file's inode,
859-
# device and filesystem for file-backed ops. Resolve the path from the
860-
# inode→path cache populated by OPEN events (same strategy as VFS events)
861-
# so io_uring I/O carries the same file identity as the fs trace.
862-
inode_val = e.inode if getattr(e, "inode", 0) else ""
825+
# device and filesystem. Resolve the path from the inode→path cache
826+
# (same strategy as VFS events) so mirrored io_uring I/O carries the
827+
# same file identity as the fs trace.
863828
dev_val = self._format_dev(e.dev) if getattr(e, "dev", 0) else ""
864829
fs_type_val = (
865830
self.flag_mapper.format_fs_type(e.fs_magic)
@@ -873,59 +838,9 @@ def _print_event_io_uring(self, cpu, data, size):
873838
if self.anonymous and filename:
874839
filename = hash_filename_in_path(Path(filename))
875840

876-
# Surface io_uring file READ/WRITE in the main fs/VFS trace stream so
877-
# async I/O appears alongside syscall reads/writes. Mirror only on
878-
# COMPLETE (result/latency known) and only read/write opcodes, which
879-
# bypass vfs_read/vfs_write and would otherwise be invisible there.
880-
if e.event_type == 2:
881-
self._mirror_io_uring_to_fs(e, comm, filename, ts, dev_val, fs_type_val)
841+
# Mirror completed file READ/WRITE into the main fs/VFS trace stream.
842+
self._mirror_io_uring_to_fs(e, comm, filename, ts, dev_val, fs_type_val)
882843

883-
# Build CSV row matching the unified schema from the guide
884-
output = format_csv_row(
885-
ts.strftime("%Y-%m-%d %H:%M:%S.%f"),
886-
str(e.timestamp_ns),
887-
event_type,
888-
str(e.pid),
889-
str(e.tid),
890-
comm,
891-
str(e.cpu),
892-
ring_fd,
893-
ring_ptr,
894-
to_submit,
895-
min_complete,
896-
enter_flags,
897-
req_ptr,
898-
user_data,
899-
opcode,
900-
fd,
901-
length,
902-
offset,
903-
sqe_flags,
904-
ioprio,
905-
buf_index,
906-
personality,
907-
result,
908-
is_error,
909-
cqe_errno,
910-
submit_ts,
911-
complete_ts,
912-
latency_ns,
913-
worker_pid,
914-
worker_tid,
915-
worker_cpu,
916-
is_async,
917-
sq_head,
918-
sq_tail,
919-
cq_head,
920-
cq_tail,
921-
sq_depth,
922-
cq_depth,
923-
inode_val,
924-
filename,
925-
dev_val,
926-
fs_type_val,
927-
)
928-
self.writer.append_io_uring_log(output)
929844

930845
def _mirror_io_uring_to_fs(self, e, comm, filename, ts, dev_val, fs_type_val):
931846
"""

src/tracer/WriterManager.py

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
8383
self.output_process_file = f"{self.output_dir}/process/process_{self.current_datetime.strftime('%Y%m%d_%H%M%S_%f')[:-3]}.csv"
8484
self.output_fs_snapshot_file = f"{self.output_dir}/filesystem_snapshot/filesystem_snapshot_{self.current_datetime.strftime('%Y%m%d_%H%M%S_%f')[:-3]}.csv"
8585
self.output_pagefault_file = f"{self.output_dir}/pagefault/pagefault_{self.current_datetime.strftime('%Y%m%d_%H%M%S_%f')[:-3]}.csv"
86-
self.output_io_uring_file = f"{self.output_dir}/io_uring/io_uring_{self.current_datetime.strftime('%Y%m%d_%H%M%S_%f')[:-3]}.csv"
8786

8887
# Create output directories
8988
os.makedirs(f"{self.output_dir}/system_spec", exist_ok=True)
@@ -93,7 +92,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
9392
os.makedirs(f"{self.output_dir}/process", exist_ok=True)
9493
os.makedirs(f"{self.output_dir}/filesystem_snapshot", exist_ok=True)
9594
os.makedirs(f"{self.output_dir}/pagefault", exist_ok=True)
96-
os.makedirs(f"{self.output_dir}/io_uring", exist_ok=True)
9795

9896
self.upload_manager = upload_manager
9997
self.automatic_upload = automatic_upload
@@ -105,7 +103,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
105103
self.process_buffer = deque()
106104
self.fs_snap_buffer = deque()
107105
self.pagefault_buffer = deque()
108-
self.io_uring_buffer = deque()
109106

110107
# Event rate tracking
111108
self.event_timestamps = {
@@ -115,7 +112,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
115112
'fs_state': deque(maxlen=1000),
116113
'proc_state': deque(maxlen=1000),
117114
'pagefault': deque(maxlen=1000),
118-
'io_uring': deque(maxlen=1000),
119115
}
120116

121117
# Dynamic thresholds (min, max). Raised roughly 10x from the original
@@ -130,7 +126,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
130126
'fs_state': (80000, 200000),
131127
'proc_state': (80000, 100000),
132128
'pagefault': (80000, 400000),
133-
'io_uring': (80000, 400000),
134129
}
135130

136131
# Start adaptive sizing thread
@@ -152,7 +147,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
152147
self.process_max_events = 80000 # Large enough to fit entire hourly snapshot
153148
self.fs_snap_max_events = 80000
154149
self.pagefault_max_events = 80000
155-
self.io_uring_max_events = 80000
156150

157151
# Per-stream locks. Buffer flushes are triggered both from the
158152
# perf-callback (polling) thread via append_*_log -> flush_*_only and
@@ -166,7 +160,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
166160
'process': threading.Lock(),
167161
'fs_snap': threading.Lock(),
168162
'pagefault': threading.Lock(),
169-
'io_uring': threading.Lock(),
170163
}
171164

172165
# File handles for each output
@@ -176,7 +169,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
176169
self._process_handle = None
177170
self._pagefault_handle = None
178171
self._fs_snap_handle = None
179-
self._io_uring_handle = None
180172

181173
# Registry of the continuous event streams that support generic
182174
# rotation. Snapshots (process, fs_snap) are intentionally excluded:
@@ -188,7 +180,6 @@ def __init__(self, output_dir: str, upload_manager: ObjectStorageManager, automa
188180
'block': {'subdir': 'ds', 'prefix': 'ds', 'buf': 'block_buffer', 'handle': '_block_handle', 'file': 'output_block_file', 'log': 'Block'},
189181
'cache': {'subdir': 'cache', 'prefix': 'cache', 'buf': 'cache_buffer', 'handle': '_cache_handle', 'file': 'output_cache_file', 'log': 'Cache'},
190182
'pagefault': {'subdir': 'pagefault', 'prefix': 'pagefault', 'buf': 'pagefault_buffer', 'handle': '_pagefault_handle', 'file': 'output_pagefault_file', 'log': 'PageFault'},
191-
'io_uring': {'subdir': 'io_uring', 'prefix': 'io_uring', 'buf': 'io_uring_buffer', 'handle': '_io_uring_handle', 'file': 'output_io_uring_file', 'log': 'IO_Uring'},
192183
}
193184

194185
# Time/size based rotation so a slow stream's log doesn't wait until
@@ -247,7 +238,7 @@ def _adaptive_sizing(self):
247238
while True:
248239
time.sleep(10)
249240

250-
for event_type in ['vfs', 'block', 'cache', 'fs_state','proc_state', 'pagefault', 'io_uring']:
241+
for event_type in ['vfs', 'block', 'cache', 'fs_state','proc_state', 'pagefault']:
251242
rate = self._calculate_event_rate(event_type)
252243
min_limit, max_limit = self.dynamic_limits[event_type]
253244

@@ -272,8 +263,6 @@ def _adaptive_sizing(self):
272263
self.process_max_events = new_limit
273264
elif event_type == 'pagefault':
274265
self.pagefault_max_events = new_limit
275-
elif event_type == 'io_uring':
276-
self.io_uring_max_events = new_limit
277266

278267
def _periodic_flush(self):
279268
"""
@@ -330,9 +319,6 @@ def _log_status(self):
330319
buffer_info.append(f"Cache:{len(self.cache_buffer)}")
331320
if len(self.pagefault_buffer) > 0:
332321
buffer_info.append(f"PgFault:{len(self.pagefault_buffer)}")
333-
if len(self.io_uring_buffer) > 0:
334-
buffer_info.append(f"IO_Uring:{len(self.io_uring_buffer)}")
335-
336322
if buffer_info:
337323
status_parts.append(f"Buffers: {', '.join(buffer_info)}")
338324

@@ -391,10 +377,6 @@ def should_flush_pagefault(self) -> bool:
391377
"""Check if pagefault buffer should be flushed."""
392378
return (len(self.pagefault_buffer) >= self.pagefault_max_events)
393379

394-
def should_flush_io_uring(self) -> bool:
395-
"""Check if io_uring buffer should be flushed."""
396-
return (len(self.io_uring_buffer) >= self.io_uring_max_events)
397-
398380
def append_fs_snap_log(self, log_output: str):
399381
"""
400382
Add a filesystem snapshot log entry.
@@ -497,16 +479,6 @@ def append_pagefault_log(self, log_output: str):
497479
else:
498480
logger("error", "Invalid pagefault log output format. Expected a string.")
499481

500-
def append_io_uring_log(self, log_output: str):
501-
"""Add an io_uring event log entry."""
502-
if isinstance(log_output, str):
503-
self.io_uring_buffer.append(log_output)
504-
self.event_timestamps['io_uring'].append(time.time())
505-
if self.should_flush_io_uring():
506-
self.flush_io_uring_only()
507-
else:
508-
logger("error", "Invalid io_uring log output format. Expected a string.")
509-
510482
def direct_write(self, output_path: str, spec_str: str):
511483
"""
512484
Write a system specification file directly.
@@ -708,10 +680,6 @@ def flush_pagefault_only(self):
708680
"""Flush pagefault buffer to file (rotate + compress + upload)."""
709681
self._rotate_stream('pagefault')
710682

711-
def flush_io_uring_only(self):
712-
"""Flush io_uring buffer to file (rotate + compress + upload)."""
713-
self._rotate_stream('io_uring')
714-
715683
def _rotate_stream(self, key: str):
716684
"""Rotate one continuous stream's current log and queue it for upload.
717685
@@ -827,7 +795,6 @@ def force_flush(self):
827795
self.fs_snap_buffer.clear()
828796

829797
self.compress_log(self.output_pagefault_file)
830-
self.compress_log(self.output_io_uring_file)
831798
self.compress_dir(self.output_dir)
832799

833800

@@ -840,7 +807,6 @@ def clear_events(self):
840807
self.process_buffer.clear()
841808
self.fs_snap_buffer.clear()
842809
self.pagefault_buffer.clear()
843-
self.io_uring_buffer.clear()
844810

845811
def _write_buffer_to_file(self, buffer, file_handle, buffer_name: str):
846812
"""
@@ -915,13 +881,6 @@ def write_pagefault():
915881
self._pagefault_handle = open(self.output_pagefault_file, 'a', buffering=8192)
916882
self._write_buffer_to_file(self.pagefault_buffer, self._pagefault_handle, "PageFault")
917883

918-
def write_io_uring():
919-
with self._stream_locks['io_uring']:
920-
if self.io_uring_buffer:
921-
if self._io_uring_handle is None:
922-
self._io_uring_handle = open(self.output_io_uring_file, 'a', buffering=8192)
923-
self._write_buffer_to_file(self.io_uring_buffer, self._io_uring_handle, "IO_Uring")
924-
925884
threads = []
926885

927886
# Start parallel write threads for each buffer
@@ -955,11 +914,6 @@ def write_io_uring():
955914
threads.append(t7)
956915
t7.start()
957916

958-
if self.io_uring_buffer:
959-
t13 = threading.Thread(target=write_io_uring)
960-
threads.append(t13)
961-
t13.start()
962-
963917
# Wait for all threads to complete
964918
for thread in threads:
965919
thread.join()
@@ -1032,7 +986,6 @@ def close_handles(self):
1032986
(self._process_handle, "Process State"),
1033987
(self._fs_snap_handle, "Filesystem Snapshot"),
1034988
(self._pagefault_handle, "PageFault"),
1035-
(self._io_uring_handle, "IO_Uring"),
1036989
]
1037990

1038991
for handle, name in handles:
@@ -1050,4 +1003,3 @@ def close_handles(self):
10501003
self._process_handle = None
10511004
self._fs_snap_handle = None
10521005
self._pagefault_handle = None
1053-
self._io_uring_handle = None

tests/test_writer_upload.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ def test_thresholds_are_enlarged(self):
117117
self.assertGreaterEqual(self.wm.block_max_events, 80000)
118118
self.assertGreaterEqual(self.wm.cache_max_events, 100000)
119119
self.assertGreaterEqual(self.wm.pagefault_max_events, 80000)
120-
self.assertGreaterEqual(self.wm.io_uring_max_events, 80000)
121120
# Dynamic minimums must stay consistent (min <= max) after enlargement.
122121
for name, (lo, hi) in self.wm.dynamic_limits.items():
123122
self.assertGreaterEqual(lo, 80000, name)

0 commit comments

Comments
 (0)