move api and create dependent option
authorBrenden Blanco <bblanco@gmail.com>
Thu, 29 Jun 2017 00:37:06 +0000 (17:37 -0700)
committerBrenden Blanco <bblanco@gmail.com>
Fri, 25 Aug 2017 23:41:36 +0000 (16:41 -0700)
Move the C++ api files under a new subdirectory.
Use CMAKE_DEPENDENT_OPTION to enforce sdt->cpp_api relationship.

Since linking .a into a .so can cause global symbols to be dropped, add
a helper file to force exported symbols to be retained (link_all.cc).
This problem doesn't exist for building the static cpp examples.

Signed-off-by: Brenden Blanco <bblanco@gmail.com>
14 files changed:
CMakeLists.txt
examples/cpp/CMakeLists.txt
src/cc/BPF.cc [deleted file]
src/cc/BPF.h [deleted file]
src/cc/BPFTable.cc [deleted file]
src/cc/BPFTable.h [deleted file]
src/cc/CMakeLists.txt
src/cc/api/BPF.cc [new file with mode: 0644]
src/cc/api/BPF.h [new file with mode: 0644]
src/cc/api/BPFTable.cc [new file with mode: 0644]
src/cc/api/BPFTable.h [new file with mode: 0644]
src/cc/api/CMakeLists.txt [new file with mode: 0644]
src/cc/link_all.cc [new file with mode: 0644]
tests/cc/CMakeLists.txt

index b23261cc9c3fcfab61d3c48692dc1b63014b9163..f2bdd554ffd0452ce51593f9f8fb9c2010de6c1d 100644 (file)
@@ -11,6 +11,7 @@ enable_testing()
 
 include(cmake/GetGitRevisionDescription.cmake)
 include(cmake/version.cmake)
+include(CMakeDependentOption)
 include(GNUInstallDirs)
 include(CheckCXXCompilerFlag)
 include(cmake/FindCompilerFlag.cmake)
index 49da82962347aa868aad63391840d65adfc0f90f..7d6ccee258e918808885c4e48c0f793f12f5e336 100644 (file)
@@ -2,6 +2,7 @@
 # Licensed under the Apache License, Version 2.0 (the "License")
 
 include_directories(${CMAKE_SOURCE_DIR}/src/cc)
+include_directories(${CMAKE_SOURCE_DIR}/src/cc/api)
 
 option(INSTALL_CPP_EXAMPLES "Install C++ examples. Those binaries are statically linked and can take plenty of disk space" OFF)
 
