src/cc: cmake file cleanup and split usdt into subdir
authorBrenden Blanco <bblanco@gmail.com>
Mon, 26 Jun 2017 20:37:34 +0000 (13:37 -0700)
committerBrenden Blanco <bblanco@gmail.com>
Fri, 25 Aug 2017 23:41:36 +0000 (16:41 -0700)
Move two usdt related files to a new subdirectory and link them into the
final product. Introduce a cmake option to disable that feature (turning
it off currently fails to compile).

Move some of the cmake flag computation into a helper cmake file and
include that rather than inlining it.

Signed-off-by: Brenden Blanco <bblanco@gmail.com>
cmake/clang_libs.cmake [new file with mode: 0644]
cmake/static_libstdc++.cmake [new file with mode: 0644]
src/cc/CMakeLists.txt
src/cc/usdt.cc [deleted file]
src/cc/usdt/CMakeLists.txt [new file with mode: 0644]
src/cc/usdt/usdt.cc [new file with mode: 0644]
src/cc/usdt/usdt_args.cc [new file with mode: 0644]
src/cc/usdt_args.cc [deleted file]

diff --git a/cmake/clang_libs.cmake b/cmake/clang_libs.cmake
new file mode 100644 (file)
index 0000000..907b784
--- /dev/null
@@ -0,0 +1,42 @@
+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}")
diff --git a/cmake/static_libstdc++.cmake b/cmake/static_libstdc++.cmake
new file mode 100644 (file)
index 0000000..3c8ac17
--- /dev/null
@@ -0,0 +1,15 @@
+# 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()
index 5c4cfa5a768be72ce2eb36487987aa66d3171992..bc25a8db0ecb24fe81d4f00041435aa701ded2ac 100644 (file)
@@ -17,21 +17,7 @@ configure_file(libbcc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libbcc.pc @ONLY)
 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)
@@ -40,8 +26,8 @@ set_target_properties(bpf-shared 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)
 
@@ -49,42 +35,25 @@ add_library(bcc-loader-static STATIC bcc_elf.c bcc_perf_map.c bcc_proc.c
   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})
diff --git a/src/cc/usdt.cc b/src/cc/usdt.cc
deleted file mode 100644 (file)
index ccb7caa..0000000
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * 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);
-}
-}
diff --git a/src/cc/usdt/CMakeLists.txt b/src/cc/usdt/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6a2e895
--- /dev/null
@@ -0,0 +1 @@
+add_library(usdt-static STATIC usdt_args.cc usdt.cc)
diff --git a/src/cc/usdt/usdt.cc b/src/cc/usdt/usdt.cc
new file mode 100644 (file)
index 0000000..ccb7caa
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * 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);
+}
+}
diff --git a/src/cc/usdt/usdt_args.cc b/src/cc/usdt/usdt_args.cc
new file mode 100644 (file)
index 0000000..42fb360
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * 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(&regname, &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;
+}
+}
diff --git a/src/cc/usdt_args.cc b/src/cc/usdt_args.cc
deleted file mode 100644 (file)
index 42fb360..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * 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(&regname, &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;
-}
-}