mirror of
https://github.com/varun-r-mallya/Python-BPF.git
synced 2026-02-07 21:50:55 +00:00
docs: Fix user-guide/bpf-structs
This commit is contained in:
@ -119,18 +119,17 @@ def capture_event(ctx: c_void_p) -> c_int64:
|
|||||||
# Set fields
|
# Set fields
|
||||||
event.timestamp = ktime()
|
event.timestamp = ktime()
|
||||||
event.pid = pid()
|
event.pid = pid()
|
||||||
# Note: comm() requires a buffer parameter to fill
|
comm(event.comm) # Fills event.comm with process name
|
||||||
# comm(event.comm) # Fills event.comm with process name
|
|
||||||
|
|
||||||
# Use the struct
|
# Use the struct
|
||||||
print(f"Process with PID {event.pid}")
|
print(f"Process with PID {event.pid}")
|
||||||
|
|
||||||
return c_int64(0)
|
return 0
|
||||||
```
|
```
|
||||||
|
|
||||||
### As Map Values
|
### As Map Keys and Values
|
||||||
|
|
||||||
Use structs as values in maps for complex state storage:
|
Use structs as keys and values in maps for complex state storage:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from pythonbpf import bpf, struct, map, section
|
from pythonbpf import bpf, struct, map, section
|
||||||
@ -168,12 +167,12 @@ def track_syscalls(ctx: c_void_p) -> c_int64:
|
|||||||
else:
|
else:
|
||||||
# Create new stats
|
# Create new stats
|
||||||
new_stats = ProcessStats()
|
new_stats = ProcessStats()
|
||||||
new_stats.syscall_count = c_uint64(1)
|
new_stats.syscall_count = 1
|
||||||
new_stats.total_time = c_uint64(0)
|
new_stats.total_time = 0
|
||||||
new_stats.max_latency = c_uint64(0)
|
new_stats.max_latency = 0
|
||||||
stats.update(process_id, new_stats)
|
stats.update(process_id, new_stats)
|
||||||
|
|
||||||
return c_int64(0)
|
return 0
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Perf Events
|
### With Perf Events
|
||||||
@ -205,19 +204,16 @@ def trace_fork(ctx: c_void_p) -> c_int64:
|
|||||||
event = ProcessEvent()
|
event = ProcessEvent()
|
||||||
event.timestamp = ktime()
|
event.timestamp = ktime()
|
||||||
event.pid = pid()
|
event.pid = pid()
|
||||||
# Note: comm() requires a buffer parameter
|
comm(event.comm) # Fills event.comm with process name
|
||||||
# comm(event.comm) # Fills event.comm with process name
|
|
||||||
|
|
||||||
# Send to userspace
|
# Send to userspace
|
||||||
events.output(event)
|
events.output(event)
|
||||||
|
|
||||||
return c_int64(0)
|
return 0
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Ring Buffers
|
### With Ring Buffers
|
||||||
|
|
||||||
Ring buffers provide efficient event delivery:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from pythonbpf import bpf, struct, map, section
|
from pythonbpf import bpf, struct, map, section
|
||||||
from pythonbpf.maps import RingBuffer
|
from pythonbpf.maps import RingBuffer
|
||||||
@ -240,11 +236,10 @@ def trace_open(ctx: c_void_p) -> c_int64:
|
|||||||
event = FileEvent()
|
event = FileEvent()
|
||||||
event.timestamp = ktime()
|
event.timestamp = ktime()
|
||||||
event.pid = pid()
|
event.pid = pid()
|
||||||
# event.filename would be populated from ctx
|
|
||||||
|
|
||||||
events.output(event)
|
events.output(event)
|
||||||
|
|
||||||
return c_int64(0)
|
return 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Field Access and Modification
|
## Field Access and Modification
|
||||||
@ -267,31 +262,7 @@ Assign values to fields:
|
|||||||
event = Event()
|
event = Event()
|
||||||
event.timestamp = ktime()
|
event.timestamp = ktime()
|
||||||
event.pid = pid()
|
event.pid = pid()
|
||||||
# Note: comm() requires a buffer parameter
|
comm(event.comm)
|
||||||
# comm(event.comm) # Fills event.comm with process name
|
|
||||||
```
|
|
||||||
|
|
||||||
### String Fields
|
|
||||||
|
|
||||||
String fields have special handling:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@bpf
|
|
||||||
@struct
|
|
||||||
class Message:
|
|
||||||
text: str(64)
|
|
||||||
|
|
||||||
@bpf
|
|
||||||
def example(ctx: c_void_p) -> c_int64:
|
|
||||||
msg = Message()
|
|
||||||
|
|
||||||
# Assign string value
|
|
||||||
msg.text = "Hello from BPF"
|
|
||||||
|
|
||||||
# Use helper to get process name (requires buffer)
|
|
||||||
# comm(msg.text) # Fills msg.text with process name
|
|
||||||
|
|
||||||
return c_int64(0)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## StructType Class
|
## StructType Class
|
||||||
@ -347,7 +318,6 @@ def capture_packets(ctx: c_void_p) -> c_int64:
|
|||||||
|
|
||||||
packets.output(pkt)
|
packets.output(pkt)
|
||||||
|
|
||||||
# XDP_PASS
|
|
||||||
return XDP_PASS
|
return XDP_PASS
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -381,12 +351,10 @@ def track_fork(ctx: c_void_p) -> c_int64:
|
|||||||
info = ProcessLifecycle()
|
info = ProcessLifecycle()
|
||||||
info.pid = process_id
|
info.pid = process_id
|
||||||
info.start_time = ktime()
|
info.start_time = ktime()
|
||||||
# Note: comm() requires a buffer parameter
|
|
||||||
# comm(info.comm) # Fills info.comm with process name
|
|
||||||
|
|
||||||
process_info.update(process_id, info)
|
process_info.update(process_id, info)
|
||||||
|
|
||||||
return c_int64(0)
|
return 0
|
||||||
|
|
||||||
@bpf
|
@bpf
|
||||||
@section("tracepoint/sched/sched_process_exit")
|
@section("tracepoint/sched/sched_process_exit")
|
||||||
@ -398,100 +366,7 @@ def track_exit(ctx: c_void_p) -> c_int64:
|
|||||||
info.exit_time = ktime()
|
info.exit_time = ktime()
|
||||||
process_info.update(process_id, info)
|
process_info.update(process_id, info)
|
||||||
|
|
||||||
return c_int64(0)
|
return 0
|
||||||
```
|
|
||||||
|
|
||||||
### Aggregated Statistics
|
|
||||||
|
|
||||||
```python
|
|
||||||
@bpf
|
|
||||||
@struct
|
|
||||||
class FileStats:
|
|
||||||
read_count: c_uint64
|
|
||||||
write_count: c_uint64
|
|
||||||
total_bytes_read: c_uint64
|
|
||||||
total_bytes_written: c_uint64
|
|
||||||
last_access: c_uint64
|
|
||||||
|
|
||||||
@bpf
|
|
||||||
@map
|
|
||||||
def file_stats() -> HashMap:
|
|
||||||
return HashMap(
|
|
||||||
key=str(256), # Filename as key
|
|
||||||
value=FileStats,
|
|
||||||
max_entries=1024
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Memory Layout
|
|
||||||
|
|
||||||
Structs in BPF follow C struct layout rules:
|
|
||||||
|
|
||||||
* Fields are laid out in order
|
|
||||||
* Padding may be added for alignment
|
|
||||||
* Size is rounded up to alignment
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@bpf
|
|
||||||
@struct
|
|
||||||
class Aligned:
|
|
||||||
a: c_uint8 # 1 byte
|
|
||||||
# 3 bytes padding
|
|
||||||
b: c_uint32 # 4 bytes
|
|
||||||
c: c_uint64 # 8 bytes
|
|
||||||
# Total: 16 bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
```{tip}
|
|
||||||
For optimal memory usage, order fields from largest to smallest to minimize padding.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Use descriptive field names** - Makes code self-documenting
|
|
||||||
2. **Order fields by size** - Reduces padding and memory usage
|
|
||||||
3. **Use appropriate sizes** - Don't use `c_uint64` when `c_uint32` suffices
|
|
||||||
4. **Document complex structs** - Add comments explaining field purposes
|
|
||||||
5. **Keep structs focused** - Each struct should represent one logical entity
|
|
||||||
6. **Use fixed-size strings** - Always specify string lengths explicitly
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
### Timestamp + Data Pattern
|
|
||||||
|
|
||||||
```python
|
|
||||||
@bpf
|
|
||||||
@struct
|
|
||||||
class TimestampedEvent:
|
|
||||||
timestamp: c_uint64 # Always first for sorting
|
|
||||||
# ... other fields
|
|
||||||
```
|
|
||||||
|
|
||||||
### Identification Pattern
|
|
||||||
|
|
||||||
```python
|
|
||||||
@bpf
|
|
||||||
@struct
|
|
||||||
class Identifiable:
|
|
||||||
pid: c_uint32
|
|
||||||
tid: c_uint32
|
|
||||||
cpu: c_uint32
|
|
||||||
# ... additional fields
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stats Aggregation Pattern
|
|
||||||
|
|
||||||
```python
|
|
||||||
@bpf
|
|
||||||
@struct
|
|
||||||
class Statistics:
|
|
||||||
count: c_uint64
|
|
||||||
sum: c_uint64
|
|
||||||
min: c_uint64
|
|
||||||
max: c_uint64
|
|
||||||
avg: c_uint64 # Computed in userspace
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
@ -522,17 +397,8 @@ If you get type errors:
|
|||||||
After capturing struct data, read it in Python:
|
After capturing struct data, read it in Python:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import ctypes
|
|
||||||
from pylibbpf import BpfMap
|
from pylibbpf import BpfMap
|
||||||
|
|
||||||
# Define matching Python class
|
|
||||||
class Event(ctypes.Structure):
|
|
||||||
_fields_ = [
|
|
||||||
("timestamp", ctypes.c_uint64),
|
|
||||||
("pid", ctypes.c_uint32),
|
|
||||||
("comm", ctypes.c_char * 16),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Read from map
|
# Read from map
|
||||||
map_obj = BpfMap(b, stats)
|
map_obj = BpfMap(b, stats)
|
||||||
for key, value_bytes in map_obj.items():
|
for key, value_bytes in map_obj.items():
|
||||||
|
|||||||
Reference in New Issue
Block a user