diff --git a/src/cc/BPF.cc b/src/cc/BPF.cc
deleted file mode 100644 (file)
index 0a6cb72..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Copyright (c) 2016 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <linux/bpf.h>
-#include <linux/perf_event.h>
-#include <unistd.h>
-#include <cstdio>
-#include <cstring>
-#include <exception>
-#include <iostream>
-#include <memory>
-#include <sstream>
-#include <utility>
-#include <vector>
-
-#include "bcc_exception.h"
-#include "bcc_syms.h"
-#include "bpf_module.h"
-#include "common.h"
-#include "libbpf.h"
-#include "perf_reader.h"
-#include "table_storage.h"
-#include "usdt.h"
-
-#include "BPF.h"
-
-namespace ebpf {
-
-std::string uint_to_hex(uint64_t value) {
-  std::stringstream ss;
-  ss << std::hex << value;
-  return ss.str();
-}
-
-std::string sanitize_str(std::string str, bool (*validator)(char),
-                         char replacement = '_') {
-  for (size_t i = 0; i < str.length(); i++)
-    if (!validator(str[i]))
-      str[i] = replacement;
-  return str;
-}
-
-StatusTuple BPF::init(const std::string& bpf_program,
-                      const std::vector<std::string>& cflags,
-                      const std::vector<USDT>& usdt) {
-  std::string all_bpf_program;
-
-  for (auto u : usdt) {
-    if (!u.initialized_)
-      TRY2(u.init());
-    all_bpf_program += u.program_text_;
-    usdt_.push_back(std::move(u));
-  }
-
-  auto flags_len = cflags.size();
-  const char* flags[flags_len];
-  for (size_t i = 0; i < flags_len; i++)
-    flags[i] = cflags[i].c_str();
-
-  all_bpf_program += bpf_program;
-  if (bpf_module_->load_string(all_bpf_program, flags, flags_len) != 0)
-    return StatusTuple(-1, "Unable to initialize BPF program");
-
-  return StatusTuple(0);
-};
-
-BPF::~BPF() {
-  auto res = detach_all();
-  if (res.code() != 0)
-    std::cerr << "Failed to detach all probes on destruction: " << std::endl
-              << res.msg() << std::endl;
-}
-
-StatusTuple BPF::detach_all() {
-  bool has_error = false;
-  std::string error_msg;
-
-  for (auto& it : kprobes_) {
-    auto res = detach_kprobe_event(it.first, it.second);
-    if (res.code() != 0) {
-      error_msg += "Failed to detach kprobe event " + it.first + ": ";
-      error_msg += res.msg() + "\n";
-      has_error = true;
-    }
-  }
-
-  for (auto& it : uprobes_) {
-    auto res = detach_uprobe_event(it.first, it.second);
-    if (res.code() != 0) {
-      error_msg += "Failed to detach uprobe event " + it.first + ": ";
-      error_msg += res.msg() + "\n";
-      has_error = true;
-    }
-  }
-
-  for (auto& it : tracepoints_) {
-    auto res = detach_tracepoint_event(it.first, it.second);
-    if (res.code() != 0) {
-      error_msg += "Failed to detach Tracepoint " + it.first + ": ";
-      error_msg += res.msg() + "\n";
-      has_error = true;
-    }
-  }
-
-  for (auto& it : perf_buffers_) {
-    auto res = it.second->close_all_cpu();
-    if (res.code() != 0) {
-      error_msg += "Failed to close perf buffer " + it.first + ": ";
-      error_msg += res.msg() + "\n";
-      has_error = true;
-    }
-    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) {
-      error_msg += res.msg() + "\n";
-      has_error = true;
-    }
-  }
-
-  for (auto& it : funcs_) {
-    int res = close(it.second);
-    if (res != 0) {
-      error_msg += "Failed to unload BPF program for " + it.first + ": ";
-      error_msg += std::string(std::strerror(errno)) + "\n";
-      has_error = true;
-    }
-  }
-
-  if (has_error)
-    return StatusTuple(-1, error_msg);
-  else
-    return StatusTuple(0);
-}
-
-StatusTuple BPF::attach_kprobe(const std::string& kernel_func,
-                               const std::string& probe_func,
-                               bpf_probe_attach_type attach_type,
-                               pid_t pid, int cpu, int group_fd,
-                               perf_reader_cb cb, void* cb_cookie) {
-  std::string probe_event = get_kprobe_event(kernel_func, attach_type);
-  if (kprobes_.find(probe_event) != kprobes_.end())
-    return StatusTuple(-1, "kprobe %s already attached", probe_event.c_str());
-
-  int probe_fd;
-  TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
-
-  void* res =
-      bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(), kernel_func.c_str(),
-                        pid, cpu, group_fd, cb, cb_cookie);
-
-  if (!res) {
-    TRY2(unload_func(probe_func));
-    return StatusTuple(-1, "Unable to attach %skprobe for %s using %s",
-                       attach_type_debug(attach_type).c_str(),
-                       kernel_func.c_str(), probe_func.c_str());
-  }
-
-  open_probe_t p = {};
-  p.reader_ptr = res;
-  p.func = probe_func;
-  kprobes_[probe_event] = std::move(p);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::attach_uprobe(const std::string& binary_path,
-                               const std::string& symbol,
-                               const std::string& probe_func,
-                               uint64_t symbol_addr,
-                               bpf_probe_attach_type attach_type,
-                               pid_t pid, int cpu, int group_fd,
-                               perf_reader_cb cb, void* cb_cookie) {
-  std::string module;
-  uint64_t offset;
-  TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
-
-  std::string probe_event = get_uprobe_event(module, offset, attach_type, pid);
-  if (uprobes_.find(probe_event) != uprobes_.end())
-    return StatusTuple(-1, "uprobe %s already attached", probe_event.c_str());
-
-  int probe_fd;
-  TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
-
-  void* res =
-      bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(), binary_path.c_str(),
-                        offset, pid, cpu, group_fd, cb, cb_cookie);
-
-  if (!res) {
-    TRY2(unload_func(probe_func));
-    return StatusTuple(
-        -1,
-        "Unable to attach %suprobe for binary %s symbol %s addr %lx using %s\n",
-        attach_type_debug(attach_type).c_str(), binary_path.c_str(),
-        symbol.c_str(), symbol_addr, probe_func.c_str());
-  }
-
-  open_probe_t p = {};
-  p.reader_ptr = res;
-  p.func = probe_func;
-  uprobes_[probe_event] = std::move(p);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid, int cpu,
-                             int group_fd) {
-  for (const auto& u : usdt_)
-    if (u == usdt) {
-      bool failed = false;
-      std::string err_msg;
-      int cnt = 0;
-      for (auto addr : u.addresses_) {
-        auto res =
-            attach_uprobe(u.binary_path_, std::string(), u.probe_func_, addr);
-        if (res.code() != 0) {
-          failed = true;
-          err_msg += "USDT " + u.print_name() + " at " + std::to_string(addr);
-          err_msg += ": " + res.msg() + "\n";
-          break;
-        }
-        cnt++;
-      }
-      if (failed) {
-        for (int i = 0; i < cnt; i++) {
-          auto res =
-              detach_uprobe(u.binary_path_, std::string(), u.addresses_[i]);
-          err_msg += "During clean up: " + res.msg() + "\n";
-        }
-        return StatusTuple(-1, err_msg);
-      } else
-        return StatusTuple(0);
-    }
-  return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str());
-}
-
-StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
-                                   const std::string& probe_func,
-                                   pid_t pid, int cpu, int group_fd,
-                                   perf_reader_cb cb, void* cb_cookie) {
-  if (tracepoints_.find(tracepoint) != tracepoints_.end())
-    return StatusTuple(-1, "Tracepoint %s already attached",
-                       tracepoint.c_str());
-
-  auto pos = tracepoint.find(":");
-  if ((pos == std::string::npos) || (pos != tracepoint.rfind(":")))
-    return StatusTuple(-1, "Unable to parse Tracepoint %s", tracepoint.c_str());
-  std::string tp_category = tracepoint.substr(0, pos);
-  std::string tp_name = tracepoint.substr(pos + 1);
-
-  int probe_fd;
-  TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd));
-
-  void* res =
-      bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str(), pid,
-                            cpu, group_fd, cb, cb_cookie);
-
-  if (!res) {
-    TRY2(unload_func(probe_func));
-    return StatusTuple(-1, "Unable to attach Tracepoint %s using %s",
-                       tracepoint.c_str(), probe_func.c_str());
-  }
-
-  open_probe_t p = {};
-  p.reader_ptr = res;
-  p.func = probe_func;
-  tracepoints_[tracepoint] = std::move(p);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config,
-                                   const std::string& probe_func,
-                                   uint64_t sample_period, uint64_t sample_freq,
-                                   pid_t pid, int cpu, int group_fd) {
-  auto ev_pair = std::make_pair(ev_type, ev_config);
-  if (perf_events_.find(ev_pair) != perf_events_.end())
-    return StatusTuple(-1, "Perf event type %d config %d already attached",
-                       ev_type, ev_config);
-
-  int probe_fd;
-  TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd));
-
-  auto fds = new std::map<int, int>();
-  std::vector<int> cpus;
-  if (cpu >= 0)
-    cpus.push_back(cpu);
-  else
-    cpus = get_online_cpus();
-  for (int i: cpus) {
-    int fd = bpf_attach_perf_event(probe_fd, ev_type, ev_config, sample_period,
-                                   sample_freq, pid, i, group_fd);
-    if (fd < 0) {
-      for (const auto& it : *fds)
-        close(it.second);
-      delete fds;
-      TRY2(unload_func(probe_func));
-      return StatusTuple(-1, "Failed to attach perf event type %d config %d",
-                         ev_type, ev_config);
-    }
-    fds->emplace(i, fd);
-  }
-
-  open_probe_t p = {};
-  p.func = probe_func;
-  p.per_cpu_fd = fds;
-  perf_events_[ev_pair] = std::move(p);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_kprobe(const std::string& kernel_func,
-                               bpf_probe_attach_type attach_type) {
-  std::string event = get_kprobe_event(kernel_func, attach_type);
-
-  auto it = kprobes_.find(event);
-  if (it == kprobes_.end())
-    return StatusTuple(-1, "No open %skprobe for %s",
-                       attach_type_debug(attach_type).c_str(),
-                       kernel_func.c_str());
-
-  TRY2(detach_kprobe_event(it->first, it->second));
-  kprobes_.erase(it);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_uprobe(const std::string& binary_path,
-                               const std::string& symbol, uint64_t symbol_addr,
-                               bpf_probe_attach_type attach_type,
-                               pid_t pid) {
-  std::string module;
-  uint64_t offset;
-  TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
-
-  std::string event = get_uprobe_event(module, offset, attach_type, pid);
-  auto it = uprobes_.find(event);
-  if (it == uprobes_.end())
-    return StatusTuple(-1, "No open %suprobe for binary %s symbol %s addr %lx",
-                       attach_type_debug(attach_type).c_str(),
-                       binary_path.c_str(), symbol.c_str(), symbol_addr);
-
-  TRY2(detach_uprobe_event(it->first, it->second));
-  uprobes_.erase(it);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_usdt(const USDT& usdt) {
-  for (const auto& u : usdt_)
-    if (u == usdt) {
-      bool failed = false;
-      std::string err_msg;
-      for (auto addr : u.addresses_) {
-        auto res = detach_uprobe(u.binary_path_, std::string(), addr);
-        if (res.code() != 0) {
-          failed = true;
-          err_msg += "USDT " + u.print_name() + " at " + std::to_string(addr);
-          err_msg += ": " + res.msg() + "\n";
-        }
-      }
-      if (failed)
-        return StatusTuple(-1, err_msg);
-      else
-        return StatusTuple(0);
-    }
-  return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str());
-}
-
-StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) {
-  auto it = tracepoints_.find(tracepoint);
-  if (it == tracepoints_.end())
-    return StatusTuple(-1, "No open Tracepoint %s", tracepoint.c_str());
-
-  TRY2(detach_tracepoint_event(it->first, it->second));
-  tracepoints_.erase(it);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) {
-  auto it = perf_events_.find(std::make_pair(ev_type, ev_config));
-  if (it == perf_events_.end())
-    return StatusTuple(-1, "Perf Event type %d config %d not attached",
-                       ev_type, ev_config);
-  TRY2(detach_perf_event_all_cpu(it->second));
-  perf_events_.erase(it);
-  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,
-                                  void* cb_cookie,
-                                  int page_cnt) {
-  if (perf_buffers_.find(name) == perf_buffers_.end()) {
-    TableStorage::iterator it;
-    if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-      return StatusTuple(-1,
-                         "open_perf_buffer: unable to find table_storage %s",
-                         name.c_str());
-    perf_buffers_[name] = new BPFPerfBuffer(it->second);
-  }
-  if ((page_cnt & (page_cnt - 1)) != 0)
-    return StatusTuple(-1, "open_perf_buffer page_cnt must be a power of two");
-  auto table = perf_buffers_[name];
-  TRY2(table->open_all_cpu(cb, lost_cb, cb_cookie, page_cnt));
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::close_perf_buffer(const std::string& name) {
-  auto it = perf_buffers_.find(name);
-  if (it == perf_buffers_.end())
-    return StatusTuple(-1, "Perf buffer for %s not open", name.c_str());
-  TRY2(it->second->close_all_cpu());
-  return StatusTuple(0);
-}
-
-void BPF::poll_perf_buffer(const std::string& name, int timeout) {
-  auto it = perf_buffers_.find(name);
-  if (it == perf_buffers_.end())
-    return;
-  it->second->poll(timeout);
-}
-
-StatusTuple BPF::load_func(const std::string& func_name,
-                           bpf_prog_type type, int& fd) {
-  if (funcs_.find(func_name) != funcs_.end()) {
-    fd = funcs_[func_name];
-    return StatusTuple(0);
-  }
-
-  uint8_t* func_start = bpf_module_->function_start(func_name);
-  if (!func_start)
-    return StatusTuple(-1, "Can't find start of function %s",
-                       func_name.c_str());
-  size_t func_size = bpf_module_->function_size(func_name);
-
-  fd = bpf_prog_load(type, reinterpret_cast<struct bpf_insn*>(func_start),
-                     func_size, bpf_module_->license(),
-                     bpf_module_->kern_version(), nullptr,
-                     0  // BPFModule will handle error printing
-                     );
-
-  if (fd < 0)
-    return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
-  funcs_[func_name] = fd;
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::unload_func(const std::string& func_name) {
-  auto it = funcs_.find(func_name);
-  if (it == funcs_.end())
-    return StatusTuple(0);
-
-  int res = close(it->second);
-  if (res != 0)
-    return StatusTuple(-1, "Can't close FD for %s: %d", it->first.c_str(), res);
-
-  funcs_.erase(it);
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::check_binary_symbol(const std::string& binary_path,
-                                     const std::string& symbol,
-                                     uint64_t symbol_addr,
-                                     std::string &module_res,
-                                     uint64_t &offset_res) {
-  bcc_symbol output;
-  int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(),
-                                symbol_addr, -1, nullptr, &output);
-  if (res < 0)
-    return StatusTuple(
-        -1, "Unable to find offset for binary %s symbol %s address %lx",
-        binary_path.c_str(), symbol.c_str(), symbol_addr);
-
-  if (output.module) {
-    module_res = output.module;
-    ::free(const_cast<char*>(output.module));
-  } else {
-    module_res = "";
-  }
-  offset_res = output.offset;
-  return StatusTuple(0);
-}
-
-std::string BPF::get_kprobe_event(const std::string& kernel_func,
-                                  bpf_probe_attach_type type) {
-  std::string res = attach_type_prefix(type) + "_";
-  res += sanitize_str(kernel_func, &BPF::kprobe_event_validator);
-  return res;
-}
-
-BPFProgTable BPF::get_prog_table(const std::string& name) {
-  TableStorage::iterator it;
-  if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-    return BPFProgTable(it->second);
-  return BPFProgTable({});
-}
-
-BPFStackTable BPF::get_stack_table(const std::string& name,
-                                   bool use_debug_file,
-                                   bool check_debug_file_crc) {
-  TableStorage::iterator it;
-  if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-    return BPFStackTable(it->second, use_debug_file, check_debug_file_crc);
-  return BPFStackTable({}, use_debug_file, check_debug_file_crc);
-}
-
-std::string BPF::get_uprobe_event(const std::string& binary_path,
-                                  uint64_t offset, bpf_probe_attach_type type,
-                                  pid_t pid) {
-  std::string res = attach_type_prefix(type) + "_";
-  res += sanitize_str(binary_path, &BPF::uprobe_path_validator);
-  res += "_0x" + uint_to_hex(offset);
-  if (pid != -1)
-    res += "_" + std::to_string(pid);
-  return res;
-}
-
-StatusTuple BPF::detach_kprobe_event(const std::string& event,
-                                     open_probe_t& attr) {
-  if (attr.reader_ptr) {
-    perf_reader_free(attr.reader_ptr);
-    attr.reader_ptr = nullptr;
-  }
-  TRY2(unload_func(attr.func));
-  if (bpf_detach_kprobe(event.c_str()) < 0)
-    return StatusTuple(-1, "Unable to detach kprobe %s", event.c_str());
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_uprobe_event(const std::string& event,
-                                     open_probe_t& attr) {
-  if (attr.reader_ptr) {
-    perf_reader_free(attr.reader_ptr);
-    attr.reader_ptr = nullptr;
-  }
-  TRY2(unload_func(attr.func));
-  if (bpf_detach_uprobe(event.c_str()) < 0)
-    return StatusTuple(-1, "Unable to detach uprobe %s", event.c_str());
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_tracepoint_event(const std::string& tracepoint,
-                                         open_probe_t& attr) {
-  if (attr.reader_ptr) {
-    perf_reader_free(attr.reader_ptr);
-    attr.reader_ptr = nullptr;
-  }
-  TRY2(unload_func(attr.func));
-
-  // TODO: bpf_detach_tracepoint currently does nothing.
-  return StatusTuple(0);
-}
-
-StatusTuple BPF::detach_perf_event_all_cpu(open_probe_t& attr) {
-  bool has_error = false;
-  std::string err_msg;
-  for (const auto& it : *attr.per_cpu_fd) {
-    int res = bpf_close_perf_event_fd(it.second);
-    if (res != 0) {
-      has_error = true;
-      err_msg += "Failed to close perf event FD " + std::to_string(it.second) +
-                 " For CPU " + std::to_string(it.first) + ": ";
-      err_msg += std::string(std::strerror(errno)) + "\n";
-    }
-  }
-  delete attr.per_cpu_fd;
-  TRY2(unload_func(attr.func));
-
-  if (has_error)
-    return StatusTuple(-1, err_msg);
-  return StatusTuple(0);
-}
-
-StatusTuple USDT::init() {
-  ::USDT::Context ctx(binary_path_);
-  if (!ctx.loaded())
-    return StatusTuple(-1, "Unable to load USDT " + print_name());
-  auto probe = ctx.get(name_);
-  if (probe == nullptr)
-    return StatusTuple(-1, "Unable to find USDT " + print_name());
-
-  if (!probe->enable(probe_func_))
-    return StatusTuple(-1, "Failed to enable USDT " + print_name());
-  std::ostringstream stream;
-  if (!probe->usdt_getarg(stream))
-    return StatusTuple(
-        -1, "Unable to generate program text for USDT " + print_name());
-  program_text_ = ::USDT::USDT_PROGRAM_HEADER + stream.str();
-
-  addresses_.reserve(probe->num_locations());
-  for (size_t i = 0; i < probe->num_locations(); i++)
-    addresses_.emplace_back(probe->address(i));
-
-  initialized_ = true;
-  return StatusTuple(0);
-}
-
-}  // namespace ebpf
diff --git a/src/cc/BPF.h b/src/cc/BPF.h
deleted file mode 100644 (file)
index 4a4c46c..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (c) 2016 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cctype>
-#include <cstdint>
-#include <memory>
-#include <string>
-
-#include "BPFTable.h"
-#include "bcc_exception.h"
-#include "bcc_syms.h"
-#include "bpf_module.h"
-#include "compat/linux/bpf.h"
-#include "libbpf.h"
-#include "table_storage.h"
-
-static const int DEFAULT_PERF_BUFFER_PAGE_CNT = 8;
-
-namespace ebpf {
-
-struct open_probe_t {
-  void* reader_ptr;
-  std::string func;
-  std::map<int, int>* per_cpu_fd;
-};
-
-class USDT;
-
-class BPF {
-public:
-  static const int BPF_MAX_STACK_DEPTH = 127;
-
-  explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr)
-      : bpf_module_(new BPFModule(flag, ts)) {}
-  StatusTuple init(const std::string& bpf_program,
-                   const std::vector<std::string>& cflags = {},
-                   const std::vector<USDT>& usdt = {});
-
-  ~BPF();
-  StatusTuple detach_all();
-
-  StatusTuple attach_kprobe(
-      const std::string& kernel_func, const std::string& probe_func,
-      bpf_probe_attach_type = BPF_PROBE_ENTRY,
-      pid_t pid = -1, int cpu = 0, int group_fd = -1,
-      perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
-  StatusTuple detach_kprobe(
-      const std::string& kernel_func,
-      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY);
-
-  StatusTuple attach_uprobe(
-      const std::string& binary_path, const std::string& symbol,
-      const std::string& probe_func, uint64_t symbol_addr = 0,
-      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
-      pid_t pid = -1, int cpu = 0, int group_fd = -1,
-      perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
-  StatusTuple detach_uprobe(
-      const std::string& binary_path, const std::string& symbol,
-      uint64_t symbol_addr = 0,
-      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
-      pid_t pid = -1);
-  StatusTuple attach_usdt(const USDT& usdt, pid_t pid = -1, int cpu = 0,
-                          int group_fd = -1);
-  StatusTuple detach_usdt(const USDT& usdt);
-
-  StatusTuple attach_tracepoint(const std::string& tracepoint,
-                                const std::string& probe_func,
-                                pid_t pid = -1, int cpu = 0, int group_fd = -1,
-                                perf_reader_cb cb = nullptr,
-                                void* cb_cookie = nullptr);
-  StatusTuple detach_tracepoint(const std::string& tracepoint);
-
-  StatusTuple attach_perf_event(uint32_t ev_type, uint32_t ev_config,
-                                const std::string& probe_func,
-                                uint64_t sample_period, uint64_t sample_freq,
-                                pid_t pid = -1, int cpu = -1,
-                                int group_fd = -1);
-  StatusTuple detach_perf_event(uint32_t ev_type, uint32_t ev_config);
-
-  BPFTable get_table(const std::string& name) {
-    TableStorage::iterator it;
-    if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-      return BPFTable(it->second);
-    return BPFTable({});
-  }
-
-  template <class ValueType>
-  BPFArrayTable<ValueType> get_array_table(const std::string& name) {
-    TableStorage::iterator it;
-    if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-      return BPFArrayTable<ValueType>(it->second);
-    return BPFArrayTable<ValueType>({});
-  }
-
-  template <class KeyType, class ValueType>
-  BPFHashTable<KeyType, ValueType> get_hash_table(const std::string& name) {
-    TableStorage::iterator it;
-    if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
-      return BPFHashTable<KeyType, ValueType>(it->second);
-    return BPFHashTable<KeyType, ValueType>({});
-  }
-
-  BPFProgTable get_prog_table(const std::string& name);
-
-  BPFStackTable get_stack_table(const std::string& name,
-                                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,
-                               void* cb_cookie = nullptr,
-                               int page_cnt = DEFAULT_PERF_BUFFER_PAGE_CNT);
-  StatusTuple close_perf_buffer(const std::string& name);
-  void poll_perf_buffer(const std::string& name, int timeout = -1);
-
-  StatusTuple load_func(const std::string& func_name, enum bpf_prog_type type,
-                        int& fd);
-  StatusTuple unload_func(const std::string& func_name);
-
-private:
-  std::string get_kprobe_event(const std::string& kernel_func,
-                               bpf_probe_attach_type type);
-  std::string get_uprobe_event(const std::string& binary_path, uint64_t offset,
-                               bpf_probe_attach_type type, pid_t pid);
-
-  StatusTuple detach_kprobe_event(const std::string& event, open_probe_t& attr);
-  StatusTuple detach_uprobe_event(const std::string& event, open_probe_t& attr);
-  StatusTuple detach_tracepoint_event(const std::string& tracepoint,
-                                      open_probe_t& attr);
-  StatusTuple detach_perf_event_all_cpu(open_probe_t& attr);
-
-  std::string attach_type_debug(bpf_probe_attach_type type) {
-    switch (type) {
-    case BPF_PROBE_ENTRY:
-      return "";
-    case BPF_PROBE_RETURN:
-      return "return ";
-    }
-    return "ERROR";
-  }
-
-  std::string attach_type_prefix(bpf_probe_attach_type type) {
-    switch (type) {
-    case BPF_PROBE_ENTRY:
-      return "p";
-    case BPF_PROBE_RETURN:
-      return "r";
-    }
-    return "ERROR";
-  }
-
-  static bool kprobe_event_validator(char c) {
-    return (c != '+') && (c != '.');
-  }
-
-  static bool uprobe_path_validator(char c) {
-    return std::isalpha(c) || std::isdigit(c) || (c == '_');
-  }
-
-  StatusTuple check_binary_symbol(const std::string& binary_path,
-                                  const std::string& symbol,
-                                  uint64_t symbol_addr,
-                                  std::string &module_res,
-                                  uint64_t &offset_res);
-
-  std::unique_ptr<BPFModule> bpf_module_;
-
-  std::map<std::string, int> funcs_;
-
-  std::vector<USDT> usdt_;
-
-  std::map<std::string, open_probe_t> kprobes_;
-  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_;
-};
-
-class USDT {
-public:
-  USDT(const std::string& binary_path, const std::string& provider,
-       const std::string& name, const std::string& probe_func)
-      : initialized_(false),
-        binary_path_(binary_path),
-        provider_(provider),
-        name_(name),
-        probe_func_(probe_func) {}
-
-  bool operator==(const USDT& other) const {
-    return (provider_ == other.provider_) && (name_ == other.name_) &&
-           (binary_path_ == other.binary_path_) &&
-           (probe_func_ == other.probe_func_);
-  }
-
-  std::string print_name() const {
-    return provider_ + ":" + name_ + " from " + binary_path_;
-  }
-
-private:
-  StatusTuple init();
-  bool initialized_;
-
-  std::string binary_path_;
-  std::string provider_;
-  std::string name_;
-  std::string probe_func_;
-
-  std::vector<uintptr_t> addresses_;
-
-  std::string program_text_;
-
-  friend class BPF;
-};
-
-}  // namespace ebpf
diff --git a/src/cc/BPFTable.cc b/src/cc/BPFTable.cc
deleted file mode 100644 (file)
index 559af4a..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (c) 2016 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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>
-
-#include "BPFTable.h"
-
-#include "bcc_exception.h"
-#include "bcc_syms.h"
-#include "common.h"
-#include "libbpf.h"
-#include "perf_reader.h"
-
-namespace ebpf {
-
-BPFTable::BPFTable(const TableDesc& desc) : BPFTableBase<void, void>(desc) {}
-
-StatusTuple BPFTable::get_value(const std::string& key_str,
-                                std::string& value_str) {
-  char key[desc.key_size];
-  char value[desc.leaf_size];
-
-  StatusTuple r(0);
-
-  r = string_to_key(key_str, key);
-  if (r.code() != 0)
-    return r;
-
-  if (!lookup(key, value))
-    return StatusTuple(-1, "error getting value");
-
-  return leaf_to_string(value, value_str);
-}
-
-StatusTuple BPFTable::update_value(const std::string& key_str,
-                                   const std::string& value_str) {
-  char key[desc.key_size];
-  char value[desc.leaf_size];
-
-  StatusTuple r(0);
-
-  r = string_to_key(key_str, key);
-  if (r.code() != 0)
-    return r;
-
-  r = string_to_leaf(value_str, value);
-  if (r.code() != 0)
-    return r;
-
-  if (!update(key, value))
-    return StatusTuple(-1, "error updating element");
-
-  return StatusTuple(0);
-}
-
-StatusTuple BPFTable::remove_value(const std::string& key_str) {
-  char key[desc.key_size];
-
-  StatusTuple r(0);
-
-  r = string_to_key(key_str, key);
-  if (r.code() != 0)
-    return r;
-
-  if (!remove(key))
-    return StatusTuple(-1, "error removing element");
-
-  return StatusTuple(0);
-}
-
-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)
-  };
-}
-
-BPFStackTable::~BPFStackTable() {
-  for (auto it : pid_sym_)
-    bcc_free_symcache(it.second, it.first);
-}
-
-std::vector<uintptr_t> BPFStackTable::get_stack_addr(int stack_id) {
-  std::vector<uintptr_t> res;
-  stacktrace_t stack;
-  if (!lookup(&stack_id, &stack))
-    return res;
-  for (int i = 0; (i < BPF_MAX_STACK_DEPTH) && (stack.ip[i] != 0); i++)
-    res.push_back(stack.ip[i]);
-  return res;
-}
-
-std::vector<std::string> BPFStackTable::get_stack_symbol(int stack_id,
-                                                         int pid) {
-  auto addresses = get_stack_addr(stack_id);
-  std::vector<std::string> res;
-  res.reserve(addresses.size());
-
-  if (pid < 0)
-    pid = -1;
-  if (pid_sym_.find(pid) == pid_sym_.end())
-    pid_sym_[pid] = bcc_symcache_new(pid, &symbol_option_);
-  void* cache = pid_sym_[pid];
-
-  bcc_symbol symbol;
-  for (auto addr : addresses)
-    if (bcc_symcache_resolve(cache, addr, &symbol) != 0)
-      res.emplace_back("[UNKNOWN]");
-    else {
-      res.push_back(symbol.demangle_name);
-      bcc_symbol_free_demangle_name(&symbol);
-    }
-
-  return res;
-}
-
-StatusTuple BPFPerfBuffer::open_on_cpu(perf_reader_raw_cb cb,
-                                       perf_reader_lost_cb lost_cb,
-                                       int cpu, void* cb_cookie, int page_cnt) {
-  if (cpu_readers_.find(cpu) != cpu_readers_.end())
-    return StatusTuple(-1, "Perf buffer already open on CPU %d", cpu);
-
-  auto reader = static_cast<perf_reader*>(
-      bpf_open_perf_buffer(cb, lost_cb, cb_cookie, -1, cpu, page_cnt));
-  if (reader == nullptr)
-    return StatusTuple(-1, "Unable to construct perf reader");
-
-  int reader_fd = perf_reader_fd(reader);
-  if (!update(&cpu, &reader_fd)) {
-    perf_reader_free(static_cast<void*>(reader));
-    return StatusTuple(-1, "Unable to open perf buffer on CPU %d: %s", cpu,
-                       std::strerror(errno));
-  }
-
-  struct epoll_event event = {};
-  event.events = EPOLLIN;
-  event.data.ptr = static_cast<void*>(reader);
-  if (epoll_ctl(epfd_, EPOLL_CTL_ADD, reader_fd, &event) != 0) {
-    perf_reader_free(static_cast<void*>(reader));
-    return StatusTuple(-1, "Unable to add perf_reader FD to epoll: %s",
-                       std::strerror(errno));
-  }
-
-  cpu_readers_[cpu] = reader;
-  return StatusTuple(0);
-}
-
-StatusTuple BPFPerfBuffer::open_all_cpu(perf_reader_raw_cb cb,
-                                        perf_reader_lost_cb lost_cb,
-                                        void* cb_cookie, int page_cnt) {
-  if (cpu_readers_.size() != 0 || epfd_ != -1)
-    return StatusTuple(-1, "Previously opened perf buffer not cleaned");
-
-  std::vector<int> cpus = get_online_cpus();
-  ep_events_.reset(new epoll_event[cpus.size()]);
-  epfd_ = epoll_create1(EPOLL_CLOEXEC);
-
-  for (int i : cpus) {
-    auto res = open_on_cpu(cb, lost_cb, i, cb_cookie, page_cnt);
-    if (res.code() != 0) {
-      TRY2(close_all_cpu());
-      return res;
-    }
-  }
-  return StatusTuple(0);
-}
-
-StatusTuple BPFPerfBuffer::close_on_cpu(int cpu) {
-  auto it = cpu_readers_.find(cpu);
-  if (it == cpu_readers_.end())
-    return StatusTuple(0);
-  perf_reader_free(static_cast<void*>(it->second));
-  if (!remove(const_cast<int*>(&(it->first))))
-    return StatusTuple(-1, "Unable to close perf buffer on CPU %d", it->first);
-  cpu_readers_.erase(it);
-  return StatusTuple(0);
-}
-
-StatusTuple BPFPerfBuffer::close_all_cpu() {
-  std::string errors;
-  bool has_error = false;
-
-  if (epfd_ >= 0) {
-    int close_res = close(epfd_);
-    epfd_ = -1;
-    ep_events_.reset();
-    if (close_res != 0) {
-      has_error = true;
-      errors += std::string(std::strerror(errno)) + "\n";
-    }
-  }
-
-  std::vector<int> opened_cpus;
-  for (auto it : cpu_readers_)
-    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 buffer: ";
-      errors += res.msg() + "\n";
-      has_error = true;
-    }
-  }
-
-  if (has_error)
-    return StatusTuple(-1, errors);
-  return StatusTuple(0);
-}
-
-void BPFPerfBuffer::poll(int timeout) {
-  if (epfd_ < 0)
-    return;
-  int cnt = epoll_wait(epfd_, ep_events_.get(), cpu_readers_.size(), timeout);
-  if (cnt <= 0)
-    return;
-  for (int i = 0; i < cnt; i++)
-    perf_reader_event_read(static_cast<perf_reader*>(ep_events_[i].data.ptr));
-}
-
-BPFPerfBuffer::~BPFPerfBuffer() {
-  auto res = close_all_cpu();
-  if (res.code() != 0)
-    std::cerr << "Failed to close all perf buffer on destruction: " << res.msg()
-              << 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
diff --git a/src/cc/BPFTable.h b/src/cc/BPFTable.h
deleted file mode 100644 (file)
index 2333680..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (c) 2016 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstring>
-#include <sys/epoll.h>
-#include <exception>
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "bcc_exception.h"
-#include "bcc_syms.h"
-#include "bpf_module.h"
-#include "libbpf.h"
-#include "perf_reader.h"
-#include "table_desc.h"
-
-namespace ebpf {
-
-template <class KeyType, class ValueType>
-class BPFTableBase {
- public:
-  size_t capacity() { return desc.max_entries; }
-
-  StatusTuple string_to_key(const std::string& key_str, KeyType* key) {
-    return desc.key_sscanf(key_str.c_str(), key);
-  }
-
-  StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) {
-    return desc.leaf_sscanf(value_str.c_str(), value);
-  }
-
-  StatusTuple key_to_string(const KeyType* key, std::string& key_str) {
-    char buf[8 * desc.key_size];
-    StatusTuple rc = desc.key_snprintf(buf, sizeof(buf), key);
-    if (!rc.code())
-      key_str.assign(buf);
-    return rc;
-  }
-
-  StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) {
-    char buf[8 * desc.leaf_size];
-    StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value);
-    if (!rc.code())
-      value_str.assign(buf);
-    return rc;
-  }
-
- protected:
-  explicit BPFTableBase(const TableDesc& desc) : desc(desc) {}
-
-  bool lookup(KeyType* key, ValueType* value) {
-    return bpf_lookup_elem(desc.fd, static_cast<void*>(key),
-                           static_cast<void*>(value)) >= 0;
-  }
-
-  bool first(KeyType* key) {
-    return bpf_get_first_key(desc.fd, static_cast<void*>(key),
-                             desc.key_size) >= 0;
-  }
-
-  bool next(KeyType* key, KeyType* next_key) {
-    return bpf_get_next_key(desc.fd, static_cast<void*>(key),
-                            static_cast<void*>(next_key)) >= 0;
-  }
-
-  bool update(KeyType* key, ValueType* value) {
-    return bpf_update_elem(desc.fd, static_cast<void*>(key),
-                           static_cast<void*>(value), 0) >= 0;
-  }
-
-  bool remove(KeyType* key) {
-    return bpf_delete_elem(desc.fd, static_cast<void*>(key)) >= 0;
-  }
-
-  const TableDesc& desc;
-};
-
-class BPFTable : public BPFTableBase<void, void> {
- public:
-  BPFTable(const TableDesc& desc);
-
-  StatusTuple get_value(const std::string& key_str, std::string& value);
-  StatusTuple update_value(const std::string& key_str,
-                           const std::string& value_str);
-  StatusTuple remove_value(const std::string& key_str);
-};
-
-template <class ValueType>
-class BPFArrayTable : public BPFTableBase<int, ValueType> {
-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");
-  }
-
-  StatusTuple get_value(const int& index, ValueType& value) {
-    if (!this->lookup(const_cast<int*>(&index), &value))
-      return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
-    return StatusTuple(0);
-  }
-
-  StatusTuple update_value(const int& index, const ValueType& value) {
-    if (!this->update(const_cast<int*>(&index), const_cast<ValueType*>(&value)))
-      return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
-    return StatusTuple(0);
-  }
-
-  ValueType operator[](const int& key) {
-    ValueType value;
-    get_value(key, value);
-    return value;
-  }
-
-  std::vector<ValueType> get_table_offline() {
-    std::vector<ValueType> res(this->capacity());
-
-    for (int i = 0; i < (int) this->capacity(); i++) {
-      get_value(i, res[i]);
-    }
-
-    return res;
-  }
-};
-
-template <class KeyType, class ValueType>
-class BPFHashTable : public BPFTableBase<KeyType, ValueType> {
- public:
-  explicit BPFHashTable(const TableDesc& desc)
-      : BPFTableBase<KeyType, ValueType>(desc) {
-    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_LRU_PERCPU_HASH)
-      throw std::invalid_argument("Table '" + desc.name + "' is not a hash table");
-  }
-
-  StatusTuple get_value(const KeyType& key, ValueType& value) {
-    if (!this->lookup(const_cast<KeyType*>(&key), &value))
-      return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
-    return StatusTuple(0);
-  }
-
-  StatusTuple update_value(const KeyType& key, const ValueType& value) {
-    if (!this->update(const_cast<KeyType*>(&key), const_cast<ValueType*>(&value)))
-      return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
-    return StatusTuple(0);
-  }
-
-  StatusTuple remove_value(const KeyType& key) {
-    if (!this->remove(const_cast<KeyType*>(&key)))
-      return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
-    return StatusTuple(0);
-  }
-
-  ValueType operator[](const KeyType& key) {
-    ValueType value;
-    get_value(key, value);
-    return value;
-  }
-
-  std::vector<std::pair<KeyType, ValueType>> get_table_offline() {
-    std::vector<std::pair<KeyType, ValueType>> res;
-    KeyType cur;
-    ValueType value;
-
-    if (!this->first(&cur))
-      return res;
-
-    while (true) {
-      if (!this->lookup(&cur, &value))
-        break;
-      res.emplace_back(cur, value);
-      if (!this->next(&cur, &cur))
-        break;
-    }
-
-    return res;
-  }
-};
-
-// From src/cc/export/helpers.h
-static const int BPF_MAX_STACK_DEPTH = 127;
-struct stacktrace_t {
-  uintptr_t ip[BPF_MAX_STACK_DEPTH];
-};
-
-class BPFStackTable : public BPFTableBase<int, stacktrace_t> {
- public:
-  BPFStackTable(const TableDesc& desc,
-                bool use_debug_file,
-                bool check_debug_file_crc);
-  ~BPFStackTable();
-
-  std::vector<uintptr_t> get_stack_addr(int stack_id);
-  std::vector<std::string> get_stack_symbol(int stack_id, int pid);
-
- private:
-  bcc_symbol_option symbol_option_;
-  std::map<int, void*> pid_sym_;
-};
-
-class BPFPerfBuffer : public BPFTableBase<int, int> {
- public:
-  BPFPerfBuffer(const TableDesc& desc)
-      : BPFTableBase<int, int>(desc), epfd_(-1) {}
-  ~BPFPerfBuffer();
-
-  StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
-                           void* cb_cookie, int page_cnt);
-  StatusTuple close_all_cpu();
-  void poll(int timeout);
-
- private:
-  StatusTuple open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
-                          int cpu, void* cb_cookie, int page_cnt);
-  StatusTuple close_on_cpu(int cpu);
-
-  std::map<int, perf_reader*> cpu_readers_;
-
-  int epfd_;
-  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)
-      : BPFTableBase<int, int>(desc) {
-    if (desc.type != BPF_MAP_TYPE_PROG_ARRAY)
-      throw std::invalid_argument("Table '" + desc.name + "' is not a prog table");
-  }
-
-  // updates an element
-  StatusTuple update_value(const int& index, const int& value) {
-    if (!this->update(const_cast<int*>(&index), const_cast<int*>(&value)))
-      return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
-    return StatusTuple(0);
-  }
-};
-
-}  // namespace ebpf
index 799f5481d3f6ff29bf1b1aff2621abae6e731cb5..8d4b7dc2d14aeff0c44cee1e60b1970c8abe1b8c 100644 (file)
@@ -28,18 +28,17 @@ set(bcc_common_sources bpf_common.cc bpf_module.cc exported_files.cc)
 set(bcc_table_sources table_storage.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc)
 set(bcc_util_sources ns_guard.cc common.cc)
 set(bcc_sym_sources bcc_syms.cc bcc_elf.c bcc_perf_map.c bcc_proc.c)
