Commit 3bb44859 authored by yonghong-song's avatar yonghong-song Committed by GitHub

Merge pull request #1716 from palmtenor/cppmisc

Misc improvements to C++ API and example
parents bbc3fbfe ce657b11
......@@ -83,10 +83,11 @@ int main(int argc, char** argv) {
auto table_handle = bpf.get_hash_table<query_probe_t, int>("queries");
auto table = table_handle.get_table_offline();
std::sort(table.begin(), table.end(), [](std::pair<query_probe_t, int> a,
std::pair<query_probe_t, int> b) {
return a.first.ts < b.first.ts;
});
std::sort(
table.begin(), table.end(),
[](std::pair<query_probe_t, int> a, std::pair<query_probe_t, int> b) {
return a.first.ts < b.first.ts;
});
std::cout << table.size() << " queries recorded:" << std::endl;
for (auto it : table) {
std::cout << "Time: " << it.first.ts << " PID: " << it.first.pid
......
......@@ -27,17 +27,15 @@ struct stack_key_t {
int kernel_stack;
};
BPF_STACK_TRACE(stack_traces, 10240);
BPF_STACK_TRACE(stack_traces, 16384);
BPF_HASH(counts, struct stack_key_t, uint64_t);
int on_tcp_send(struct pt_regs *ctx) {
struct stack_key_t key = {};
key.pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&key.name, sizeof(key.name));
key.kernel_stack = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID);
key.user_stack = stack_traces.get_stackid(
ctx, BPF_F_REUSE_STACKID | BPF_F_USER_STACK
);
key.kernel_stack = stack_traces.get_stackid(ctx, 0);
key.user_stack = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
u64 zero = 0, *val;
val = counts.lookup_or_init(&key, &zero);
......@@ -76,39 +74,56 @@ int main(int argc, char** argv) {
std::cout << "Probing for " << probe_time << " seconds" << std::endl;
sleep(probe_time);
auto detach_res = bpf.detach_kprobe("tcp_sendmsg");
if (detach_res.code() != 0) {
std::cerr << detach_res.msg() << std::endl;
return 1;
}
auto table =
bpf.get_hash_table<stack_key_t, uint64_t>("counts").get_table_offline();
std::sort(table.begin(), table.end(), [](std::pair<stack_key_t, uint64_t> a,
std::pair<stack_key_t, uint64_t> b) {
return a.second < b.second;
});
std::sort(
table.begin(), table.end(),
[](std::pair<stack_key_t, uint64_t> a,
std::pair<stack_key_t, uint64_t> b) { return a.second < b.second; });
auto stacks = bpf.get_stack_table("stack_traces");
int lost_stacks = 0;
for (auto it : table) {
std::cout << "PID: " << it.first.pid << " (" << it.first.name << ") "
<< "made " << it.second
<< " TCP sends on following stack: " << std::endl;
std::cout << " Kernel Stack:" << std::endl;
if (it.first.kernel_stack >= 0) {
std::cout << " Kernel Stack:" << std::endl;
auto syms = stacks.get_stack_symbol(it.first.kernel_stack, -1);
for (auto sym : syms)
std::cout << " " << sym << std::endl;
} else
std::cout << " " << it.first.kernel_stack << std::endl;
std::cout << " User Stack:" << std::endl;
} else {
// -EFAULT normally means the stack is not availiable and not an error
if (it.first.kernel_stack != -EFAULT) {
lost_stacks++;
std::cout << " [Lost Kernel Stack" << it.first.kernel_stack << "]"
<< std::endl;
}
}
if (it.first.user_stack >= 0) {
std::cout << " User Stack:" << std::endl;
auto syms = stacks.get_stack_symbol(it.first.user_stack, it.first.pid);
for (auto sym : syms)
std::cout << " " << sym << std::endl;
} else
std::cout << " " << it.first.user_stack << std::endl;
} else {
// -EFAULT normally means the stack is not availiable and not an error
if (it.first.user_stack != -EFAULT) {
lost_stacks++;
std::cout << " [Lost User Stack " << it.first.user_stack << "]"
<< std::endl;
}
}
}
auto detach_res = bpf.detach_kprobe("tcp_sendmsg");
if (detach_res.code() != 0) {
std::cerr << detach_res.msg() << std::endl;
return 1;
}
if (lost_stacks > 0)
std::cout << "Total " << lost_stacks << " stack-traces lost due to "
<< "hash collision or stack table full" << std::endl;
return 0;
}
......@@ -39,9 +39,9 @@
namespace ebpf {
static const char *syscall_prefix[] = {
"sys_",
"__x64_sys_",
static const char* syscall_prefix[] = {
"sys_",
"__x64_sys_",
};
std::string uint_to_hex(uint64_t value) {
......@@ -63,7 +63,7 @@ StatusTuple BPF::init(const std::string& bpf_program,
const std::vector<USDT>& usdt) {
std::string all_bpf_program;
bcc_symbol_option symbol_option = {};
void *ksym_cache;
void* ksym_cache;
uint64_t addr;
int ret;
......@@ -284,8 +284,8 @@ StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
int probe_fd;
TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd));
int res_fd = bpf_attach_tracepoint(probe_fd, tp_category.c_str(),
tp_name.c_str());
int res_fd =
bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str());
if (res_fd < 0) {
TRY2(unload_func(probe_func));
......@@ -341,8 +341,8 @@ StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config,
}
StatusTuple BPF::attach_perf_event_raw(void* perf_event_attr,
const std::string& probe_func,
pid_t pid, int cpu, int group_fd) {
const std::string& probe_func, pid_t pid,
int cpu, int group_fd) {
auto attr = static_cast<struct perf_event_attr*>(perf_event_attr);
auto ev_pair = std::make_pair(attr->type, attr->config);
if (perf_events_.find(ev_pair) != perf_events_.end())
......@@ -377,7 +377,6 @@ StatusTuple BPF::attach_perf_event_raw(void* perf_event_attr,
p.per_cpu_fd = fds;
perf_events_[ev_pair] = std::move(p);
return StatusTuple(0);
}
StatusTuple BPF::detach_kprobe(const std::string& kernel_func,
......@@ -567,7 +566,7 @@ StatusTuple BPF::unload_func(const std::string& func_name) {
return StatusTuple(0);
}
std::string BPF::get_syscall_fnname(const std::string &name) {
std::string BPF::get_syscall_fnname(const std::string& name) {
std::string fn_name = syscall_prefix[syscall_prefix_idx_] + name;
return std::move(fn_name);
}
......
......@@ -47,7 +47,8 @@ class BPF {
explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr,
bool rw_engine_enabled = true)
: flag_(flag), syscall_prefix_idx_(0),
: flag_(flag),
syscall_prefix_idx_(0),
bpf_module_(new BPFModule(flag, ts, rw_engine_enabled)) {}
StatusTuple init(const std::string& bpf_program,
const std::vector<std::string>& cflags = {},
......@@ -91,7 +92,7 @@ class BPF {
int group_fd = -1);
StatusTuple detach_perf_event(uint32_t ev_type, uint32_t ev_config);
StatusTuple detach_perf_event_raw(void* perf_event_attr);
std::string get_syscall_fnname(const std::string &name);
std::string get_syscall_fnname(const std::string& name);
BPFTable get_table(const std::string& name) {
TableStorage::iterator it;
......@@ -109,7 +110,8 @@ class BPF {
}
template <class ValueType>
BPFPercpuArrayTable<ValueType> get_percpu_array_table(const std::string& name) {
BPFPercpuArrayTable<ValueType> get_percpu_array_table(
const std::string& name) {
TableStorage::iterator it;
if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
return BPFPercpuArrayTable<ValueType>(it->second);
......@@ -125,7 +127,8 @@ class BPF {
}
template <class KeyType, class ValueType>
BPFPercpuHashTable<KeyType, ValueType> get_percpu_hash_table(const std::string& name) {
BPFPercpuHashTable<KeyType, ValueType> get_percpu_hash_table(
const std::string& name) {
TableStorage::iterator it;
if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
return BPFPercpuHashTable<KeyType, ValueType>(it->second);
......@@ -159,9 +162,9 @@ class BPF {
BPFPerfBuffer* get_perf_buffer(const std::string& name);
// Poll an opened Perf Buffer of given name with given timeout, using callback
// provided when opening. Do nothing if such open Perf Buffer doesn't exist.
// Returns:
// -1 on error or if perf buffer with such name doesn't exist;
// 0, if no data was available before timeout;
// Returns:
// -1 on error or if perf buffer with such name doesn't exist;
// 0, if no data was available before timeout;
// number of CPUs that have new data, otherwise.
int poll_perf_buffer(const std::string& name, int timeout_ms = -1);
......
......@@ -14,10 +14,10 @@
* limitations under the License.
*/
#include <fcntl.h>
#include <linux/elf.h>
#include <linux/perf_event.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cinttypes>
......@@ -145,28 +145,25 @@ StatusTuple BPFTable::remove_value(const std::string& key_str) {
}
StatusTuple BPFTable::clear_table_non_atomic() {
if (desc.type == BPF_MAP_TYPE_HASH ||
desc.type == BPF_MAP_TYPE_PERCPU_HASH ||
if (desc.type == BPF_MAP_TYPE_HASH || desc.type == BPF_MAP_TYPE_PERCPU_HASH ||
desc.type == BPF_MAP_TYPE_LRU_HASH ||
desc.type == BPF_MAP_TYPE_PERCPU_HASH ||
desc.type == BPF_MAP_TYPE_HASH_OF_MAPS) {
// For hash maps, use the first() interface (which uses get_next_key) to
// iterate through the map and clear elements
auto key = std::unique_ptr<void, decltype(::free)*>(
::malloc(desc.key_size),
::free);
auto key = std::unique_ptr<void, decltype(::free)*>(::malloc(desc.key_size),
::free);
while (this->first(key.get()))
if (!this->remove(key.get())) {
return StatusTuple(
-1,
"Failed to delete element when clearing table %s",
desc.name.c_str());
return StatusTuple(-1,
"Failed to delete element when clearing table %s",
desc.name.c_str());
}
} else if (desc.type == BPF_MAP_TYPE_ARRAY ||
desc.type == BPF_MAP_TYPE_PERCPU_ARRAY) {
return StatusTuple(
-1, "Array map %s do not support clearing elements", desc.name.c_str());
return StatusTuple(-1, "Array map %s do not support clearing elements",
desc.name.c_str());
} else if (desc.type == BPF_MAP_TYPE_PROG_ARRAY ||
desc.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY ||
desc.type == BPF_MAP_TYPE_STACK_TRACE ||
......@@ -176,26 +173,25 @@ StatusTuple BPFTable::clear_table_non_atomic() {
this->remove(&i);
}
} else {
return StatusTuple(
-1, "Clearing for map type of %s not supported yet", desc.name.c_str());
return StatusTuple(-1, "Clearing for map type of %s not supported yet",
desc.name.c_str());
}
return StatusTuple(0);
}
size_t BPFTable::get_possible_cpu_count() {
return get_possible_cpus().size();
}
size_t BPFTable::get_possible_cpu_count() { return get_possible_cpus().size(); }
BPFStackTable::BPFStackTable(const TableDesc& desc,
bool use_debug_file,
BPFStackTable::BPFStackTable(const TableDesc& desc, bool use_debug_file,
bool check_debug_file_crc)
: BPFTableBase<int, stacktrace_t>(desc) {
symbol_option_ = {
.use_debug_file = use_debug_file,
.check_debug_file_crc = check_debug_file_crc,
.use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
};
if (desc.type != BPF_MAP_TYPE_STACK_TRACE)
throw std::invalid_argument("Table '" + desc.name +
"' is not a stack table");
symbol_option_ = {.use_debug_file = use_debug_file,
.check_debug_file_crc = check_debug_file_crc,
.use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)};
}
BPFStackTable::BPFStackTable(BPFStackTable&& that)
......@@ -254,6 +250,13 @@ std::vector<std::string> BPFStackTable::get_stack_symbol(int stack_id,
return res;
}
BPFPerfBuffer::BPFPerfBuffer(const TableDesc& desc)
: BPFTableBase<int, int>(desc), epfd_(-1) {
if (desc.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
throw std::invalid_argument("Table '" + desc.name +
"' is not a perf buffer");
}
StatusTuple BPFPerfBuffer::open_on_cpu(perf_reader_raw_cb cb,
perf_reader_lost_cb lost_cb, int cpu,
void* cb_cookie, int page_cnt) {
......@@ -350,7 +353,8 @@ StatusTuple BPFPerfBuffer::close_all_cpu() {
int BPFPerfBuffer::poll(int timeout_ms) {
if (epfd_ < 0)
return -1;
int cnt = epoll_wait(epfd_, ep_events_.get(), cpu_readers_.size(), timeout_ms);
int cnt =
epoll_wait(epfd_, ep_events_.get(), cpu_readers_.size(), timeout_ms);
for (int i = 0; i < cnt; i++)
perf_reader_event_read(static_cast<perf_reader*>(ep_events_[i].data.ptr));
return cnt;
......@@ -363,6 +367,13 @@ BPFPerfBuffer::~BPFPerfBuffer() {
<< std::endl;
}
BPFPerfEventArray::BPFPerfEventArray(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {
if (desc.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
throw std::invalid_argument("Table '" + desc.name +
"' is not a perf event array");
}
StatusTuple BPFPerfEventArray::open_all_cpu(uint32_t type, uint64_t config) {
if (cpu_fds_.size() != 0)
return StatusTuple(-1, "Previously opened perf event not cleaned");
......@@ -411,8 +422,8 @@ StatusTuple BPFPerfEventArray::open_on_cpu(int cpu, uint32_t type,
}
if (!update(&cpu, &fd)) {
bpf_close_perf_event_fd(fd);
return StatusTuple(-1, "Unable to open perf event on CPU %d: %s",
cpu, std::strerror(errno));
return StatusTuple(-1, "Unable to open perf event on CPU %d: %s", cpu,
std::strerror(errno));
}
cpu_fds_[cpu] = fd;
return StatusTuple(0);
......@@ -436,6 +447,13 @@ BPFPerfEventArray::~BPFPerfEventArray() {
}
}
BPFProgTable::BPFProgTable(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {
if (desc.type != BPF_MAP_TYPE_PROG_ARRAY)
throw std::invalid_argument("Table '" + desc.name +
"' is not a prog table");
}
StatusTuple BPFProgTable::update_value(const int& index, const int& prog_fd) {
if (!this->update(const_cast<int*>(&index), const_cast<int*>(&prog_fd)))
return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
......@@ -448,6 +466,13 @@ StatusTuple BPFProgTable::remove_value(const int& index) {
return StatusTuple(0);
}
BPFCgroupArray::BPFCgroupArray(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {
if (desc.type != BPF_MAP_TYPE_CGROUP_ARRAY)
throw std::invalid_argument("Table '" + desc.name +
"' is not a cgroup array");
}
StatusTuple BPFCgroupArray::update_value(const int& index,
const int& cgroup2_fd) {
if (!this->update(const_cast<int*>(&index), const_cast<int*>(&cgroup2_fd)))
......
......@@ -16,9 +16,9 @@
#pragma once
#include <cstring>
#include <errno.h>
#include <sys/epoll.h>
#include <cstring>
#include <exception>
#include <map>
#include <memory>
......@@ -83,9 +83,7 @@ class BPFTableBase {
return bpf_update_elem(desc.fd, key, value, 0) >= 0;
}
bool remove(void* key) {
return bpf_delete_elem(desc.fd, key) >= 0;
}
bool remove(void* key) { return bpf_delete_elem(desc.fd, key) >= 0; }
const TableDesc& desc;
};
......@@ -111,19 +109,23 @@ class BPFTable : public BPFTableBase<void, void> {
};
template <class ValueType>
void * get_value_addr(ValueType& t) { return &t; }
void* get_value_addr(ValueType& t) {
return &t;
}
template <class ValueType>
void * get_value_addr(std::vector<ValueType>& t) { return t.data(); }
void* get_value_addr(std::vector<ValueType>& t) {
return t.data();
}
template <class ValueType>
class BPFArrayTable : public BPFTableBase<int, ValueType> {
public:
BPFArrayTable(const TableDesc& desc)
: BPFTableBase<int, ValueType>(desc) {
public:
BPFArrayTable(const TableDesc& desc) : BPFTableBase<int, ValueType>(desc) {
if (desc.type != BPF_MAP_TYPE_ARRAY &&
desc.type != BPF_MAP_TYPE_PERCPU_ARRAY)
throw std::invalid_argument("Table '" + desc.name + "' is not an array table");
throw std::invalid_argument("Table '" + desc.name +
"' is not an array table");
}
virtual StatusTuple get_value(const int& index, ValueType& value) {
......@@ -133,7 +135,8 @@ public:
}
virtual StatusTuple update_value(const int& index, const ValueType& value) {
if (!this->update(const_cast<int*>(&index), get_value_addr(const_cast<ValueType&>(value))))
if (!this->update(const_cast<int*>(&index),
get_value_addr(const_cast<ValueType&>(value))))
return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
return StatusTuple(0);
}
......@@ -147,7 +150,7 @@ public:
std::vector<ValueType> get_table_offline() {
std::vector<ValueType> res(this->capacity());
for (int i = 0; i < (int) this->capacity(); i++) {
for (int i = 0; i < (int)this->capacity(); i++) {
get_value(i, res[i]);
}
......@@ -162,8 +165,10 @@ class BPFPercpuArrayTable : public BPFArrayTable<std::vector<ValueType>> {
: BPFArrayTable<std::vector<ValueType>>(desc),
ncpus(BPFTable::get_possible_cpu_count()) {
if (desc.type != BPF_MAP_TYPE_PERCPU_ARRAY)
throw std::invalid_argument("Table '" + desc.name + "' is not a percpu array table");
// leaf structures have to be aligned to 8 bytes as hardcoded in the linux kernel.
throw std::invalid_argument("Table '" + desc.name +
"' is not a percpu array table");
// leaf structures have to be aligned to 8 bytes as hardcoded in the linux
// kernel.
if (sizeof(ValueType) % 8)
throw std::invalid_argument("leaf must be aligned to 8 bytes");
}
......@@ -173,11 +178,13 @@ class BPFPercpuArrayTable : public BPFArrayTable<std::vector<ValueType>> {
return BPFArrayTable<std::vector<ValueType>>::get_value(index, value);
}
StatusTuple update_value(const int& index, const std::vector<ValueType>& value) {
StatusTuple update_value(const int& index,
const std::vector<ValueType>& value) {
if (value.size() != ncpus)
return StatusTuple(-1, "bad value size");
return BPFArrayTable<std::vector<ValueType>>::update_value(index, value);
}
private:
unsigned int ncpus;
};
......@@ -191,7 +198,8 @@ class BPFHashTable : public BPFTableBase<KeyType, ValueType> {
desc.type != BPF_MAP_TYPE_PERCPU_HASH &&
desc.type != BPF_MAP_TYPE_LRU_HASH &&
desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
throw std::invalid_argument("Table '" + desc.name + "' is not a hash table");
throw std::invalid_argument("Table '" + desc.name +
"' is not a hash table");
}
virtual StatusTuple get_value(const KeyType& key, ValueType& value) {
......@@ -201,7 +209,8 @@ class BPFHashTable : public BPFTableBase<KeyType, ValueType> {
}
virtual StatusTuple update_value(const KeyType& key, const ValueType& value) {
if (!this->update(const_cast<KeyType*>(&key), get_value_addr(const_cast<ValueType&>(value))))
if (!this->update(const_cast<KeyType*>(&key),
get_value_addr(const_cast<ValueType&>(value))))
return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
return StatusTuple(0);
}
......@@ -247,15 +256,18 @@ class BPFHashTable : public BPFTableBase<KeyType, ValueType> {
};
template <class KeyType, class ValueType>
class BPFPercpuHashTable : public BPFHashTable<KeyType, std::vector<ValueType>> {
class BPFPercpuHashTable
: public BPFHashTable<KeyType, std::vector<ValueType>> {
public:
explicit BPFPercpuHashTable(const TableDesc& desc)
: BPFHashTable<KeyType, std::vector<ValueType>>(desc),
ncpus(BPFTable::get_possible_cpu_count()) {
if (desc.type != BPF_MAP_TYPE_PERCPU_HASH &&
desc.type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
throw std::invalid_argument("Table '" + desc.name + "' is not a percpu hash table");
// leaf structures have to be aligned to 8 bytes as hardcoded in the linux kernel.
throw std::invalid_argument("Table '" + desc.name +
"' is not a percpu hash table");
// leaf structures have to be aligned to 8 bytes as hardcoded in the linux
// kernel.
if (sizeof(ValueType) % 8)
throw std::invalid_argument("leaf must be aligned to 8 bytes");
}
......@@ -265,11 +277,14 @@ class BPFPercpuHashTable : public BPFHashTable<KeyType, std::vector<ValueType>>
return BPFHashTable<KeyType, std::vector<ValueType>>::get_value(key, value);
}
StatusTuple update_value(const KeyType& key, const std::vector<ValueType>& value) {
StatusTuple update_value(const KeyType& key,
const std::vector<ValueType>& value) {
if (value.size() != ncpus)
return StatusTuple(-1, "bad value size");
return BPFHashTable<KeyType, std::vector<ValueType>>::update_value(key, value);
return BPFHashTable<KeyType, std::vector<ValueType>>::update_value(key,
value);
}
private:
unsigned int ncpus;
};
......@@ -282,8 +297,7 @@ struct stacktrace_t {
class BPFStackTable : public BPFTableBase<int, stacktrace_t> {
public:
BPFStackTable(const TableDesc& desc,
bool use_debug_file,
BPFStackTable(const TableDesc& desc, bool use_debug_file,
bool check_debug_file_crc);
BPFStackTable(BPFStackTable&& that);
~BPFStackTable();
......@@ -299,8 +313,7 @@ class BPFStackTable : public BPFTableBase<int, stacktrace_t> {
class BPFPerfBuffer : public BPFTableBase<int, int> {
public:
BPFPerfBuffer(const TableDesc& desc)
: BPFTableBase<int, int>(desc), epfd_(-1) {}
BPFPerfBuffer(const TableDesc& desc);
~BPFPerfBuffer();
StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
......@@ -321,8 +334,7 @@ class BPFPerfBuffer : public BPFTableBase<int, int> {
class BPFPerfEventArray : public BPFTableBase<int, int> {
public:
BPFPerfEventArray(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {}
BPFPerfEventArray(const TableDesc& desc);
~BPFPerfEventArray();
StatusTuple open_all_cpu(uint32_t type, uint64_t config);
......@@ -336,24 +348,16 @@ class BPFPerfEventArray : public BPFTableBase<int, int> {
};
class BPFProgTable : public BPFTableBase<int, int> {
public:
BPFProgTable(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {
if (desc.type != BPF_MAP_TYPE_PROG_ARRAY)
throw std::invalid_argument("Table '" + desc.name + "' is not a prog table");
}
public:
BPFProgTable(const TableDesc& desc);
StatusTuple update_value(const int& index, const int& prog_fd);
StatusTuple remove_value(const int& index);
};
class BPFCgroupArray : public BPFTableBase<int, int> {
public:
BPFCgroupArray(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {
if (desc.type != BPF_MAP_TYPE_CGROUP_ARRAY)
throw std::invalid_argument("Table '" + desc.name + "' is not a cgroup array");
}
public:
BPFCgroupArray(const TableDesc& desc);
StatusTuple update_value(const int& index, const int& cgroup2_fd);
StatusTuple update_value(const int& index, const std::string& cgroup2_path);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment