mirror of
https://github.com/varun-r-mallya/pylibbpf.git
synced 2026-03-25 14:41:30 +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)
|
cmake_minimum_required(VERSION 4.0)
|
||||||
project(pylibbpf)
|
project(pylibbpf)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
# pybind11
|
# pybind11
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/src)
|
include_directories(${CMAKE_SOURCE_DIR}/src)
|
||||||
add_subdirectory(pybind11)
|
add_subdirectory(pybind11)
|
||||||
@ -9,9 +13,13 @@ pybind11_add_module(
|
|||||||
src/core/bpf_program.h
|
src/core/bpf_program.h
|
||||||
src/core/bpf_exception.h
|
src/core/bpf_exception.h
|
||||||
src/core/bpf_map.h
|
src/core/bpf_map.h
|
||||||
|
src/core/bpf_object.h
|
||||||
|
src/core/bpf_perf_buffer.h
|
||||||
src/bindings/main.cpp
|
src/bindings/main.cpp
|
||||||
src/core/bpf_program.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 ---
|
# --- libbpf build rules ---
|
||||||
set(LIBBPF_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libbpf/src)
|
set(LIBBPF_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libbpf/src)
|
||||||
|
|||||||
@ -6,9 +6,11 @@ extern "C" {
|
|||||||
#include <libbpf.h>
|
#include <libbpf.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "core/bpf_object.h"
|
||||||
#include "core/bpf_program.h"
|
#include "core/bpf_program.h"
|
||||||
#include "core/bpf_exception.h"
|
#include "core/bpf_exception.h"
|
||||||
#include "core/bpf_map.h"
|
#include "core/bpf_map.h"
|
||||||
|
#include "core/bpf_perf_buffer.h"
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
|
|
||||||
@ -30,33 +32,48 @@ PYBIND11_MODULE(pylibbpf, m) {
|
|||||||
// Register the custom exception
|
// Register the custom exception
|
||||||
py::register_exception<BpfException>(m, "BpfException");
|
py::register_exception<BpfException>(m, "BpfException");
|
||||||
|
|
||||||
py::class_<BpfProgram>(m, "BpfProgram")
|
// BpfObject
|
||||||
.def(py::init<const std::string &>())
|
py::class_<BpfObject, std::shared_ptr<BpfObject>>(m, "BpfObject")
|
||||||
.def(py::init<const std::string &, const std::string &>())
|
.def(py::init<std::string>(), py::arg("object_path"))
|
||||||
.def("load", &BpfProgram::load)
|
.def("load", &BpfObject::load)
|
||||||
.def("attach", &BpfProgram::attach)
|
.def("is_loaded", &BpfObject::is_loaded)
|
||||||
.def("destroy", &BpfProgram::destroy)
|
.def("get_program_names", &BpfObject::get_program_names)
|
||||||
.def("load_and_attach", &BpfProgram::load_and_attach)
|
.def("get_program", &BpfObject::get_program, py::arg("name"))
|
||||||
.def("is_loaded", &BpfProgram::is_loaded)
|
.def("attach_all", &BpfObject::attach_all)
|
||||||
.def("is_attached", &BpfProgram::is_attached);
|
.def("get_map_names", &BpfObject::get_map_names)
|
||||||
|
.def("get_map", &BpfObject::get_map, py::arg("name"));
|
||||||
|
|
||||||
py::class_<BpfMap>(m, "BpfMap")
|
// BpfProgram
|
||||||
.def(py::init<BpfProgram *, py::object &>())
|
py::class_<BpfProgram, std::shared_ptr<BpfProgram>>(m, "BpfProgram")
|
||||||
.def("lookup", &BpfMap::lookup)
|
.def("attach", &BpfProgram::attach)
|
||||||
.def("update", &BpfMap::update)
|
.def("detach", &BpfProgram::detach)
|
||||||
.def("delete", &BpfMap::delete_elem)
|
.def("is_attached", &BpfProgram::is_attached)
|
||||||
.def("get_next_key", &BpfMap::get_next_key, py::arg("key") = py::none())
|
.def("get_name", &BpfProgram::get_name);
|
||||||
.def("items", &BpfMap::items)
|
|
||||||
.def("keys", &BpfMap::keys)
|
// BpfMap
|
||||||
.def("values", &BpfMap::values)
|
py::class_<BpfMap, std::shared_ptr<BpfMap>>(m, "BpfMap")
|
||||||
.def("get_name", &BpfMap::get_name)
|
.def("lookup", &BpfMap::lookup, py::arg("key"))
|
||||||
.def("get_type", &BpfMap::get_type)
|
.def("update", &BpfMap::update, py::arg("key"), py::arg("value"))
|
||||||
.def("get_key_size", &BpfMap::get_key_size)
|
.def("delete_elem", &BpfMap::delete_elem, py::arg("key"))
|
||||||
.def("get_value_size", &BpfMap::get_value_size)
|
.def("get_next_key", &BpfMap::get_next_key, py::arg("key") = py::none())
|
||||||
.def("get_max_entries", &BpfMap::get_max_entries)
|
.def("items", &BpfMap::items)
|
||||||
.def("__getitem__", &BpfMap::lookup)
|
.def("keys", &BpfMap::keys)
|
||||||
.def("__setitem__", &BpfMap::update)
|
.def("values", &BpfMap::values)
|
||||||
.def("__delitem__", &BpfMap::delete_elem);
|
.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
|
#ifdef VERSION_INFO
|
||||||
|
|||||||
@ -6,13 +6,10 @@
|
|||||||
|
|
||||||
class BpfException final : public std::runtime_error {
|
class BpfException final : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
explicit BpfException(const std::string &message)
|
explicit BpfException(const std::string &message)
|
||||||
: std::runtime_error(message) {
|
: std::runtime_error(message) {}
|
||||||
}
|
|
||||||
|
|
||||||
explicit BpfException(const char *message)
|
explicit BpfException(const char *message) : std::runtime_error(message) {}
|
||||||
: std::runtime_error(message) {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PYLIBBPF_BPF_EXCEPTION_H
|
#endif // PYLIBBPF_BPF_EXCEPTION_H
|
||||||
|
|||||||
@ -74,7 +74,7 @@ void BpfObject::load() {
|
|||||||
|
|
||||||
// ==================== Program Methods ====================
|
// ==================== Program Methods ====================
|
||||||
|
|
||||||
py::list BpfObject::get_program_names() const {
|
py::list BpfObject::get_program_names() {
|
||||||
if (!loaded_) {
|
if (!loaded_) {
|
||||||
throw BpfException("BPF object not loaded");
|
throw BpfException("BPF object not loaded");
|
||||||
}
|
}
|
||||||
@ -106,7 +106,8 @@ BpfObject::_get_or_create_program(struct bpf_program *prog) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create and cache
|
// 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;
|
prog_cache_[prog_name] = bpf_prog;
|
||||||
|
|
||||||
return bpf_prog;
|
return bpf_prog;
|
||||||
@ -125,7 +126,7 @@ std::shared_ptr<BpfProgram> BpfObject::get_program(const std::string &name) {
|
|||||||
|
|
||||||
// Create and cache
|
// Create and cache
|
||||||
struct bpf_program *raw_prog = find_program_by_name(name);
|
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;
|
prog_cache_[name] = prog;
|
||||||
|
|
||||||
return 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 BpfObject::get_cached_programs() const {
|
||||||
py::dict programs;
|
py::dict programs;
|
||||||
for (const auto &[name, prog] : prog_cache_) {
|
for (const auto &entry : prog_cache_) {
|
||||||
programs[name] = prog;
|
programs[entry.first.c_str()] = entry.second;
|
||||||
}
|
}
|
||||||
return programs;
|
return programs;
|
||||||
}
|
}
|
||||||
@ -178,7 +179,7 @@ py::dict BpfObject::attach_all() {
|
|||||||
|
|
||||||
// ==================== Map Methods ====================
|
// ==================== Map Methods ====================
|
||||||
|
|
||||||
py::list BpfObject::get_map_names() const {
|
py::list BpfObject::get_map_names() {
|
||||||
if (!loaded_) {
|
if (!loaded_) {
|
||||||
throw BpfException("BPF object not 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 BpfObject::get_cached_maps() const {
|
||||||
py::dict maps;
|
py::dict maps;
|
||||||
for (const auto &[name, map] : maps_cache_) {
|
for (const auto &entry : maps_cache_) {
|
||||||
maps[name] = map;
|
maps[entry.first.c_str()] = entry.second;
|
||||||
}
|
}
|
||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,7 +65,7 @@ public:
|
|||||||
py::dict attach_all();
|
py::dict attach_all();
|
||||||
|
|
||||||
// Program access
|
// Program access
|
||||||
[[nodiscard]] py::list get_program_names() const;
|
[[nodiscard]] py::list get_program_names();
|
||||||
[[nodiscard]] std::shared_ptr<BpfProgram>
|
[[nodiscard]] std::shared_ptr<BpfProgram>
|
||||||
get_program(const std::string &name);
|
get_program(const std::string &name);
|
||||||
[[nodiscard]] struct bpf_program *
|
[[nodiscard]] struct bpf_program *
|
||||||
@ -73,7 +73,7 @@ public:
|
|||||||
[[nodiscard]] py::dict get_cached_programs() const;
|
[[nodiscard]] py::dict get_cached_programs() const;
|
||||||
|
|
||||||
// Map access
|
// 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]] std::shared_ptr<BpfMap> get_map(const std::string &name);
|
||||||
[[nodiscard]] struct bpf_map *find_map_by_name(const std::string &name) const;
|
[[nodiscard]] struct bpf_map *find_map_by_name(const std::string &name) const;
|
||||||
[[nodiscard]] py::dict get_cached_maps() const;
|
[[nodiscard]] py::dict get_cached_maps() const;
|
||||||
|
|||||||
@ -1,6 +1,38 @@
|
|||||||
#include "bpf_perf_buffer.h"
|
#include "bpf_perf_buffer.h"
|
||||||
#include "bpf_exception.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,
|
void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data,
|
||||||
unsigned int size) {
|
unsigned int size) {
|
||||||
auto *self = static_cast<BpfPerfBuffer *>(ctx);
|
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) {
|
int BpfPerfBuffer::poll(int timeout_ms) {
|
||||||
// Release GIL during blocking poll
|
// Release GIL during blocking poll
|
||||||
py::gil_scoped_release release;
|
py::gil_scoped_release release;
|
||||||
|
|||||||
@ -20,11 +20,12 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
BpfPerfBuffer(int map_fd, int page_cnt, py::function callback,
|
BpfPerfBuffer(int map_fd, int page_cnt, py::function callback,
|
||||||
py::object lost_callback);
|
py::object lost_callback = py::none());
|
||||||
~BpfPerfBuffer();
|
~BpfPerfBuffer();
|
||||||
|
|
||||||
int poll(int timeout_ms);
|
int poll(int timeout_ms);
|
||||||
int consume();
|
int consume();
|
||||||
|
[[nodiscard]] int fd() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PYLIBBPF_BPF_PERF_BUFFER_H
|
#endif // PYLIBBPF_BPF_PERF_BUFFER_H
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "bpf_program.h"
|
#include "bpf_program.h"
|
||||||
#include "bpf_exception.h"
|
#include "bpf_exception.h"
|
||||||
|
#include "bpf_object.h"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#ifndef PYLIBBPF_BPF_PROGRAM_H
|
#ifndef PYLIBBPF_BPF_PROGRAM_H
|
||||||
#define PYLIBBPF_BPF_PROGRAM_H
|
#define PYLIBBPF_BPF_PROGRAM_H
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <libbpf.h>
|
#include <libbpf.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -26,8 +27,8 @@ public:
|
|||||||
BpfProgram(BpfProgram &&) noexcept;
|
BpfProgram(BpfProgram &&) noexcept;
|
||||||
BpfProgram &operator=(BpfProgram &&) noexcept;
|
BpfProgram &operator=(BpfProgram &&) noexcept;
|
||||||
|
|
||||||
bool attach();
|
void attach();
|
||||||
bool detach();
|
void detach();
|
||||||
|
|
||||||
[[nodiscard]] bool is_attached() const { return link_ != nullptr; }
|
[[nodiscard]] bool is_attached() const { return link_ != nullptr; }
|
||||||
[[nodiscard]] std::string get_name() const { return program_name_; }
|
[[nodiscard]] std::string get_name() const { return program_name_; }
|
||||||
|
|||||||
@ -3,5 +3,5 @@ import pylibbpf as m
|
|||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
assert m.__version__ == "0.0.5"
|
assert m.__version__ == "0.0.5"
|
||||||
prog = m.BpfProgram("tests/execve2.o")
|
prog = m.BpfObject("tests/execve2.o")
|
||||||
print(prog)
|
print(prog)
|
||||||
|
|||||||
Reference in New Issue
Block a user