From 4180333ce281a87bf31350c8d59d8cd1d6b43219 Mon Sep 17 00:00:00 2001 From: Romain Date: Tue, 11 Jul 2017 11:15:09 -0700 Subject: [PATCH] cc: Add open_perf_event to the C/C++ API (#1232) --- src/cc/BPF.cc | 37 +++++++++++++++++++++++ src/cc/BPF.h | 8 +++++ src/cc/BPFTable.cc | 75 ++++++++++++++++++++++++++++++++++++++++++++++ src/cc/BPFTable.h | 16 ++++++++++ src/cc/libbpf.h | 2 ++ 5 files changed, 138 insertions(+) diff --git a/src/cc/BPF.cc b/src/cc/BPF.cc index 0aec4ebb..e1d6c6fa 100644 --- a/src/cc/BPF.cc +++ b/src/cc/BPF.cc @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -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, diff --git a/src/cc/BPF.h b/src/cc/BPF.h index 30c864ae..1c659b18 100644 --- a/src/cc/BPF.h +++ b/src/cc/BPF.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -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 uprobes_; std::map tracepoints_; std::map perf_buffers_; + std::map perf_event_arrays_; std::map, open_probe_t> perf_events_; }; diff --git a/src/cc/BPFTable.cc b/src/cc/BPFTable.cc index b9f6397c..559af4a6 100644 --- a/src/cc/BPFTable.cc +++ b/src/cc/BPFTable.cc @@ -15,9 +15,12 @@ */ #include +#include #include #include #include +#include +#include #include #include #include @@ -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 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 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 diff --git a/src/cc/BPFTable.h b/src/cc/BPFTable.h index 7e7763be..23336804 100644 --- a/src/cc/BPFTable.h +++ b/src/cc/BPFTable.h @@ -241,6 +241,22 @@ class BPFPerfBuffer : public BPFTableBase { std::unique_ptr ep_events_; }; +class BPFPerfEventArray : public BPFTableBase { + public: + BPFPerfEventArray(const TableDesc& desc) + : BPFTableBase(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 cpu_fds_; +}; + class BPFProgTable : public BPFTableBase { public: BPFProgTable(const TableDesc& desc) diff --git a/src/cc/libbpf.h b/src/cc/libbpf.h index d73d1940..48c76c2b 100644 --- a/src/cc/libbpf.h +++ b/src/cc/libbpf.h @@ -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); -- 2.34.1