Skip to content

Commit aded125

Browse files
committed
docs: Fix helpers and maps guide
1 parent 581269e commit aded125

File tree

2 files changed

+58
-130
lines changed

2 files changed

+58
-130
lines changed

docs/user-guide/helpers.md

Lines changed: 47 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
PythonBPF provides helper functions and utilities for BPF programs and userspace code.
44

5+
```{note}
6+
**Work in Progress:** PythonBPF is under active development. We are constantly adding support for more helpers, kfuncs, and map types. Check back for updates!
7+
```
8+
For comprehensive documentation on BPF helpers, see the [eBPF Helper Functions documentation on ebpf.io](https://ebpf.io/what-is-ebpf/#helper-calls).
9+
510
## BPF Helper Functions
611

712
BPF helper functions are kernel-provided functions that BPF programs can call to interact with the system. PythonBPF exposes these through the `pythonbpf.helper` module.
@@ -16,6 +21,8 @@ from pythonbpf.helper import pid, ktime, comm
1621

1722
Get the current process ID.
1823

24+
> **Linux Kernel Helper:** `bpf_get_current_pid_tgid()`
25+
1926
```python
2027
from pythonbpf.helper import pid
2128

@@ -24,39 +31,28 @@ from pythonbpf.helper import pid
2431
def trace_open(ctx: c_void_p) -> c_int64:
2532
process_id = pid()
2633
print(f"Process {process_id} opened a file")
27-
return c_int64(0)
34+
return 0
2835
```
2936

3037
**Returns:** `c_int32` - The process ID of the current task
3138

3239
#### comm()
3340

34-
Get the current process command name (up to 16 characters).
41+
Get the current process command name.
3542

36-
```python
37-
from pythonbpf.helper import comm
38-
39-
@bpf
40-
@section("tracepoint/syscalls/sys_enter_execve")
41-
def trace_exec(ctx: c_void_p) -> c_int64:
42-
# comm requires a buffer to fill
43-
process_name = str(16)
44-
comm(process_name)
45-
print(f"Executing: {process_name}")
46-
return c_int64(0)
47-
```
43+
> **Linux Kernel Helper:** `bpf_get_current_comm()`
4844
4945
**Parameters:**
5046
* `buf` - Buffer to fill with the process command name
5147

5248
**Returns:** `c_int64` - 0 on success, negative on error
5349

54-
**Note:** The buffer should be at least 16 bytes (TASK_COMM_LEN) to hold the full command name.
55-
5650
#### uid()
5751

5852
Get the current user ID.
5953

54+
> **Linux Kernel Helper:** `bpf_get_current_uid_gid()`
55+
6056
```python
6157
from pythonbpf.helper import uid
6258

@@ -66,7 +62,7 @@ def trace_open(ctx: c_void_p) -> c_int64:
6662
user_id = uid()
6763
if user_id == 0:
6864
print("Root user opened a file")
69-
return c_int64(0)
65+
return 0
7066
```
7167

7268
**Returns:** `c_int32` - The user ID of the current task
@@ -77,6 +73,8 @@ def trace_open(ctx: c_void_p) -> c_int64:
7773

7874
Get the current kernel time in nanoseconds since system boot.
7975

76+
> **Linux Kernel Helper:** `bpf_ktime_get_ns()`
77+
8078
```python
8179
from pythonbpf.helper import ktime
8280

@@ -85,7 +83,7 @@ from pythonbpf.helper import ktime
8583
def measure_latency(ctx: c_void_p) -> c_int64:
8684
start_time = ktime()
8785
# Store for later comparison
88-
return c_int64(0)
86+
return 0
8987
```
9088

9189
**Returns:** `c_int64` - Current time in nanoseconds
@@ -102,6 +100,8 @@ def measure_latency(ctx: c_void_p) -> c_int64:
102100

103101
Get the ID of the CPU on which the BPF program is running.
104102

103+
> **Linux Kernel Helper:** `bpf_get_smp_processor_id()`
104+
105105
```python
106106
from pythonbpf.helper import smp_processor_id
107107

@@ -110,7 +110,7 @@ from pythonbpf.helper import smp_processor_id
110110
def track_cpu(ctx: c_void_p) -> c_int64:
111111
cpu = smp_processor_id()
112112
print(f"Running on CPU {cpu}")
113-
return c_int64(0)
113+
return 0
114114
```
115115

116116
**Returns:** `c_int32` - The current CPU ID
@@ -126,19 +126,21 @@ def track_cpu(ctx: c_void_p) -> c_int64:
126126

127127
Safely read data from kernel memory.
128128

129+
> **Linux Kernel Helper:** `bpf_probe_read()`
130+
129131
```python
130132
from pythonbpf.helper import probe_read
131133

132134
@bpf
133135
def read_kernel_data(ctx: c_void_p) -> c_int64:
134-
dst = c_uint64(0)
136+
dst = 0
135137
size = 8
136-
src = c_void_p(...) # kernel address
137-
138+
src = ctx # kernel address
139+
138140
result = probe_read(dst, size, src)
139141
if result == 0:
140142
print(f"Read value: {dst}")
141-
return c_int64(0)
143+
return 0
142144
```
143145

144146
**Parameters:**
@@ -154,52 +156,22 @@ def read_kernel_data(ctx: c_void_p) -> c_int64:
154156

155157
Safely read a null-terminated string from kernel memory.
156158

157-
```python
158-
from pythonbpf.helper import probe_read_str
159-
160-
@bpf
161-
def read_filename(ctx: c_void_p) -> c_int64:
162-
filename = str(256)
163-
src = c_void_p(...) # pointer to filename in kernel
164-
165-
result = probe_read_str(filename, src)
166-
if result > 0:
167-
print(f"Filename: {filename}")
168-
return c_int64(0)
169-
```
159+
> **Linux Kernel Helper:** `bpf_probe_read_str()`
170160
171161
**Parameters:**
172162
* `dst` - Destination buffer (string)
173163
* `src` - Source kernel address
174164

175165
**Returns:** `c_int64` - Length of string on success, negative on error
176166

177-
#### deref()
178-
179-
Dereference a pointer safely.
180-
181-
```python
182-
from pythonbpf.helper import deref
183-
184-
@bpf
185-
def access_pointer(ctx: c_void_p) -> c_int64:
186-
ptr = c_void_p(...)
187-
value = deref(ptr)
188-
print(f"Value at pointer: {value}")
189-
return c_int64(0)
190-
```
191-
192-
**Parameters:**
193-
* `ptr` - Pointer to dereference
194-
195-
**Returns:** The dereferenced value or 0 if null
196-
197167
### Random Numbers
198168

199169
#### random()
200170

201171
Generate a pseudo-random 32-bit number.
202172

173+
> **Linux Kernel Helper:** `bpf_get_prandom_u32()`
174+
203175
```python
204176
from pythonbpf.helper import random
205177

@@ -209,23 +181,19 @@ def sample_events(ctx: c_void_p) -> c_int64:
209181
# Sample 1% of events
210182
if (random() % 100) == 0:
211183
print("Sampled event")
212-
return c_int64(0)
184+
return 0
213185
```
214186

215187
**Returns:** `c_int32` - A pseudo-random number
216188

217-
**Use cases:**
218-
* Event sampling
219-
* Load shedding
220-
* A/B testing
221-
* Randomized algorithms
222-
223189
### Network Helpers
224190

225191
#### skb_store_bytes()
226192

227193
Store bytes into a socket buffer (for network programs).
228194

195+
> **Linux Kernel Helper:** `bpf_skb_store_bytes()`
196+
229197
```python
230198
from pythonbpf.helper import skb_store_bytes
231199

@@ -235,9 +203,9 @@ def modify_packet(ctx: c_void_p) -> c_int32:
235203
offset = 14 # Skip Ethernet header
236204
data = b"\x00\x01\x02\x03"
237205
size = len(data)
238-
206+
239207
result = skb_store_bytes(offset, data, size)
240-
return c_int32(0)
208+
return 0
241209
```
242210

243211
**Parameters:**
@@ -277,7 +245,7 @@ from ctypes import c_void_p, c_int64
277245
@section("tracepoint/syscalls/sys_enter_execve")
278246
def trace_exec(ctx: c_void_p) -> c_int64:
279247
print("Process started") # This goes to trace_pipe
280-
return c_int64(0)
248+
return 0
281249

282250
@bpf
283251
@bpfglobal
@@ -336,7 +304,7 @@ from ctypes import c_void_p, c_int64
336304
@section("tracepoint/syscalls/sys_enter_execve")
337305
def trace_exec(ctx: c_void_p) -> c_int64:
338306
print(f"PID:{pid()}")
339-
return c_int64(0)
307+
return 0
340308

341309
@bpf
342310
@bpfglobal
@@ -382,20 +350,20 @@ def read_start(ctx: c_void_p) -> c_int64:
382350
process_id = pid()
383351
start = ktime()
384352
start_times.update(process_id, start)
385-
return c_int64(0)
353+
return 0
386354

387355
@bpf
388356
@section("tracepoint/syscalls/sys_exit_read")
389357
def read_end(ctx: c_void_p) -> c_int64:
390358
process_id = pid()
391359
start = start_times.lookup(process_id)
392-
360+
393361
if start:
394362
latency = ktime() - start
395363
print(f"Read latency: {latency} ns")
396364
start_times.delete(process_id)
397-
398-
return c_int64(0)
365+
366+
return 0
399367

400368
@bpf
401369
@bpfglobal
@@ -419,9 +387,9 @@ from ctypes import c_void_p, c_int64
419387
def track_exec(ctx: c_void_p) -> c_int64:
420388
process_id = pid()
421389
user_id = uid()
422-
390+
423391
print(f"User {user_id} started process (PID: {process_id})")
424-
return c_int64(0)
392+
return 0
425393

426394
@bpf
427395
@bpfglobal
@@ -451,13 +419,13 @@ def cpu_counts() -> HashMap:
451419
def count_switches(ctx: c_void_p) -> c_int64:
452420
cpu = smp_processor_id()
453421
count = cpu_counts.lookup(cpu)
454-
422+
455423
if count:
456424
cpu_counts.update(cpu, count + 1)
457425
else:
458-
cpu_counts.update(cpu, c_uint64(1))
459-
460-
return c_int64(0)
426+
cpu_counts.update(cpu, 1)
427+
428+
return 0
461429

462430
@bpf
463431
@bpfglobal
@@ -491,8 +459,8 @@ def sample_opens(ctx: c_void_p) -> c_int64:
491459
if (random() % 100) < 5:
492460
process_id = pid()
493461
print(f"Sampled: PID {process_id} opening file")
494-
495-
return c_int64(0)
462+
463+
return 0
496464

497465
@bpf
498466
@bpfglobal
@@ -504,56 +472,12 @@ b.load_and_attach()
504472
trace_pipe()
505473
```
506474

507-
## Best Practices
508-
509-
1. **Use appropriate helpers** - Choose the right helper for your use case
510-
2. **Handle errors** - Check return values from helpers like `probe_read()`
511-
3. **Minimize overhead** - Helper calls have cost; use judiciously
512-
4. **Sample when appropriate** - Use `random()` for high-frequency events
513-
5. **Clean up resources** - Delete map entries when done
514-
515-
## Common Patterns
516-
517-
### Store-and-Compare Pattern
518-
519-
```python
520-
# Store a value
521-
key = pid()
522-
value = ktime()
523-
my_map.update(key, value)
524-
525-
# Later: compare
526-
stored = my_map.lookup(key)
527-
if stored:
528-
difference = ktime() - stored
529-
```
530-
531-
### Filtering Pattern
532-
533-
```python
534-
# Filter by user
535-
user_id = uid()
536-
if user_id == 0: # Only root
537-
# Process event
538-
pass
539-
```
540-
541-
### Sampling Pattern
542-
543-
```python
544-
# Sample 1 in N events
545-
if (random() % N) == 0:
546-
# Process sampled event
547-
pass
548-
```
549-
550475
## Troubleshooting
551476

552477
### Helper Not Available
553478

554479
If a helper function doesn't work:
555480
* Check your kernel version (some helpers are newer)
556-
* Verify the helper is available with `bpftool feature`
557481
* Ensure your LICENSE is GPL-compatible
558482

559483
### Trace Pipe Access Denied
@@ -563,13 +487,6 @@ If `trace_pipe()` fails:
563487
* Check `/sys/kernel/tracing/` is accessible
564488
* Verify tracing is enabled in kernel config
565489

566-
### probe_read Failures
567-
568-
If `probe_read()` returns errors:
569-
* Ensure the source address is valid kernel memory
570-
* Check that the size is reasonable
571-
* Verify you're not reading from restricted areas
572-
573490
## Next Steps
574491

575492
* Explore {doc}`maps` for data storage with helpers

0 commit comments

Comments
 (0)