3 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
16 changed files with 72 additions and 728 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,41 +0,0 @@
from pythonbpf import bpf, map, section, bpfglobal, compile
from pythonbpf.helpers import XDP_PASS
from pythonbpf.maps import HashMap
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/pybpf1.py
# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf1.o
# 4. Attach object file to any network device with something like ./check.sh xdp ../demo/pybpf1.o tailscale0
# 5. send traffic through the device and observe effects
@bpf
@map
def count() -> HashMap:
return HashMap(key=c_int64, value=c_int64, max_entries=1)
@bpf
@section("xdp")
def hello_world(ctx: c_void_p) -> c_int64:
key = 0
one = 1
prev = count().lookup(key)
if prev:
prevval = prev + 1
print(f"count: {prevval}")
count().update(key, prevval)
return XDP_PASS
else:
count().update(key, one)
return XDP_PASS
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile()

View File

@ -1,43 +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/pybpf2.py
# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf2.o
# 4. Start a Python repl and `import os` and then keep entering `os.sync()` to see reponses.
@bpf
@map
def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
@bpf
@section("tracepoint/syscalls/sys_enter_sync")
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"sync called 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"
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()

View File

@ -1,56 +0,0 @@
import time
from pythonbpf import bpf, map, section, bpfglobal, BPF
from pythonbpf.helpers import pid
from pythonbpf.maps import HashMap
from pylibbpf import *
from ctypes import c_void_p, c_int64, c_uint64, c_int32
import matplotlib.pyplot as plt
# This program attaches an eBPF tracepoint to sys_enter_clone,
# counts per-PID clone syscalls, stores them in a hash map,
# and then plots the distribution as a histogram using matplotlib.
# It provides a quick view of process creation activity over 10 seconds.
# Everything is done with Python only code and with the new pylibbpf library.
# Run `sudo /path/to/python/binary/ pybpf4.py`
@bpf
@map
def hist() -> HashMap:
return HashMap(key=c_int32, value=c_uint64, max_entries=4096)
@bpf
@section("tracepoint/syscalls/sys_enter_clone")
def hello(ctx: c_void_p) -> c_int64:
process_id = pid()
one = 1
prev = hist().lookup(process_id)
if prev:
previous_value = prev + 1
print(f"count: {previous_value} with {process_id}")
hist().update(process_id, previous_value)
return c_int64(0)
else:
hist().update(process_id, one)
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
b.load_and_attach()
hist = BpfMap(b, hist)
print("Recording")
time.sleep(10)
counts = list(hist.values())
plt.hist(counts, bins=20)
plt.xlabel("Clone calls per PID")
plt.ylabel("Frequency")
plt.title("Syscall clone counts")
plt.show()

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";

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"

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 = {}