*/
#include <linux/bpf.h>
+#include <linux/perf_event.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
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) {
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,
#pragma once
#include <cctype>
+#include <cstdint>
#include <memory>
#include <string>
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,
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_;
};
*/
#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>
<< 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
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)
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);