-set(bcc_api_sources BPF.cc BPFTable.cc)
 
 add_library(bcc-shared SHARED
+  link_all.cc
   ${bcc_common_sources} ${bcc_table_sources} ${bcc_sym_sources}
-  ${bcc_util_sources} ${bcc_api_sources})
+  ${bcc_util_sources})
 set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0)
 set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc)
 
 add_library(bcc-loader-static STATIC ${bcc_sym_sources} ${bcc_util_sources})
 add_library(bcc-static STATIC
-  ${bcc_common_sources} ${bcc_table_sources}
-  ${bcc_util_sources} ${bcc_api_sources})
+  ${bcc_common_sources} ${bcc_table_sources} ${bcc_util_sources})
 set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc)
 
 include(clang_libs)
@@ -50,6 +49,13 @@ set(bcc_common_libs b_frontend clang_frontend bpf-static
   ${clang_libs} ${llvm_libs} ${LIBELF_LIBRARIES})
 
 option(ENABLE_USDT "Enable User-level Statically Defined Tracing" ON)
+CMAKE_DEPENDENT_OPTION(ENABLE_CPP_API "Enable C++ API" ON "ENABLE_USDT" OFF)
+
+if(ENABLE_CPP_API)
+  add_subdirectory(api)
+  list(APPEND bcc_common_libs api-static)
+endif()
+
 if(ENABLE_USDT)
   add_subdirectory(usdt)
   list(APPEND bcc_common_libs usdt-static)
