Skip to content

Commit c07707a

Browse files
committed
Add vfsreadlat.py BCC example
1 parent d0fecbc commit c07707a

File tree

2 files changed

+130
-3
lines changed

2 files changed

+130
-3
lines changed

BCC-Examples/hello_perf_output.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from pythonbpf import bpf, map, struct, section, bpfglobal, BPF
22
from pythonbpf.helper import ktime, pid, comm
33
from pythonbpf.maps import PerfEventArray
4-
from ctypes import c_void_p, c_int64, c_uint64
4+
from ctypes import c_void_p, c_int64
55

66

77
@bpf
88
@struct
99
class data_t:
10-
pid: c_uint64
11-
ts: c_uint64
10+
pid: c_int64
11+
ts: c_int64
1212
comm: str(16) # type: ignore [valid-type]
1313

1414

BCC-Examples/vfsreadlat.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
from pythonbpf import bpf, map, struct, section, bpfglobal, BPF
2+
from pythonbpf.helper import ktime, pid
3+
from pythonbpf.maps import HashMap, PerfEventArray
4+
from ctypes import c_void_p, c_uint64
5+
import matplotlib.pyplot as plt
6+
import numpy as np
7+
8+
9+
@bpf
10+
@struct
11+
class latency_event:
12+
pid: c_uint64
13+
delta_us: c_uint64 # Latency in microseconds
14+
15+
16+
@bpf
17+
@map
18+
def start() -> HashMap:
19+
return HashMap(key=c_uint64, value=c_uint64, max_entries=10240)
20+
21+
22+
@bpf
23+
@map
24+
def events() -> PerfEventArray:
25+
return PerfEventArray(key_size=c_uint64, value_size=c_uint64)
26+
27+
28+
@bpf
29+
@section("kprobe/vfs_read")
30+
def do_entry(ctx: c_void_p) -> c_uint64:
31+
p, ts = pid(), ktime()
32+
start.update(p, ts)
33+
return 0 # type: ignore [return-value]
34+
35+
36+
@bpf
37+
@section("kretprobe/vfs_read")
38+
def do_return(ctx: c_void_p) -> c_uint64:
39+
p = pid()
40+
tsp = start.lookup(p)
41+
42+
if tsp:
43+
delta_ns = ktime() - tsp
44+
45+
# Only track if latency > 1 microsecond
46+
if delta_ns > 1000:
47+
evt = latency_event()
48+
evt.pid, evt.delta_us = p, delta_ns // 1000
49+
events.output(evt)
50+
51+
start.delete(p)
52+
53+
return 0 # type: ignore [return-value]
54+
55+
56+
@bpf
57+
@bpfglobal
58+
def LICENSE() -> str:
59+
return "GPL"
60+
61+
62+
# Load BPF
63+
print("Loading BPF program...")
64+
b = BPF()
65+
b.load()
66+
b.attach_all()
67+
68+
# Collect latencies
69+
latencies = []
70+
71+
72+
def callback(cpu, event):
73+
latencies.append(event.delta_us)
74+
75+
76+
b["events"].open_perf_buffer(callback, struct_name="latency_event")
77+
78+
print("Tracing vfs_read latency... Hit Ctrl-C to end.")
79+
80+
try:
81+
while True:
82+
b["events"].poll(1000)
83+
if len(latencies) > 0 and len(latencies) % 1000 == 0:
84+
print(f"Collected {len(latencies)} samples...")
85+
86+
except KeyboardInterrupt:
87+
print(f"Collected {len(latencies)} samples. Generating histogram...")
88+
89+
# Create histogram with matplotlib
90+
if latencies:
91+
# Use log scale for better visualization
92+
log_latencies = np.log2(latencies)
93+
94+
plt.figure(figsize=(12, 6))
95+
96+
# Plot 1: Linear histogram
97+
plt.subplot(1, 2, 1)
98+
plt.hist(latencies, bins=50, edgecolor="black", alpha=0.7)
99+
plt.xlabel("Latency (microseconds)")
100+
plt.ylabel("Count")
101+
plt.title("VFS Read Latency Distribution (Linear)")
102+
plt.grid(True, alpha=0.3)
103+
104+
# Plot 2: Log2 histogram (like BCC)
105+
plt.subplot(1, 2, 2)
106+
plt.hist(log_latencies, bins=50, edgecolor="black", alpha=0.7, color="orange")
107+
plt.xlabel("log2(Latency in µs)")
108+
plt.ylabel("Count")
109+
plt.title("VFS Read Latency Distribution (Log2)")
110+
plt.grid(True, alpha=0.3)
111+
112+
# Add statistics
113+
print("Statistics:")
114+
print(f" Count: {len(latencies)}")
115+
print(f" Min: {min(latencies)} µs")
116+
print(f" Max: {max(latencies)} µs")
117+
print(f" Mean: {np.mean(latencies):.2f} µs")
118+
print(f" Median: {np.median(latencies):.2f} µs")
119+
print(f" P95: {np.percentile(latencies, 95):.2f} µs")
120+
print(f" P99: {np.percentile(latencies, 99):.2f} µs")
121+
122+
plt.tight_layout()
123+
plt.savefig("vfs_read_latency.png", dpi=150)
124+
print("Histogram saved to vfs_read_latency.png")
125+
plt.show()
126+
else:
127+
print("No samples collected!")

0 commit comments

Comments
 (0)