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>
include(cmake/GetGitRevisionDescription.cmake)
include(cmake/version.cmake)
+include(CMakeDependentOption)
include(GNUInstallDirs)
include(CheckCXXCompilerFlag)
include(cmake/FindCompilerFlag.cmake)
# 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)
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
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)
${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)
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+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)
--- /dev/null
+// 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
+}
# 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)