diff --git a/src/core/bpf_map.cpp b/src/core/bpf_map.cpp index b6f9069..2eafdec 100644 --- a/src/core/bpf_map.cpp +++ b/src/core/bpf_map.cpp @@ -1,250 +1,227 @@ #include "bpf_map.h" -#include "bpf_object.h" #include "bpf_exception.h" +#include "bpf_object.h" -BpfMap::BpfMap(std::shared_ptr parent, struct bpf_map *raw_map, const std::string &map_name) - : parent_obj_(parent), - map_(raw_map), - map_fd_(-1), - map_name_(map_name), - key_size_(0), - value_size_(0) { - if (!parent) - throw BpfException("Parent BpfObject is null"); - if(!(parent->is_loaded())) - throw BpfException("Parent BpfObject is not loaded"); - if (!raw_map) - throw BpfException("bpf_map pointer is null"); - - map_fd_ = bpf_map__fd(map_); - if (map_fd_ < 0) - throw BpfException("Failed to get file descriptor for map '" + map_name_ + "'"); +BpfMap::BpfMap(std::shared_ptr parent, struct bpf_map *raw_map, + const std::string &map_name) + : parent_obj_(parent), map_(raw_map), map_fd_(-1), map_name_(map_name), + key_size_(0), value_size_(0) { + if (!parent) + throw BpfException("Parent BpfObject is null"); + if (!(parent->is_loaded())) + throw BpfException("Parent BpfObject is not loaded"); + if (!raw_map) + throw BpfException("bpf_map pointer is null"); - key_size_ = bpf_map__key_size(map_); - value_size_ = bpf_map__value_size(map_); + map_fd_ = bpf_map__fd(map_); + if (map_fd_ < 0) + throw BpfException("Failed to get file descriptor for map '" + map_name_ + + "'"); + + key_size_ = bpf_map__key_size(map_); + value_size_ = bpf_map__value_size(map_); } py::object BpfMap::lookup(const py::object &key) const { - if (map_fd_ < 0) - throw BpfException("Map '" + map_name_ + "' is not initialized properly"); + if (map_fd_ < 0) + throw BpfException("Map '" + map_name_ + "' is not initialized properly"); - BufferManager<> key_buf, value_buf; - auto key_span = key_buf.get_span(key_size_); - auto value_span = value_buf.get_span(value_size_); - - // Convert Python → bytes - python_to_bytes_inplace(key, key_span); + BufferManager<> key_buf, value_buf; + auto key_span = key_buf.get_span(key_size_); + auto value_span = value_buf.get_span(value_size_); - // The flags field here matters only when spin locks are used. - // Skipping it for now. - const int ret = bpf_map__lookup_elem( - map_, - key_span.data(), - key_size_, - value_span.data(), - value_size_, - BPF_ANY); - if (ret < 0) { - if (ret == -ENOENT) - throw py::key_error("Key not found in map '" + map_name_ + "'"); - throw BpfException( - "Failed to lookup key in map '" + map_name_ + "': " + - std::strerror(-ret) - ); - } + // Convert Python → bytes + python_to_bytes_inplace(key, key_span); - return bytes_to_python(value_span); + // The flags field here matters only when spin locks are used. + // Skipping it for now. + const int ret = bpf_map__lookup_elem(map_, key_span.data(), key_size_, + value_span.data(), value_size_, BPF_ANY); + if (ret < 0) { + if (ret == -ENOENT) + throw py::key_error("Key not found in map '" + map_name_ + "'"); + throw BpfException("Failed to lookup key in map '" + map_name_ + + "': " + std::strerror(-ret)); + } + + return bytes_to_python(value_span); } void BpfMap::update(const py::object &key, const py::object &value) const { - if (map_fd_ < 0) - throw BpfException("Map '" + map_name_ + "' is not initialized properly"); + if (map_fd_ < 0) + throw BpfException("Map '" + map_name_ + "' is not initialized properly"); - BufferManager<> key_buf, value_buf; - auto key_span = key_buf.get_span(key_size_); - auto value_span = value_buf.get_span(value_size_); + BufferManager<> key_buf, value_buf; + auto key_span = key_buf.get_span(key_size_); + auto value_span = value_buf.get_span(value_size_); - python_to_bytes_inplace(key, key_span); - python_to_bytes_inplace(value, value_span); + python_to_bytes_inplace(key, key_span); + python_to_bytes_inplace(value, value_span); - const int ret = bpf_map__update_elem( - map_, - key_span.data(), - key_size_, - value_span.data(), - value_size_, - BPF_ANY); - if (ret < 0) { - throw BpfException( - "Failed to update key in map '" + map_name_ + "': " + - std::strerror(-ret) - ); - } + const int ret = bpf_map__update_elem(map_, key_span.data(), key_size_, + value_span.data(), value_size_, BPF_ANY); + if (ret < 0) { + throw BpfException("Failed to update key in map '" + map_name_ + + "': " + std::strerror(-ret)); + } } void BpfMap::delete_elem(const py::object &key) const { - if (map_fd_ < 0) - throw BpfException("Map '" + map_name_ + "' is not initialized properly"); + if (map_fd_ < 0) + throw BpfException("Map '" + map_name_ + "' is not initialized properly"); - BufferManager<> key_buf; - auto key_span = key_buf.get_span(key_size_); + BufferManager<> key_buf; + auto key_span = key_buf.get_span(key_size_); - // Convert Python → bytes - python_to_bytes_inplace(key, key_span); + // Convert Python → bytes + python_to_bytes_inplace(key, key_span); - const int ret = bpf_map__delete_elem(map_, key_span.data(), key_size_, BPF_ANY); + const int ret = + bpf_map__delete_elem(map_, key_span.data(), key_size_, BPF_ANY); - if (ret != 0) { - if (ret == -ENOENT) - throw py::key_error("Key not found in map '" + map_name_ + "'"); - throw BpfException( - "Failed to delete key from map '" + map_name_ + "': " + - std::strerror(-ret) - ); - } + if (ret != 0) { + if (ret == -ENOENT) + throw py::key_error("Key not found in map '" + map_name_ + "'"); + throw BpfException("Failed to delete key from map '" + map_name_ + + "': " + std::strerror(-ret)); + } } py::object BpfMap::get_next_key(const py::object &key) const { - BufferManager<> next_key_buf; - auto next_key = next_key_buf.get_span(key_size_); + BufferManager<> next_key_buf; + auto next_key = next_key_buf.get_span(key_size_); - int ret; - if (key.is_none()) { - ret = bpf_map__get_next_key(map_, nullptr, next_key.data(), key_size_); - } else { - BufferManager<> key_buf; - auto key_bytes = key_buf.get_span(key_size_); - python_to_bytes_inplace(key, key_bytes); - ret = bpf_map__get_next_key(map_, key_bytes.data(), next_key.data(), key_size_); + int ret; + if (key.is_none()) { + ret = bpf_map__get_next_key(map_, nullptr, next_key.data(), key_size_); + } else { + BufferManager<> key_buf; + auto key_bytes = key_buf.get_span(key_size_); + python_to_bytes_inplace(key, key_bytes); + ret = bpf_map__get_next_key(map_, key_bytes.data(), next_key.data(), + key_size_); + } + + if (ret < 0) { + if (ret == -ENOENT) { + // No more keys + return py::none(); } + throw BpfException("Failed to get next key in map '" + map_name_ + + "': " + std::strerror(-ret)); + } - if (ret < 0) { - if (ret == -ENOENT) { - // No more keys - return py::none(); - } - throw BpfException( - "Failed to get next key in map '" + map_name_ + "': " + - std::strerror(-ret) - ); - } - - return bytes_to_python(next_key); + return bytes_to_python(next_key); } py::dict BpfMap::items() const { - py::dict result; - - py::object current_key = get_next_key(py::none()); - if (current_key.is_none()) { - return result; - } - - while (!current_key.is_none()) { - try { - py::object value = lookup(current_key); - result[current_key] = value; - current_key = get_next_key(current_key); - } catch (const py::key_error&) { - break; - } - } - + py::dict result; + + py::object current_key = get_next_key(py::none()); + if (current_key.is_none()) { return result; + } + + while (!current_key.is_none()) { + try { + py::object value = lookup(current_key); + result[current_key] = value; + current_key = get_next_key(current_key); + } catch (const py::key_error &) { + break; + } + } + + return result; } py::list BpfMap::keys() const { - py::list result; - - py::object current_key = get_next_key(py::none()); - if (current_key.is_none()) { - return result; - } - - while (!current_key.is_none()) { - result.append(current_key); - current_key = get_next_key(current_key); - } - + py::list result; + + py::object current_key = get_next_key(py::none()); + if (current_key.is_none()) { return result; + } + + while (!current_key.is_none()) { + result.append(current_key); + current_key = get_next_key(current_key); + } + + return result; } py::list BpfMap::values() const { - py::list result; - - py::object current_key = get_next_key(py::none()); - if (current_key.is_none()) { - return result; - } - - while (!current_key.is_none()) { - try { - py::object value = lookup(current_key); - result.append(value); - current_key = get_next_key(current_key); - } catch (const py::key_error&) { - break; - } - } - + py::list result; + + py::object current_key = get_next_key(py::none()); + if (current_key.is_none()) { return result; + } + + while (!current_key.is_none()) { + try { + py::object value = lookup(current_key); + result.append(value); + current_key = get_next_key(current_key); + } catch (const py::key_error &) { + break; + } + } + + return result; } -int BpfMap::get_type() const { - return bpf_map__type(map_); -} - -int BpfMap::get_max_entries() const { - return bpf_map__max_entries(map_); -} +int BpfMap::get_type() const { return bpf_map__type(map_); } +int BpfMap::get_max_entries() const { return bpf_map__max_entries(map_); } // Helper functions -void BpfMap::python_to_bytes_inplace(const py::object &obj, std::span buffer) { - std::fill(buffer.begin(), buffer.end(), 0); - - if (py::isinstance(obj)) { - if (buffer.size() <= sizeof(uint64_t)) { - uint64_t value = obj.cast(); - std::memcpy(buffer.data(), &value, buffer.size()); - } else { - throw BpfException("Integer key/value size exceeds maximum (8 bytes)"); - } - } else if (py::isinstance(obj)) { - std::string bytes_str = obj.cast(); - - if (bytes_str.size() > buffer.size()) { - throw BpfException( - "Bytes size " + std::to_string(bytes_str.size()) + - " exceeds expected size " + std::to_string(buffer.size()) - ); - } - - std::memcpy(buffer.data(), bytes_str.data(), bytes_str.size()); - } else if (py::isinstance(obj)) { - std::string str_val = obj.cast(); - - if (str_val.size() >= buffer.size()) { - throw BpfException("String size exceeds expected size"); - } - - std::memcpy(buffer.data(), str_val.data(), str_val.size()); - buffer[str_val.size()] = '\0'; +void BpfMap::python_to_bytes_inplace(const py::object &obj, + std::span buffer) { + std::fill(buffer.begin(), buffer.end(), 0); + + if (py::isinstance(obj)) { + if (buffer.size() <= sizeof(uint64_t)) { + uint64_t value = obj.cast(); + std::memcpy(buffer.data(), &value, buffer.size()); } else { - throw BpfException("Unsupported type for BPF map key/value"); + throw BpfException("Integer key/value size exceeds maximum (8 bytes)"); } + } else if (py::isinstance(obj)) { + std::string bytes_str = obj.cast(); + + if (bytes_str.size() > buffer.size()) { + throw BpfException("Bytes size " + std::to_string(bytes_str.size()) + + " exceeds expected size " + + std::to_string(buffer.size())); + } + + std::memcpy(buffer.data(), bytes_str.data(), bytes_str.size()); + } else if (py::isinstance(obj)) { + std::string str_val = obj.cast(); + + if (str_val.size() >= buffer.size()) { + throw BpfException("String size exceeds expected size"); + } + + std::memcpy(buffer.data(), str_val.data(), str_val.size()); + buffer[str_val.size()] = '\0'; + } else { + throw BpfException("Unsupported type for BPF map key/value"); + } } py::object BpfMap::bytes_to_python(std::span data) { - if (data.size() == 4) { - uint32_t value; - std::memcpy(&value, data.data(), 4); - return py::cast(value); - } else if (data.size() == 8) { - uint64_t value; - std::memcpy(&value, data.data(), 8); - return py::cast(value); - } else { - return py::bytes(reinterpret_cast(data.data()), data.size()); - } + if (data.size() == 4) { + uint32_t value; + std::memcpy(&value, data.data(), 4); + return py::cast(value); + } else if (data.size() == 8) { + uint64_t value; + std::memcpy(&value, data.data(), 8); + return py::cast(value); + } else { + return py::bytes(reinterpret_cast(data.data()), data.size()); + } } diff --git a/src/core/bpf_map.h b/src/core/bpf_map.h index 03302b5..a7c2008 100644 --- a/src/core/bpf_map.h +++ b/src/core/bpf_map.h @@ -1,15 +1,15 @@ #ifndef PYLIBBPF_BPF_MAP_H #define PYLIBBPF_BPF_MAP_H +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include -#include -#include +#include +#include class BpfObject; @@ -17,55 +17,56 @@ namespace py = pybind11; class BpfMap { private: - std::weak_ptr parent_obj_; - struct bpf_map *map_; - int map_fd_; - std::string map_name_; - __u32 key_size_, value_size_; + std::weak_ptr parent_obj_; + struct bpf_map *map_; + int map_fd_; + std::string map_name_; + __u32 key_size_, value_size_; - template - struct BufferManager { - std::array stack_buf; - std::vector heap_buf; - - std::span get_span(size_t size) { - if (size <= StackSize) { - return std::span(stack_buf.data(), size); - } else { - heap_buf.resize(size); - return std::span(heap_buf); - } - } - }; + template struct BufferManager { + std::array stack_buf; + std::vector heap_buf; + + std::span get_span(size_t size) { + if (size <= StackSize) { + return std::span(stack_buf.data(), size); + } else { + heap_buf.resize(size); + return std::span(heap_buf); + } + } + }; public: - BpfMap(std::shared_ptr parent, struct bpf_map *raw_map, const std::string &map_name); + BpfMap(std::shared_ptr parent, struct bpf_map *raw_map, + const std::string &map_name); - ~BpfMap() = default; + ~BpfMap() = default; - BpfMap(const BpfMap&) = delete; - BpfMap& operator=(const BpfMap&) = delete; - BpfMap(BpfMap&&) noexcept = default; - BpfMap& operator=(BpfMap&&) noexcept = default; + BpfMap(const BpfMap &) = delete; + BpfMap &operator=(const BpfMap &) = delete; + BpfMap(BpfMap &&) noexcept = default; + BpfMap &operator=(BpfMap &&) noexcept = default; - [[nodiscard]] py::object lookup(const py::object &key) const; - void update(const py::object &key, const py::object &value) const; - void delete_elem(const py::object &key) const; - py::object get_next_key(const py::object &key = py::none()) const; - py::dict items() const; - py::list keys() const; - py::list values() const; + [[nodiscard]] py::object lookup(const py::object &key) const; + void update(const py::object &key, const py::object &value) const; + void delete_elem(const py::object &key) const; + py::object get_next_key(const py::object &key = py::none()) const; + py::dict items() const; + py::list keys() const; + py::list values() const; - [[nodiscard]] std::string get_name() const { return map_name_; } - [[nodiscard]] int get_fd() const { return map_fd_; } - [[nodiscard]] int get_type() const; - [[nodiscard]] int get_key_size() const { return key_size_; }; - [[nodiscard]] int get_value_size() const { return value_size_; }; - [[nodiscard]] int get_max_entries() const; + [[nodiscard]] std::string get_name() const { return map_name_; } + [[nodiscard]] int get_fd() const { return map_fd_; } + [[nodiscard]] int get_type() const; + [[nodiscard]] int get_key_size() const { return key_size_; }; + [[nodiscard]] int get_value_size() const { return value_size_; }; + [[nodiscard]] int get_max_entries() const; private: - static void python_to_bytes_inplace(const py::object &obj, std::span buffer); - static py::object bytes_to_python(std::span data); + static void python_to_bytes_inplace(const py::object &obj, + std::span buffer); + static py::object bytes_to_python(std::span data); }; -#endif //PYLIBBPF_MAPS_H +#endif // PYLIBBPF_MAPS_H diff --git a/src/core/bpf_object.cpp b/src/core/bpf_object.cpp index babaa92..660d1b5 100644 --- a/src/core/bpf_object.cpp +++ b/src/core/bpf_object.cpp @@ -1,255 +1,256 @@ #include "bpf_object.h" -#include "bpf_program.h" -#include "bpf_map.h" #include "bpf_exception.h" +#include "bpf_map.h" +#include "bpf_program.h" #include BpfObject::BpfObject(std::string object_path) - : obj_(nullptr), object_path_(std::move(object_path)), loaded_(false) { -} + : obj_(nullptr), object_path_(std::move(object_path)), loaded_(false) {} BpfObject::~BpfObject() { - // Clear caches first (order matters!) - prog_cache_.clear(); // Detaches programs - maps_cache_.clear(); // Closes maps - - // Then close object - if (obj_) { - bpf_object__close(obj_); - obj_ = nullptr; - } + // Clear caches first (order matters!) + prog_cache_.clear(); // Detaches programs + maps_cache_.clear(); // Closes maps + + // Then close object + if (obj_) { + bpf_object__close(obj_); + obj_ = nullptr; + } } -BpfObject::BpfObject(BpfObject&& other) noexcept - : obj_(other.obj_), - object_path_(std::move(other.object_path_)), - loaded_(other.loaded_), - prog_cache_(std::move(other.prog_cache_)), +BpfObject::BpfObject(BpfObject &&other) noexcept + : obj_(other.obj_), object_path_(std::move(other.object_path_)), + loaded_(other.loaded_), prog_cache_(std::move(other.prog_cache_)), maps_cache_(std::move(other.maps_cache_)) { - + + other.obj_ = nullptr; + other.loaded_ = false; +} + +BpfObject &BpfObject::operator=(BpfObject &&other) noexcept { + if (this != &other) { + prog_cache_.clear(); + maps_cache_.clear(); + if (obj_) { + bpf_object__close(obj_); + } + + obj_ = other.obj_; + object_path_ = std::move(other.object_path_); + loaded_ = other.loaded_; + prog_cache_ = std::move(other.prog_cache_); + maps_cache_ = std::move(other.maps_cache_); + other.obj_ = nullptr; other.loaded_ = false; -} - -BpfObject& BpfObject::operator=(BpfObject&& other) noexcept { - if (this != &other) { - prog_cache_.clear(); - maps_cache_.clear(); - if (obj_) { - bpf_object__close(obj_); - } - - obj_ = other.obj_; - object_path_ = std::move(other.object_path_); - loaded_ = other.loaded_; - prog_cache_ = std::move(other.prog_cache_); - maps_cache_ = std::move(other.maps_cache_); - - other.obj_ = nullptr; - other.loaded_ = false; - } - return *this; + } + return *this; } void BpfObject::load() { - if (loaded_) { - throw BpfException("BPF object already loaded"); - } - - std::string error_msg = "Failed to open BPF object"; - obj_ = bpf_object__open_file(object_path_.c_str(), nullptr); + if (loaded_) { + throw BpfException("BPF object already loaded"); + } - if (!obj_) { - error_msg += " file '" + object_path_ + "': " + std::strerror(errno); - throw BpfException(error_msg); - } - - if (bpf_object__load(obj_)) { - error_msg += " object from file '" + object_path_ + "': " + std::strerror(errno); - bpf_object__close(obj_); - obj_ = nullptr; - throw BpfException(error_msg); - } - - loaded_ = true; + std::string error_msg = "Failed to open BPF object"; + obj_ = bpf_object__open_file(object_path_.c_str(), nullptr); + + if (!obj_) { + error_msg += " file '" + object_path_ + "': " + std::strerror(errno); + throw BpfException(error_msg); + } + + if (bpf_object__load(obj_)) { + error_msg += + " object from file '" + object_path_ + "': " + std::strerror(errno); + bpf_object__close(obj_); + obj_ = nullptr; + throw BpfException(error_msg); + } + + loaded_ = true; } // ==================== Program Methods ==================== py::list BpfObject::get_program_names() const { - if (!loaded_) { - throw BpfException("BPF object not loaded"); - } - - py::list names; - struct bpf_program *prog = nullptr; - - bpf_object__for_each_program(prog, obj_) { - _get_or_create_program(prog); // Ensure cached - names.append(bpf_program__name(prog)); - } - - return names; + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + py::list names; + struct bpf_program *prog = nullptr; + + bpf_object__for_each_program(prog, obj_) { + _get_or_create_program(prog); // Ensure cached + names.append(bpf_program__name(prog)); + } + + return names; } -std::shared_ptr BpfObject::_get_or_create_program(struct bpf_program *prog) { - if (!prog) { - throw BpfException("bpf_program pointer is null"); - } - - const char *name = bpf_program__name(prog); - std::string prog_name(name ? name : ""); - - // Check cache - auto it = prog_cache_.find(prog_name); - if (it != prog_cache_.end()) { - return it->second; - } - - // Create and cache - auto bpf_prog = std::make_shared(this, prog, prog_name); - prog_cache_[prog_name] = bpf_prog; - - return bpf_prog; +std::shared_ptr +BpfObject::_get_or_create_program(struct bpf_program *prog) { + if (!prog) { + throw BpfException("bpf_program pointer is null"); + } + + const char *name = bpf_program__name(prog); + std::string prog_name(name ? name : ""); + + // Check cache + auto it = prog_cache_.find(prog_name); + if (it != prog_cache_.end()) { + return it->second; + } + + // Create and cache + auto bpf_prog = std::make_shared(this, prog, prog_name); + prog_cache_[prog_name] = bpf_prog; + + return bpf_prog; } -std::shared_ptr BpfObject::get_program(const std::string& name) { - if (!loaded_) { - throw BpfException("BPF object not loaded"); - } - - // Check cache - auto it = prog_cache_.find(name); - if (it != prog_cache_.end()) { - return it->second; - } - - // Create and cache - struct bpf_program *raw_prog = find_program_by_name(name); - auto prog = std::make_shared(this, raw_prog, name); - prog_cache_[name] = prog; - - return prog; +std::shared_ptr BpfObject::get_program(const std::string &name) { + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + // Check cache + auto it = prog_cache_.find(name); + if (it != prog_cache_.end()) { + return it->second; + } + + // Create and cache + struct bpf_program *raw_prog = find_program_by_name(name); + auto prog = std::make_shared(this, raw_prog, name); + prog_cache_[name] = prog; + + return prog; } -struct bpf_program* BpfObject::find_program_by_name(const std::string& name) const { - if (!loaded_) { - throw BpfException("BPF object not loaded"); - } - - struct bpf_program *prog = bpf_object__find_program_by_name(obj_, name.c_str()); - if (!prog) { - throw BpfException("Program '" + name + "' not found"); - } - - return prog; +struct bpf_program * +BpfObject::find_program_by_name(const std::string &name) const { + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + struct bpf_program *prog = + bpf_object__find_program_by_name(obj_, name.c_str()); + if (!prog) { + throw BpfException("Program '" + name + "' not found"); + } + + return prog; } py::dict BpfObject::get_cached_programs() const { - py::dict programs; - for (const auto& [name, prog] : prog_cache_) { - programs[name] = prog; - } - return programs; + py::dict programs; + for (const auto &[name, prog] : prog_cache_) { + programs[name] = prog; + } + return programs; } py::dict BpfObject::attach_all() { - if (!loaded_) { - throw BpfException("BPF object not loaded"); + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + py::dict attached_programs; + struct bpf_program *prog = nullptr; + + bpf_object__for_each_program(prog, obj_) { + auto bpf_prog = _get_or_create_program(prog); + + if (!bpf_prog->is_attached()) { + bpf_prog->attach(); } - - py::dict attached_programs; - struct bpf_program *prog = nullptr; - - bpf_object__for_each_program(prog, obj_) { - auto bpf_prog = _get_or_create_program(prog); - - if (!bpf_prog->is_attached()) { - bpf_prog->attach(); - } - - const char *name = bpf_program__name(prog); - attached_programs[name] = bpf_prog; - } - - return attached_programs; + + const char *name = bpf_program__name(prog); + attached_programs[name] = bpf_prog; + } + + return attached_programs; } // ==================== Map Methods ==================== py::list BpfObject::get_map_names() const { - if (!loaded_) { - throw BpfException("BPF object not loaded"); - } - - py::list names; - struct bpf_map *map = nullptr; - - bpf_object__for_each_map(map, obj_) { - _get_or_create_map(map); // Ensure cached - names.append(bpf_map__name(map)); - } - - return names; + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + py::list names; + struct bpf_map *map = nullptr; + + bpf_object__for_each_map(map, obj_) { + _get_or_create_map(map); // Ensure cached + names.append(bpf_map__name(map)); + } + + return names; } -std::shared_ptr BpfObject::get_map(const std::string& name) { - if (!loaded_) { - throw BpfException("BPF object not loaded"); - } - - // Check cache - auto it = maps_cache_.find(name); - if (it != maps_cache_.end()) { - return it->second; - } - - // Create and cache - struct bpf_map *raw_map = find_map_by_name(name); - auto map = std::make_shared(shared_from_this(), raw_map, name); - maps_cache_[name] = map; - - return map; +std::shared_ptr BpfObject::get_map(const std::string &name) { + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + // Check cache + auto it = maps_cache_.find(name); + if (it != maps_cache_.end()) { + return it->second; + } + + // Create and cache + struct bpf_map *raw_map = find_map_by_name(name); + auto map = std::make_shared(shared_from_this(), raw_map, name); + maps_cache_[name] = map; + + return map; } std::shared_ptr BpfObject::_get_or_create_map(struct bpf_map *map) { - if (!map) { - throw BpfException("bpf_map pointer is null"); - } - - const char *name = bpf_map__name(map); - std::string map_name(name ? name : ""); - - // Check cache - auto it = maps_cache_.find(map_name); - if (it != maps_cache_.end()) { - return it->second; - } - - // Create and cache - auto bpf_map = std::make_shared(shared_from_this(), map, map_name); - maps_cache_[map_name] = bpf_map; - - return bpf_map; + if (!map) { + throw BpfException("bpf_map pointer is null"); + } + + const char *name = bpf_map__name(map); + std::string map_name(name ? name : ""); + + // Check cache + auto it = maps_cache_.find(map_name); + if (it != maps_cache_.end()) { + return it->second; + } + + // Create and cache + auto bpf_map = std::make_shared(shared_from_this(), map, map_name); + maps_cache_[map_name] = bpf_map; + + return bpf_map; } -struct bpf_map* BpfObject::find_map_by_name(const std::string& name) const { - if (!loaded_) { - throw BpfException("BPF object not loaded"); - } - - struct bpf_map *map = bpf_object__find_map_by_name(obj_, name.c_str()); - if (!map) { - throw BpfException("Map '" + name + "' not found"); - } - - return map; +struct bpf_map *BpfObject::find_map_by_name(const std::string &name) const { + if (!loaded_) { + throw BpfException("BPF object not loaded"); + } + + struct bpf_map *map = bpf_object__find_map_by_name(obj_, name.c_str()); + if (!map) { + throw BpfException("Map '" + name + "' not found"); + } + + return map; } py::dict BpfObject::get_cached_maps() const { - py::dict maps; - for (const auto& [name, map] : maps_cache_) { - maps[name] = map; - } - return maps; + py::dict maps; + for (const auto &[name, map] : maps_cache_) { + maps[name] = map; + } + return maps; } diff --git a/src/core/bpf_object.h b/src/core/bpf_object.h index a82474d..43ba7a2 100644 --- a/src/core/bpf_object.h +++ b/src/core/bpf_object.h @@ -2,8 +2,8 @@ #define PYLIBBPF_BPF_OBJECT_H #include -#include #include +#include #include #include #include @@ -15,65 +15,68 @@ class BpfMap; /** * BpfObject - Represents a loaded BPF object file. - * + * * This is the main entry point for loading BPF programs. * Owns the bpf_object* and manages all programs and maps within it. */ class BpfObject : public std::enable_shared_from_this { private: - struct bpf_object *obj_; - std::string object_path_; - bool loaded_; + struct bpf_object *obj_; + std::string object_path_; + bool loaded_; - mutable std::unordered_map> maps_cache_; - mutable std::unordered_map> prog_cache_; + mutable std::unordered_map> maps_cache_; + mutable std::unordered_map> + prog_cache_; - std::shared_ptr _get_or_create_program(struct bpf_program *prog); - std::shared_ptr _get_or_create_map(struct bpf_map *map); + std::shared_ptr _get_or_create_program(struct bpf_program *prog); + std::shared_ptr _get_or_create_map(struct bpf_map *map); public: - explicit BpfObject(std::string object_path); - ~BpfObject(); - - // Disable copy, allow move - BpfObject(const BpfObject&) = delete; - BpfObject& operator=(const BpfObject&) = delete; - BpfObject(BpfObject&&) noexcept; - BpfObject& operator=(BpfObject&&) noexcept; - - /** - * Load the BPF object into the kernel. - * Must be called before accessing programs or maps. - */ - void load(); - - /** - * Check if object is loaded. - */ - [[nodiscard]] bool is_loaded() const { return loaded_; } - - /** - * Get the underlying bpf_object pointer. - * Only for internal use by BpfProgram and BpfMap. - */ - [[nodiscard]] struct bpf_object* get_obj() const { return obj_; } + explicit BpfObject(std::string object_path); + ~BpfObject(); - /** - * Attach all programs in the object. - */ - py::dict attach_all(); - - // Program access - [[nodiscard]] py::list get_program_names() const; - [[nodiscard]] std::shared_ptr get_program(const std::string& name); - [[nodiscard]] struct bpf_program* find_program_by_name(const std::string& name) const; - [[nodiscard]] py::dict get_cached_programs() const; - - // Map access - [[nodiscard]] py::list get_map_names() const; - [[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; + // Disable copy, allow move + BpfObject(const BpfObject &) = delete; + BpfObject &operator=(const BpfObject &) = delete; + BpfObject(BpfObject &&) noexcept; + BpfObject &operator=(BpfObject &&) noexcept; + + /** + * Load the BPF object into the kernel. + * Must be called before accessing programs or maps. + */ + void load(); + + /** + * Check if object is loaded. + */ + [[nodiscard]] bool is_loaded() const { return loaded_; } + + /** + * Get the underlying bpf_object pointer. + * Only for internal use by BpfProgram and BpfMap. + */ + [[nodiscard]] struct bpf_object *get_obj() const { return obj_; } + + /** + * Attach all programs in the object. + */ + py::dict attach_all(); + + // Program access + [[nodiscard]] py::list get_program_names() const; + [[nodiscard]] std::shared_ptr + get_program(const std::string &name); + [[nodiscard]] struct bpf_program * + find_program_by_name(const std::string &name) const; + [[nodiscard]] py::dict get_cached_programs() const; + + // Map access + [[nodiscard]] py::list get_map_names() const; + [[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; }; #endif // PYLIBBPF_BPF_OBJECT_H diff --git a/src/core/bpf_perf_buffer.cpp b/src/core/bpf_perf_buffer.cpp index e0f56f9..a1e7276 100644 --- a/src/core/bpf_perf_buffer.cpp +++ b/src/core/bpf_perf_buffer.cpp @@ -1,72 +1,75 @@ #include "bpf_perf_buffer.h" #include "bpf_exception.h" -void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data, unsigned int size) { - auto *self = static_cast(ctx); - - // Acquire GIL for Python calls - py::gil_scoped_acquire acquire; - - try { - // Convert data to Python bytes - py::bytes py_data(static_cast(data), size); - - // Call Python callback: callback(cpu, data, size) - self->callback_(cpu, py_data, size); - } catch (const py::error_already_set &e) { - PyErr_Print(); - } +void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data, + unsigned int size) { + auto *self = static_cast(ctx); + + // Acquire GIL for Python calls + py::gil_scoped_acquire acquire; + + try { + // Convert data to Python bytes + py::bytes py_data(static_cast(data), size); + + // Call Python callback: callback(cpu, data, size) + self->callback_(cpu, py_data, size); + } catch (const py::error_already_set &e) { + PyErr_Print(); + } } -void BpfPerfBuffer::lost_callback_wrapper(void *ctx, int cpu, unsigned long long cnt) { - auto *self = static_cast(ctx); - - if (self->lost_callback_.is_none()) { - return; - } - - py::gil_scoped_acquire acquire; - - try { - self->lost_callback_(cpu, cnt); - } catch (const py::error_already_set &e) { - PyErr_Print(); - } +void BpfPerfBuffer::lost_callback_wrapper(void *ctx, int cpu, + unsigned long long cnt) { + auto *self = static_cast(ctx); + + if (self->lost_callback_.is_none()) { + return; + } + + py::gil_scoped_acquire acquire; + + try { + self->lost_callback_(cpu, cnt); + } catch (const py::error_already_set &e) { + PyErr_Print(); + } } -BpfPerfBuffer::BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, py::object lost_callback) +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(); - } - - // 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"); - } + + if (!lost_callback.is_none()) { + lost_callback_ = lost_callback.cast(); + } + + // 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_); - } + if (pb_) { + perf_buffer__free(pb_); + } } int BpfPerfBuffer::poll(int timeout_ms) { - // Release GIL during blocking poll - py::gil_scoped_release release; - return perf_buffer__poll(pb_, timeout_ms); + // Release GIL during blocking poll + py::gil_scoped_release release; + return perf_buffer__poll(pb_, timeout_ms); } int BpfPerfBuffer::consume() { - py::gil_scoped_release release; - return perf_buffer__consume(pb_); + py::gil_scoped_release release; + return perf_buffer__consume(pb_); } diff --git a/src/core/bpf_perf_buffer.h b/src/core/bpf_perf_buffer.h index 23b7db8..3794a2d 100644 --- a/src/core/bpf_perf_buffer.h +++ b/src/core/bpf_perf_buffer.h @@ -2,27 +2,29 @@ #define PYLIBBPF_BPF_PERF_BUFFER_H #include -#include #include +#include namespace py = pybind11; class BpfPerfBuffer { private: - struct perf_buffer *pb_; - py::function callback_; - py::function lost_callback_; + struct perf_buffer *pb_; + py::function callback_; + py::function lost_callback_; - // 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); + // 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, py::object lost_callback); - ~BpfPerfBuffer(); + BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, + py::object lost_callback); + ~BpfPerfBuffer(); - int poll(int timeout_ms); - int consume(); + int poll(int timeout_ms); + int consume(); }; #endif // PYLIBBPF_BPF_PERF_BUFFER_H diff --git a/src/core/bpf_program.cpp b/src/core/bpf_program.cpp index 1d4202a..1979e5e 100644 --- a/src/core/bpf_program.cpp +++ b/src/core/bpf_program.cpp @@ -1,75 +1,72 @@ #include "bpf_program.h" #include "bpf_exception.h" -#include #include +#include -BpfProgram::BpfProgram(std::shared_ptr parent, struct bpf_program *raw_prog, const std::string& program_name) - : parent_obj_(parent), - prog_(raw_prog), - link_(nullptr), +BpfProgram::BpfProgram(std::shared_ptr parent, + struct bpf_program *raw_prog, + const std::string &program_name) + : parent_obj_(parent), prog_(raw_prog), link_(nullptr), program_name_(program_name) { - if (!parent) - throw BpfException("Parent BpfObject is null"); - if(!(parent->is_loaded())) - throw BpfException("Parent BpfObject is not loaded"); - if (!raw_prog) - throw BpfException("bpf_program pointer is null"); + if (!parent) + throw BpfException("Parent BpfObject is null"); + if (!(parent->is_loaded())) + throw BpfException("Parent BpfObject is not loaded"); + if (!raw_prog) + throw BpfException("bpf_program pointer is null"); } -BpfProgram::~BpfProgram() { +BpfProgram::~BpfProgram() { detach(); } + +BpfProgram::BpfProgram(BpfProgram &&other) noexcept + : parent_obj_(std::move(other.parent_obj_)), prog_(other.prog_), + link_(other.link_), program_name_(std::move(other.program_name_)) { + + other.prog_ = nullptr; + other.link_ = nullptr; +} + +BpfProgram &BpfProgram::operator=(BpfProgram &&other) noexcept { + if (this != &other) { detach(); -} -BpfProgram::BpfProgram(BpfProgram&& other) noexcept - : parent_obj_(std::move(other.parent_obj_)), - prog_(other.prog_), - link_(other.link_), - program_name_(std::move(other.program_name_)) { - + parent_obj_ = std::move(other.parent_obj_); + prog_ = other.prog_; + link_ = other.link_; + program_name_ = std::move(other.program_name_); + other.prog_ = nullptr; other.link_ = nullptr; -} - -BpfProgram& BpfProgram::operator=(BpfProgram&& other) noexcept { - if (this != &other) { - detach(); - - parent_obj_ = std::move(other.parent_obj_); - prog_ = other.prog_; - link_ = other.link_; - program_name_ = std::move(other.program_name_); - - other.prog_ = nullptr; - other.link_ = nullptr; - } - return *this; + } + return *this; } void BpfProgram::attach() { - // Check if parent is still alive - auto parent = parent_obj_.lock(); - if (!parent) { - throw BpfException("Parent BpfObject has been destroyed"); - } - - if (link_) { - throw BpfException("Program '" + program_name_ + "' already attached"); - } - - if (!prog_) { - throw BpfException("Program '" + program_name_ + "' not initialized"); - } - - link_ = bpf_program__attach(prog_); - if (!link_) { - std::string err_msg = "bpf_program__attach failed for program '" + program_name_ + "': " + std::strerror(errno); - throw BpfException(err_msg); - } + // Check if parent is still alive + auto parent = parent_obj_.lock(); + if (!parent) { + throw BpfException("Parent BpfObject has been destroyed"); + } + + if (link_) { + throw BpfException("Program '" + program_name_ + "' already attached"); + } + + if (!prog_) { + throw BpfException("Program '" + program_name_ + "' not initialized"); + } + + link_ = bpf_program__attach(prog_); + if (!link_) { + std::string err_msg = "bpf_program__attach failed for program '" + + program_name_ + "': " + std::strerror(errno); + throw BpfException(err_msg); + } } void BpfProgram::detach() { - if (link_) { - bpf_link__destroy(link_); - link_ = nullptr; - } + if (link_) { + bpf_link__destroy(link_); + link_ = nullptr; + } } diff --git a/src/core/bpf_program.h b/src/core/bpf_program.h index dd0dce4..3863ee6 100644 --- a/src/core/bpf_program.h +++ b/src/core/bpf_program.h @@ -9,26 +9,28 @@ class BpfObject; class BpfProgram { private: - std::weak_ptr parent_obj_; - struct bpf_program *prog_; - struct bpf_link *link_; - std::string program_name_; + std::weak_ptr parent_obj_; + struct bpf_program *prog_; + struct bpf_link *link_; + std::string program_name_; public: - explicit BpfProgram(std::shared_ptr parent, struct bpf_program *raw_prog, const std::string& program_name); + explicit BpfProgram(std::shared_ptr parent, + struct bpf_program *raw_prog, + const std::string &program_name); - ~BpfProgram(); + ~BpfProgram(); - BpfProgram(const BpfProgram&) = delete; - BpfProgram& operator=(const BpfProgram&) = delete; - BpfProgram(BpfProgram&&) noexcept; - BpfProgram& operator=(BpfProgram&&) noexcept; + BpfProgram(const BpfProgram &) = delete; + BpfProgram &operator=(const BpfProgram &) = delete; + BpfProgram(BpfProgram &&) noexcept; + BpfProgram &operator=(BpfProgram &&) noexcept; - bool attach(); - bool detach(); + bool attach(); + bool detach(); - [[nodiscard]] bool is_attached() const { return link_ != nullptr; } - [[nodiscard]] std::string get_name() const { return program_name_; } + [[nodiscard]] bool is_attached() const { return link_ != nullptr; } + [[nodiscard]] std::string get_name() const { return program_name_; } }; -#endif //PYLIBBPF_BPF_PROGRAM_H +#endif // PYLIBBPF_BPF_PROGRAM_H