cc: Add open_perf_event to the C/C++ API (#1232)
authorRomain <romain-intel@users.noreply.github.com>
Tue, 11 Jul 2017 18:15:09 +0000 (11:15 -0700)
committerSasha Goldshtein <goldshtn@gmail.com>
Tue, 11 Jul 2017 18:15:09 +0000 (21:15 +0300)
src/cc/BPF.cc
src/cc/BPF.h
src/cc/BPFTable.cc
src/cc/BPFTable.h
src/cc/libbpf.h

index 0aec4eb..e1d6c6f 100644 (file)
@@ -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,
index 30c864a..1c659b1 100644 (file)
@@ -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_;
 };
 
index b9f6397..559af4a 100644 (file)
  */
 
 #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
index 7e7763b..2333680 100644 (file)
@@ -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)
index d73d194..48c76c2 100644 (file)
@@ -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);