Skip to content

Commit 8d27a35

Browse files
committed
Add src/core/bpf_perf_buffer
1 parent 5a3937b commit 8d27a35

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

src/core/bpf_perf_buffer.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "bpf_perf_buffer.h"
2+
#include "bpf_exception.h"
3+
4+
void BpfPerfBuffer::sample_callback_wrapper(void *ctx, int cpu, void *data, unsigned int size) {
5+
auto *self = static_cast<BpfPerfBuffer *>(ctx);
6+
7+
// Acquire GIL for Python calls
8+
py::gil_scoped_acquire acquire;
9+
10+
try {
11+
// Convert data to Python bytes
12+
py::bytes py_data(static_cast<const char *>(data), size);
13+
14+
// Call Python callback: callback(cpu, data, size)
15+
self->callback_(cpu, py_data, size);
16+
} catch (const py::error_already_set &e) {
17+
PyErr_Print();
18+
}
19+
}
20+
21+
void BpfPerfBuffer::lost_callback_wrapper(void *ctx, int cpu, unsigned long long cnt) {
22+
auto *self = static_cast<BpfPerfBuffer *>(ctx);
23+
24+
if (self->lost_callback_.is_none()) {
25+
return;
26+
}
27+
28+
py::gil_scoped_acquire acquire;
29+
30+
try {
31+
self->lost_callback_(cpu, cnt);
32+
} catch (const py::error_already_set &e) {
33+
PyErr_Print();
34+
}
35+
}
36+
37+
BpfPerfBuffer::BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, py::object lost_callback)
38+
: pb_(nullptr), callback_(std::move(callback)) {
39+
40+
if (!lost_callback.is_none()) {
41+
lost_callback_ = lost_callback.cast<py::function>();
42+
}
43+
44+
// Setup perf buffer options
45+
perf_buffer_opts pb_opts = {};
46+
pb_opts.sample_cb = sample_callback_wrapper;
47+
pb_opts.lost_cb = lost_callback.is_none() ? nullptr : lost_callback_wrapper;
48+
pb_opts.ctx = this;
49+
50+
// Create perf buffer
51+
pb_ = perf_buffer__new(map_fd, page_cnt, &pb_opts);
52+
if (!pb_) {
53+
throw BpfException("Failed to create perf buffer");
54+
}
55+
}
56+
57+
BpfPerfBuffer::~BpfPerfBuffer() {
58+
if (pb_) {
59+
perf_buffer__free(pb_);
60+
}
61+
}
62+
63+
int BpfPerfBuffer::poll(int timeout_ms) {
64+
// Release GIL during blocking poll
65+
py::gil_scoped_release release;
66+
return perf_buffer__poll(pb_, timeout_ms);
67+
}
68+
69+
int BpfPerfBuffer::consume() {
70+
py::gil_scoped_release release;
71+
return perf_buffer__consume(pb_);
72+
}

src/core/bpf_perf_buffer.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef PYLIBBPF_BPF_PERF_BUFFER_H
2+
#define PYLIBBPF_BPF_PERF_BUFFER_H
3+
4+
#include <libbpf.h>
5+
#include <pybind11/pybind11.h>
6+
#include <pybind11/functional.h>
7+
8+
namespace py = pybind11;
9+
10+
class BpfPerfBuffer {
11+
private:
12+
struct perf_buffer *pb_;
13+
py::function callback_;
14+
py::function lost_callback_;
15+
16+
// Static callback wrappers for C API
17+
static void sample_callback_wrapper(void *ctx, int cpu, void *data, unsigned int size);
18+
static void lost_callback_wrapper(void *ctx, int cpu, unsigned long long cnt);
19+
20+
public:
21+
BpfPerfBuffer(int map_fd, int page_cnt, py::function callback, py::object lost_callback);
22+
~BpfPerfBuffer();
23+
24+
int poll(int timeout_ms);
25+
int consume();
26+
};
27+
28+
#endif // PYLIBBPF_BPF_PERF_BUFFER_H

0 commit comments

Comments
 (0)