Skip to content

Commit 8bfd998

Browse files
committed
docs: Fix user-guide/bpf-structs
1 parent a31ef39 commit 8bfd998

File tree

1 file changed

+31
-165
lines changed

1 file changed

+31
-165
lines changed

docs/user-guide/structs.md

Lines changed: 31 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Numbers:
3737
short_int: c_int16 # -32768 to 32767
3838
int_val: c_int32 # -2^31 to 2^31-1
3939
long_int: c_int64 # -2^63 to 2^63-1
40-
40+
4141
byte: c_uint8 # 0 to 255
4242
word: c_uint16 # 0 to 65535
4343
dword: c_uint32 # 0 to 2^32-1
@@ -115,22 +115,21 @@ class Event:
115115
def capture_event(ctx: c_void_p) -> c_int64:
116116
# Create an instance
117117
event = Event()
118-
118+
119119
# Set fields
120120
event.timestamp = ktime()
121121
event.pid = pid()
122-
# Note: comm() requires a buffer parameter to fill
123-
# comm(event.comm) # Fills event.comm with process name
124-
122+
comm(event.comm) # Fills event.comm with process name
123+
125124
# Use the struct
126125
print(f"Process with PID {event.pid}")
127-
128-
return c_int64(0)
126+
127+
return 0
129128
```
130129

131-
### As Map Values
130+
### As Map Keys and Values
132131

133-
Use structs as values in maps for complex state storage:
132+
Use structs as keys and values in maps for complex state storage:
134133

135134
```python
136135
from pythonbpf import bpf, struct, map, section
@@ -157,23 +156,23 @@ def stats() -> HashMap:
157156
@section("tracepoint/syscalls/sys_enter_read")
158157
def track_syscalls(ctx: c_void_p) -> c_int64:
159158
process_id = pid()
160-
159+
161160
# Lookup existing stats
162161
s = stats.lookup(process_id)
163-
162+
164163
if s:
165164
# Update existing stats
166165
s.syscall_count = s.syscall_count + 1
167166
stats.update(process_id, s)
168167
else:
169168
# Create new stats
170169
new_stats = ProcessStats()
171-
new_stats.syscall_count = c_uint64(1)
172-
new_stats.total_time = c_uint64(0)
173-
new_stats.max_latency = c_uint64(0)
170+
new_stats.syscall_count = 1
171+
new_stats.total_time = 0
172+
new_stats.max_latency = 0
174173
stats.update(process_id, new_stats)
175-
176-
return c_int64(0)
174+
175+
return 0
177176
```
178177

179178
### With Perf Events
@@ -205,19 +204,16 @@ def trace_fork(ctx: c_void_p) -> c_int64:
205204
event = ProcessEvent()
206205
event.timestamp = ktime()
207206
event.pid = pid()
208-
# Note: comm() requires a buffer parameter
209-
# comm(event.comm) # Fills event.comm with process name
210-
207+
comm(event.comm) # Fills event.comm with process name
208+
211209
# Send to userspace
212210
events.output(event)
213-
214-
return c_int64(0)
211+
212+
return 0
215213
```
216214

217215
### With Ring Buffers
218216

219-
Ring buffers provide efficient event delivery:
220-
221217
```python
222218
from pythonbpf import bpf, struct, map, section
223219
from pythonbpf.maps import RingBuffer
@@ -240,11 +236,10 @@ def trace_open(ctx: c_void_p) -> c_int64:
240236
event = FileEvent()
241237
event.timestamp = ktime()
242238
event.pid = pid()
243-
# event.filename would be populated from ctx
244-
239+
245240
events.output(event)
246-
247-
return c_int64(0)
241+
242+
return 0
248243
```
249244

250245
## Field Access and Modification
@@ -267,31 +262,7 @@ Assign values to fields:
267262
event = Event()
268263
event.timestamp = ktime()
269264
event.pid = pid()
270-
# Note: comm() requires a buffer parameter
271-
# comm(event.comm) # Fills event.comm with process name
272-
```
273-
274-
### String Fields
275-
276-
String fields have special handling:
277-
278-
```python
279-
@bpf
280-
@struct
281-
class Message:
282-
text: str(64)
283-
284-
@bpf
285-
def example(ctx: c_void_p) -> c_int64:
286-
msg = Message()
287-
288-
# Assign string value
289-
msg.text = "Hello from BPF"
290-
291-
# Use helper to get process name (requires buffer)
292-
# comm(msg.text) # Fills msg.text with process name
293-
294-
return c_int64(0)
265+
comm(event.comm)
295266
```
296267

297268
## StructType Class
@@ -344,10 +315,9 @@ def capture_packets(ctx: c_void_p) -> c_int64:
344315
pkt = PacketEvent()
345316
pkt.timestamp = ktime()
346317
# Parse packet data from ctx...
347-
318+
348319
packets.output(pkt)
349-
350-
# XDP_PASS
320+
351321
return XDP_PASS
352322
```
353323