@@ -62,7 +68,7 @@ target_link_libraries(bcc-static ${bcc_common_libs} bcc-loader-static)
 install(TARGETS bcc-shared LIBRARY COMPONENT libbcc
   DESTINATION ${CMAKE_INSTALL_LIBDIR})
 install(FILES bpf_common.h bpf_module.h bcc_syms.h bcc_exception.h file_desc.h
-              libbpf.h perf_reader.h BPF.h BPFTable.h
+              libbpf.h perf_reader.h
               table_desc.h table_storage.h COMPONENT libbcc
   DESTINATION include/bcc)
 install(DIRECTORY compat/linux/ COMPONENT libbcc
diff --git a/src/cc/api/BPF.cc b/src/cc/api/BPF.cc
new file mode 100644 (file)
index 0000000..0a6cb72
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/bpf.h>
+#include <linux/perf_event.h>
+#include <unistd.h>
+#include <cstdio>
+#include <cstring>
+#include <exception>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include "bcc_exception.h"
+#include "bcc_syms.h"
+#include "bpf_module.h"
+#include "common.h"
+#include "libbpf.h"
+#include "perf_reader.h"
+#include "table_storage.h"
+#include "usdt.h"
+
+#include "BPF.h"
+
+namespace ebpf {
+
+std::string uint_to_hex(uint64_t value) {
+  std::stringstream ss;
+  ss << std::hex << value;
+  return ss.str();
+}
+
+std::string sanitize_str(std::string str, bool (*validator)(char),
+                         char replacement = '_') {
+  for (size_t i = 0; i < str.length(); i++)
+    if (!validator(str[i]))
+      str[i] = replacement;
+  return str;
+}
+
+StatusTuple BPF::init(const std::string& bpf_program,
+                      const std::vector<std::string>& cflags,
+                      const std::vector<USDT>& usdt) {
+  std::string all_bpf_program;
+
+  for (auto u : usdt) {
+    if (!u.initialized_)
+      TRY2(u.init());
+    all_bpf_program += u.program_text_;
+    usdt_.push_back(std::move(u));
+  }
+
+  auto flags_len = cflags.size();
+  const char* flags[flags_len];
+  for (size_t i = 0; i < flags_len; i++)
+    flags[i] = cflags[i].c_str();
+
+  all_bpf_program += bpf_program;
+  if (bpf_module_->load_string(all_bpf_program, flags, flags_len) != 0)
+    return StatusTuple(-1, "Unable to initialize BPF program");
+
+  return StatusTuple(0);
+};
+
+BPF::~BPF() {
+  auto res = detach_all();
+  if (res.code() != 0)
+    std::cerr << "Failed to detach all probes on destruction: " << std::endl
+              << res.msg() << std::endl;
+}
+
+StatusTuple BPF::detach_all() {
+  bool has_error = false;
+  std::string error_msg;
+
+  for (auto& it : kprobes_) {
+    auto res = detach_kprobe_event(it.first, it.second);
+    if (res.code() != 0) {
+      error_msg += "Failed to detach kprobe event " + it.first + ": ";
+      error_msg += res.msg() + "\n";
+      has_error = true;
+    }
+  }
+
+  for (auto& it : uprobes_) {
+    auto res = detach_uprobe_event(it.first, it.second);
+    if (res.code() != 0) {
+      error_msg += "Failed to detach uprobe event " + it.first + ": ";
+      error_msg += res.msg() + "\n";
+      has_error = true;
+    }
+  }
+
+  for (auto& it : tracepoints_) {
+    auto res = detach_tracepoint_event(it.first, it.second);
+    if (res.code() != 0) {
+      error_msg += "Failed to detach Tracepoint " + it.first + ": ";
+      error_msg += res.msg() + "\n";
+      has_error = true;
+    }
+  }
+
+  for (auto& it : perf_buffers_) {
+    auto res = it.second->close_all_cpu();
+    if (res.code() != 0) {
+      error_msg += "Failed to close perf buffer " + it.first + ": ";
+      error_msg += res.msg() + "\n";
+      has_error = true;
+    }
+    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) {
+      error_msg += res.msg() + "\n";
+      has_error = true;
+    }
+  }
+
+  for (auto& it : funcs_) {
+    int res = close(it.second);
+    if (res != 0) {
+      error_msg += "Failed to unload BPF program for " + it.first + ": ";
+      error_msg += std::string(std::strerror(errno)) + "\n";
+      has_error = true;
+    }
+  }
+
+  if (has_error)
+    return StatusTuple(-1, error_msg);
+  else
+    return StatusTuple(0);
+}
+
+StatusTuple BPF::attach_kprobe(const std::string& kernel_func,
+                               const std::string& probe_func,
+                               bpf_probe_attach_type attach_type,
+                               pid_t pid, int cpu, int group_fd,
+                               perf_reader_cb cb, void* cb_cookie) {
+  std::string probe_event = get_kprobe_event(kernel_func, attach_type);
+  if (kprobes_.find(probe_event) != kprobes_.end())
+    return StatusTuple(-1, "kprobe %s already attached", probe_event.c_str());
+
+  int probe_fd;
+  TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
+
+  void* res =
+      bpf_attach_kprobe(probe_fd, attach_type, probe_event.c_str(), kernel_func.c_str(),
+                        pid, cpu, group_fd, cb, cb_cookie);
+
+  if (!res) {
+    TRY2(unload_func(probe_func));
+    return StatusTuple(-1, "Unable to attach %skprobe for %s using %s",
+                       attach_type_debug(attach_type).c_str(),
+                       kernel_func.c_str(), probe_func.c_str());
+  }
+
+  open_probe_t p = {};
+  p.reader_ptr = res;
+  p.func = probe_func;
+  kprobes_[probe_event] = std::move(p);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::attach_uprobe(const std::string& binary_path,
+                               const std::string& symbol,
+                               const std::string& probe_func,
+                               uint64_t symbol_addr,
+                               bpf_probe_attach_type attach_type,
+                               pid_t pid, int cpu, int group_fd,
+                               perf_reader_cb cb, void* cb_cookie) {
+  std::string module;
+  uint64_t offset;
+  TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
+
+  std::string probe_event = get_uprobe_event(module, offset, attach_type, pid);
+  if (uprobes_.find(probe_event) != uprobes_.end())
+    return StatusTuple(-1, "uprobe %s already attached", probe_event.c_str());
+
+  int probe_fd;
+  TRY2(load_func(probe_func, BPF_PROG_TYPE_KPROBE, probe_fd));
+
+  void* res =
+      bpf_attach_uprobe(probe_fd, attach_type, probe_event.c_str(), binary_path.c_str(),
+                        offset, pid, cpu, group_fd, cb, cb_cookie);
+
+  if (!res) {
+    TRY2(unload_func(probe_func));
+    return StatusTuple(
+        -1,
+        "Unable to attach %suprobe for binary %s symbol %s addr %lx using %s\n",
+        attach_type_debug(attach_type).c_str(), binary_path.c_str(),
+        symbol.c_str(), symbol_addr, probe_func.c_str());
+  }
+
+  open_probe_t p = {};
+  p.reader_ptr = res;
+  p.func = probe_func;
+  uprobes_[probe_event] = std::move(p);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::attach_usdt(const USDT& usdt, pid_t pid, int cpu,
+                             int group_fd) {
+  for (const auto& u : usdt_)
+    if (u == usdt) {
+      bool failed = false;
+      std::string err_msg;
+      int cnt = 0;
+      for (auto addr : u.addresses_) {
+        auto res =
+            attach_uprobe(u.binary_path_, std::string(), u.probe_func_, addr);
+        if (res.code() != 0) {
+          failed = true;
+          err_msg += "USDT " + u.print_name() + " at " + std::to_string(addr);
+          err_msg += ": " + res.msg() + "\n";
+          break;
+        }
+        cnt++;
+      }
+      if (failed) {
+        for (int i = 0; i < cnt; i++) {
+          auto res =
+              detach_uprobe(u.binary_path_, std::string(), u.addresses_[i]);
+          err_msg += "During clean up: " + res.msg() + "\n";
+        }
+        return StatusTuple(-1, err_msg);
+      } else
+        return StatusTuple(0);
+    }
+  return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str());
+}
+
+StatusTuple BPF::attach_tracepoint(const std::string& tracepoint,
+                                   const std::string& probe_func,
+                                   pid_t pid, int cpu, int group_fd,
+                                   perf_reader_cb cb, void* cb_cookie) {
+  if (tracepoints_.find(tracepoint) != tracepoints_.end())
+    return StatusTuple(-1, "Tracepoint %s already attached",
+                       tracepoint.c_str());
+
+  auto pos = tracepoint.find(":");
+  if ((pos == std::string::npos) || (pos != tracepoint.rfind(":")))
+    return StatusTuple(-1, "Unable to parse Tracepoint %s", tracepoint.c_str());
+  std::string tp_category = tracepoint.substr(0, pos);
+  std::string tp_name = tracepoint.substr(pos + 1);
+
+  int probe_fd;
+  TRY2(load_func(probe_func, BPF_PROG_TYPE_TRACEPOINT, probe_fd));
+
+  void* res =
+      bpf_attach_tracepoint(probe_fd, tp_category.c_str(), tp_name.c_str(), pid,
+                            cpu, group_fd, cb, cb_cookie);
+
+  if (!res) {
+    TRY2(unload_func(probe_func));
+    return StatusTuple(-1, "Unable to attach Tracepoint %s using %s",
+                       tracepoint.c_str(), probe_func.c_str());
+  }
+
+  open_probe_t p = {};
+  p.reader_ptr = res;
+  p.func = probe_func;
+  tracepoints_[tracepoint] = std::move(p);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::attach_perf_event(uint32_t ev_type, uint32_t ev_config,
+                                   const std::string& probe_func,
+                                   uint64_t sample_period, uint64_t sample_freq,
+                                   pid_t pid, int cpu, int group_fd) {
+  auto ev_pair = std::make_pair(ev_type, ev_config);
+  if (perf_events_.find(ev_pair) != perf_events_.end())
+    return StatusTuple(-1, "Perf event type %d config %d already attached",
+                       ev_type, ev_config);
+
+  int probe_fd;
+  TRY2(load_func(probe_func, BPF_PROG_TYPE_PERF_EVENT, probe_fd));
+
+  auto fds = new std::map<int, int>();
+  std::vector<int> cpus;
+  if (cpu >= 0)
+    cpus.push_back(cpu);
+  else
+    cpus = get_online_cpus();
+  for (int i: cpus) {
+    int fd = bpf_attach_perf_event(probe_fd, ev_type, ev_config, sample_period,
+                                   sample_freq, pid, i, group_fd);
+    if (fd < 0) {
+      for (const auto& it : *fds)
+        close(it.second);
+      delete fds;
+      TRY2(unload_func(probe_func));
+      return StatusTuple(-1, "Failed to attach perf event type %d config %d",
+                         ev_type, ev_config);
+    }
+    fds->emplace(i, fd);
+  }
+
+  open_probe_t p = {};
+  p.func = probe_func;
+  p.per_cpu_fd = fds;
+  perf_events_[ev_pair] = std::move(p);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_kprobe(const std::string& kernel_func,
+                               bpf_probe_attach_type attach_type) {
+  std::string event = get_kprobe_event(kernel_func, attach_type);
+
+  auto it = kprobes_.find(event);
+  if (it == kprobes_.end())
+    return StatusTuple(-1, "No open %skprobe for %s",
+                       attach_type_debug(attach_type).c_str(),
+                       kernel_func.c_str());
+
+  TRY2(detach_kprobe_event(it->first, it->second));
+  kprobes_.erase(it);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_uprobe(const std::string& binary_path,
+                               const std::string& symbol, uint64_t symbol_addr,
+                               bpf_probe_attach_type attach_type,
+                               pid_t pid) {
+  std::string module;
+  uint64_t offset;
+  TRY2(check_binary_symbol(binary_path, symbol, symbol_addr, module, offset));
+
+  std::string event = get_uprobe_event(module, offset, attach_type, pid);
+  auto it = uprobes_.find(event);
+  if (it == uprobes_.end())
+    return StatusTuple(-1, "No open %suprobe for binary %s symbol %s addr %lx",
+                       attach_type_debug(attach_type).c_str(),
+                       binary_path.c_str(), symbol.c_str(), symbol_addr);
+
+  TRY2(detach_uprobe_event(it->first, it->second));
+  uprobes_.erase(it);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_usdt(const USDT& usdt) {
+  for (const auto& u : usdt_)
+    if (u == usdt) {
+      bool failed = false;
+      std::string err_msg;
+      for (auto addr : u.addresses_) {
+        auto res = detach_uprobe(u.binary_path_, std::string(), addr);
+        if (res.code() != 0) {
+          failed = true;
+          err_msg += "USDT " + u.print_name() + " at " + std::to_string(addr);
+          err_msg += ": " + res.msg() + "\n";
+        }
+      }
+      if (failed)
+        return StatusTuple(-1, err_msg);
+      else
+        return StatusTuple(0);
+    }
+  return StatusTuple(-1, "USDT %s not found", usdt.print_name().c_str());
+}
+
+StatusTuple BPF::detach_tracepoint(const std::string& tracepoint) {
+  auto it = tracepoints_.find(tracepoint);
+  if (it == tracepoints_.end())
+    return StatusTuple(-1, "No open Tracepoint %s", tracepoint.c_str());
+
+  TRY2(detach_tracepoint_event(it->first, it->second));
+  tracepoints_.erase(it);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_perf_event(uint32_t ev_type, uint32_t ev_config) {
+  auto it = perf_events_.find(std::make_pair(ev_type, ev_config));
+  if (it == perf_events_.end())
+    return StatusTuple(-1, "Perf Event type %d config %d not attached",
+                       ev_type, ev_config);
+  TRY2(detach_perf_event_all_cpu(it->second));
+  perf_events_.erase(it);
+  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,
+                                  void* cb_cookie,
+                                  int page_cnt) {
+  if (perf_buffers_.find(name) == perf_buffers_.end()) {
+    TableStorage::iterator it;
+    if (!bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
+      return StatusTuple(-1,
+                         "open_perf_buffer: unable to find table_storage %s",
+                         name.c_str());
+    perf_buffers_[name] = new BPFPerfBuffer(it->second);
+  }
+  if ((page_cnt & (page_cnt - 1)) != 0)
+    return StatusTuple(-1, "open_perf_buffer page_cnt must be a power of two");
+  auto table = perf_buffers_[name];
+  TRY2(table->open_all_cpu(cb, lost_cb, cb_cookie, page_cnt));
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::close_perf_buffer(const std::string& name) {
+  auto it = perf_buffers_.find(name);
+  if (it == perf_buffers_.end())
+    return StatusTuple(-1, "Perf buffer for %s not open", name.c_str());
+  TRY2(it->second->close_all_cpu());
+  return StatusTuple(0);
+}
+
+void BPF::poll_perf_buffer(const std::string& name, int timeout) {
+  auto it = perf_buffers_.find(name);
+  if (it == perf_buffers_.end())
+    return;
+  it->second->poll(timeout);
+}
+
+StatusTuple BPF::load_func(const std::string& func_name,
+                           bpf_prog_type type, int& fd) {
+  if (funcs_.find(func_name) != funcs_.end()) {
+    fd = funcs_[func_name];
+    return StatusTuple(0);
+  }
+
+  uint8_t* func_start = bpf_module_->function_start(func_name);
+  if (!func_start)
+    return StatusTuple(-1, "Can't find start of function %s",
+                       func_name.c_str());
+  size_t func_size = bpf_module_->function_size(func_name);
+
+  fd = bpf_prog_load(type, reinterpret_cast<struct bpf_insn*>(func_start),
+                     func_size, bpf_module_->license(),
+                     bpf_module_->kern_version(), nullptr,
+                     0  // BPFModule will handle error printing
+                     );
+
+  if (fd < 0)
+    return StatusTuple(-1, "Failed to load %s: %d", func_name.c_str(), fd);
+  funcs_[func_name] = fd;
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::unload_func(const std::string& func_name) {
+  auto it = funcs_.find(func_name);
+  if (it == funcs_.end())
+    return StatusTuple(0);
+
+  int res = close(it->second);
+  if (res != 0)
+    return StatusTuple(-1, "Can't close FD for %s: %d", it->first.c_str(), res);
+
+  funcs_.erase(it);
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::check_binary_symbol(const std::string& binary_path,
+                                     const std::string& symbol,
+                                     uint64_t symbol_addr,
+                                     std::string &module_res,
+                                     uint64_t &offset_res) {
+  bcc_symbol output;
+  int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(),
+                                symbol_addr, -1, nullptr, &output);
+  if (res < 0)
+    return StatusTuple(
+        -1, "Unable to find offset for binary %s symbol %s address %lx",
+        binary_path.c_str(), symbol.c_str(), symbol_addr);
+
+  if (output.module) {
+    module_res = output.module;
+    ::free(const_cast<char*>(output.module));
+  } else {
+    module_res = "";
+  }
+  offset_res = output.offset;
+  return StatusTuple(0);
+}
+
+std::string BPF::get_kprobe_event(const std::string& kernel_func,
+                                  bpf_probe_attach_type type) {
+  std::string res = attach_type_prefix(type) + "_";
+  res += sanitize_str(kernel_func, &BPF::kprobe_event_validator);
+  return res;
+}
+
+BPFProgTable BPF::get_prog_table(const std::string& name) {
+  TableStorage::iterator it;
+  if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
+    return BPFProgTable(it->second);
+  return BPFProgTable({});
+}
+
+BPFStackTable BPF::get_stack_table(const std::string& name,
+                                   bool use_debug_file,
+                                   bool check_debug_file_crc) {
+  TableStorage::iterator it;
+  if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
+    return BPFStackTable(it->second, use_debug_file, check_debug_file_crc);
+  return BPFStackTable({}, use_debug_file, check_debug_file_crc);
+}
+
+std::string BPF::get_uprobe_event(const std::string& binary_path,
+                                  uint64_t offset, bpf_probe_attach_type type,
+                                  pid_t pid) {
+  std::string res = attach_type_prefix(type) + "_";
+  res += sanitize_str(binary_path, &BPF::uprobe_path_validator);
+  res += "_0x" + uint_to_hex(offset);
+  if (pid != -1)
+    res += "_" + std::to_string(pid);
+  return res;
+}
+
+StatusTuple BPF::detach_kprobe_event(const std::string& event,
+                                     open_probe_t& attr) {
+  if (attr.reader_ptr) {
+    perf_reader_free(attr.reader_ptr);
+    attr.reader_ptr = nullptr;
+  }
+  TRY2(unload_func(attr.func));
+  if (bpf_detach_kprobe(event.c_str()) < 0)
+    return StatusTuple(-1, "Unable to detach kprobe %s", event.c_str());
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_uprobe_event(const std::string& event,
+                                     open_probe_t& attr) {
+  if (attr.reader_ptr) {
+    perf_reader_free(attr.reader_ptr);
+    attr.reader_ptr = nullptr;
+  }
+  TRY2(unload_func(attr.func));
+  if (bpf_detach_uprobe(event.c_str()) < 0)
+    return StatusTuple(-1, "Unable to detach uprobe %s", event.c_str());
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_tracepoint_event(const std::string& tracepoint,
+                                         open_probe_t& attr) {
+  if (attr.reader_ptr) {
+    perf_reader_free(attr.reader_ptr);
+    attr.reader_ptr = nullptr;
+  }
+  TRY2(unload_func(attr.func));
+
+  // TODO: bpf_detach_tracepoint currently does nothing.
+  return StatusTuple(0);
+}
+
+StatusTuple BPF::detach_perf_event_all_cpu(open_probe_t& attr) {
+  bool has_error = false;
+  std::string err_msg;
+  for (const auto& it : *attr.per_cpu_fd) {
+    int res = bpf_close_perf_event_fd(it.second);
+    if (res != 0) {
+      has_error = true;
+      err_msg += "Failed to close perf event FD " + std::to_string(it.second) +
+                 " For CPU " + std::to_string(it.first) + ": ";
+      err_msg += std::string(std::strerror(errno)) + "\n";
+    }
+  }
+  delete attr.per_cpu_fd;
+  TRY2(unload_func(attr.func));
+
+  if (has_error)
+    return StatusTuple(-1, err_msg);
+  return StatusTuple(0);
+}
+
+StatusTuple USDT::init() {
+  ::USDT::Context ctx(binary_path_);
+  if (!ctx.loaded())
+    return StatusTuple(-1, "Unable to load USDT " + print_name());
+  auto probe = ctx.get(name_);
+  if (probe == nullptr)
+    return StatusTuple(-1, "Unable to find USDT " + print_name());
+
+  if (!probe->enable(probe_func_))
+    return StatusTuple(-1, "Failed to enable USDT " + print_name());
+  std::ostringstream stream;
+  if (!probe->usdt_getarg(stream))
+    return StatusTuple(
+        -1, "Unable to generate program text for USDT " + print_name());
+  program_text_ = ::USDT::USDT_PROGRAM_HEADER + stream.str();
+
+  addresses_.reserve(probe->num_locations());
+  for (size_t i = 0; i < probe->num_locations(); i++)
+    addresses_.emplace_back(probe->address(i));
+
+  initialized_ = true;
+  return StatusTuple(0);
+}
+
+}  // namespace ebpf
diff --git a/src/cc/api/BPF.h b/src/cc/api/BPF.h
new file mode 100644 (file)
index 0000000..4a4c46c
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cctype>
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "BPFTable.h"
+#include "bcc_exception.h"
+#include "bcc_syms.h"
+#include "bpf_module.h"
+#include "compat/linux/bpf.h"
+#include "libbpf.h"
+#include "table_storage.h"
+
+static const int DEFAULT_PERF_BUFFER_PAGE_CNT = 8;
+
+namespace ebpf {
+
+struct open_probe_t {
+  void* reader_ptr;
+  std::string func;
+  std::map<int, int>* per_cpu_fd;
+};
+
+class USDT;
+
+class BPF {
+public:
+  static const int BPF_MAX_STACK_DEPTH = 127;
+
+  explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr)
+      : bpf_module_(new BPFModule(flag, ts)) {}
+  StatusTuple init(const std::string& bpf_program,
+                   const std::vector<std::string>& cflags = {},
+                   const std::vector<USDT>& usdt = {});
+
+  ~BPF();
+  StatusTuple detach_all();
+
+  StatusTuple attach_kprobe(
+      const std::string& kernel_func, const std::string& probe_func,
+      bpf_probe_attach_type = BPF_PROBE_ENTRY,
+      pid_t pid = -1, int cpu = 0, int group_fd = -1,
+      perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
+  StatusTuple detach_kprobe(
+      const std::string& kernel_func,
+      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY);
+
+  StatusTuple attach_uprobe(
+      const std::string& binary_path, const std::string& symbol,
+      const std::string& probe_func, uint64_t symbol_addr = 0,
+      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
+      pid_t pid = -1, int cpu = 0, int group_fd = -1,
+      perf_reader_cb cb = nullptr, void* cb_cookie = nullptr);
+  StatusTuple detach_uprobe(
+      const std::string& binary_path, const std::string& symbol,
+      uint64_t symbol_addr = 0,
+      bpf_probe_attach_type attach_type = BPF_PROBE_ENTRY,
+      pid_t pid = -1);
+  StatusTuple attach_usdt(const USDT& usdt, pid_t pid = -1, int cpu = 0,
+                          int group_fd = -1);
+  StatusTuple detach_usdt(const USDT& usdt);
+
+  StatusTuple attach_tracepoint(const std::string& tracepoint,
+                                const std::string& probe_func,
+                                pid_t pid = -1, int cpu = 0, int group_fd = -1,
+                                perf_reader_cb cb = nullptr,
+                                void* cb_cookie = nullptr);
+  StatusTuple detach_tracepoint(const std::string& tracepoint);
+
+  StatusTuple attach_perf_event(uint32_t ev_type, uint32_t ev_config,
+                                const std::string& probe_func,
+                                uint64_t sample_period, uint64_t sample_freq,
+                                pid_t pid = -1, int cpu = -1,
+                                int group_fd = -1);
+  StatusTuple detach_perf_event(uint32_t ev_type, uint32_t ev_config);
+
+  BPFTable get_table(const std::string& name) {
+    TableStorage::iterator it;
+    if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
+      return BPFTable(it->second);
+    return BPFTable({});
+  }
+
+  template <class ValueType>
+  BPFArrayTable<ValueType> get_array_table(const std::string& name) {
+    TableStorage::iterator it;
+    if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
+      return BPFArrayTable<ValueType>(it->second);
+    return BPFArrayTable<ValueType>({});
+  }
+
+  template <class KeyType, class ValueType>
+  BPFHashTable<KeyType, ValueType> get_hash_table(const std::string& name) {
+    TableStorage::iterator it;
+    if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
+      return BPFHashTable<KeyType, ValueType>(it->second);
+    return BPFHashTable<KeyType, ValueType>({});
+  }
+
+  BPFProgTable get_prog_table(const std::string& name);
+
+  BPFStackTable get_stack_table(const std::string& name,
+                                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,
+                               void* cb_cookie = nullptr,
+                               int page_cnt = DEFAULT_PERF_BUFFER_PAGE_CNT);
+  StatusTuple close_perf_buffer(const std::string& name);
+  void poll_perf_buffer(const std::string& name, int timeout = -1);
+
+  StatusTuple load_func(const std::string& func_name, enum bpf_prog_type type,
+                        int& fd);
+  StatusTuple unload_func(const std::string& func_name);
+
+private:
+  std::string get_kprobe_event(const std::string& kernel_func,
+                               bpf_probe_attach_type type);
+  std::string get_uprobe_event(const std::string& binary_path, uint64_t offset,
+                               bpf_probe_attach_type type, pid_t pid);
+
+  StatusTuple detach_kprobe_event(const std::string& event, open_probe_t& attr);
+  StatusTuple detach_uprobe_event(const std::string& event, open_probe_t& attr);
+  StatusTuple detach_tracepoint_event(const std::string& tracepoint,
+                                      open_probe_t& attr);
+  StatusTuple detach_perf_event_all_cpu(open_probe_t& attr);
+
+  std::string attach_type_debug(bpf_probe_attach_type type) {
+    switch (type) {
+    case BPF_PROBE_ENTRY:
+      return "";
+    case BPF_PROBE_RETURN:
+      return "return ";
+    }
+    return "ERROR";
+  }
+
+  std::string attach_type_prefix(bpf_probe_attach_type type) {
+    switch (type) {
+    case BPF_PROBE_ENTRY:
+      return "p";
+    case BPF_PROBE_RETURN:
+      return "r";
+    }
+    return "ERROR";
+  }
+
+  static bool kprobe_event_validator(char c) {
+    return (c != '+') && (c != '.');
+  }
+
+  static bool uprobe_path_validator(char c) {
+    return std::isalpha(c) || std::isdigit(c) || (c == '_');
+  }
+
+  StatusTuple check_binary_symbol(const std::string& binary_path,
+                                  const std::string& symbol,
+                                  uint64_t symbol_addr,
+                                  std::string &module_res,
+                                  uint64_t &offset_res);
+
+  std::unique_ptr<BPFModule> bpf_module_;
+
+  std::map<std::string, int> funcs_;
+
+  std::vector<USDT> usdt_;
+
+  std::map<std::string, open_probe_t> kprobes_;
+  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_;
+};
+
+class USDT {
+public:
+  USDT(const std::string& binary_path, const std::string& provider,
+       const std::string& name, const std::string& probe_func)
+      : initialized_(false),
+        binary_path_(binary_path),
+        provider_(provider),
+        name_(name),
+        probe_func_(probe_func) {}
+
+  bool operator==(const USDT& other) const {
+    return (provider_ == other.provider_) && (name_ == other.name_) &&
+           (binary_path_ == other.binary_path_) &&
+           (probe_func_ == other.probe_func_);
+  }
+
+  std::string print_name() const {
+    return provider_ + ":" + name_ + " from " + binary_path_;
+  }
+
+private:
+  StatusTuple init();
+  bool initialized_;
+
+  std::string binary_path_;
+  std::string provider_;
+  std::string name_;
+  std::string probe_func_;
+
+  std::vector<uintptr_t> addresses_;
+
+  std::string program_text_;
+
+  friend class BPF;
+};
+
+}  // namespace ebpf
diff --git a/src/cc/api/BPFTable.cc b/src/cc/api/BPFTable.cc
new file mode 100644 (file)
index 0000000..559af4a
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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>
+
+#include "BPFTable.h"
+
+#include "bcc_exception.h"
+#include "bcc_syms.h"
+#include "common.h"
+#include "libbpf.h"
+#include "perf_reader.h"
+
+namespace ebpf {
+
+BPFTable::BPFTable(const TableDesc& desc) : BPFTableBase<void, void>(desc) {}
+
+StatusTuple BPFTable::get_value(const std::string& key_str,
+                                std::string& value_str) {
+  char key[desc.key_size];
+  char value[desc.leaf_size];
+
+  StatusTuple r(0);
+
+  r = string_to_key(key_str, key);
+  if (r.code() != 0)
+    return r;
+
+  if (!lookup(key, value))
+    return StatusTuple(-1, "error getting value");
+
+  return leaf_to_string(value, value_str);
+}
+
+StatusTuple BPFTable::update_value(const std::string& key_str,
+                                   const std::string& value_str) {
+  char key[desc.key_size];
+  char value[desc.leaf_size];
+
+  StatusTuple r(0);
+
+  r = string_to_key(key_str, key);
+  if (r.code() != 0)
+    return r;
+
+  r = string_to_leaf(value_str, value);
+  if (r.code() != 0)
+    return r;
+
+  if (!update(key, value))
+    return StatusTuple(-1, "error updating element");
+
+  return StatusTuple(0);
+}
+
+StatusTuple BPFTable::remove_value(const std::string& key_str) {
+  char key[desc.key_size];
+
+  StatusTuple r(0);
+
+  r = string_to_key(key_str, key);
+  if (r.code() != 0)
+    return r;
+
+  if (!remove(key))
+    return StatusTuple(-1, "error removing element");
+
+  return StatusTuple(0);
+}
+
+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)
+  };
+}
+
+BPFStackTable::~BPFStackTable() {
+  for (auto it : pid_sym_)
+    bcc_free_symcache(it.second, it.first);
+}
+
+std::vector<uintptr_t> BPFStackTable::get_stack_addr(int stack_id) {
+  std::vector<uintptr_t> res;
+  stacktrace_t stack;
+  if (!lookup(&stack_id, &stack))
+    return res;
+  for (int i = 0; (i < BPF_MAX_STACK_DEPTH) && (stack.ip[i] != 0); i++)
+    res.push_back(stack.ip[i]);
+  return res;
+}
+
+std::vector<std::string> BPFStackTable::get_stack_symbol(int stack_id,
+                                                         int pid) {
+  auto addresses = get_stack_addr(stack_id);
+  std::vector<std::string> res;
+  res.reserve(addresses.size());
+
+  if (pid < 0)
+    pid = -1;
+  if (pid_sym_.find(pid) == pid_sym_.end())
+    pid_sym_[pid] = bcc_symcache_new(pid, &symbol_option_);
+  void* cache = pid_sym_[pid];
+
+  bcc_symbol symbol;
+  for (auto addr : addresses)
+    if (bcc_symcache_resolve(cache, addr, &symbol) != 0)
+      res.emplace_back("[UNKNOWN]");
+    else {
+      res.push_back(symbol.demangle_name);
+      bcc_symbol_free_demangle_name(&symbol);
+    }
+
+  return res;
+}
+
+StatusTuple BPFPerfBuffer::open_on_cpu(perf_reader_raw_cb cb,
+                                       perf_reader_lost_cb lost_cb,
+                                       int cpu, void* cb_cookie, int page_cnt) {
+  if (cpu_readers_.find(cpu) != cpu_readers_.end())
+    return StatusTuple(-1, "Perf buffer already open on CPU %d", cpu);
+
+  auto reader = static_cast<perf_reader*>(
+      bpf_open_perf_buffer(cb, lost_cb, cb_cookie, -1, cpu, page_cnt));
+  if (reader == nullptr)
+    return StatusTuple(-1, "Unable to construct perf reader");
+
+  int reader_fd = perf_reader_fd(reader);
+  if (!update(&cpu, &reader_fd)) {
+    perf_reader_free(static_cast<void*>(reader));
+    return StatusTuple(-1, "Unable to open perf buffer on CPU %d: %s", cpu,
+                       std::strerror(errno));
+  }
+
+  struct epoll_event event = {};
+  event.events = EPOLLIN;
+  event.data.ptr = static_cast<void*>(reader);
+  if (epoll_ctl(epfd_, EPOLL_CTL_ADD, reader_fd, &event) != 0) {
+    perf_reader_free(static_cast<void*>(reader));
+    return StatusTuple(-1, "Unable to add perf_reader FD to epoll: %s",
+                       std::strerror(errno));
+  }
+
+  cpu_readers_[cpu] = reader;
+  return StatusTuple(0);
+}
+
+StatusTuple BPFPerfBuffer::open_all_cpu(perf_reader_raw_cb cb,
+                                        perf_reader_lost_cb lost_cb,
+                                        void* cb_cookie, int page_cnt) {
+  if (cpu_readers_.size() != 0 || epfd_ != -1)
+    return StatusTuple(-1, "Previously opened perf buffer not cleaned");
+
+  std::vector<int> cpus = get_online_cpus();
+  ep_events_.reset(new epoll_event[cpus.size()]);
+  epfd_ = epoll_create1(EPOLL_CLOEXEC);
+
+  for (int i : cpus) {
+    auto res = open_on_cpu(cb, lost_cb, i, cb_cookie, page_cnt);
+    if (res.code() != 0) {
+      TRY2(close_all_cpu());
+      return res;
+    }
+  }
+  return StatusTuple(0);
+}
+
+StatusTuple BPFPerfBuffer::close_on_cpu(int cpu) {
+  auto it = cpu_readers_.find(cpu);
+  if (it == cpu_readers_.end())
+    return StatusTuple(0);
+  perf_reader_free(static_cast<void*>(it->second));
+  if (!remove(const_cast<int*>(&(it->first))))
+    return StatusTuple(-1, "Unable to close perf buffer on CPU %d", it->first);
+  cpu_readers_.erase(it);
+  return StatusTuple(0);
+}
+
+StatusTuple BPFPerfBuffer::close_all_cpu() {
+  std::string errors;
+  bool has_error = false;
+
+  if (epfd_ >= 0) {
+    int close_res = close(epfd_);
+    epfd_ = -1;
+    ep_events_.reset();
+    if (close_res != 0) {
+      has_error = true;
+      errors += std::string(std::strerror(errno)) + "\n";
+    }
+  }
+
+  std::vector<int> opened_cpus;
+  for (auto it : cpu_readers_)
+    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 buffer: ";
+      errors += res.msg() + "\n";
+      has_error = true;
+    }
+  }
+
+  if (has_error)
+    return StatusTuple(-1, errors);
+  return StatusTuple(0);
+}
+
+void BPFPerfBuffer::poll(int timeout) {
+  if (epfd_ < 0)
+    return;
+  int cnt = epoll_wait(epfd_, ep_events_.get(), cpu_readers_.size(), timeout);
+  if (cnt <= 0)
+    return;
+  for (int i = 0; i < cnt; i++)
+    perf_reader_event_read(static_cast<perf_reader*>(ep_events_[i].data.ptr));
+}
+
+BPFPerfBuffer::~BPFPerfBuffer() {
+  auto res = close_all_cpu();
+  if (res.code() != 0)
+    std::cerr << "Failed to close all perf buffer on destruction: " << res.msg()
+              << 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
diff --git a/src/cc/api/BPFTable.h b/src/cc/api/BPFTable.h
new file mode 100644 (file)
index 0000000..2333680
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstring>
+#include <sys/epoll.h>
+#include <exception>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "bcc_exception.h"
+#include "bcc_syms.h"
+#include "bpf_module.h"
+#include "libbpf.h"
+#include "perf_reader.h"
+#include "table_desc.h"
+
+namespace ebpf {
+
+template <class KeyType, class ValueType>
+class BPFTableBase {
+ public:
+  size_t capacity() { return desc.max_entries; }
+
+  StatusTuple string_to_key(const std::string& key_str, KeyType* key) {
+    return desc.key_sscanf(key_str.c_str(), key);
+  }
+
+  StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) {
+    return desc.leaf_sscanf(value_str.c_str(), value);
+  }
+
+  StatusTuple key_to_string(const KeyType* key, std::string& key_str) {
+    char buf[8 * desc.key_size];
+    StatusTuple rc = desc.key_snprintf(buf, sizeof(buf), key);
+    if (!rc.code())
+      key_str.assign(buf);
+    return rc;
+  }
+
+  StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) {
+    char buf[8 * desc.leaf_size];
+    StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value);
+    if (!rc.code())
+      value_str.assign(buf);
+    return rc;
+  }
+
+ protected:
+  explicit BPFTableBase(const TableDesc& desc) : desc(desc) {}
+
+  bool lookup(KeyType* key, ValueType* value) {
+    return bpf_lookup_elem(desc.fd, static_cast<void*>(key),
+                           static_cast<void*>(value)) >= 0;
+  }
+
+  bool first(KeyType* key) {
+    return bpf_get_first_key(desc.fd, static_cast<void*>(key),
+                             desc.key_size) >= 0;
+  }
+
+  bool next(KeyType* key, KeyType* next_key) {
+    return bpf_get_next_key(desc.fd, static_cast<void*>(key),
+                            static_cast<void*>(next_key)) >= 0;
+  }
+
+  bool update(KeyType* key, ValueType* value) {
+    return bpf_update_elem(desc.fd, static_cast<void*>(key),
+                           static_cast<void*>(value), 0) >= 0;
+  }
+
+  bool remove(KeyType* key) {
+    return bpf_delete_elem(desc.fd, static_cast<void*>(key)) >= 0;
+  }
+
+  const TableDesc& desc;
+};
+
+class BPFTable : public BPFTableBase<void, void> {
+ public:
+  BPFTable(const TableDesc& desc);
+
+  StatusTuple get_value(const std::string& key_str, std::string& value);
+  StatusTuple update_value(const std::string& key_str,
+                           const std::string& value_str);
+  StatusTuple remove_value(const std::string& key_str);
+};
+
+template <class ValueType>
+class BPFArrayTable : public BPFTableBase<int, ValueType> {
+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");
+  }
+
+  StatusTuple get_value(const int& index, ValueType& value) {
+    if (!this->lookup(const_cast<int*>(&index), &value))
+      return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
+    return StatusTuple(0);
+  }
+
+  StatusTuple update_value(const int& index, const ValueType& value) {
+    if (!this->update(const_cast<int*>(&index), const_cast<ValueType*>(&value)))
+      return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
+    return StatusTuple(0);
+  }
+
+  ValueType operator[](const int& key) {
+    ValueType value;
+    get_value(key, value);
+    return value;
+  }
+
+  std::vector<ValueType> get_table_offline() {
+    std::vector<ValueType> res(this->capacity());
+
+    for (int i = 0; i < (int) this->capacity(); i++) {
+      get_value(i, res[i]);
+    }
+
+    return res;
+  }
+};
+
+template <class KeyType, class ValueType>
+class BPFHashTable : public BPFTableBase<KeyType, ValueType> {
+ public:
+  explicit BPFHashTable(const TableDesc& desc)
+      : BPFTableBase<KeyType, ValueType>(desc) {
+    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_LRU_PERCPU_HASH)
+      throw std::invalid_argument("Table '" + desc.name + "' is not a hash table");
+  }
+
+  StatusTuple get_value(const KeyType& key, ValueType& value) {
+    if (!this->lookup(const_cast<KeyType*>(&key), &value))
+      return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
+    return StatusTuple(0);
+  }
+
+  StatusTuple update_value(const KeyType& key, const ValueType& value) {
+    if (!this->update(const_cast<KeyType*>(&key), const_cast<ValueType*>(&value)))
+      return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
+    return StatusTuple(0);
+  }
+
+  StatusTuple remove_value(const KeyType& key) {
+    if (!this->remove(const_cast<KeyType*>(&key)))
+      return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
+    return StatusTuple(0);
+  }
+
+  ValueType operator[](const KeyType& key) {
+    ValueType value;
+    get_value(key, value);
+    return value;
+  }
+
+  std::vector<std::pair<KeyType, ValueType>> get_table_offline() {
+    std::vector<std::pair<KeyType, ValueType>> res;
+    KeyType cur;
+    ValueType value;
+
+    if (!this->first(&cur))
+      return res;
+
+    while (true) {
+      if (!this->lookup(&cur, &value))
+        break;
+      res.emplace_back(cur, value);
+      if (!this->next(&cur, &cur))
+        break;
+    }
+
+    return res;
+  }
+};
+
+// From src/cc/export/helpers.h
+static const int BPF_MAX_STACK_DEPTH = 127;
+struct stacktrace_t {
+  uintptr_t ip[BPF_MAX_STACK_DEPTH];
+};
+
+class BPFStackTable : public BPFTableBase<int, stacktrace_t> {
+ public:
+  BPFStackTable(const TableDesc& desc,
+                bool use_debug_file,
+                bool check_debug_file_crc);
+  ~BPFStackTable();
+
+  std::vector<uintptr_t> get_stack_addr(int stack_id);
+  std::vector<std::string> get_stack_symbol(int stack_id, int pid);
+
+ private:
+  bcc_symbol_option symbol_option_;
+  std::map<int, void*> pid_sym_;
+};
+
+class BPFPerfBuffer : public BPFTableBase<int, int> {
+ public:
+  BPFPerfBuffer(const TableDesc& desc)
+      : BPFTableBase<int, int>(desc), epfd_(-1) {}
+  ~BPFPerfBuffer();
+
+  StatusTuple open_all_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
+                           void* cb_cookie, int page_cnt);
+  StatusTuple close_all_cpu();
+  void poll(int timeout);
+
+ private:
+  StatusTuple open_on_cpu(perf_reader_raw_cb cb, perf_reader_lost_cb lost_cb,
+                          int cpu, void* cb_cookie, int page_cnt);
+  StatusTuple close_on_cpu(int cpu);
+
+  std::map<int, perf_reader*> cpu_readers_;
+
+  int epfd_;
+  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)
+      : BPFTableBase<int, int>(desc) {
+    if (desc.type != BPF_MAP_TYPE_PROG_ARRAY)
+      throw std::invalid_argument("Table '" + desc.name + "' is not a prog table");
+  }
+
+  // updates an element
+  StatusTuple update_value(const int& index, const int& value) {
+    if (!this->update(const_cast<int*>(&index), const_cast<int*>(&value)))
+      return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
+    return StatusTuple(0);
+  }
+};
+
+}  // namespace ebpf
diff --git a/src/cc/api/CMakeLists.txt b/src/cc/api/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4234e20
--- /dev/null
@@ -0,0 +1,3 @@
+set(bcc_api_sources BPF.cc BPFTable.cc)
+add_library(api-static STATIC ${bcc_api_sources})
+install(FILES BPF.h BPFTable.h COMPONENT libbcc DESTINATION include/bcc)
diff --git a/src/cc/link_all.cc b/src/cc/link_all.cc
new file mode 100644 (file)
index 0000000..1105776
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (c) 2017 VMware, Inc.
+// Licensed under the Apache License, Version 2.0 (the "License")
+#include <cstdlib>
+
+#include "bcc_usdt.h"
+
+namespace {
+  // Take this trick from llvm for forcing exported functions in helper
+  // libraries to be included in the final .so
+  struct LinkAll {
+    LinkAll() {
+      // getenv never returns -1, but compiler doesn't know!
+      if (::getenv("bar") != (char *)-1)
+        return;
+
+      (void)bcc_usdt_new_frompid(-1);
+      (void)bcc_usdt_new_frompath(nullptr);
+      (void)bcc_usdt_close(nullptr);
+    }
+  } LinkAll;  // declare one instance to invoke the constructor
+}
index be986bfa7a5e805bebc53700b35fa1cca8ffdf87..373bfcb787a9e53377ea6acef462b54bf9778b26 100644 (file)
@@ -2,6 +2,7 @@
 # Licensed under the Apache License, Version 2.0 (the "License")
 
 include_directories(${CMAKE_SOURCE_DIR}/src/cc)
+include_directories(${CMAKE_SOURCE_DIR}/src/cc/api)
 
 add_executable(test_static test_static.c)
 target_link_libraries(test_static bcc-static)