Commit 4180333c authored by Romain's avatar Romain Committed by Sasha Goldshtein

cc: Add open_perf_event to the C/C++ API (#1232)

parent 2c1799c9
......@@ -15,6 +15,7 @@
*/
#include <linux/bpf.h>
#include <linux/perf_event.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
......@@ -124,6 +125,16 @@ StatusTuple BPF::detach_all() {
delete it.second;
}
for (auto& it : perf_event_arrays_) {
auto res = it.second->close_all_cpu();
if (res.code() != 0) {
error_msg += "Failed to close perf event array " + it.first + ": ";
error_msg += res.msg() + "\n";
has_error = true;
}
delete it.second;
}
for (auto& it : perf_events_) {
auto res = detach_perf_event_all_cpu(it.second);
if (res.code() != 0) {
......@@ -394,6 +405,32 @@ StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) {
return StatusTuple(0);
}
StatusTuple BPF::open_perf_event(const std::string& name,
uint32_t type,
uint64_t config) {
if (perf_event_arrays_.find(name) == perf_event_arrays_.end()) {
TableStorage::iterator it;
if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
return StatusTuple(-1,
"open_perf_event: unable to find table_storage %s",
name.c_str());
perf_event_arrays_[name] = new BPFPerfEventArray(it->second);
}
if (type != PERF_TYPE_RAW && type != PERF_TYPE_HARDWARE)
return StatusTuple(-1, "open_perf_event unsupported type");
auto table = perf_event_arrays_[name];
TRY2(table->open_all_cpu(type, config));
return StatusTuple(0);
}
StatusTuple BPF::close_perf_event(const std::string& name) {
auto it = perf_event_arrays_.find(name);
if (it == perf_event_arrays_.end())
return StatusTuple(-1, "Perf Event for %s not open", name.c_str());
TRY2(it->second->close_all_cpu());
return StatusTuple(0);
}
StatusTuple BPF::open_perf_buffer(const std::string& name,
perf_reader_raw_cb cb,
perf_reader_lost_cb lost_cb,
......
......@@ -17,6 +17,7 @@
#pragma once
#include <cctype>
#include <cstdint>
#include <memory>
#include <string>
......@@ -119,6 +120,12 @@ public:
bool use_debug_file = true,
bool check_debug_file_crc = true);
StatusTuple open_perf_event(const std::string& name,
uint32_t type,
uint64_t config);
StatusTuple close_perf_event(const std::string& name);
StatusTuple open_perf_buffer(const std::string& name,
perf_reader_raw_cb cb,
perf_reader_lost_cb lost_cb = nullptr,
......@@ -187,6 +194,7 @@ private:
std::map<std::string, open_probe_t> uprobes_;
std::map<std::string, open_probe_t> tracepoints_;
std::map<std::string, BPFPerfBuffer*> perf_buffers_;
std::map<std::string, BPFPerfEventArray*> perf_event_arrays_;
std::map<std::pair<uint32_t, uint32_t>, open_probe_t> perf_events_;
};
......
......@@ -15,9 +15,12 @@
*/
#include <linux/elf.h>
#include <linux/perf_event.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <cerrno>
#include <cinttypes>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <memory>
......@@ -247,4 +250,76 @@ BPFPerfBuffer::~BPFPerfBuffer() {
<< std::endl;
}
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");
std::vector<int> cpus = get_online_cpus();
for (int i : cpus) {
auto res = open_on_cpu(i, type, config);
if (res.code() != 0) {
TRY2(close_all_cpu());
return res;
}
}
return StatusTuple(0);
}
StatusTuple BPFPerfEventArray::close_all_cpu() {
std::string errors;
bool has_error = false;
std::vector<int> opened_cpus;
for (auto it : cpu_fds_)
opened_cpus.push_back(it.first);
for (int i : opened_cpus) {
auto res = close_on_cpu(i);
if (res.code() != 0) {
errors += "Failed to close CPU" + std::to_string(i) + " perf event: ";
errors += res.msg() + "\n";
has_error = true;
}
}
if (has_error)
return StatusTuple(-1, errors);
return StatusTuple(0);
}
StatusTuple BPFPerfEventArray::open_on_cpu(int cpu, uint32_t type,
uint64_t config) {
if (cpu_fds_.find(cpu) != cpu_fds_.end())
return StatusTuple(-1, "Perf event already open on CPU %d", cpu);
int fd = bpf_open_perf_event(type, config, -1, cpu);
if (fd < 0) {
return StatusTuple(-1, "Error constructing perf event %" PRIu32 ":%" PRIu64,
type, config);
}
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));
}
cpu_fds_[cpu] = fd;
return StatusTuple(0);
}
StatusTuple BPFPerfEventArray::close_on_cpu(int cpu) {
auto it = cpu_fds_.find(cpu);
if (it == cpu_fds_.end()) {
return StatusTuple(0);
}
bpf_close_perf_event_fd(it->second);
cpu_fds_.erase(it);
return StatusTuple(0);
}
BPFPerfEventArray::~BPFPerfEventArray() {
auto res = close_all_cpu();
if (res.code() != 0) {
std::cerr << "Failed to close all perf buffer on destruction: " << res.msg()
<< std::endl;
}
}
} // namespace ebpf
......@@ -241,6 +241,22 @@ class BPFPerfBuffer : public BPFTableBase<int, int> {
std::unique_ptr<epoll_event[]> ep_events_;
};
class BPFPerfEventArray : public BPFTableBase<int, int> {
public:
BPFPerfEventArray(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {}
~BPFPerfEventArray();
StatusTuple open_all_cpu(uint32_t type, uint64_t config);
StatusTuple close_all_cpu();
private:
StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config);
StatusTuple close_on_cpu(int cpu);
std::map<int, int> cpu_fds_;
};
class BPFProgTable : public BPFTableBase<int, int> {
public:
BPFProgTable(const TableDesc& desc)
......
......@@ -83,6 +83,8 @@ int bpf_attach_perf_event(int progfd, uint32_t ev_type, uint32_t ev_config,
uint64_t sample_period, uint64_t sample_freq,
pid_t pid, int cpu, int group_fd);
int bpf_open_perf_event(uint32_t type, uint64_t config, int pid, int cpu);
int bpf_close_perf_event_fd(int fd);
int bpf_obj_pin(int fd, const char *pathname);
......
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