mirror of
https://github.com/varun-r-mallya/pylibbpf.git
synced 2026-03-22 05:01:31 +00:00
Compare commits
8 Commits
4a5ff0c1c2
...
771d8fef0a
| Author | SHA1 | Date | |
|---|---|---|---|
| 771d8fef0a | |||
| cbe019c2bc | |||
| 1c956da07f | |||
| fc4d9a51e7 | |||
| c0b982a514 | |||
| 1c2e170bab | |||
| f233cf2134 | |||
| 744a50925e |
@ -1,6 +1,10 @@
|
||||
cmake_minimum_required(VERSION 4.0)
|
||||
project(pylibbpf)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# pybind11
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||
add_subdirectory(pybind11)
|
||||
@ -9,9 +13,13 @@ pybind11_add_module(
|
||||
src/core/bpf_program.h
|
||||
src/core/bpf_exception.h
|
||||
src/core/bpf_map.h
|
||||
src/core/bpf_object.h
|
||||
src/core/bpf_perf_buffer.h
|
||||
src/bindings/main.cpp
|
||||
src/core/bpf_program.cpp
|
||||
src/core/bpf_map.cpp)
|
||||
src/core/bpf_map.cpp
|
||||
src/core/bpf_object.cpp
|
||||
src/core/bpf_perf_buffer.cpp)
|
||||
|
||||
# --- libbpf build rules ---
|
||||
set(LIBBPF_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libbpf/src)
|
||||
|
||||
@ -6,9 +6,11 @@ extern "C" {
|
||||
#include <libbpf.h>
|
||||
}
|
||||
|
||||
#include "core/bpf_object.h"
|
||||
#include "core/bpf_program.h"
|
||||
#include "core/bpf_exception.h"
|
||||
#include "core/bpf_map.h"
|
||||
#include "core/bpf_perf_buffer.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
@ -30,33 +32,48 @@ PYBIND11_MODULE(pylibbpf, m) {
|
||||
// Register the custom exception
|
||||
py::register_exception<BpfException>(m, "BpfException");
|
||||
|
||||
py::class_<BpfProgram>(m, "BpfProgram")
|
||||
.def(py::init<const std::string &>())
|
||||
.def(py::init<const std::string &, const std::string &>())
|
||||
.def("load", &BpfProgram::load)
|
||||
.def("attach", &BpfProgram::attach)
|
||||
.def("destroy", &BpfProgram::destroy)
|
||||
.def("load_and_attach", &BpfProgram::load_and_attach)
|
||||
.def("is_loaded", &BpfProgram::is_loaded)
|
||||
.def("is_attached", &BpfProgram::is_attached);
|
||||
// BpfObject
|
||||
py::class_<BpfObject, std::shared_ptr<BpfObject>>(m, "BpfObject")
|
||||
.def(py::init<std::string>(), py::arg("object_path"))
|
||||
.def("load", &BpfObject::load)
|
||||
.def("is_loaded", &BpfObject::is_loaded)
|
||||
.def("get_program_names", &BpfObject::get_program_names)
|
||||
.def("get_program", &BpfObject::get_program, py::arg("name"))
|
||||
.def("attach_all", &BpfObject::attach_all)
|
||||
.def("get_map_names", &BpfObject::get_map_names)
|
||||
.def("get_map", &BpfObject::get_map, py::arg("name"));
|
||||
|
||||
py::class_<BpfMap>(m, "BpfMap")
|
||||
.def(py::init<BpfProgram *, py::object &>())
|
||||
.def("lookup", &BpfMap::lookup)
|
||||
.def("update", &BpfMap::update)
|
||||
.def("delete", &BpfMap::delete_elem)
|
||||
.def("get_next_key", &BpfMap::get_next_key, py::arg("key") = py::none())
|
||||
.def("items", &BpfMap::items)
|
||||
.def("keys", &BpfMap::keys)
|
||||
.def("values", &BpfMap::values)
|
||||
.def("get_name", &BpfMap::get_name)
|
||||
.def("get_type", &BpfMap::get_type)
|
||||
.def("get_key_size", &BpfMap::get_key_size)
|
||||
.def("get_value_size", &BpfMap::get_value_size)
|
||||
.def("get_max_entries", &BpfMap::get_max_entries)
|
||||
.def("__getitem__", &BpfMap::lookup)
|
||||
.def("__setitem__", &BpfMap::update)
|
||||
.def("__delitem__", &BpfMap::delete_elem);
|
||||
// BpfProgram
|
||||
py::class_<BpfProgram, std::shared_ptr<BpfProgram>>(m, "BpfProgram")
|
||||
.def("attach", &BpfProgram::attach)
|
||||
.def("detach", &BpfProgram::detach)
|
||||
.def("is_attached", &BpfProgram::is_attached)
|
||||
.def("get_name", &BpfProgram::get_name);
|
||||
|
||||
// BpfMap
|
||||
py::class_<BpfMap, std::shared_ptr<BpfMap>>(m, "BpfMap")
|
||||
.def("lookup", &BpfMap::lookup, py::arg("key"))
|
||||
.def("update", &BpfMap::update, py::arg("key"), py::arg("value"))
|
||||
.def("delete_elem", &BpfMap::delete_elem, py::arg("key"))
|
||||
.def("get_next_key", &BpfMap::get_next_key, py::arg("key") = py::none())
|
||||
.def("items", &BpfMap::items)
|
||||
.def("keys", &BpfMap::keys)
|
||||
.def("values", &BpfMap::values)
|
||||
.def("get_name", &BpfMap::get_name)
|
||||
.def("get_fd", &BpfMap::get_fd)
|
||||
.def("get_type", &BpfMap::get_type)
|
||||
.def("get_key_size", &BpfMap::get_key_size)
|
||||
.def("get_value_size", &BpfMap::get_value_size)
|
||||
.def("get_max_entries", &BpfMap::get_max_entries);
|
||||
|
||||
py::class_<BpfPerfBuffer>(m, "BpfPerfBuffer")
|
||||
.def(py::init<int, int, py::function, py::object>(),
|
||||
py::arg("map_fd"),
|
||||
py::arg("page_cnt") = 8,
|
||||
py::arg("callback"),
|
||||
py::arg("lost_callback") = py::none())
|
||||
.def("poll", &BpfPerfBuffer::poll, py::arg("timeout_ms") = -1)
|
||||
.def("consume", &BpfPerfBuffer::consume);
|
||||
|
||||
|
||||
#ifdef VERSION_INFO
|
||||
|
||||
@ -6,13 +6,10 @@
|
||||
|
||||
class BpfException final : public std::runtime_error {
|
||||
public:
|
||||
explicit BpfException(const std::string &message)
|
||||
: std::runtime_error(message) {
|
||||
}
|
||||
explicit BpfException(const std::string &message)
|
||||
: std::runtime_error(message) {}
|
||||
|
||||
explicit BpfException(const char *message)
|
||||
: std::runtime_error(message) {
|
||||
}
|
||||
explicit BpfException(const char *message) : std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
#endif // PYLIBBPF_BPF_EXCEPTION_H
|
||||
|
||||
@ -74,7 +74,7 @@ void BpfObject::load() {
|
||||
|
||||
// ==================== Program Methods ====================
|
||||
|
||||
py::list BpfObject::get_program_names() const {
|
||||
py::list BpfObject::get_program_names() {
|
||||
if (!loaded_) {
|
||||
throw BpfException("BPF object not loaded");
|
||||
}
|
||||
@ -106,7 +106,8 @@ BpfObject::_get_or_create_program(struct bpf_program *prog) {
|
||||
}
|
||||
|
||||
// Create and cache
|
||||
auto bpf_prog = std::make_shared<BpfProgram>(this, prog, prog_name);
|
||||
auto bpf_prog =
|
||||
std::make_shared<BpfProgram>(shared_from_this(), prog, prog_name);
|
||||
prog_cache_[prog_name] = bpf_prog;
|
||||
|
||||
return bpf_prog;
|
||||
@ -125,7 +126,7 @@ std::shared_ptr<BpfProgram> BpfObject::get_program(const std::string &name) {
|
||||
|
||||
// Create and cache
|
||||
struct bpf_program *raw_prog = find_program_by_name(name);
|
||||
auto prog = std::make_shared<BpfProgram>(this, raw_prog, name);
|
||||
auto prog = std::make_shared<BpfProgram>(shared_from_this(), raw_prog, name);
|
||||
prog_cache_[name] = prog;
|
||||
|
||||
return prog;
|
||||
@ -148,8 +149,8 @@ BpfObject::find_program_by_name(const std::string &name) const {
|
||||
|
||||
py::dict BpfObject::get_cached_programs() const {
|
||||
py::dict programs;
|
||||
for (const auto &[name, prog] : prog_cache_) {
|
||||
programs[name] = prog;
|
||||
for (const auto &entry : prog_cache_) {
|
||||
programs[entry.first.c_str()] = entry.second;
|
||||
}
|
||||
return programs;
|
||||
}
|
||||
@ -178,7 +179,7 @@ py::dict BpfObject::attach_all() {
|
||||
|
||||
// ==================== Map Methods ====================
|
||||
|
||||
py::list BpfObject::get_map_names() const {
|
||||
py::list BpfObject::get_map_names() {
|
||||
if (!loaded_) {
|
||||
throw BpfException("BPF object not loaded");
|
||||
}
|
||||
@ -249,8 +250,8 @@ struct bpf_map *BpfObject::find_map_by_name(const std::string &name) const {
|
||||
|
||||
py::dict BpfObject::get_cached_maps() const {
|
||||
py::dict maps;
|
||||
for (const auto &[name, map] : maps_cache_) {
|
||||
maps[name] = map;
|
||||
for (const auto &entry : maps_cache_) {
|
||||
maps[entry.first.c_str()] = entry.second;
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ public:
|
||||
py::dict attach_all();
|
||||
|
||||
// Program access
|
||||
[[nodiscard]] py::list get_program_names() const;
|
||||
[[nodiscard]] py::list get_program_names();
|
||||
[[nodiscard]] std::shared_ptr<BpfProgram>
|
||||
get_program(const std::string &name);
|
||||
[[nodiscard]] struct bpf_program *
|
||||
@ -73,7 +73,7 @@ public:
|
||||
[[nodiscard]] py::dict get_cached_programs() const;
|
||||
|
||||
// Map access
|
||||
[[nodiscard]] py::list get_map_names() const;
|
||||
[[nodiscard]] py::list get_map_names();
|
||||
[[nodiscard]] std::shared_ptr<BpfMap> get_map(const std::string &name);
|
||||
[[nodiscard]] struct bpf_map *find_map_by_name(const std::string &name) const;
|
||||
[[nodiscard]] py::dict get_cached_maps() const;
|
||||
|
||||
@ -1,6 +1,38 @@
|
||||
#include "bpf_perf_buffer.h"
|
||||
#include "bpf_exception.h"
|
||||
|
||||
BpfPerfBuffer::BpfPerfBuffer(int map_fd, int page_cnt, py::function callback,
|
||||
py::object lost_callback)
|
||||
: pb_(nullptr), callback_(std::move(callback)),
|
||||
lost_callback_(lost_callback) {
|
||||
|
||||
if (page_cnt <= 0 || (page_cnt & (page_cnt - 1)) != 0) {
|
||||
throw BpfException("page_cnt must be a positive power of 2");
|
||||
}
|
||||
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
pb_opts.sz = sizeof(pb_opts); // Required for forward compatibility
|
||||
|
||||
pb_ = perf_buffer__new(
|
||||
map_fd, page_cnt,
|
||||
sample_callback_wrapper, // sample_cb
|
||||
lost_callback.is_none() ? nullptr : lost_callback_wrapper, // lost_cb
|
||||
this, // ctx
|
||||
&pb_opts // opts
|
||||
);
|
||||
|
||||
if (!pb_) {
|
||||
throw BpfException("Failed to create perf buffer: " +
|
||||
std::string(std::strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
BpfPerfBuffer::~BpfPerfBuffer() {
|
||||
if (pb_) {
|
||||
perf_buffer__free(pb_);
|
||||
}
|
||||
}
|
||||
|
||||
void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data,
|
||||
unsigned int size) {
|
||||
auto *self = static_cast<BpfPerfBuffer *>(ctx);
|
||||
@ -36,33 +68,6 @@ void BpfPerfBuffer::lost_callback_wrapper(void *ctx, int cpu,
|
||||
}
|
||||
}
|
||||
|
||||
BpfPerfBuffer::BpfPerfBuffer(int map_fd, int page_cnt, py::function callback,
|
||||
py::object lost_callback)
|
||||
: pb_(nullptr), callback_(std::move(callback)) {
|
||||
|
||||
if (!lost_callback.is_none()) {
|
||||
lost_callback_ = lost_callback.cast<py::function>();
|
||||
}
|
||||
|
||||
// Setup perf buffer options
|
||||
perf_buffer_opts pb_opts = {};
|
||||
pb_opts.sample_cb = sample_callback_wrapper;
|
||||
pb_opts.lost_cb = lost_callback.is_none() ? nullptr : lost_callback_wrapper;
|
||||
pb_opts.ctx = this;
|
||||
|
||||
// Create perf buffer
|
||||
pb_ = perf_buffer__new(map_fd, page_cnt, &pb_opts);
|
||||
if (!pb_) {
|
||||
throw BpfException("Failed to create perf buffer");
|
||||
}
|
||||
}
|
||||
|
||||
BpfPerfBuffer::~BpfPerfBuffer() {
|
||||
if (pb_) {
|
||||
perf_buffer__free(pb_);
|
||||
}
|
||||
}
|
||||
|
||||
int BpfPerfBuffer::poll(int timeout_ms) {
|
||||
// Release GIL during blocking poll
|
||||
py::gil_scoped_release release;
|
||||
|
||||
@ -20,11 +20,12 @@ private:
|
||||
|
||||
public:
|
||||
BpfPerfBuffer(int map_fd, int page_cnt, py::function callback,
|
||||
py::object lost_callback);
|
||||
py::object lost_callback = py::none());
|
||||
~BpfPerfBuffer();
|
||||
|
||||
int poll(int timeout_ms);
|
||||
int consume();
|
||||
[[nodiscard]] int fd() const;
|
||||
};
|
||||
|
||||
#endif // PYLIBBPF_BPF_PERF_BUFFER_H
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "bpf_program.h"
|
||||
#include "bpf_exception.h"
|
||||
#include "bpf_object.h"
|
||||
#include <cerrno>
|
||||
#include <utility>
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#ifndef PYLIBBPF_BPF_PROGRAM_H
|
||||
#define PYLIBBPF_BPF_PROGRAM_H
|
||||
|
||||
#include <cstring>
|
||||
#include <libbpf.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -26,8 +27,8 @@ public:
|
||||
BpfProgram(BpfProgram &&) noexcept;
|
||||
BpfProgram &operator=(BpfProgram &&) noexcept;
|
||||
|
||||
bool attach();
|
||||
bool detach();
|
||||
void attach();
|
||||
void detach();
|
||||
|
||||
[[nodiscard]] bool is_attached() const { return link_ != nullptr; }
|
||||
[[nodiscard]] std::string get_name() const { return program_name_; }
|
||||
|
||||
@ -3,5 +3,5 @@ import pylibbpf as m
|
||||
|
||||
def test_main():
|
||||
assert m.__version__ == "0.0.5"
|
||||
prog = m.BpfProgram("tests/execve2.o")
|
||||
prog = m.BpfObject("tests/execve2.o")
|
||||
print(prog)
|
||||
|
||||
Reference in New Issue
Block a user