4 Commits

Author SHA1 Message Date
2ec67b6978 add globals todo 2025-09-30 14:48:51 +05:30
a2274ef29d deletions of demo 2025-09-30 14:29:55 +05:30
eb73001063 add debug info module 2025-09-30 14:29:20 +05:30
26f8f769c5 remove demos and add examples
Signed-off-by: varun-r-mallya <varunrmallya@gmail.com>
2025-09-29 23:44:49 +05:30
20 changed files with 1016 additions and 592 deletions

View File

@ -1,46 +0,0 @@
from __future__ import print_function
from bcc import BPF
from bcc.utils import printb
# load BPF program
b = BPF(text="""
#include <uapi/linux/ptrace.h>
BPF_HASH(last);
int do_trace(struct pt_regs *ctx) {
u64 ts, *tsp, delta, key = 0;
// attempt to read stored timestamp
tsp = last.lookup(&key);
if (tsp != NULL) {
delta = bpf_ktime_get_ns() - *tsp;
if (delta < 1000000000) {
// output if time is less than 1 second
bpf_trace_printk("%d\\n", delta / 1000000);
}
last.delete(&key);
}
// update stored timestamp
ts = bpf_ktime_get_ns();
last.update(&key, &ts);
return 0;
}
""")
b.attach_kprobe(event=b.get_syscall_fnname("sync"), fn_name="do_trace")
print("Tracing for quick sync's... Ctrl-C to end")
# TODO
# format output
start = 0
while 1:
try:
(task, pid, cpu, flags, ts, ms) = b.trace_fields()
if start == 0:
start = ts
ts = ts - start
printb(b"At time %.2f s: multiple syncs detected, last %s ms ago" % (ts, ms))
except KeyboardInterrupt:
exit()

File diff suppressed because one or more lines are too long

View File

@ -1,23 +0,0 @@
from pythonbpf import bpf, section, bpfglobal, compile
from ctypes import c_void_p, c_int64
# Instructions to how to run this program
# 1. Install PythonBPF: pip install pythonbpf
# 2. Run the program: python demo/pybpf0.py
# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf0.o
# 4. Start up any program and watch the output
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello_world(ctx: c_void_p) -> c_int64:
print("Hello, World!")
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile()

View File

@ -1,52 +0,0 @@
from pythonbpf import bpf, map, section, bpfglobal, compile
from pythonbpf.helpers import ktime
from pythonbpf.maps import HashMap
from ctypes import c_void_p, c_int64, c_uint64
# Instructions to how to run this program
# 1. Install PythonBPF: pip install pythonbpf
# 2. Run the program: python demo/pybpf3.py
# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf3.o
# 4. Start up any program and watch the output
@bpf
@map
def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def do_trace(ctx: c_void_p) -> c_int64:
key = 0
tsp = last().lookup(key)
if tsp:
kt = ktime()
delta = (kt - tsp)
if delta < 1000000000:
time_ms = (delta // 1000000)
print(f"Execve syscall entered within last second, last {time_ms} ms ago")
last().delete(key)
else:
kt = ktime()
last().update(key, kt)
return c_int64(0)
@bpf
@section("tracepoint/syscalls/sys_exit_execve")
def do_exit(ctx: c_void_p) -> c_int64:
va = 8
nm = 5 ^ va
al = 6 & 3
ru = (nm + al)
print(f"this is a variable {ru}")
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile()

430
examples/IO-run.ipynb Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,38 +2,39 @@
#include <bpf/bpf_helpers.h>
#define u64 unsigned long long
#define u32 unsigned int
// Define the map
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u64);
__type(value, u64);
__type(value, u32);
__uint(max_entries, 4);
} last SEC(".maps");
// Handler for syscall entry
SEC("tracepoint/syscalls/sys_enter_execve")
int hello(void *ctx) {
bpf_printk("entered");
bpf_printk("multi constant support");
return 0;
}
// // Handler for syscall entry
// SEC("tracepoint/syscalls/sys_enter_execve")
// int hello(void *ctx) {
// bpf_printk("entered");
// bpf_printk("multi constant support");
// return 0;
// }
// Handler for syscall exit
SEC("tracepoint/syscalls/sys_exit_execve")
long hello_again(void *ctx) {
bpf_printk("exited");
// // Handler for syscall exit
// SEC("tracepoint/syscalls/sys_exit_execve")
// long hello_again(void *ctx) {
// bpf_printk("exited");
// Create a key for map lookup
u64 key = 0;
// // Create a key for map lookup
// u64 key = 0;
// Simple lookup without conditionals
u64 *tsp = bpf_map_lookup_elem(&last, &key);
// // Simple lookup without conditionals
// u64 *tsp = bpf_map_lookup_elem(&last, &key);
// Get current timestamp
u64 ts = bpf_ktime_get_ns();
// // Get current timestamp
// u64 ts = bpf_ktime_get_ns();
return 0;
}
// return 0;
// }
char LICENSE[] SEC("license") = "GPL";

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
from pythonbpf.decorators import bpf, map, section, bpfglobal
from pythonbpf import bpf, map, section, bpfglobal, compile_to_ir
from ctypes import c_void_p, c_int64, c_int32, c_uint64
from pythonbpf.helpers import ktime
from pythonbpf.maps import HashMap
@ -10,26 +10,28 @@ def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=1)
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello(ctx: c_void_p) -> c_int32:
print("entered")
print("multi constant support")
return c_int32(0)
# @bpf
# @section("tracepoint/syscalls/sys_enter_execve")
# def hello(ctx: c_void_p) -> c_int32:
# print("entered")
# print("multi constant support")
# return c_int32(0)
@bpf
@section("tracepoint/syscalls/sys_exit_execve")
def hello_again(ctx: c_void_p) -> c_int64:
print("exited")
key = 0
tsp = last().lookup(key)
print(tsp)
ts = ktime()
return c_int64(0)
# @bpf
# @section("tracepoint/syscalls/sys_exit_execve")
# def hello_again(ctx: c_void_p) -> c_int64:
# print("exited")
# key = 0
# tsp = last().lookup(key)
# print(tsp)
# ts = ktime()
# return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile_to_ir("execve2.py", "execve2.ll")

