diff --git a/CMakeLists.txt b/CMakeLists.txt index 5aee60d..cf2236e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,8 @@ pybind11_add_module( src/core/bpf_object.cpp # Maps - src/maps/bpf_perf_buffer.h - src/maps/bpf_perf_buffer.cpp + src/maps/perf_event_array.h + src/maps/perf_event_array.cpp # Utils src/utils/struct_parser.h diff --git a/src/bindings/main.cpp b/src/bindings/main.cpp index e2336f2..d374ff7 100644 --- a/src/bindings/main.cpp +++ b/src/bindings/main.cpp @@ -10,7 +10,7 @@ extern "C" { #include "core/bpf_program.h" #include "core/bpf_exception.h" #include "core/bpf_map.h" -#include "maps/bpf_perf_buffer.h" +#include "maps/perf_event_array.h" namespace py = pybind11; @@ -66,14 +66,14 @@ PYBIND11_MODULE(pylibbpf, m) { .def("get_value_size", &BpfMap::get_value_size) .def("get_max_entries", &BpfMap::get_max_entries); - py::class_(m, "BpfPerfBuffer") + py::class_(m, "PerfEventArray") .def(py::init(), 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); + .def("poll", &PerfEventArray::poll, py::arg("timeout_ms") = -1) + .def("consume", &PerfEventArray::consume); #ifdef VERSION_INFO diff --git a/src/core/bpf_object.cpp b/src/core/bpf_object.cpp index 1cb5eba..dbc66d4 100644 --- a/src/core/bpf_object.cpp +++ b/src/core/bpf_object.cpp @@ -2,11 +2,12 @@ #include "bpf_exception.h" #include "bpf_map.h" #include "bpf_program.h" +#include "utils/struct_parser.h" #include BpfObject::BpfObject(std::string object_path, py::dict structs) : obj_(nullptr), object_path_(std::move(object_path)), loaded_(false), - struct_defs_(structs) {} + struct_defs_(structs), struct_parser_(nullptr) {} BpfObject::~BpfObject() { // Clear caches first (order matters!) @@ -256,3 +257,11 @@ py::dict BpfObject::get_cached_maps() const { } return maps; } + +std::shared_ptr BpfObject::get_struct_parser() const { + if (!struct_parser_ && !struct_defs_.empty()) { + // Create parser on first access + struct_parser_ = std::make_shared(struct_defs_); + } + return struct_parser_; +} diff --git a/src/core/bpf_object.h b/src/core/bpf_object.h index a66e73b..b5ec4e9 100644 --- a/src/core/bpf_object.h +++ b/src/core/bpf_object.h @@ -12,6 +12,7 @@ namespace py = pybind11; class BpfProgram; class BpfMap; +class StructParser; /** * BpfObject - Represents a loaded BPF object file. @@ -29,6 +30,7 @@ private: mutable std::unordered_map> prog_cache_; py::dict struct_defs_; + mutable std::shared_ptr struct_parser_; std::shared_ptr _get_or_create_program(struct bpf_program *prog); std::shared_ptr _get_or_create_map(struct bpf_map *map); @@ -78,6 +80,10 @@ public: [[nodiscard]] std::shared_ptr 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; + + // Struct parsing + [[nodiscard]] py::dict get_struct_defs() const { return struct_defs_; } + [[nodiscard]] std::shared_ptr get_struct_parser() const; }; #endif // PYLIBBPF_BPF_OBJECT_H diff --git a/src/maps/bpf_perf_buffer.h b/src/maps/bpf_perf_buffer.h index 3f21213..d0ee10d 100644 --- a/src/maps/bpf_perf_buffer.h +++ b/src/maps/bpf_perf_buffer.h @@ -1,31 +1,37 @@ -#ifndef PYLIBBPF_BPF_PERF_BUFFER_H -#define PYLIBBPF_BPF_PERF_BUFFER_H +#ifndef PYLIBBPF_PERF_EVENT_ARRAY_H +#define PYLIBBPF_PERF_EVENT_ARRAY_H #include #include #include +#include + +class StructParser; namespace py = pybind11; -class BpfPerfBuffer { +class PerfEventArray { private: struct perf_buffer *pb_; py::function callback_; py::function lost_callback_; + std::shared_ptr parser_; + std::string struct_name_; + // Static callback wrappers for C API static void sample_callback_wrapper(void *ctx, int cpu, void *data, unsigned int size); static void lost_callback_wrapper(void *ctx, int cpu, unsigned long long cnt); public: - BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, + PerfEventArray(int map_fd, int page_cnt, py::function callback, py::object lost_callback = py::none()); - ~BpfPerfBuffer(); + ~PerfEventArray(); int poll(int timeout_ms); int consume(); [[nodiscard]] int fd() const; }; -#endif // PYLIBBPF_BPF_PERF_BUFFER_H +#endif // PYLIBBPF_PERF_EVENT_ARRAY_H diff --git a/src/maps/bpf_perf_buffer.cpp b/src/maps/perf_event_array.cpp similarity index 79% rename from src/maps/bpf_perf_buffer.cpp rename to src/maps/perf_event_array.cpp index 7b5c4e1..7d53e9d 100644 --- a/src/maps/bpf_perf_buffer.cpp +++ b/src/maps/perf_event_array.cpp @@ -1,7 +1,7 @@ -#include "bpf_perf_buffer.h" +#include "perf_event_array.h" #include "core/bpf_exception.h" -BpfPerfBuffer::BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, +PerfEventArray::PerfEventArray(int map_fd, int page_cnt, py::function callback, py::object lost_callback) : pb_(nullptr), callback_(std::move(callback)), lost_callback_(lost_callback) { @@ -27,15 +27,15 @@ BpfPerfBuffer::BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, } } -BpfPerfBuffer::~BpfPerfBuffer() { +PerfEventArray::~PerfEventArray() { if (pb_) { perf_buffer__free(pb_); } } -void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data, +void PerfEventArray::sample_callback_wrapper(void *ctx, int cpu, void *data, unsigned int size) { - auto *self = static_cast(ctx); + auto *self = static_cast(ctx); // Acquire GIL for Python calls py::gil_scoped_acquire acquire; @@ -51,9 +51,9 @@ void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data, } } -void BpfPerfBuffer::lost_callback_wrapper(void *ctx, int cpu, +void PerfEventArray::lost_callback_wrapper(void *ctx, int cpu, unsigned long long cnt) { - auto *self = static_cast(ctx); + auto *self = static_cast(ctx); if (self->lost_callback_.is_none()) { return; @@ -68,13 +68,13 @@ void BpfPerfBuffer::lost_callback_wrapper(void *ctx, int cpu, } } -int BpfPerfBuffer::poll(int timeout_ms) { +int PerfEventArray::poll(int timeout_ms) { // Release GIL during blocking poll py::gil_scoped_release release; return perf_buffer__poll(pb_, timeout_ms); } -int BpfPerfBuffer::consume() { +int PerfEventArray::consume() { py::gil_scoped_release release; return perf_buffer__consume(pb_); } diff --git a/src/maps/perf_event_array.h b/src/maps/perf_event_array.h new file mode 100644 index 0000000..86a2c83 --- /dev/null +++ b/src/maps/perf_event_array.h @@ -0,0 +1,37 @@ +#ifndef PYLIBBPF_BPF_PERF_BUFFER_H +#define PYLIBBPF_BPF_PERF_BUFFER_H + +#include +#include +#include +#include + +class StructParser; + +namespace py = pybind11; + +class PerfEventArray { +private: + struct perf_buffer *pb_; + py::function callback_; + py::function lost_callback_; + + std::shared_ptr parser_; + std::string struct_name_; + + // Static callback wrappers for C API + static void sample_callback_wrapper(void *ctx, int cpu, void *data, + unsigned int size); + static void lost_callback_wrapper(void *ctx, int cpu, unsigned long long cnt); + +public: + PerfEventArray(int map_fd, int page_cnt, py::function callback, + py::object lost_callback = py::none()); + ~PerfEventArray(); + + int poll(int timeout_ms); + int consume(); + [[nodiscard]] int fd() const; +}; + +#endif // PYLIBBPF_BPF_PERF_BUFFER_H