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> #include <bpf/bpf_helpers.h>
#define u64 unsigned long long #define u64 unsigned long long
#define u32 unsigned int
// Define the map // Define the map
struct { struct {
__uint(type, BPF_MAP_TYPE_HASH); __uint(type, BPF_MAP_TYPE_HASH);
__type(key, u64); __type(key, u64);
__type(value, u64); __type(value, u32);
__uint(max_entries, 4); __uint(max_entries, 4);
} last SEC(".maps"); } last SEC(".maps");
// Handler for syscall entry // // Handler for syscall entry
SEC("tracepoint/syscalls/sys_enter_execve") // SEC("tracepoint/syscalls/sys_enter_execve")
int hello(void *ctx) { // int hello(void *ctx) {
bpf_printk("entered"); // bpf_printk("entered");
bpf_printk("multi constant support"); // bpf_printk("multi constant support");
return 0; // return 0;
} // }
// Handler for syscall exit // // Handler for syscall exit
SEC("tracepoint/syscalls/sys_exit_execve") // SEC("tracepoint/syscalls/sys_exit_execve")
long hello_again(void *ctx) { // long hello_again(void *ctx) {
bpf_printk("exited"); // bpf_printk("exited");
// Create a key for map lookup // // Create a key for map lookup
u64 key = 0; // u64 key = 0;
// Simple lookup without conditionals // // Simple lookup without conditionals
u64 *tsp = bpf_map_lookup_elem(&last, &key); // u64 *tsp = bpf_map_lookup_elem(&last, &key);
// Get current timestamp // // Get current timestamp
u64 ts = bpf_ktime_get_ns(); // u64 ts = bpf_ktime_get_ns();
return 0; // return 0;
} // }
char LICENSE[] SEC("license") = "GPL"; 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 ctypes import c_void_p, c_int64, c_int32, c_uint64
from pythonbpf.helpers import ktime from pythonbpf.helpers import ktime
from pythonbpf.maps import HashMap from pythonbpf.maps import HashMap
@ -10,26 +10,28 @@ def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=1) return HashMap(key=c_uint64, value=c_uint64, max_entries=1)
@bpf # @bpf
@section("tracepoint/syscalls/sys_enter_execve") # @section("tracepoint/syscalls/sys_enter_execve")
def hello(ctx: c_void_p) -> c_int32: # def hello(ctx: c_void_p) -> c_int32:
print("entered") # print("entered")
print("multi constant support") # print("multi constant support")
return c_int32(0) # return c_int32(0)
@bpf # @bpf
@section("tracepoint/syscalls/sys_exit_execve") # @section("tracepoint/syscalls/sys_exit_execve")
def hello_again(ctx: c_void_p) -> c_int64: # def hello_again(ctx: c_void_p) -> c_int64:
print("exited") # print("exited")
key = 0 # key = 0
tsp = last().lookup(key) # tsp = last().lookup(key)
print(tsp) # print(tsp)
ts = ktime() # ts = ktime()
return c_int64(0) # return c_int64(0)
@bpf @bpf
@bpfglobal @bpfglobal
def LICENSE() -> str: def LICENSE() -> str:
return "GPL" 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 import bpf, map, section, bpfglobal, BPF
from pythonbpf.helpers import pid from pythonbpf.helpers import pid
from pythonbpf.maps import HashMap from pythonbpf.maps import HashMap
from pylibbpf import * from pylibbpf import BpfMap
from ctypes import c_void_p, c_int64, c_uint64, c_int32 from ctypes import c_void_p, c_int64, c_uint64, c_int32
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@ -40,7 +41,6 @@ def hello(ctx: c_void_p) -> c_int64:
def LICENSE() -> str: def LICENSE() -> str:
return "GPL" return "GPL"
b = BPF() b = BPF()
b.load_and_attach() b.load_and_attach()
hist = BpfMap(b, hist) hist = BpfMap(b, hist)
@ -48,9 +48,15 @@ print("Recording")
time.sleep(10) time.sleep(10)
counts = list(hist.values()) 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.hist(counts, bins=20)
plt.xlabel("Clone calls per PID") plt.xlabel("Clone calls per PID")
plt.ylabel("Frequency") plt.ylabel("Number of processes that called clone() x times in last 10 seconds")
plt.title("Syscall clone counts") plt.title("x")
plt.show() plt.show()

View File

@ -1,2 +1,4 @@
from .decorators import bpf, map, section, bpfglobal, struct from .decorators import bpf, map, section, bpfglobal, struct
from .codegen import compile_to_ir, compile, BPF 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 import ast
from llvmlite import ir from llvmlite import ir
from .debuginfo import DW_LANG_C11, DwarfBehaviorEnum
from .license_pass import license_processing from .license_pass import license_processing
from .functions_pass import func_proc from .functions_pass import func_proc
from .maps_pass import maps_proc from .maps_pass import maps_proc
@ -12,6 +14,7 @@ from pathlib import Path
from pylibbpf import BpfProgram from pylibbpf import BpfProgram
import tempfile import tempfile
version = "v0.1.3"
def find_bpf_chunks(tree): def find_bpf_chunks(tree):
"""Find all functions decorated with @bpf in the AST.""" """Find all functions decorated with @bpf in the AST."""
@ -56,10 +59,12 @@ def compile_to_ir(filename: str, output: str):
}) })
module._debug_compile_unit = module.add_debug_info("DICompileUnit", { # type: ignore module._debug_compile_unit = module.add_debug_info("DICompileUnit", { # type: ignore
"language": 29, # DW_LANG_C11 "language": DW_LANG_C11,
"file": module._file_metadata, # type: ignore "file": module._file_metadata, # type: ignore
"producer": "PythonBPF DSL Compiler", "producer": f"PythonBPF {version}",
"isOptimized": True, "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, "runtimeVersion": 0,
"emissionKind": 1, "emissionKind": 1,
"splitDebugInlining": False, "splitDebugInlining": False,
@ -71,28 +76,28 @@ def compile_to_ir(filename: str, output: str):
processor(source, filename, module) 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", "wchar_size",
ir.Constant(ir.IntType(32), 4)]) 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", "frame-pointer",
ir.Constant(ir.IntType(32), 2)]) ir.Constant(ir.IntType(32), 2)])
# Add Debug Info Version (3 = DWARF v3, which LLVM expects) # Add Debug Info Version 3
debug_info_version = module.add_metadata([ir.Constant(ir.IntType(32), 2), debug_info_version = module.add_metadata([DwarfBehaviorEnum.WARNING_IF_MISMATCH,
"Debug Info Version", "Debug Info Version",
ir.Constant(ir.IntType(32), 3)]) ir.Constant(ir.IntType(32), 3)])
# Add explicit DWARF version (4 is common, works with LLVM BPF backend) # Add explicit DWARF version 5
dwarf_version = module.add_metadata([ir.Constant(ir.IntType(32), 2), dwarf_version = module.add_metadata([DwarfBehaviorEnum.OVERRIDE_USE_LARGEST,
"Dwarf Version", "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", wchar_size)
module.add_named_metadata("llvm.module.flags", frame_pointer) 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", debug_info_version)
module.add_named_metadata("llvm.module.flags", dwarf_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}") print(f"IR written to {output}")
with open(output, "w") as f: with open(output, "w") as f:

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 import ast
from llvmlite import ir from llvmlite import ir
from .type_deducer import ctypes_to_ir from .debuginfo import dwarf_constants as dc
from . import dwarf_constants as dc
map_sym_tab = {} map_sym_tab = {}