View File

@ -1,15 +0,0 @@
# This is what it is going to look like
# pylint: disable-all# type: ignore
from pythonbpf.decorators import tracepoint, syscalls, bpfglobal, bpf
from ctypes import c_void_p, c_int32
@bpf
@tracepoint(syscalls.sys_clone)
def trace_clone(ctx: c_void_p) -> c_int32:
print("Hello, World!")
return c_int32(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"

35
examples/pybpf0.py Normal file
View File

@ -0,0 +1,35 @@
from pythonbpf import bpf, section, bpfglobal, BPF
import sys
from ctypes import c_void_p, c_int64
# Instructions to how to run this program
# 1. Install PythonBPF: pip install pythonbpf
# 2. `sudo /path/to/venv/bin/python ./python-bpf/demo/pybpf0.py`
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello_world(ctx: c_void_p) -> c_int64:
print("Hello, World!")
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
b.load_and_attach()
def main():
try:
with open("/sys/kernel/debug/tracing/trace_pipe", "r") as f:
for line in f:
sys.stdout.write(line)
sys.stdout.flush()
except KeyboardInterrupt:
pass
except PermissionError:
sys.stderr.write("Need root privileges to read trace_pipe\n")
if __name__ == "__main__":
main()

49
examples/pybpf3.py Normal file
View File

@ -0,0 +1,49 @@
from pythonbpf import *
from pylibbpf import *
import sys
from ctypes import c_void_p, c_int64, c_uint64
@bpf
@map
def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
@bpf
@section("tracepoint/syscalls/sys_enter_clone")
def do_trace(ctx: c_void_p) -> c_int64:
key = 0
tsp = last().lookup(key)
if tsp:
kt = ktime()
delta = (kt - tsp)
if delta < 1000000000:
time_ms = (delta // 1000000)
print(f"Clone syscall entered within last second, last {time_ms} ms ago")
last().delete(key)
else:
kt = ktime()
last().update(key, kt)
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
# autoattaches tracepoints
b.load_and_attach()
def main():
try:
with open("/sys/kernel/debug/tracing/trace_pipe", "r") as f:
for line in f:
sys.stdout.write(line)
sys.stdout.flush()
except KeyboardInterrupt:
pass
except PermissionError:
sys.stderr.write("Need root privileges to read trace_pipe\n")
if __name__ == "__main__":
main()

View File

@ -3,7 +3,8 @@ import time
from pythonbpf import bpf, map, section, bpfglobal, BPF
from pythonbpf.helpers import pid
from pythonbpf.maps import HashMap
from pylibbpf import *
from pylibbpf import BpfMap
from ctypes import c_void_p, c_int64, c_uint64, c_int32
import matplotlib.pyplot as plt
@ -40,7 +41,6 @@ def hello(ctx: c_void_p) -> c_int64:
def LICENSE() -> str:
return "GPL"
b = BPF()
b.load_and_attach()
hist = BpfMap(b, hist)
@ -48,9 +48,15 @@ print("Recording")
time.sleep(10)
counts = list(hist.values())
x = 0
for key in hist.keys():
if hist[key] > 40:
x += 1
print(f"PID {key} called clone() >40 times")
print(f"Total PIDs with clone() >40 times: {x}")
plt.hist(counts, bins=20)
plt.xlabel("Clone calls per PID")
plt.ylabel("Frequency")
plt.title("Syscall clone counts")
plt.ylabel("Number of processes that called clone() x times in last 10 seconds")
plt.title("x")
plt.show()

View File

@ -1,2 +1,4 @@
from .decorators import bpf, map, section, bpfglobal, struct
from .codegen import compile_to_ir, compile, BPF
from .maps import HashMap, PerfEventArray
from .helpers import pid, XDP_DROP, XDP_PASS, ktime, deref

View File

@ -1,5 +1,7 @@
import ast
from llvmlite import ir
from .debuginfo import DW_LANG_C11, DwarfBehaviorEnum
from .license_pass import license_processing
from .functions_pass import func_proc
from .maps_pass import maps_proc
@ -12,6 +14,7 @@ from pathlib import Path
from pylibbpf import BpfProgram
import tempfile
version = "v0.1.3"
def find_bpf_chunks(tree):
"""Find all functions decorated with @bpf in the AST."""
@ -50,16 +53,18 @@ def compile_to_ir(filename: str, output: str):
module.triple = "bpf"
if not hasattr(module, '_debug_compile_unit'):
module._file_metadata = module.add_debug_info("DIFile", { # type: ignore
module._file_metadata = module.add_debug_info("DIFile", { # type: ignore
"filename": filename,
"directory": os.path.dirname(filename)
})
module._debug_compile_unit = module.add_debug_info("DICompileUnit", { # type: ignore
"language": 29, # DW_LANG_C11
"file": module._file_metadata, # type: ignore
"producer": "PythonBPF DSL Compiler",
"isOptimized": True,
module._debug_compile_unit = module.add_debug_info("DICompileUnit", { # type: ignore
"language": DW_LANG_C11,
"file": module._file_metadata, # type: ignore
"producer": f"PythonBPF {version}",
"isOptimized": True, #TODO: This is probably not true
#TODO: add a global field here that keeps track of all the globals. Works without it, but I think it might
# be required for kprobes.
"runtimeVersion": 0,
"emissionKind": 1,
"splitDebugInlining": False,
@ -67,32 +72,32 @@ def compile_to_ir(filename: str, output: str):
}, is_distinct=True)
module.add_named_metadata(
"llvm.dbg.cu", module._debug_compile_unit) # type: ignore
"llvm.dbg.cu", module._debug_compile_unit) # type: ignore
processor(source, filename, module)
wchar_size = module.add_metadata([ir.Constant(ir.IntType(32), 1),
wchar_size = module.add_metadata([DwarfBehaviorEnum.ERROR_IF_MISMATCH,
"wchar_size",
ir.Constant(ir.IntType(32), 4)])
frame_pointer = module.add_metadata([ir.Constant(ir.IntType(32), 7),
frame_pointer = module.add_metadata([DwarfBehaviorEnum.OVERRIDE_USE_LARGEST,
"frame-pointer",
ir.Constant(ir.IntType(32), 2)])
# Add Debug Info Version (3 = DWARF v3, which LLVM expects)
debug_info_version = module.add_metadata([ir.Constant(ir.IntType(32), 2),
# Add Debug Info Version 3
debug_info_version = module.add_metadata([DwarfBehaviorEnum.WARNING_IF_MISMATCH,
"Debug Info Version",
ir.Constant(ir.IntType(32), 3)])
# Add explicit DWARF version (4 is common, works with LLVM BPF backend)
dwarf_version = module.add_metadata([ir.Constant(ir.IntType(32), 2),
# Add explicit DWARF version 5
dwarf_version = module.add_metadata([DwarfBehaviorEnum.OVERRIDE_USE_LARGEST,
"Dwarf Version",
ir.Constant(ir.IntType(32), 4)])
ir.Constant(ir.IntType(32), 5)])
module.add_named_metadata("llvm.module.flags", wchar_size)
module.add_named_metadata("llvm.module.flags", frame_pointer)
module.add_named_metadata("llvm.module.flags", debug_info_version)
module.add_named_metadata("llvm.module.flags", dwarf_version)
module.add_named_metadata("llvm.ident", ["llvmlite PythonBPF v0.0.1"])
module.add_named_metadata("llvm.ident", [f"PythonBPF {version}"])
print(f"IR written to {output}")
with open(output, "w") as f:
@ -125,8 +130,8 @@ def BPF() -> BpfProgram:
caller_frame = inspect.stack()[1]
src = inspect.getsource(caller_frame.frame)
with tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".py") as f, \
tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".ll") as inter, \
tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".o") as obj_file:
tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".ll") as inter, \
tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".o") as obj_file:
f.write(src)
f.flush()
source = f.name

View File

@ -0,0 +1,2 @@
from .dwarf_constants import *
from .dtypes import *

View File

@ -0,0 +1,6 @@
import llvmlite.ir as ir
class DwarfBehaviorEnum:
ERROR_IF_MISMATCH = ir.Constant(ir.IntType(32), 1)
WARNING_IF_MISMATCH = ir.Constant(ir.IntType(32), 2)
OVERRIDE_USE_LARGEST = ir.Constant(ir.IntType(32), 7)

View File

@ -1,7 +1,6 @@
import ast
from llvmlite import ir
from .type_deducer import ctypes_to_ir
from . import dwarf_constants as dc
from .debuginfo import dwarf_constants as dc
map_sym_tab = {}