Skip to content

Commit 600993f

Browse files
add syscall monitor
1 parent 6c55d56 commit 600993f

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import time
2+
import os
3+
from pathlib import Path
4+
from pythonbpf import bpf, map, section, bpfglobal, BPF
5+
from pythonbpf.maps import HashMap
6+
from pythonbpf.helper import get_current_cgroup_id
7+
from ctypes import c_void_p, c_int32, c_uint64
8+
9+
10+
@bpf
11+
@map
12+
def syscall_count() -> HashMap:
13+
"""Map tracking syscall count by cgroup ID"""
14+
return HashMap(key=c_uint64, value=c_uint64, max_entries=1024)
15+
16+
17+
@bpf
18+
@section("tracepoint/raw_syscalls/sys_enter")
19+
def count_syscalls(ctx: c_void_p) -> c_int32:
20+
"""
21+
Increment syscall counter for the current cgroup.
22+
Attached to raw_syscalls/sys_enter tracepoint to catch all syscalls.
23+
"""
24+
cgroup_id = get_current_cgroup_id()
25+
26+
# Lookup current count
27+
count_ptr = syscall_count.lookup(cgroup_id)
28+
29+
if count_ptr:
30+
# Increment existing count
31+
new_count = count_ptr + c_uint64(1)
32+
syscall_count.update(cgroup_id, new_count)
33+
else:
34+
# First syscall for this cgroup
35+
syscall_count.update(cgroup_id, c_uint64(1))
36+
37+
return c_int32(0)
38+
39+
40+
@bpf
41+
@bpfglobal
42+
def LICENSE() -> str:
43+
return "GPL"
44+
45+
46+
# Load and attach BPF program
47+
b = BPF()
48+
b.load()
49+
b.attach_all()
50+
51+
# Get map reference
52+
syscall_count_ref = b["syscall_count"]
53+
54+
55+
def get_cgroup_ids():
56+
"""Get all cgroup IDs from the system"""
57+
cgroup_ids = set()
58+
59+
# Get cgroup IDs from running processes
60+
for proc_dir in Path("/proc").glob("[0-9]*"):
61+
try:
62+
cgroup_file = proc_dir / "cgroup"
63+
if cgroup_file.exists():
64+
with open(cgroup_file) as f:
65+
for line in f:
66+
# Parse cgroup path and get inode
67+
parts = line.strip().split(":")
68+
if len(parts) >= 3:
69+
cgroup_path = parts[2]
70+
# Try to get the cgroup inode which is used as ID
71+
cgroup_mount = f"/sys/fs/cgroup{cgroup_path}"
72+
if os.path.exists(cgroup_mount):
73+
stat_info = os.stat(cgroup_mount)
74+
cgroup_ids.add(stat_info.st_ino)
75+
except (PermissionError, FileNotFoundError, OSError):
76+
continue
77+
78+
return cgroup_ids
79+
80+
81+
# Display function
82+
def display_stats():
83+
"""Read and display syscall statistics from BPF maps"""
84+
print("\n" + "=" * 60)
85+
print(f"{'CGROUP ID':<20} {'SYSCALL COUNT':<20}")
86+
print("=" * 60)
87+
88+
# Get cgroup IDs from the system
89+
cgroup_ids = get_cgroup_ids()
90+
91+
if not cgroup_ids:
92+
print("No cgroups found...")
93+
print("=" * 60)
94+
return
95+
96+
# Initialize totals
97+
total_syscalls = 0
98+
99+
# Track which cgroups have data
100+
cgroups_with_data = []
101+
102+
# Display stats for each cgroup
103+
for cgroup_id in sorted(cgroup_ids):
104+
# Get syscall count using lookup
105+
syscall_cnt = 0
106+
107+
count = syscall_count_ref.lookup(cgroup_id)
108+
if count is not None:
109+
syscall_cnt = int(count)
110+
total_syscalls += syscall_cnt
111+
112+
print(f"{cgroup_id:<20} {syscall_cnt:<20}")
113+
cgroups_with_data.append(cgroup_id)
114+
115+
if not cgroups_with_data:
116+
print("No data collected yet...")
117+
118+
print("=" * 60)
119+
print(f"{'TOTAL':<20} {total_syscalls:<20}")
120+
print()
121+
122+
123+
# Main loop
124+
if __name__ == "__main__":
125+
print("Tracing syscalls per cgroup... Press Ctrl+C to exit\n")
126+
127+
try:
128+
while True:
129+
time.sleep(5) # Update every 5 seconds
130+
display_stats()
131+
except KeyboardInterrupt:
132+
print("\nStopped")

0 commit comments

Comments
 (0)