--- /dev/null
+set(llvm_raw_libs bitwriter bpfcodegen irreader linker
+ mcjit objcarcopts option passes nativecodegen lto)
+list(FIND LLVM_AVAILABLE_LIBS "LLVMCoverage" _llvm_coverage)
+if (${_llvm_coverage} GREATER -1)
+ list(APPEND llvm_raw_libs coverage)
+endif()
+list(FIND LLVM_AVAILABLE_LIBS "LLVMCoroutines" _llvm_coroutines)
+if (${_llvm_coroutines} GREATER -1)
+ list(APPEND llvm_raw_libs coroutines)
+endif()
+llvm_map_components_to_libnames(_llvm_libs ${llvm_raw_libs})
+llvm_expand_dependencies(llvm_libs ${_llvm_libs})
+
+# order is important
+set(clang_libs
+ ${libclangFrontend}
+ ${libclangSerialization}
+ ${libclangDriver}
+ ${libclangParse}
+ ${libclangSema}
+ ${libclangCodeGen}
+ ${libclangAnalysis}
+ ${libclangRewrite}
+ ${libclangEdit}
+ ${libclangAST}
+ ${libclangLex}
+ ${libclangBasic})
+
+# prune unused llvm static library stuff when linking into the new .so
+set(_exclude_flags)
+foreach(_lib ${clang_libs})
+ get_filename_component(_lib ${_lib} NAME)
+ set(_exclude_flags "${_exclude_flags} -Wl,--exclude-libs=${_lib}")
+endforeach(_lib)
+set(clang_lib_exclude_flags "${_exclude_flags}")
+
+set(_exclude_flags)
+foreach(_lib ${llvm_libs})
+ get_filename_component(_lib ${_lib} NAME)
+ set(_exclude_flags "${_exclude_flags} -Wl,--exclude-libs=lib${_lib}.a")
+endforeach(_lib)
+set(llvm_lib_exclude_flags "${_exclude_flags}")
--- /dev/null
+# only turn on static-libstdc++ if also linking statically against clang
+string(REGEX MATCH ".*[.]a$" LIBCLANG_ISSTATIC "${libclangBasic}")
+# if gcc 4.9 or higher is used, static libstdc++ is a good option
+if (CMAKE_COMPILER_IS_GNUCC AND LIBCLANG_ISSTATIC)
+ execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
+ if (GCC_VERSION VERSION_GREATER 4.9 OR GCC_VERSION VERSION_EQUAL 4.9)
+ execute_process(COMMAND ${CMAKE_C_COMPILER} -print-libgcc-file-name OUTPUT_VARIABLE GCC_LIB)
+ get_filename_component(GCC_DIR "${GCC_LIB}" DIRECTORY)
+ find_library(GCC_LIBSTDCPP libstdc++.a PATHS "${GCC_DIR}" NO_DEFAULT_PATH)
+ if (GCC_LIBSTDCPP)
+ message(STATUS "Using static-libstdc++")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++")
+ endif()
+ endif()
+endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
-# only turn on static-libstdc++ if also linking statically against clang
-string(REGEX MATCH ".*[.]a$" LIBCLANG_ISSTATIC "${libclangBasic}")
-# if gcc 4.9 or higher is used, static libstdc++ is a good option
-if (CMAKE_COMPILER_IS_GNUCC AND LIBCLANG_ISSTATIC)
- execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
- if (GCC_VERSION VERSION_GREATER 4.9 OR GCC_VERSION VERSION_EQUAL 4.9)
- execute_process(COMMAND ${CMAKE_C_COMPILER} -print-libgcc-file-name OUTPUT_VARIABLE GCC_LIB)
- get_filename_component(GCC_DIR "${GCC_LIB}" DIRECTORY)
- find_library(GCC_LIBSTDCPP libstdc++.a PATHS "${GCC_DIR}" NO_DEFAULT_PATH)
- if (GCC_LIBSTDCPP)
- message(STATUS "Using static-libstdc++")
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++")
- endif()
- endif()
-endif()
+include(static_libstdc++)
add_library(bpf-static STATIC libbpf.c perf_reader.c)
set_target_properties(bpf-static PROPERTIES OUTPUT_NAME bpf)
add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc table_storage.cc
shared_table.cc bpffs_table.cc json_map_decl_visitor.cc exported_files.cc
- bcc_elf.c bcc_perf_map.c bcc_proc.c bcc_syms.cc ns_guard.cc usdt_args.cc
- usdt.cc common.cc BPF.cc BPFTable.cc)
+ bcc_elf.c bcc_perf_map.c bcc_proc.c bcc_syms.cc ns_guard.cc common.cc BPF.cc
+ BPFTable.cc)
set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0)
set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc)
bcc_syms.cc ns_guard.cc)
add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc
bpffs_table.cc json_map_decl_visitor.cc table_storage.cc exported_files.cc
- usdt_args.cc usdt.cc common.cc BPF.cc BPFTable.cc)
+ common.cc BPF.cc BPFTable.cc)
set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc)
-set(llvm_raw_libs bitwriter bpfcodegen irreader linker
- mcjit objcarcopts option passes nativecodegen lto)
-list(FIND LLVM_AVAILABLE_LIBS "LLVMCoverage" _llvm_coverage)
-if (${_llvm_coverage} GREATER -1)
- list(APPEND llvm_raw_libs coverage)
-endif()
-list(FIND LLVM_AVAILABLE_LIBS "LLVMCoroutines" _llvm_coroutines)
-if (${_llvm_coroutines} GREATER -1)
- list(APPEND llvm_raw_libs coroutines)
-endif()
-llvm_map_components_to_libnames(llvm_libs ${llvm_raw_libs})
-llvm_expand_dependencies(expanded_libs ${llvm_libs})
+include(clang_libs)
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${clang_lib_exclude_flags}")
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${llvm_lib_exclude_flags}")
-# order is important
-set(clang_libs ${libclangFrontend} ${libclangSerialization} ${libclangDriver} ${libclangParse}
- ${libclangSema} ${libclangCodeGen} ${libclangAnalysis} ${libclangRewrite} ${libclangEdit}
- ${libclangAST} ${libclangLex} ${libclangBasic})
+set(bcc_common_libs b_frontend clang_frontend bpf-static
+ ${clang_libs} ${llvm_libs} ${LIBELF_LIBRARIES})
-# prune unused llvm static library stuff when linking into the new .so
-foreach(lib ${clang_libs})
- get_filename_component(lib ${lib} NAME)
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=${lib}")
-endforeach(lib)
-foreach(lib ${expanded_libs})
- get_filename_component(lib ${lib} NAME)
- set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=lib${lib}.a")
-endforeach(lib)
+option(ENABLE_USDT "Enable User-level Statically Defined Tracing" ON)
+if(ENABLE_USDT)
+ add_subdirectory(usdt)
+ list(APPEND bcc_common_libs usdt-static)
+endif()
# Link against LLVM libraries
-target_link_libraries(bcc-shared b_frontend clang_frontend bpf-static
- ${clang_libs} ${expanded_libs} ${LIBELF_LIBRARIES})
-target_link_libraries(bcc-static b_frontend clang_frontend bcc-loader-static
- bpf-static ${clang_libs} ${expanded_libs} ${LIBELF_LIBRARIES})
+target_link_libraries(bcc-shared ${bcc_common_libs})
+target_link_libraries(bcc-static ${bcc_common_libs} bcc-loader-static)
install(TARGETS bcc-shared LIBRARY COMPONENT libbcc
DESTINATION ${CMAKE_INSTALL_LIBDIR})
+++ /dev/null
-/*
- * Copyright (c) 2016 GitHub, 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 <cstring>
-#include <sstream>
-#include <unordered_set>
-
-#include <fcntl.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "bcc_elf.h"
-#include "bcc_proc.h"
-#include "usdt.h"
-#include "vendor/tinyformat.hpp"
-#include "bcc_usdt.h"
-
-namespace USDT {
-
-Location::Location(uint64_t addr, const char *arg_fmt) : address_(addr) {
- ArgumentParser_x64 parser(arg_fmt);
- while (!parser.done()) {
- Argument arg;
- if (!parser.parse(&arg))
- continue;
- arguments_.push_back(std::move(arg));
- }
-}
-
-Probe::Probe(const char *bin_path, const char *provider, const char *name,
- uint64_t semaphore, const optional<int> &pid, ProcMountNS *ns)
- : bin_path_(bin_path),
- provider_(provider),
- name_(name),
- semaphore_(semaphore),
- pid_(pid),
- mount_ns_(ns) {}
-
-bool Probe::in_shared_object() {
- if (!in_shared_object_) {
- ProcMountNSGuard g(mount_ns_);
- in_shared_object_ = bcc_elf_is_shared_obj(bin_path_.c_str());
- }
- return in_shared_object_.value();
-}
-
-bool Probe::resolve_global_address(uint64_t *global, const uint64_t addr) {
- if (in_shared_object()) {
- return (pid_ &&
- !bcc_resolve_global_addr(*pid_, bin_path_.c_str(), addr, global));
- }
-
- *global = addr;
- return true;
-}
-
-bool Probe::add_to_semaphore(int16_t val) {
- assert(pid_);
-
- if (!attached_semaphore_) {
- uint64_t addr;
- if (!resolve_global_address(&addr, semaphore_))
- return false;
- attached_semaphore_ = addr;
- }
-
- off_t address = static_cast<off_t>(attached_semaphore_.value());
-
- std::string procmem = tfm::format("/proc/%d/mem", pid_.value());
- int memfd = ::open(procmem.c_str(), O_RDWR);
- if (memfd < 0)
- return false;
-
- int16_t original;
-
- if (::lseek(memfd, address, SEEK_SET) < 0 ||
- ::read(memfd, &original, 2) != 2) {
- ::close(memfd);
- return false;
- }
-
- original = original + val;
-
- if (::lseek(memfd, address, SEEK_SET) < 0 ||
- ::write(memfd, &original, 2) != 2) {
- ::close(memfd);
- return false;
- }
-
- ::close(memfd);
- return true;
-}
-
-bool Probe::enable(const std::string &fn_name) {
- if (attached_to_)
- return false;
-
- if (need_enable()) {
- if (!pid_)
- return false;
-
- if (!add_to_semaphore(+1))
- return false;
- }
-
- attached_to_ = fn_name;
- return true;
-}
-
-bool Probe::disable() {
- if (!attached_to_)
- return false;
-
- attached_to_ = nullopt;
-
- if (need_enable()) {
- assert(pid_);
- return add_to_semaphore(-1);
- }
- return true;
-}
-
-std::string Probe::largest_arg_type(size_t arg_n) {
- Argument *largest = nullptr;
- for (Location &location : locations_) {
- Argument *candidate = &location.arguments_[arg_n];
- if (!largest ||
- std::abs(candidate->arg_size()) > std::abs(largest->arg_size()))
- largest = candidate;
- }
-
- assert(largest);
- return largest->ctype();
-}
-
-bool Probe::usdt_getarg(std::ostream &stream) {
- const size_t arg_count = locations_[0].arguments_.size();
-
- if (!attached_to_)
- return false;
-
- if (arg_count == 0)
- return true;
-
- for (size_t arg_n = 0; arg_n < arg_count; ++arg_n) {
- std::string ctype = largest_arg_type(arg_n);
- std::string cptr = tfm::format("*((%s *)dest)", ctype);
-
- tfm::format(stream,
- "static __always_inline int _bpf_readarg_%s_%d("
- "struct pt_regs *ctx, void *dest, size_t len) {\n"
- " if (len != sizeof(%s)) return -1;\n",
- attached_to_.value(), arg_n + 1, ctype);
-
- if (locations_.size() == 1) {
- Location &location = locations_.front();
- stream << " ";
- if (!location.arguments_[arg_n].assign_to_local(stream, cptr, bin_path_,
- pid_))
- return false;
- stream << "\n return 0;\n}\n";
- } else {
- stream << " switch(ctx->ip) {\n";
- for (Location &location : locations_) {
- uint64_t global_address;
-
- if (!resolve_global_address(&global_address, location.address_))
- return false;
-
- tfm::format(stream, " case 0x%xULL: ", global_address);
- if (!location.arguments_[arg_n].assign_to_local(stream, cptr, bin_path_,
- pid_))
- return false;
-
- stream << " return 0;\n";
- }
- stream << " }\n";
- stream << " return -1;\n}\n";
- }
- }
- return true;
-}
-
-void Probe::add_location(uint64_t addr, const char *fmt) {
- locations_.emplace_back(addr, fmt);
-}
-
-void Context::_each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
- void *p) {
- Context *ctx = static_cast<Context *>(p);
- ctx->add_probe(binpath, probe);
-}
-
-int Context::_each_module(const char *modpath, uint64_t, uint64_t, bool, void *p) {
- Context *ctx = static_cast<Context *>(p);
- // Modules may be reported multiple times if they contain more than one
- // executable region. We are going to parse the ELF on disk anyway, so we
- // don't need these duplicates.
- if (ctx->modules_.insert(modpath).second /*inserted new?*/) {
- ProcMountNSGuard g(ctx->mount_ns_instance_.get());
- bcc_elf_foreach_usdt(modpath, _each_probe, p);
- }
- return 0;
-}
-
-void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) {
- for (auto &p : probes_) {
- if (p->provider_ == probe->provider && p->name_ == probe->name) {
- p->add_location(probe->pc, probe->arg_fmt);
- return;
- }
- }
-
- probes_.emplace_back(
- new Probe(binpath, probe->provider, probe->name, probe->semaphore, pid_,
- mount_ns_instance_.get()));
- probes_.back()->add_location(probe->pc, probe->arg_fmt);
-}
-
-std::string Context::resolve_bin_path(const std::string &bin_path) {
- std::string result;
-
- if (char *which = bcc_procutils_which(bin_path.c_str())) {
- result = which;
- ::free(which);
- } else if (char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) {
- result = which_so;
- ::free(which_so);
- }
-
- return result;
-}
-
-Probe *Context::get(const std::string &probe_name) {
- for (auto &p : probes_) {
- if (p->name_ == probe_name)
- return p.get();
- }
- return nullptr;
-}
-
-bool Context::enable_probe(const std::string &probe_name,
- const std::string &fn_name) {
- if (pid_stat_ && pid_stat_->is_stale())
- return false;
-
- auto p = get(probe_name);
- return p && p->enable(fn_name);
-}
-
-void Context::each(each_cb callback) {
- for (const auto &probe : probes_) {
- struct bcc_usdt info = {0};
- info.provider = probe->provider().c_str();
- info.bin_path = probe->bin_path().c_str();
- info.name = probe->name().c_str();
- info.semaphore = probe->semaphore();
- info.num_locations = probe->num_locations();
- info.num_arguments = probe->num_arguments();
- callback(&info);
- }
-}
-
-void Context::each_uprobe(each_uprobe_cb callback) {
- for (auto &p : probes_) {
- if (!p->enabled())
- continue;
-
- for (Location &loc : p->locations_) {
- callback(p->bin_path_.c_str(), p->attached_to_->c_str(), loc.address_,
- pid_.value_or(-1));
- }
- }
-}
-
-Context::Context(const std::string &bin_path) : loaded_(false) {
- std::string full_path = resolve_bin_path(bin_path);
- if (!full_path.empty()) {
- if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0) {
- cmd_bin_path_ = full_path;
- loaded_ = true;
- }
- }
-}
-
-Context::Context(int pid) : pid_(pid), pid_stat_(pid),
- mount_ns_instance_(new ProcMountNS(pid)), loaded_(false) {
- if (bcc_procutils_each_module(pid, _each_module, this) == 0) {
- // get exe command from /proc/<pid>/exe
- // assume the maximum path length 4096, which should be
- // sufficiently large to cover all use cases
- char source[64];
- char cmd_buf[4096];
- snprintf(source, sizeof(source), "/proc/%d/exe", pid);
- ssize_t cmd_len = readlink(source, cmd_buf, sizeof(cmd_buf) - 1);
- if (cmd_len == -1)
- return;
- cmd_buf[cmd_len] = '\0';
- cmd_bin_path_.assign(cmd_buf, cmd_len + 1);
-
- loaded_ = true;
- }
-}
-
-Context::~Context() {
- if (pid_stat_ && !pid_stat_->is_stale()) {
- for (auto &p : probes_) p->disable();
- }
-}
-}
-
-extern "C" {
-
-void *bcc_usdt_new_frompid(int pid) {
- USDT::Context *ctx = new USDT::Context(pid);
- if (!ctx->loaded()) {
- delete ctx;
- return nullptr;
- }
- return static_cast<void *>(ctx);
-}
-
-void *bcc_usdt_new_frompath(const char *path) {
- USDT::Context *ctx = new USDT::Context(path);
- if (!ctx->loaded()) {
- delete ctx;
- return nullptr;
- }
- return static_cast<void *>(ctx);
-}
-
-void bcc_usdt_close(void *usdt) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
- delete ctx;
-}
-
-int bcc_usdt_enable_probe(void *usdt, const char *probe_name,
- const char *fn_name) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
- return ctx->enable_probe(probe_name, fn_name) ? 0 : -1;
-}
-
-const char *bcc_usdt_genargs(void **usdt_array, int len) {
- static std::string storage_;
- std::ostringstream stream;
-
- if (!len)
- return "";
-
- stream << USDT::USDT_PROGRAM_HEADER;
- // Generate genargs codes for an array of USDT Contexts.
- //
- // Each mnt_point + cmd_bin_path + probe_provider + probe_name
- // uniquely identifies a probe.
- std::unordered_set<std::string> generated_probes;
- for (int i = 0; i < len; i++) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt_array[i]);
-
- for (size_t j = 0; j < ctx->num_probes(); j++) {
- USDT::Probe *p = ctx->get(j);
- if (p->enabled()) {
- std::string key = std::to_string(ctx->inode()) + "*"
- + ctx->cmd_bin_path() + "*" + p->provider() + "*" + p->name();
- if (generated_probes.find(key) != generated_probes.end())
- continue;
- if (!p->usdt_getarg(stream))
- return nullptr;
- generated_probes.insert(key);
- }
- }
- }
-
- storage_ = stream.str();
- return storage_.c_str();
-}
-
-const char *bcc_usdt_get_probe_argctype(
- void *ctx, const char* probe_name, const int arg_index
-) {
- USDT::Probe *p = static_cast<USDT::Context *>(ctx)->get(probe_name);
- std::string res = p ? p->get_arg_ctype(arg_index) : "";
- return res.c_str();
-}
-
-void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
- ctx->each(callback);
-}
-
-int bcc_usdt_get_location(void *usdt, const char *probe_name,
- int index, struct bcc_usdt_location *location) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
- USDT::Probe *probe = ctx->get(probe_name);
- if (!probe)
- return -1;
- if (index < 0 || (size_t)index >= probe->num_locations())
- return -1;
- location->address = probe->address(index);
- return 0;
-}
-
-int bcc_usdt_get_argument(void *usdt, const char *probe_name,
- int location_index, int argument_index,
- struct bcc_usdt_argument *argument) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
- USDT::Probe *probe = ctx->get(probe_name);
- if (!probe)
- return -1;
- if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments())
- return -1;
- if (location_index < 0 || (size_t)location_index >= probe->num_locations())
- return -1;
- auto const &location = probe->location(location_index);
- auto const &arg = location.arguments_[argument_index];
- argument->size = arg.arg_size();
- argument->valid = BCC_USDT_ARGUMENT_NONE;
- if (arg.constant()) {
- argument->valid |= BCC_USDT_ARGUMENT_CONSTANT;
- argument->constant = *(arg.constant());
- }
- if (arg.deref_offset()) {
- argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET;
- argument->deref_offset = *(arg.deref_offset());
- }
- if (arg.deref_ident()) {
- argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT;
- argument->deref_ident = arg.deref_ident()->c_str();
- }
- if (arg.base_register_name()) {
- argument->valid |= BCC_USDT_ARGUMENT_BASE_REGISTER_NAME;
- argument->base_register_name = arg.base_register_name()->c_str();
- }
- if (arg.index_register_name()) {
- argument->valid |= BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME;
- argument->index_register_name = arg.index_register_name()->c_str();
- }
- if (arg.scale()) {
- argument->valid |= BCC_USDT_ARGUMENT_SCALE;
- argument->scale = *(arg.scale());
- }
- return 0;
-}
-
-void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback) {
- USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
- ctx->each_uprobe(callback);
-}
-}
--- /dev/null
+add_library(usdt-static STATIC usdt_args.cc usdt.cc)
--- /dev/null
+/*
+ * Copyright (c) 2016 GitHub, 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 <cstring>
+#include <sstream>
+#include <unordered_set>
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "bcc_elf.h"
+#include "bcc_proc.h"
+#include "usdt.h"
+#include "vendor/tinyformat.hpp"
+#include "bcc_usdt.h"
+
+namespace USDT {
+
+Location::Location(uint64_t addr, const char *arg_fmt) : address_(addr) {
+ ArgumentParser_x64 parser(arg_fmt);
+ while (!parser.done()) {
+ Argument arg;
+ if (!parser.parse(&arg))
+ continue;
+ arguments_.push_back(std::move(arg));
+ }
+}
+
+Probe::Probe(const char *bin_path, const char *provider, const char *name,
+ uint64_t semaphore, const optional<int> &pid, ProcMountNS *ns)
+ : bin_path_(bin_path),
+ provider_(provider),
+ name_(name),
+ semaphore_(semaphore),
+ pid_(pid),
+ mount_ns_(ns) {}
+
+bool Probe::in_shared_object() {
+ if (!in_shared_object_) {
+ ProcMountNSGuard g(mount_ns_);
+ in_shared_object_ = bcc_elf_is_shared_obj(bin_path_.c_str());
+ }
+ return in_shared_object_.value();
+}
+
+bool Probe::resolve_global_address(uint64_t *global, const uint64_t addr) {
+ if (in_shared_object()) {
+ return (pid_ &&
+ !bcc_resolve_global_addr(*pid_, bin_path_.c_str(), addr, global));
+ }
+
+ *global = addr;
+ return true;
+}
+
+bool Probe::add_to_semaphore(int16_t val) {
+ assert(pid_);
+
+ if (!attached_semaphore_) {
+ uint64_t addr;
+ if (!resolve_global_address(&addr, semaphore_))
+ return false;
+ attached_semaphore_ = addr;
+ }
+
+ off_t address = static_cast<off_t>(attached_semaphore_.value());
+
+ std::string procmem = tfm::format("/proc/%d/mem", pid_.value());
+ int memfd = ::open(procmem.c_str(), O_RDWR);
+ if (memfd < 0)
+ return false;
+
+ int16_t original;
+
+ if (::lseek(memfd, address, SEEK_SET) < 0 ||
+ ::read(memfd, &original, 2) != 2) {
+ ::close(memfd);
+ return false;
+ }
+
+ original = original + val;
+
+ if (::lseek(memfd, address, SEEK_SET) < 0 ||
+ ::write(memfd, &original, 2) != 2) {
+ ::close(memfd);
+ return false;
+ }
+
+ ::close(memfd);
+ return true;
+}
+
+bool Probe::enable(const std::string &fn_name) {
+ if (attached_to_)
+ return false;
+
+ if (need_enable()) {
+ if (!pid_)
+ return false;
+
+ if (!add_to_semaphore(+1))
+ return false;
+ }
+
+ attached_to_ = fn_name;
+ return true;
+}
+
+bool Probe::disable() {
+ if (!attached_to_)
+ return false;
+
+ attached_to_ = nullopt;
+
+ if (need_enable()) {
+ assert(pid_);
+ return add_to_semaphore(-1);
+ }
+ return true;
+}
+
+std::string Probe::largest_arg_type(size_t arg_n) {
+ Argument *largest = nullptr;
+ for (Location &location : locations_) {
+ Argument *candidate = &location.arguments_[arg_n];
+ if (!largest ||
+ std::abs(candidate->arg_size()) > std::abs(largest->arg_size()))
+ largest = candidate;
+ }
+
+ assert(largest);
+ return largest->ctype();
+}
+
+bool Probe::usdt_getarg(std::ostream &stream) {
+ const size_t arg_count = locations_[0].arguments_.size();
+
+ if (!attached_to_)
+ return false;
+
+ if (arg_count == 0)
+ return true;
+
+ for (size_t arg_n = 0; arg_n < arg_count; ++arg_n) {
+ std::string ctype = largest_arg_type(arg_n);
+ std::string cptr = tfm::format("*((%s *)dest)", ctype);
+
+ tfm::format(stream,
+ "static __always_inline int _bpf_readarg_%s_%d("
+ "struct pt_regs *ctx, void *dest, size_t len) {\n"
+ " if (len != sizeof(%s)) return -1;\n",
+ attached_to_.value(), arg_n + 1, ctype);
+
+ if (locations_.size() == 1) {
+ Location &location = locations_.front();
+ stream << " ";
+ if (!location.arguments_[arg_n].assign_to_local(stream, cptr, bin_path_,
+ pid_))
+ return false;
+ stream << "\n return 0;\n}\n";
+ } else {
+ stream << " switch(ctx->ip) {\n";
+ for (Location &location : locations_) {
+ uint64_t global_address;
+
+ if (!resolve_global_address(&global_address, location.address_))
+ return false;
+
+ tfm::format(stream, " case 0x%xULL: ", global_address);
+ if (!location.arguments_[arg_n].assign_to_local(stream, cptr, bin_path_,
+ pid_))
+ return false;
+
+ stream << " return 0;\n";
+ }
+ stream << " }\n";
+ stream << " return -1;\n}\n";
+ }
+ }
+ return true;
+}
+
+void Probe::add_location(uint64_t addr, const char *fmt) {
+ locations_.emplace_back(addr, fmt);
+}
+
+void Context::_each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
+ void *p) {
+ Context *ctx = static_cast<Context *>(p);
+ ctx->add_probe(binpath, probe);
+}
+
+int Context::_each_module(const char *modpath, uint64_t, uint64_t, bool, void *p) {
+ Context *ctx = static_cast<Context *>(p);
+ // Modules may be reported multiple times if they contain more than one
+ // executable region. We are going to parse the ELF on disk anyway, so we
+ // don't need these duplicates.
+ if (ctx->modules_.insert(modpath).second /*inserted new?*/) {
+ ProcMountNSGuard g(ctx->mount_ns_instance_.get());
+ bcc_elf_foreach_usdt(modpath, _each_probe, p);
+ }
+ return 0;
+}
+
+void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) {
+ for (auto &p : probes_) {
+ if (p->provider_ == probe->provider && p->name_ == probe->name) {
+ p->add_location(probe->pc, probe->arg_fmt);
+ return;
+ }
+ }
+
+ probes_.emplace_back(
+ new Probe(binpath, probe->provider, probe->name, probe->semaphore, pid_,
+ mount_ns_instance_.get()));
+ probes_.back()->add_location(probe->pc, probe->arg_fmt);
+}
+
+std::string Context::resolve_bin_path(const std::string &bin_path) {
+ std::string result;
+
+ if (char *which = bcc_procutils_which(bin_path.c_str())) {
+ result = which;
+ ::free(which);
+ } else if (char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) {
+ result = which_so;
+ ::free(which_so);
+ }
+
+ return result;
+}
+
+Probe *Context::get(const std::string &probe_name) {
+ for (auto &p : probes_) {
+ if (p->name_ == probe_name)
+ return p.get();
+ }
+ return nullptr;
+}
+
+bool Context::enable_probe(const std::string &probe_name,
+ const std::string &fn_name) {
+ if (pid_stat_ && pid_stat_->is_stale())
+ return false;
+
+ auto p = get(probe_name);
+ return p && p->enable(fn_name);
+}
+
+void Context::each(each_cb callback) {
+ for (const auto &probe : probes_) {
+ struct bcc_usdt info = {0};
+ info.provider = probe->provider().c_str();
+ info.bin_path = probe->bin_path().c_str();
+ info.name = probe->name().c_str();
+ info.semaphore = probe->semaphore();
+ info.num_locations = probe->num_locations();
+ info.num_arguments = probe->num_arguments();
+ callback(&info);
+ }
+}
+
+void Context::each_uprobe(each_uprobe_cb callback) {
+ for (auto &p : probes_) {
+ if (!p->enabled())
+ continue;
+
+ for (Location &loc : p->locations_) {
+ callback(p->bin_path_.c_str(), p->attached_to_->c_str(), loc.address_,
+ pid_.value_or(-1));
+ }
+ }
+}
+
+Context::Context(const std::string &bin_path) : loaded_(false) {
+ std::string full_path = resolve_bin_path(bin_path);
+ if (!full_path.empty()) {
+ if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0) {
+ cmd_bin_path_ = full_path;
+ loaded_ = true;
+ }
+ }
+}
+
+Context::Context(int pid) : pid_(pid), pid_stat_(pid),
+ mount_ns_instance_(new ProcMountNS(pid)), loaded_(false) {
+ if (bcc_procutils_each_module(pid, _each_module, this) == 0) {
+ // get exe command from /proc/<pid>/exe
+ // assume the maximum path length 4096, which should be
+ // sufficiently large to cover all use cases
+ char source[64];
+ char cmd_buf[4096];
+ snprintf(source, sizeof(source), "/proc/%d/exe", pid);
+ ssize_t cmd_len = readlink(source, cmd_buf, sizeof(cmd_buf) - 1);
+ if (cmd_len == -1)
+ return;
+ cmd_buf[cmd_len] = '\0';
+ cmd_bin_path_.assign(cmd_buf, cmd_len + 1);
+
+ loaded_ = true;
+ }
+}
+
+Context::~Context() {
+ if (pid_stat_ && !pid_stat_->is_stale()) {
+ for (auto &p : probes_) p->disable();
+ }
+}
+}
+
+extern "C" {
+
+void *bcc_usdt_new_frompid(int pid) {
+ USDT::Context *ctx = new USDT::Context(pid);
+ if (!ctx->loaded()) {
+ delete ctx;
+ return nullptr;
+ }
+ return static_cast<void *>(ctx);
+}
+
+void *bcc_usdt_new_frompath(const char *path) {
+ USDT::Context *ctx = new USDT::Context(path);
+ if (!ctx->loaded()) {
+ delete ctx;
+ return nullptr;
+ }
+ return static_cast<void *>(ctx);
+}
+
+void bcc_usdt_close(void *usdt) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
+ delete ctx;
+}
+
+int bcc_usdt_enable_probe(void *usdt, const char *probe_name,
+ const char *fn_name) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
+ return ctx->enable_probe(probe_name, fn_name) ? 0 : -1;
+}
+
+const char *bcc_usdt_genargs(void **usdt_array, int len) {
+ static std::string storage_;
+ std::ostringstream stream;
+
+ if (!len)
+ return "";
+
+ stream << USDT::USDT_PROGRAM_HEADER;
+ // Generate genargs codes for an array of USDT Contexts.
+ //
+ // Each mnt_point + cmd_bin_path + probe_provider + probe_name
+ // uniquely identifies a probe.
+ std::unordered_set<std::string> generated_probes;
+ for (int i = 0; i < len; i++) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt_array[i]);
+
+ for (size_t j = 0; j < ctx->num_probes(); j++) {
+ USDT::Probe *p = ctx->get(j);
+ if (p->enabled()) {
+ std::string key = std::to_string(ctx->inode()) + "*"
+ + ctx->cmd_bin_path() + "*" + p->provider() + "*" + p->name();
+ if (generated_probes.find(key) != generated_probes.end())
+ continue;
+ if (!p->usdt_getarg(stream))
+ return nullptr;
+ generated_probes.insert(key);
+ }
+ }
+ }
+
+ storage_ = stream.str();
+ return storage_.c_str();
+}
+
+const char *bcc_usdt_get_probe_argctype(
+ void *ctx, const char* probe_name, const int arg_index
+) {
+ USDT::Probe *p = static_cast<USDT::Context *>(ctx)->get(probe_name);
+ std::string res = p ? p->get_arg_ctype(arg_index) : "";
+ return res.c_str();
+}
+
+void bcc_usdt_foreach(void *usdt, bcc_usdt_cb callback) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
+ ctx->each(callback);
+}
+
+int bcc_usdt_get_location(void *usdt, const char *probe_name,
+ int index, struct bcc_usdt_location *location) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
+ USDT::Probe *probe = ctx->get(probe_name);
+ if (!probe)
+ return -1;
+ if (index < 0 || (size_t)index >= probe->num_locations())
+ return -1;
+ location->address = probe->address(index);
+ return 0;
+}
+
+int bcc_usdt_get_argument(void *usdt, const char *probe_name,
+ int location_index, int argument_index,
+ struct bcc_usdt_argument *argument) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
+ USDT::Probe *probe = ctx->get(probe_name);
+ if (!probe)
+ return -1;
+ if (argument_index < 0 || (size_t)argument_index >= probe->num_arguments())
+ return -1;
+ if (location_index < 0 || (size_t)location_index >= probe->num_locations())
+ return -1;
+ auto const &location = probe->location(location_index);
+ auto const &arg = location.arguments_[argument_index];
+ argument->size = arg.arg_size();
+ argument->valid = BCC_USDT_ARGUMENT_NONE;
+ if (arg.constant()) {
+ argument->valid |= BCC_USDT_ARGUMENT_CONSTANT;
+ argument->constant = *(arg.constant());
+ }
+ if (arg.deref_offset()) {
+ argument->valid |= BCC_USDT_ARGUMENT_DEREF_OFFSET;
+ argument->deref_offset = *(arg.deref_offset());
+ }
+ if (arg.deref_ident()) {
+ argument->valid |= BCC_USDT_ARGUMENT_DEREF_IDENT;
+ argument->deref_ident = arg.deref_ident()->c_str();
+ }
+ if (arg.base_register_name()) {
+ argument->valid |= BCC_USDT_ARGUMENT_BASE_REGISTER_NAME;
+ argument->base_register_name = arg.base_register_name()->c_str();
+ }
+ if (arg.index_register_name()) {
+ argument->valid |= BCC_USDT_ARGUMENT_INDEX_REGISTER_NAME;
+ argument->index_register_name = arg.index_register_name()->c_str();
+ }
+ if (arg.scale()) {
+ argument->valid |= BCC_USDT_ARGUMENT_SCALE;
+ argument->scale = *(arg.scale());
+ }
+ return 0;
+}
+
+void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback) {
+ USDT::Context *ctx = static_cast<USDT::Context *>(usdt);
+ ctx->each_uprobe(callback);
+}
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 GitHub, 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 <unordered_map>
+
+#include "syms.h"
+#include "usdt.h"
+#include "vendor/tinyformat.hpp"
+
+#include "bcc_elf.h"
+#include "bcc_syms.h"
+
+namespace USDT {
+
+Argument::Argument() {}
+Argument::~Argument() {}
+
+std::string Argument::ctype() const {
+ const int s = arg_size() * 8;
+ return (s < 0) ? tfm::format("int%d_t", -s) : tfm::format("uint%d_t", s);
+}
+
+bool Argument::get_global_address(uint64_t *address, const std::string &binpath,
+ const optional<int> &pid) const {
+ if (pid) {
+ static struct bcc_symbol_option default_option = {
+ .use_debug_file = 1,
+ .check_debug_file_crc = 1,
+ .use_symbol_type = BCC_SYM_ALL_TYPES
+ };
+ return ProcSyms(*pid, &default_option)
+ .resolve_name(binpath.c_str(), deref_ident_->c_str(), address);
+ }
+
+ if (!bcc_elf_is_shared_obj(binpath.c_str())) {
+ struct bcc_symbol sym;
+ if (bcc_resolve_symname(binpath.c_str(), deref_ident_->c_str(), 0x0, -1, nullptr, &sym) == 0) {
+ *address = sym.offset;
+ if (sym.module)
+ ::free(const_cast<char*>(sym.module));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Argument::assign_to_local(std::ostream &stream,
+ const std::string &local_name,
+ const std::string &binpath,
+ const optional<int> &pid) const {
+ if (constant_) {
+ tfm::format(stream, "%s = %d;", local_name, *constant_);
+ return true;
+ }
+
+ if (!deref_offset_) {
+ tfm::format(stream, "%s = ctx->%s;", local_name, *base_register_name_);
+ // Put a compiler barrier to prevent optimization
+ // like llvm SimplifyCFG SinkThenElseCodeToEnd
+ // Volatile marking is not sufficient to prevent such optimization.
+ tfm::format(stream, " %s", COMPILER_BARRIER);
+ return true;
+ }
+
+ if (deref_offset_ && !deref_ident_) {
+ tfm::format(stream, "{ u64 __addr = ctx->%s + %d",
+ *base_register_name_, *deref_offset_);
+ if (index_register_name_) {
+ int scale = scale_.value_or(1);
+ tfm::format(stream, " + (ctx->%s * %d);", *index_register_name_, scale);
+ } else {
+ tfm::format(stream, ";");
+ }
+ // Theoretically, llvm SimplifyCFG SinkThenElseCodeToEnd may still
+ // sink bpf_probe_read call, so put a barrier here to prevent sinking
+ // of ctx->#fields.
+ tfm::format(stream, " %s ", COMPILER_BARRIER);
+ tfm::format(stream,
+ "%s __res = 0x0; "
+ "bpf_probe_read(&__res, sizeof(__res), (void *)__addr); "
+ "%s = __res; }",
+ ctype(), local_name);
+ return true;
+ }
+
+ if (deref_offset_ && deref_ident_ && *base_register_name_ == "ip") {
+ uint64_t global_address;
+ if (!get_global_address(&global_address, binpath, pid))
+ return false;
+
+ tfm::format(stream,
+ "{ u64 __addr = 0x%xull + %d; %s __res = 0x0; "
+ "bpf_probe_read(&__res, sizeof(__res), (void *)__addr); "
+ "%s = __res; }",
+ global_address, *deref_offset_, ctype(), local_name);
+ return true;
+ }
+
+ return false;
+}
+
+ssize_t ArgumentParser::parse_number(ssize_t pos, optional<int> *result) {
+ char *endp;
+ int number = strtol(arg_ + pos, &endp, 0);
+ if (endp > arg_ + pos)
+ *result = number;
+ return endp - arg_;
+}
+
+ssize_t ArgumentParser::parse_identifier(ssize_t pos,
+ optional<std::string> *result) {
+ if (isalpha(arg_[pos]) || arg_[pos] == '_') {
+ ssize_t start = pos++;
+ while (isalnum(arg_[pos]) || arg_[pos] == '_') pos++;
+ if (pos - start)
+ result->emplace(arg_ + start, pos - start);
+ }
+ return pos;
+}
+
+ssize_t ArgumentParser::parse_register(ssize_t pos, std::string &name,
+ int &size) {
+ ssize_t start = ++pos;
+ if (arg_[start - 1] != '%')
+ return -start;
+
+ while (isalnum(arg_[pos])) pos++;
+
+ std::string regname(arg_ + start, pos - start);
+ if (!normalize_register(®name, &size))
+ return -start;
+
+ name = regname;
+ return pos;
+}
+
+ssize_t ArgumentParser::parse_base_register(ssize_t pos, Argument *dest) {
+ int size;
+ std::string name;
+ ssize_t res = parse_register(pos, name, size);
+ if (res < 0)
+ return res;
+
+ dest->base_register_name_ = name;
+ if (!dest->arg_size_)
+ dest->arg_size_ = size;
+
+ return res;
+}
+
+ssize_t ArgumentParser::parse_index_register(ssize_t pos, Argument *dest) {
+ int size;
+ std::string name;
+ ssize_t res = parse_register(pos, name, size);
+ if (res < 0)
+ return res;
+
+ dest->index_register_name_ = name;
+
+ return res;
+}
+
+ssize_t ArgumentParser::parse_scale(ssize_t pos, Argument *dest) {
+ return parse_number(pos, &dest->scale_);
+}
+
+ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument *dest) {
+ if (arg_[pos] == '$')
+ return parse_number(pos + 1, &dest->constant_);
+
+ if (arg_[pos] == '%')
+ return parse_base_register(pos, dest);
+
+ if (isdigit(arg_[pos]) || arg_[pos] == '-') {
+ pos = parse_number(pos, &dest->deref_offset_);
+ if (arg_[pos] == '+') {
+ pos = parse_identifier(pos + 1, &dest->deref_ident_);
+ if (!dest->deref_ident_)
+ return -pos;
+ }
+ } else {
+ dest->deref_offset_ = 0;
+ pos = parse_identifier(pos, &dest->deref_ident_);
+ if (arg_[pos] == '+' || arg_[pos] == '-') {
+ pos = parse_number(pos, &dest->deref_offset_);
+ }
+ }
+
+ if (arg_[pos] != '(')
+ return -pos;
+
+ pos = parse_base_register(pos + 1, dest);
+ if (pos < 0)
+ return pos;
+
+ if (arg_[pos] == ',') {
+ pos = parse_index_register(pos + 1, dest);
+ if (pos < 0)
+ return pos;
+
+ if (arg_[pos] == ',') {
+ pos = parse_scale(pos + 1, dest);
+ if (pos < 0)
+ return pos;
+ }
+ }
+
+ return (arg_[pos] == ')') ? pos + 1 : -pos;
+}
+
+ssize_t ArgumentParser::parse_1(ssize_t pos, Argument *dest) {
+ if (isdigit(arg_[pos]) || arg_[pos] == '-') {
+ optional<int> asize;
+ ssize_t m = parse_number(pos, &asize);
+ if (arg_[m] == '@' && asize) {
+ dest->arg_size_ = asize;
+ return parse_expr(m + 1, dest);
+ }
+ }
+ return parse_expr(pos, dest);
+}
+
+void ArgumentParser::print_error(ssize_t pos) {
+ fprintf(stderr, "Parse error:\n %s\n", arg_);
+ for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr);
+ fputc('^', stderr);
+ fputc('\n', stderr);
+}
+
+void ArgumentParser::skip_whitespace_from(size_t pos) {
+ while (isspace(arg_[pos])) pos++;
+ cur_pos_ = pos;
+}
+
+void ArgumentParser::skip_until_whitespace_from(size_t pos) {
+ while (arg_[pos] != '\0' && !isspace(arg_[pos]))
+ pos++;
+ cur_pos_ = pos;
+}
+
+bool ArgumentParser::parse(Argument *dest) {
+ if (done())
+ return false;
+
+ ssize_t res = parse_1(cur_pos_, dest);
+ if (res < 0) {
+ print_error(-res);
+ skip_whitespace_from(-res + 1);
+ return false;
+ }
+ if (!isspace(arg_[res]) && arg_[res] != '\0') {
+ print_error(res);
+ skip_until_whitespace_from(res);
+ return false;
+ }
+ skip_whitespace_from(res);
+ return true;
+}
+
+const std::unordered_map<std::string, ArgumentParser_x64::RegInfo>
+ ArgumentParser_x64::registers_ = {
+ {"rax", {REG_A, 8}}, {"eax", {REG_A, 4}},
+ {"ax", {REG_A, 2}}, {"al", {REG_A, 1}},
+
+ {"rbx", {REG_B, 8}}, {"ebx", {REG_B, 4}},
+ {"bx", {REG_B, 2}}, {"bl", {REG_B, 1}},
+
+ {"rcx", {REG_C, 8}}, {"ecx", {REG_C, 4}},
+ {"cx", {REG_C, 2}}, {"cl", {REG_C, 1}},
+
+ {"rdx", {REG_D, 8}}, {"edx", {REG_D, 4}},
+ {"dx", {REG_D, 2}}, {"dl", {REG_D, 1}},
+
+ {"rsi", {REG_SI, 8}}, {"esi", {REG_SI, 4}},
+ {"si", {REG_SI, 2}}, {"sil", {REG_SI, 1}},
+
+ {"rdi", {REG_DI, 8}}, {"edi", {REG_DI, 4}},
+ {"di", {REG_DI, 2}}, {"dil", {REG_DI, 1}},
+
+ {"rbp", {REG_BP, 8}}, {"ebp", {REG_BP, 4}},
+ {"bp", {REG_BP, 2}}, {"bpl", {REG_BP, 1}},
+
+ {"rsp", {REG_SP, 8}}, {"esp", {REG_SP, 4}},
+ {"sp", {REG_SP, 2}}, {"spl", {REG_SP, 1}},
+
+ {"r8", {REG_8, 8}}, {"r8d", {REG_8, 4}},
+ {"r8w", {REG_8, 2}}, {"r8b", {REG_8, 1}},
+
+ {"r9", {REG_9, 8}}, {"r9d", {REG_9, 4}},
+ {"r9w", {REG_9, 2}}, {"r9b", {REG_9, 1}},
+
+ {"r10", {REG_10, 8}}, {"r10d", {REG_10, 4}},
+ {"r10w", {REG_10, 2}}, {"r10b", {REG_10, 1}},
+
+ {"r11", {REG_11, 8}}, {"r11d", {REG_11, 4}},
+ {"r11w", {REG_11, 2}}, {"r11b", {REG_11, 1}},
+
+ {"r12", {REG_12, 8}}, {"r12d", {REG_12, 4}},
+ {"r12w", {REG_12, 2}}, {"r12b", {REG_12, 1}},
+
+ {"r13", {REG_13, 8}}, {"r13d", {REG_13, 4}},
+ {"r13w", {REG_13, 2}}, {"r13b", {REG_13, 1}},
+
+ {"r14", {REG_14, 8}}, {"r14d", {REG_14, 4}},
+ {"r14w", {REG_14, 2}}, {"r14b", {REG_14, 1}},
+
+ {"r15", {REG_15, 8}}, {"r15d", {REG_15, 4}},
+ {"r15w", {REG_15, 2}}, {"r15b", {REG_15, 1}},
+
+ {"rip", {REG_RIP, 8}},
+};
+
+void ArgumentParser_x64::reg_to_name(std::string *norm, Register reg) {
+ switch (reg) {
+ case REG_A:
+ *norm = "ax";
+ break;
+ case REG_B:
+ *norm = "bx";
+ break;
+ case REG_C:
+ *norm = "cx";
+ break;
+ case REG_D:
+ *norm = "dx";
+ break;
+
+ case REG_SI:
+ *norm = "si";
+ break;
+ case REG_DI:
+ *norm = "di";
+ break;
+ case REG_BP:
+ *norm = "bp";
+ break;
+ case REG_SP:
+ *norm = "sp";
+ break;
+
+ case REG_8:
+ *norm = "r8";
+ break;
+ case REG_9:
+ *norm = "r9";
+ break;
+ case REG_10:
+ *norm = "r10";
+ break;
+ case REG_11:
+ *norm = "r11";
+ break;
+ case REG_12:
+ *norm = "r12";
+ break;
+ case REG_13:
+ *norm = "r13";
+ break;
+ case REG_14:
+ *norm = "r14";
+ break;
+ case REG_15:
+ *norm = "r15";
+ break;
+
+ case REG_RIP:
+ *norm = "ip";
+ break;
+ }
+}
+
+bool ArgumentParser_x64::normalize_register(std::string *reg, int *reg_size) {
+ auto it = registers_.find(*reg);
+ if (it == registers_.end())
+ return false;
+
+ *reg_size = it->second.size;
+ reg_to_name(reg, it->second.reg);
+ return true;
+}
+}
+++ /dev/null
-/*
- * Copyright (c) 2016 GitHub, 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 <unordered_map>
-
-#include "syms.h"
-#include "usdt.h"
-#include "vendor/tinyformat.hpp"
-
-#include "bcc_elf.h"
-#include "bcc_syms.h"
-
-namespace USDT {
-
-Argument::Argument() {}
-Argument::~Argument() {}
-
-std::string Argument::ctype() const {
- const int s = arg_size() * 8;
- return (s < 0) ? tfm::format("int%d_t", -s) : tfm::format("uint%d_t", s);
-}
-
-bool Argument::get_global_address(uint64_t *address, const std::string &binpath,
- const optional<int> &pid) const {
- if (pid) {
- static struct bcc_symbol_option default_option = {
- .use_debug_file = 1,
- .check_debug_file_crc = 1,
- .use_symbol_type = BCC_SYM_ALL_TYPES
- };
- return ProcSyms(*pid, &default_option)
- .resolve_name(binpath.c_str(), deref_ident_->c_str(), address);
- }
-
- if (!bcc_elf_is_shared_obj(binpath.c_str())) {
- struct bcc_symbol sym;
- if (bcc_resolve_symname(binpath.c_str(), deref_ident_->c_str(), 0x0, -1, nullptr, &sym) == 0) {
- *address = sym.offset;
- if (sym.module)
- ::free(const_cast<char*>(sym.module));
- return true;
- }
- }
-
- return false;
-}
-
-bool Argument::assign_to_local(std::ostream &stream,
- const std::string &local_name,
- const std::string &binpath,
- const optional<int> &pid) const {
- if (constant_) {
- tfm::format(stream, "%s = %d;", local_name, *constant_);
- return true;
- }
-
- if (!deref_offset_) {
- tfm::format(stream, "%s = ctx->%s;", local_name, *base_register_name_);
- // Put a compiler barrier to prevent optimization
- // like llvm SimplifyCFG SinkThenElseCodeToEnd
- // Volatile marking is not sufficient to prevent such optimization.
- tfm::format(stream, " %s", COMPILER_BARRIER);
- return true;
- }
-
- if (deref_offset_ && !deref_ident_) {
- tfm::format(stream, "{ u64 __addr = ctx->%s + %d",
- *base_register_name_, *deref_offset_);
- if (index_register_name_) {
- int scale = scale_.value_or(1);
- tfm::format(stream, " + (ctx->%s * %d);", *index_register_name_, scale);
- } else {
- tfm::format(stream, ";");
- }
- // Theoretically, llvm SimplifyCFG SinkThenElseCodeToEnd may still
- // sink bpf_probe_read call, so put a barrier here to prevent sinking
- // of ctx->#fields.
- tfm::format(stream, " %s ", COMPILER_BARRIER);
- tfm::format(stream,
- "%s __res = 0x0; "
- "bpf_probe_read(&__res, sizeof(__res), (void *)__addr); "
- "%s = __res; }",
- ctype(), local_name);
- return true;
- }
-
- if (deref_offset_ && deref_ident_ && *base_register_name_ == "ip") {
- uint64_t global_address;
- if (!get_global_address(&global_address, binpath, pid))
- return false;
-
- tfm::format(stream,
- "{ u64 __addr = 0x%xull + %d; %s __res = 0x0; "
- "bpf_probe_read(&__res, sizeof(__res), (void *)__addr); "
- "%s = __res; }",
- global_address, *deref_offset_, ctype(), local_name);
- return true;
- }
-
- return false;
-}
-
-ssize_t ArgumentParser::parse_number(ssize_t pos, optional<int> *result) {
- char *endp;
- int number = strtol(arg_ + pos, &endp, 0);
- if (endp > arg_ + pos)
- *result = number;
- return endp - arg_;
-}
-
-ssize_t ArgumentParser::parse_identifier(ssize_t pos,
- optional<std::string> *result) {
- if (isalpha(arg_[pos]) || arg_[pos] == '_') {
- ssize_t start = pos++;
- while (isalnum(arg_[pos]) || arg_[pos] == '_') pos++;
- if (pos - start)
- result->emplace(arg_ + start, pos - start);
- }
- return pos;
-}
-
-ssize_t ArgumentParser::parse_register(ssize_t pos, std::string &name,
- int &size) {
- ssize_t start = ++pos;
- if (arg_[start - 1] != '%')
- return -start;
-
- while (isalnum(arg_[pos])) pos++;
-
- std::string regname(arg_ + start, pos - start);
- if (!normalize_register(®name, &size))
- return -start;
-
- name = regname;
- return pos;
-}
-
-ssize_t ArgumentParser::parse_base_register(ssize_t pos, Argument *dest) {
- int size;
- std::string name;
- ssize_t res = parse_register(pos, name, size);
- if (res < 0)
- return res;
-
- dest->base_register_name_ = name;
- if (!dest->arg_size_)
- dest->arg_size_ = size;
-
- return res;
-}
-
-ssize_t ArgumentParser::parse_index_register(ssize_t pos, Argument *dest) {
- int size;
- std::string name;
- ssize_t res = parse_register(pos, name, size);
- if (res < 0)
- return res;
-
- dest->index_register_name_ = name;
-
- return res;
-}
-
-ssize_t ArgumentParser::parse_scale(ssize_t pos, Argument *dest) {
- return parse_number(pos, &dest->scale_);
-}
-
-ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument *dest) {
- if (arg_[pos] == '$')
- return parse_number(pos + 1, &dest->constant_);
-
- if (arg_[pos] == '%')
- return parse_base_register(pos, dest);
-
- if (isdigit(arg_[pos]) || arg_[pos] == '-') {
- pos = parse_number(pos, &dest->deref_offset_);
- if (arg_[pos] == '+') {
- pos = parse_identifier(pos + 1, &dest->deref_ident_);
- if (!dest->deref_ident_)
- return -pos;
- }
- } else {
- dest->deref_offset_ = 0;
- pos = parse_identifier(pos, &dest->deref_ident_);
- if (arg_[pos] == '+' || arg_[pos] == '-') {
- pos = parse_number(pos, &dest->deref_offset_);
- }
- }
-
- if (arg_[pos] != '(')
- return -pos;
-
- pos = parse_base_register(pos + 1, dest);
- if (pos < 0)
- return pos;
-
- if (arg_[pos] == ',') {
- pos = parse_index_register(pos + 1, dest);
- if (pos < 0)
- return pos;
-
- if (arg_[pos] == ',') {
- pos = parse_scale(pos + 1, dest);
- if (pos < 0)
- return pos;
- }
- }
-
- return (arg_[pos] == ')') ? pos + 1 : -pos;
-}
-
-ssize_t ArgumentParser::parse_1(ssize_t pos, Argument *dest) {
- if (isdigit(arg_[pos]) || arg_[pos] == '-') {
- optional<int> asize;
- ssize_t m = parse_number(pos, &asize);
- if (arg_[m] == '@' && asize) {
- dest->arg_size_ = asize;
- return parse_expr(m + 1, dest);
- }
- }
- return parse_expr(pos, dest);
-}
-
-void ArgumentParser::print_error(ssize_t pos) {
- fprintf(stderr, "Parse error:\n %s\n", arg_);
- for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr);
- fputc('^', stderr);
- fputc('\n', stderr);
-}
-
-void ArgumentParser::skip_whitespace_from(size_t pos) {
- while (isspace(arg_[pos])) pos++;
- cur_pos_ = pos;
-}
-
-void ArgumentParser::skip_until_whitespace_from(size_t pos) {
- while (arg_[pos] != '\0' && !isspace(arg_[pos]))
- pos++;
- cur_pos_ = pos;
-}
-
-bool ArgumentParser::parse(Argument *dest) {
- if (done())
- return false;
-
- ssize_t res = parse_1(cur_pos_, dest);
- if (res < 0) {
- print_error(-res);
- skip_whitespace_from(-res + 1);
- return false;
- }
- if (!isspace(arg_[res]) && arg_[res] != '\0') {
- print_error(res);
- skip_until_whitespace_from(res);
- return false;
- }
- skip_whitespace_from(res);
- return true;
-}
-
-const std::unordered_map<std::string, ArgumentParser_x64::RegInfo>
- ArgumentParser_x64::registers_ = {
- {"rax", {REG_A, 8}}, {"eax", {REG_A, 4}},
- {"ax", {REG_A, 2}}, {"al", {REG_A, 1}},
-
- {"rbx", {REG_B, 8}}, {"ebx", {REG_B, 4}},
- {"bx", {REG_B, 2}}, {"bl", {REG_B, 1}},
-
- {"rcx", {REG_C, 8}}, {"ecx", {REG_C, 4}},
- {"cx", {REG_C, 2}}, {"cl", {REG_C, 1}},
-
- {"rdx", {REG_D, 8}}, {"edx", {REG_D, 4}},
- {"dx", {REG_D, 2}}, {"dl", {REG_D, 1}},
-
- {"rsi", {REG_SI, 8}}, {"esi", {REG_SI, 4}},
- {"si", {REG_SI, 2}}, {"sil", {REG_SI, 1}},
-
- {"rdi", {REG_DI, 8}}, {"edi", {REG_DI, 4}},
- {"di", {REG_DI, 2}}, {"dil", {REG_DI, 1}},
-
- {"rbp", {REG_BP, 8}}, {"ebp", {REG_BP, 4}},
- {"bp", {REG_BP, 2}}, {"bpl", {REG_BP, 1}},
-
- {"rsp", {REG_SP, 8}}, {"esp", {REG_SP, 4}},
- {"sp", {REG_SP, 2}}, {"spl", {REG_SP, 1}},
-
- {"r8", {REG_8, 8}}, {"r8d", {REG_8, 4}},
- {"r8w", {REG_8, 2}}, {"r8b", {REG_8, 1}},
-
- {"r9", {REG_9, 8}}, {"r9d", {REG_9, 4}},
- {"r9w", {REG_9, 2}}, {"r9b", {REG_9, 1}},
-
- {"r10", {REG_10, 8}}, {"r10d", {REG_10, 4}},
- {"r10w", {REG_10, 2}}, {"r10b", {REG_10, 1}},
-
- {"r11", {REG_11, 8}}, {"r11d", {REG_11, 4}},
- {"r11w", {REG_11, 2}}, {"r11b", {REG_11, 1}},
-
- {"r12", {REG_12, 8}}, {"r12d", {REG_12, 4}},
- {"r12w", {REG_12, 2}}, {"r12b", {REG_12, 1}},
-
- {"r13", {REG_13, 8}}, {"r13d", {REG_13, 4}},
- {"r13w", {REG_13, 2}}, {"r13b", {REG_13, 1}},
-
- {"r14", {REG_14, 8}}, {"r14d", {REG_14, 4}},
- {"r14w", {REG_14, 2}}, {"r14b", {REG_14, 1}},
-
- {"r15", {REG_15, 8}}, {"r15d", {REG_15, 4}},
- {"r15w", {REG_15, 2}}, {"r15b", {REG_15, 1}},
-
- {"rip", {REG_RIP, 8}},
-};
-
-void ArgumentParser_x64::reg_to_name(std::string *norm, Register reg) {
- switch (reg) {
- case REG_A:
- *norm = "ax";
- break;
- case REG_B:
- *norm = "bx";
- break;
- case REG_C:
- *norm = "cx";
- break;
- case REG_D:
- *norm = "dx";
- break;
-
- case REG_SI:
- *norm = "si";
- break;
- case REG_DI:
- *norm = "di";
- break;
- case REG_BP:
- *norm = "bp";
- break;
- case REG_SP:
- *norm = "sp";
- break;
-
- case REG_8:
- *norm = "r8";
- break;
- case REG_9:
- *norm = "r9";
- break;
- case REG_10:
- *norm = "r10";
- break;
- case REG_11:
- *norm = "r11";
- break;
- case REG_12:
- *norm = "r12";
- break;
- case REG_13:
- *norm = "r13";
- break;
- case REG_14:
- *norm = "r14";
- break;
- case REG_15:
- *norm = "r15";
- break;
-
- case REG_RIP:
- *norm = "ip";
- break;
- }
-}
-
-bool ArgumentParser_x64::normalize_register(std::string *reg, int *reg_size) {
- auto it = registers_.find(*reg);
- if (it == registers_.end())
- return false;
-
- *reg_size = it->second.size;
- reg_to_name(reg, it->second.reg);
- return true;
-}
-}