@@ -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:
115115def 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
136135from pythonbpf import bpf, struct, map , section
@@ -157,23 +156,23 @@ def stats() -> HashMap:
157156@section (" tracepoint/syscalls/sys_enter_read" )
158157def 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
222218from pythonbpf import bpf, struct, map , section
223219from 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:
267262event = Event()
268263event.timestamp = ktime()
269264event.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" )
378348def 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" )
393361def 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:
522397After capturing struct data, read it in Python:
523398
524399``` python
525- import ctypes
526400from 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
537403map_obj = BpfMap(b, stats)
538404for key, value_bytes in map_obj.items():
0 commit comments