From: Brenden Blanco Date: Thu, 29 Jun 2017 00:37:06 +0000 (-0700) Subject: move api and create dependent option X-Git-Tag: submit/tizen_4.0/20171018.110122~21 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=71fc3d5c890f57fb7f979c6176d97da0f04d0801;p=platform%2Fupstream%2Fbcc.git move api and create dependent option 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 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index b23261cc..f2bdd554 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ enable_testing() include(cmake/GetGitRevisionDescription.cmake) include(cmake/version.cmake) +include(CMakeDependentOption) include(GNUInstallDirs) include(CheckCXXCompilerFlag) include(cmake/FindCompilerFlag.cmake) diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 49da8296..7d6ccee2 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -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 index 0a6cb728..00000000 --- a/src/cc/BPF.cc +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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& cflags, - const std::vector& 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(); - std::vector 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(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(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 index 4a4c46c2..00000000 --- a/src/cc/BPF.h +++ /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 -#include -#include -#include - -#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* 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& cflags = {}, - const std::vector& 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 - BPFArrayTable get_array_table(const std::string& name) { - TableStorage::iterator it; - if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) - return BPFArrayTable(it->second); - return BPFArrayTable({}); - } - - template - BPFHashTable get_hash_table(const std::string& name) { - TableStorage::iterator it; - if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) - return BPFHashTable(it->second); - return BPFHashTable({}); - } - - 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 bpf_module_; - - std::map funcs_; - - std::vector usdt_; - - std::map kprobes_; - std::map uprobes_; - std::map tracepoints_; - std::map perf_buffers_; - std::map perf_event_arrays_; - std::map, 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 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 index 559af4a6..00000000 --- a/src/cc/BPFTable.cc +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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(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(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 BPFStackTable::get_stack_addr(int stack_id) { - std::vector 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 BPFStackTable::get_stack_symbol(int stack_id, - int pid) { - auto addresses = get_stack_addr(stack_id); - std::vector 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( - 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(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(reader); - if (epoll_ctl(epfd_, EPOLL_CTL_ADD, reader_fd, &event) != 0) { - perf_reader_free(static_cast(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 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(it->second)); - if (!remove(const_cast(&(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 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(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 cpus = get_online_cpus(); - - for (int i : cpus) { - auto res = open_on_cpu(i, type, config); - if (res.code() != 0) { - TRY2(close_all_cpu()); - return res; - } - } - return StatusTuple(0); -} - -StatusTuple BPFPerfEventArray::close_all_cpu() { - std::string errors; - bool has_error = false; - - std::vector opened_cpus; - for (auto it : cpu_fds_) - opened_cpus.push_back(it.first); - for (int i : opened_cpus) { - auto res = close_on_cpu(i); - if (res.code() != 0) { - errors += "Failed to close CPU" + std::to_string(i) + " perf event: "; - errors += res.msg() + "\n"; - has_error = true; - } - } - - if (has_error) - return StatusTuple(-1, errors); - return StatusTuple(0); -} - -StatusTuple BPFPerfEventArray::open_on_cpu(int cpu, uint32_t type, - uint64_t config) { - if (cpu_fds_.find(cpu) != cpu_fds_.end()) - return StatusTuple(-1, "Perf event already open on CPU %d", cpu); - int fd = bpf_open_perf_event(type, config, -1, cpu); - if (fd < 0) { - return StatusTuple(-1, "Error constructing perf event %" PRIu32 ":%" PRIu64, - type, config); - } - if (!update(&cpu, &fd)) { - bpf_close_perf_event_fd(fd); - return StatusTuple(-1, "Unable to open perf event on CPU %d: %s", - cpu, std::strerror(errno)); - } - cpu_fds_[cpu] = fd; - return StatusTuple(0); -} - -StatusTuple BPFPerfEventArray::close_on_cpu(int cpu) { - auto it = cpu_fds_.find(cpu); - if (it == cpu_fds_.end()) { - return StatusTuple(0); - } - bpf_close_perf_event_fd(it->second); - cpu_fds_.erase(it); - return StatusTuple(0); -} - -BPFPerfEventArray::~BPFPerfEventArray() { - auto res = close_all_cpu(); - if (res.code() != 0) { - std::cerr << "Failed to close all perf buffer on destruction: " << res.msg() - << std::endl; - } -} -} // namespace ebpf diff --git a/src/cc/BPFTable.h b/src/cc/BPFTable.h deleted file mode 100644 index 23336804..00000000 --- a/src/cc/BPFTable.h +++ /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 -#include -#include -#include -#include -#include -#include -#include - -#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 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(key), - static_cast(value)) >= 0; - } - - bool first(KeyType* key) { - return bpf_get_first_key(desc.fd, static_cast(key), - desc.key_size) >= 0; - } - - bool next(KeyType* key, KeyType* next_key) { - return bpf_get_next_key(desc.fd, static_cast(key), - static_cast(next_key)) >= 0; - } - - bool update(KeyType* key, ValueType* value) { - return bpf_update_elem(desc.fd, static_cast(key), - static_cast(value), 0) >= 0; - } - - bool remove(KeyType* key) { - return bpf_delete_elem(desc.fd, static_cast(key)) >= 0; - } - - const TableDesc& desc; -}; - -class BPFTable : public BPFTableBase { - 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 BPFArrayTable : public BPFTableBase { -public: - BPFArrayTable(const TableDesc& desc) - : BPFTableBase(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(&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(&index), const_cast(&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 get_table_offline() { - std::vector res(this->capacity()); - - for (int i = 0; i < (int) this->capacity(); i++) { - get_value(i, res[i]); - } - - return res; - } -}; - -template -class BPFHashTable : public BPFTableBase { - public: - explicit BPFHashTable(const TableDesc& desc) - : BPFTableBase(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(&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(&key), const_cast(&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(&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> get_table_offline() { - std::vector> 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 { - public: - BPFStackTable(const TableDesc& desc, - bool use_debug_file, - bool check_debug_file_crc); - ~BPFStackTable(); - - std::vector get_stack_addr(int stack_id); - std::vector get_stack_symbol(int stack_id, int pid); - - private: - bcc_symbol_option symbol_option_; - std::map pid_sym_; -}; - -class BPFPerfBuffer : public BPFTableBase { - public: - BPFPerfBuffer(const TableDesc& desc) - : BPFTableBase(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 cpu_readers_; - - int epfd_; - std::unique_ptr ep_events_; -}; - -class BPFPerfEventArray : public BPFTableBase { - public: - BPFPerfEventArray(const TableDesc& desc) - : BPFTableBase(desc) {} - ~BPFPerfEventArray(); - - StatusTuple open_all_cpu(uint32_t type, uint64_t config); - StatusTuple close_all_cpu(); - - private: - StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config); - StatusTuple close_on_cpu(int cpu); - - std::map cpu_fds_; -}; - -class BPFProgTable : public BPFTableBase { -public: - BPFProgTable(const TableDesc& desc) - : BPFTableBase(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(&index), const_cast(&value))) - return StatusTuple(-1, "Error updating value: %s", std::strerror(errno)); - return StatusTuple(0); - } -}; - -} // namespace ebpf diff --git a/src/cc/CMakeLists.txt b/src/cc/CMakeLists.txt index 799f5481..8d4b7dc2 100644 --- a/src/cc/CMakeLists.txt +++ b/src/cc/CMakeLists.txt @@ -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 index 00000000..0a6cb728 --- /dev/null +++ b/src/cc/api/BPF.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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& cflags, + const std::vector& 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(); + std::vector 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(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(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 index 00000000..4a4c46c2 --- /dev/null +++ b/src/cc/api/BPF.h @@ -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 +#include +#include +#include + +#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* 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& cflags = {}, + const std::vector& 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 + BPFArrayTable get_array_table(const std::string& name) { + TableStorage::iterator it; + if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) + return BPFArrayTable(it->second); + return BPFArrayTable({}); + } + + template + BPFHashTable get_hash_table(const std::string& name) { + TableStorage::iterator it; + if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it)) + return BPFHashTable(it->second); + return BPFHashTable({}); + } + + 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 bpf_module_; + + std::map funcs_; + + std::vector usdt_; + + std::map kprobes_; + std::map uprobes_; + std::map tracepoints_; + std::map perf_buffers_; + std::map perf_event_arrays_; + std::map, 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 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 index 00000000..559af4a6 --- /dev/null +++ b/src/cc/api/BPFTable.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(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(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 BPFStackTable::get_stack_addr(int stack_id) { + std::vector 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 BPFStackTable::get_stack_symbol(int stack_id, + int pid) { + auto addresses = get_stack_addr(stack_id); + std::vector 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( + 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(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(reader); + if (epoll_ctl(epfd_, EPOLL_CTL_ADD, reader_fd, &event) != 0) { + perf_reader_free(static_cast(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 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(it->second)); + if (!remove(const_cast(&(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 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(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 cpus = get_online_cpus(); + + for (int i : cpus) { + auto res = open_on_cpu(i, type, config); + if (res.code() != 0) { + TRY2(close_all_cpu()); + return res; + } + } + return StatusTuple(0); +} + +StatusTuple BPFPerfEventArray::close_all_cpu() { + std::string errors; + bool has_error = false; + + std::vector opened_cpus; + for (auto it : cpu_fds_) + opened_cpus.push_back(it.first); + for (int i : opened_cpus) { + auto res = close_on_cpu(i); + if (res.code() != 0) { + errors += "Failed to close CPU" + std::to_string(i) + " perf event: "; + errors += res.msg() + "\n"; + has_error = true; + } + } + + if (has_error) + return StatusTuple(-1, errors); + return StatusTuple(0); +} + +StatusTuple BPFPerfEventArray::open_on_cpu(int cpu, uint32_t type, + uint64_t config) { + if (cpu_fds_.find(cpu) != cpu_fds_.end()) + return StatusTuple(-1, "Perf event already open on CPU %d", cpu); + int fd = bpf_open_perf_event(type, config, -1, cpu); + if (fd < 0) { + return StatusTuple(-1, "Error constructing perf event %" PRIu32 ":%" PRIu64, + type, config); + } + if (!update(&cpu, &fd)) { + bpf_close_perf_event_fd(fd); + return StatusTuple(-1, "Unable to open perf event on CPU %d: %s", + cpu, std::strerror(errno)); + } + cpu_fds_[cpu] = fd; + return StatusTuple(0); +} + +StatusTuple BPFPerfEventArray::close_on_cpu(int cpu) { + auto it = cpu_fds_.find(cpu); + if (it == cpu_fds_.end()) { + return StatusTuple(0); + } + bpf_close_perf_event_fd(it->second); + cpu_fds_.erase(it); + return StatusTuple(0); +} + +BPFPerfEventArray::~BPFPerfEventArray() { + auto res = close_all_cpu(); + if (res.code() != 0) { + std::cerr << "Failed to close all perf buffer on destruction: " << res.msg() + << std::endl; + } +} +} // namespace ebpf diff --git a/src/cc/api/BPFTable.h b/src/cc/api/BPFTable.h new file mode 100644 index 00000000..23336804 --- /dev/null +++ b/src/cc/api/BPFTable.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 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(key), + static_cast(value)) >= 0; + } + + bool first(KeyType* key) { + return bpf_get_first_key(desc.fd, static_cast(key), + desc.key_size) >= 0; + } + + bool next(KeyType* key, KeyType* next_key) { + return bpf_get_next_key(desc.fd, static_cast(key), + static_cast(next_key)) >= 0; + } + + bool update(KeyType* key, ValueType* value) { + return bpf_update_elem(desc.fd, static_cast(key), + static_cast(value), 0) >= 0; + } + + bool remove(KeyType* key) { + return bpf_delete_elem(desc.fd, static_cast(key)) >= 0; + } + + const TableDesc& desc; +}; + +class BPFTable : public BPFTableBase { + 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 BPFArrayTable : public BPFTableBase { +public: + BPFArrayTable(const TableDesc& desc) + : BPFTableBase(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(&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(&index), const_cast(&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 get_table_offline() { + std::vector res(this->capacity()); + + for (int i = 0; i < (int) this->capacity(); i++) { + get_value(i, res[i]); + } + + return res; + } +}; + +template +class BPFHashTable : public BPFTableBase { + public: + explicit BPFHashTable(const TableDesc& desc) + : BPFTableBase(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(&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(&key), const_cast(&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(&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> get_table_offline() { + std::vector> 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 { + public: + BPFStackTable(const TableDesc& desc, + bool use_debug_file, + bool check_debug_file_crc); + ~BPFStackTable(); + + std::vector get_stack_addr(int stack_id); + std::vector get_stack_symbol(int stack_id, int pid); + + private: + bcc_symbol_option symbol_option_; + std::map pid_sym_; +}; + +class BPFPerfBuffer : public BPFTableBase { + public: + BPFPerfBuffer(const TableDesc& desc) + : BPFTableBase(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 cpu_readers_; + + int epfd_; + std::unique_ptr ep_events_; +}; + +class BPFPerfEventArray : public BPFTableBase { + public: + BPFPerfEventArray(const TableDesc& desc) + : BPFTableBase(desc) {} + ~BPFPerfEventArray(); + + StatusTuple open_all_cpu(uint32_t type, uint64_t config); + StatusTuple close_all_cpu(); + + private: + StatusTuple open_on_cpu(int cpu, uint32_t type, uint64_t config); + StatusTuple close_on_cpu(int cpu); + + std::map cpu_fds_; +}; + +class BPFProgTable : public BPFTableBase { +public: + BPFProgTable(const TableDesc& desc) + : BPFTableBase(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(&index), const_cast(&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 index 00000000..4234e20c --- /dev/null +++ b/src/cc/api/CMakeLists.txt @@ -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 index 00000000..11057766 --- /dev/null +++ b/src/cc/link_all.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2017 VMware, Inc. +// Licensed under the Apache License, Version 2.0 (the "License") +#include + +#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 +} diff --git a/tests/cc/CMakeLists.txt b/tests/cc/CMakeLists.txt index be986bfa..373bfcb7 100644 --- a/tests/cc/CMakeLists.txt +++ b/tests/cc/CMakeLists.txt @@ -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)