@@ -377,121 +347,26 @@ def process_info() -> HashMap:
377347
@section("tracepoint/sched/sched_process_fork")
378348
def track_fork(ctx: c_void_p) -> c_int64:
379349
process_id = pid()
380-
350+
381351
info = ProcessLifecycle()
382352
info.pid = process_id
383353
info.start_time = ktime()
384-
# Note: comm() requires a buffer parameter
385-
# comm(info.comm) # Fills info.comm with process name
386-
354+
387355
process_info.update(process_id, info)
388-
389-
return c_int64(0)
356+
357+
return 0
390358

391359
@bpf
392360
@section("tracepoint/sched/sched_process_exit")
393361
def track_exit(ctx: c_void_p) -> c_int64:
394362
process_id = pid()
395-
363+
396364
info = process_info.lookup(process_id)
397365
if info:
398366
info.exit_time = ktime()
399367
process_info.update(process_id, info)
400-
401-
return c_int64(0)
402-
```
403368

404-
### Aggregated Statistics
405-
406-
```python
407-
@bpf
408-
@struct
409-
class FileStats:
410-
read_count: c_uint64
411-
write_count: c_uint64
412-
total_bytes_read: c_uint64
413-
total_bytes_written: c_uint64
414-
last_access: c_uint64
415-
416-
@bpf
417-
@map
418-
def file_stats() -> HashMap:
419-
return HashMap(
420-
key=str(256), # Filename as key
421-
value=FileStats,
422-
max_entries=1024
423-
)
424-
```
425-
426-
## Memory Layout
427-
428-
Structs in BPF follow C struct layout rules:
429-
430-
* Fields are laid out in order
431-
* Padding may be added for alignment
432-
* Size is rounded up to alignment
433-
434-
Example:
435-
436-
```python
437-
@bpf
438-
@struct
439-
class Aligned:
440-
a: c_uint8 # 1 byte
441-
# 3 bytes padding
442-
b: c_uint32 # 4 bytes
443-
c: c_uint64 # 8 bytes
444-
# Total: 16 bytes
445-
```
446-
447-
```{tip}
448-
For optimal memory usage, order fields from largest to smallest to minimize padding.
449-
```
450-
451-
## Best Practices
452-
453-
1. **Use descriptive field names** - Makes code self-documenting
454-
2. **Order fields by size** - Reduces padding and memory usage
455-
3. **Use appropriate sizes** - Don't use `c_uint64` when `c_uint32` suffices
456-
4. **Document complex structs** - Add comments explaining field purposes
457-
5. **Keep structs focused** - Each struct should represent one logical entity
458-
6. **Use fixed-size strings** - Always specify string lengths explicitly
459-
460-
## Common Patterns
461-
462-
### Timestamp + Data Pattern
463-
464-
```python
465-
@bpf
466-
@struct
467-
class TimestampedEvent:
468-
timestamp: c_uint64 # Always first for sorting
469-
# ... other fields
470-
```
471-
472-
### Identification Pattern
473-
474-
```python
475-
@bpf
476-
@struct
477-
class Identifiable:
478-
pid: c_uint32
479-
tid: c_uint32
480-
cpu: c_uint32
481-
# ... additional fields
482-
```
483-
484-
### Stats Aggregation Pattern
485-
486-
```python
487-
@bpf
488-
@struct
489-
class Statistics:
490-
count: c_uint64
491-
sum: c_uint64
492-
min: c_uint64
493-
max: c_uint64
494-
avg: c_uint64 # Computed in userspace
369+
return 0
495370
```
496371

497372
## Troubleshooting
@@ -522,17 +397,8 @@ If you get type errors:
522397
After capturing struct data, read it in Python:
523398

524399
```python
525-
import ctypes
526400
from pylibbpf import BpfMap
527401

528-
# Define matching Python class
529-
class Event(ctypes.Structure):
530-
_fields_ = [
531-
("timestamp", ctypes.c_uint64),
532-
("pid", ctypes.c_uint32),
533-
("comm", ctypes.c_char * 16),
534-
]
535-
536402
# Read from map
537403
map_obj = BpfMap(b, stats)
538404
for key, value_bytes in map_obj.items():

0 commit comments

Comments
 (0)