Use device+inode to identify matching files; prepend /proc/pid/root to
paths for usdt.
diff --git a/examples/cpp/pyperf/PyPerfUtil.cc b/examples/cpp/pyperf/PyPerfUtil.cc
index 252a0fe..b1495bb 100644
--- a/examples/cpp/pyperf/PyPerfUtil.cc
+++ b/examples/cpp/pyperf/PyPerfUtil.cc
@@ -93,20 +93,19 @@
 
 const static std::string kPy36LibName = "libpython3.6";
 
-int findPythonPathCallback(const char* name, uint64_t st, uint64_t en, uint64_t,
-                           bool, void* payload) {
+int findPythonPathCallback(mod_info *mod, int, void* payload) {
   auto helper = static_cast<FindPythonPathHelper*>(payload);
-  std::string file = name;
+  std::string file = mod->name;
   auto pos = file.rfind("/");
   if (pos != std::string::npos) {
     file = file.substr(pos + 1);
   }
   if (file.find(kPy36LibName) == 0) {
-    logInfo(1, "Found Python library %s loaded at %lx-%lx for PID %d\n", name,
-            st, en, helper->pid);
+    logInfo(1, "Found Python library %s loaded at %lx-%lx for PID %d\n", mod->name,
+            mod->start_addr, mod->end_addr, helper->pid);
     helper->found = true;
-    helper->st = st;
-    helper->en = en;
+    helper->st = mod->start_addr;
+    helper->en = mod->end_addr;
     return -1;
   }
   return 0;
diff --git a/src/cc/CMakeLists.txt b/src/cc/CMakeLists.txt
index bd34fd4..571a463 100644
--- a/src/cc/CMakeLists.txt
+++ b/src/cc/CMakeLists.txt
@@ -47,11 +47,11 @@
 endif()
 
 set(bcc_table_sources table_storage.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc)
-set(bcc_util_sources ns_guard.cc common.cc)
+set(bcc_util_sources common.cc)
 set(bcc_sym_sources bcc_syms.cc bcc_elf.c bcc_perf_map.c bcc_proc.c)
 set(bcc_common_headers libbpf.h perf_reader.h)
 set(bcc_table_headers file_desc.h table_desc.h table_storage.h)
-set(bcc_api_headers bcc_common.h bpf_module.h bcc_exception.h bcc_syms.h bcc_elf.h)
+set(bcc_api_headers bcc_common.h bpf_module.h bcc_exception.h bcc_syms.h bcc_proc.h bcc_elf.h)
 
 if(ENABLE_CLANG_JIT)
 add_library(bcc-shared SHARED
diff --git a/src/cc/api/BPF.cc b/src/cc/api/BPF.cc
index db2436d..b5aa52b 100644
--- a/src/cc/api/BPF.cc
+++ b/src/cc/api/BPF.cc
@@ -740,7 +740,8 @@
       pid_(-1),
       provider_(provider),
       name_(name),
-      probe_func_(probe_func) {}
+      probe_func_(probe_func),
+      mod_match_inode_only_(0) {}
 
 USDT::USDT(pid_t pid, const std::string& provider, const std::string& name,
            const std::string& probe_func)
@@ -749,7 +750,8 @@
       pid_(pid),
       provider_(provider),
       name_(name),
-      probe_func_(probe_func) {}
+      probe_func_(probe_func),
+      mod_match_inode_only_(0) {}
 
 USDT::USDT(const std::string& binary_path, pid_t pid,
            const std::string& provider, const std::string& name,
@@ -759,7 +761,8 @@
       pid_(pid),
       provider_(provider),
       name_(name),
-      probe_func_(probe_func) {}
+      probe_func_(probe_func),
+      mod_match_inode_only_(0) {}
 
 USDT::USDT(const USDT& usdt)
     : initialized_(false),
@@ -767,7 +770,8 @@
       pid_(usdt.pid_),
       provider_(usdt.provider_),
       name_(usdt.name_),
-      probe_func_(usdt.probe_func_) {}
+      probe_func_(usdt.probe_func_),
+      mod_match_inode_only_(usdt.mod_match_inode_only_) {}
 
 USDT::USDT(USDT&& usdt) noexcept
     : initialized_(usdt.initialized_),
@@ -777,7 +781,8 @@
       name_(std::move(usdt.name_)),
       probe_func_(std::move(usdt.probe_func_)),
       probe_(std::move(usdt.probe_)),
-      program_text_(std::move(usdt.program_text_)) {
+      program_text_(std::move(usdt.program_text_)),
+      mod_match_inode_only_(usdt.mod_match_inode_only_) {
   usdt.initialized_ = false;
 }
 
@@ -787,14 +792,22 @@
          (probe_func_ == other.probe_func_);
 }
 
+int USDT::set_probe_matching_kludge(uint8_t kludge) {
+  if (kludge != 0 && kludge != 1)
+    return -1;
+
+  mod_match_inode_only_ = kludge;
+  return 0;
+}
+
 StatusTuple USDT::init() {
   std::unique_ptr<::USDT::Context> ctx;
   if (!binary_path_.empty() && pid_ > 0)
-    ctx.reset(new ::USDT::Context(pid_, binary_path_));
+    ctx.reset(new ::USDT::Context(pid_, binary_path_, mod_match_inode_only_));
   else if (!binary_path_.empty())
-    ctx.reset(new ::USDT::Context(binary_path_));
+    ctx.reset(new ::USDT::Context(binary_path_, mod_match_inode_only_));
   else if (pid_ > 0)
-    ctx.reset(new ::USDT::Context(pid_));
+    ctx.reset(new ::USDT::Context(pid_, mod_match_inode_only_));
   else
     return StatusTuple(-1, "No valid Binary Path or PID provided");
 
diff --git a/src/cc/api/BPF.h b/src/cc/api/BPF.h
index 4b26088..9c0ab19 100644
--- a/src/cc/api/BPF.h
+++ b/src/cc/api/BPF.h
@@ -287,6 +287,19 @@
                << usdt.probe_func_;
   }
 
+  // When the kludge flag is set to 1, we will only match on inode
+  // when searching for modules in /proc/PID/maps that might contain the
+  // tracepoint we're looking for. Normally match is on inode and
+  // (dev_major, dev_minor), which is a more accurate way to uniquely
+  // identify a file.
+  //
+  // This hack exists because btrfs reports different device numbers for files
+  // in /proc/PID/maps vs stat syscall. Don't use it unless you're using btrfs
+  //
+  // set_probe_matching_kludge(1) must be called before USDTs are submitted to
+  // BPF::init()
+  int set_probe_matching_kludge(uint8_t kludge);
+
  private:
   bool initialized_;
 
@@ -300,6 +313,8 @@
   std::unique_ptr<void, std::function<void(void*)>> probe_;
   std::string program_text_;
 
+  uint8_t mod_match_inode_only_;
+
   friend class BPF;
 };
 
diff --git a/src/cc/bcc_elf.c b/src/cc/bcc_elf.c
index ba8fa1d..b9d25d8 100644
--- a/src/cc/bcc_elf.c
+++ b/src/cc/bcc_elf.c
@@ -684,17 +684,17 @@
 // >0: Initialized
 static int vdso_image_fd = -1;
 
-static int find_vdso(const char *name, uint64_t st, uint64_t en,
-                     uint64_t offset, bool enter_ns, void *payload) {
+static int find_vdso(struct mod_info *info, int enter_ns, void *payload) {
   int fd;
   char tmpfile[128];
-  if (!bcc_elf_is_vdso(name))
+  if (!bcc_elf_is_vdso(info->name))
     return 0;
 
-  void *image = malloc(en - st);
+  uint64_t sz = info->end_addr - info->start_addr;
+  void *image = malloc(sz);
   if (!image)
     goto on_error;
-  memcpy(image, (void *)st, en - st);
+  memcpy(image, (void *)info->start_addr, sz);
 
   snprintf(tmpfile, sizeof(tmpfile), "/tmp/bcc_%d_vdso_image_XXXXXX", getpid());
   fd = mkostemp(tmpfile, O_CLOEXEC);
@@ -706,7 +706,7 @@
   if (unlink(tmpfile) == -1)
     fprintf(stderr, "Unlink %s failed: %s\n", tmpfile, strerror(errno));
 
-  if (write(fd, image, en - st) == -1) {
+  if (write(fd, image, sz) == -1) {
     fprintf(stderr, "Failed to write to vDSO image: %s\n", strerror(errno));
     close(fd);
     goto on_error;
diff --git a/src/cc/bcc_proc.c b/src/cc/bcc_proc.c
index 6ce63de..c8f4041 100644
--- a/src/cc/bcc_proc.c
+++ b/src/cc/bcc_proc.c
@@ -121,6 +121,50 @@
   return path;
 }
 
+// return: 0 -> callback returned < 0, stopped iterating
+//        -1 -> callback never indicated to stop
+int _procfs_maps_each_module(FILE *procmap, int pid,
+                             bcc_procutils_modulecb callback, void *payload) {
+  char buf[PATH_MAX + 1], perm[5];
+  char *name;
+  mod_info mod;
+  uint8_t enter_ns;
+  while (true) {
+    enter_ns = 1;
+    buf[0] = '\0';
+    // From fs/proc/task_mmu.c:show_map_vma
+    if (fscanf(procmap, "%lx-%lx %4s %llx %lx:%lx %lu%[^\n]",
+          &mod.start_addr, &mod.end_addr, perm, &mod.file_offset,
+          &mod.dev_major, &mod.dev_minor, &mod.inode, buf) != 8)
+      break;
+
+    if (perm[2] != 'x')
+      continue;
+
+    name = buf;
+    while (isspace(*name))
+      name++;
+    mod.name = name;
+    if (!bcc_mapping_is_file_backed(name))
+      continue;
+
+    if (strstr(name, "/memfd:")) {
+      char *memfd_name = _procutils_memfd_path(pid, mod.inode);
+      if (memfd_name != NULL) {
+        strcpy(buf, memfd_name);
+        free(memfd_name);
+        mod.name = buf;
+        enter_ns = 0;
+      }
+    }
+
+    if (callback(&mod, enter_ns, payload) < 0)
+      return 0;
+  }
+
+  return -1;
+}
+
 int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback,
                               void *payload) {
   char procmap_filename[128];
@@ -131,56 +175,34 @@
   if (!procmap)
     return -1;
 
-  char buf[PATH_MAX + 1], perm[5], dev[8];
-  char *name;
-  uint64_t begin, end, inode;
-  unsigned long long offset;
-  while (true) {
-    buf[0] = '\0';
-    // From fs/proc/task_mmu.c:show_map_vma
-    if (fscanf(procmap, "%lx-%lx %4s %llx %7s %lu%[^\n]", &begin, &end, perm,
-               &offset, dev, &inode, buf) != 7)
-      break;
-
-    if (perm[2] != 'x')
-      continue;
-
-    name = buf;
-    while (isspace(*name))
-      name++;
-    if (!bcc_mapping_is_file_backed(name))
-      continue;
-
-    if (strstr(name, "/memfd:")) {
-      char *memfd_name = _procutils_memfd_path(pid, inode);
-      if (memfd_name != NULL) {
-        strcpy(buf, memfd_name);
-        free(memfd_name);
-        name = buf;
-      }
-    }
-
-    if (callback(name, begin, end, (uint64_t)offset, true, payload) < 0)
-      break;
-  }
-
-  fclose(procmap);
+  _procfs_maps_each_module(procmap, pid, callback, payload);
 
   // Address mapping for the entire address space maybe in /tmp/perf-<PID>.map
   // This will be used if symbols aren't resolved in an earlier mapping.
   char map_path[4096];
   // Try perf-<PID>.map path with process's mount namespace, chroot and NSPID,
   // in case it is generated by the process itself.
-  if (bcc_perf_map_path(map_path, sizeof(map_path), pid))
-    if (callback(map_path, 0, -1, 0, true, payload) < 0)
-      return 0;
+  mod_info mod;
+  memset(&mod, 0, sizeof(mod_info));
+  if (bcc_perf_map_path(map_path, sizeof(map_path), pid)) {
+    mod.name = map_path;
+    mod.end_addr = -1;
+    if (callback(&mod, 1, payload) < 0)
+      goto done;
+  }
   // Try perf-<PID>.map path with global root and PID, in case it is generated
   // by other Process. Avoid checking mount namespace for this.
+  memset(&mod, 0, sizeof(mod_info));
   int res = snprintf(map_path, 4096, "/tmp/perf-%d.map", pid);
-  if (res > 0 && res < 4096)
-    if (callback(map_path, 0, -1, 0, false, payload) < 0)
-      return 0;
+  if (res > 0 && res < 4096) {
+    mod.name = map_path;
+    mod.end_addr = -1;
+    if (callback(&mod, 0, payload) < 0)
+      goto done;
+  }
 
+done:
+  fclose(procmap);
   return 0;
 }
 
diff --git a/src/cc/bcc_proc.h b/src/cc/bcc_proc.h
index 1e5a720..a56f680 100644
--- a/src/cc/bcc_proc.h
+++ b/src/cc/bcc_proc.h
@@ -23,13 +23,24 @@
 #endif
 
 #include <stdint.h>
+#include <stdio.h>
 #include <stdbool.h>
 
-// Module name, start address, end address, file_offset,
-// whether to check mount namespace, payload
+
+typedef struct mod_info {
+  char *name;
+  uint64_t start_addr;
+  uint64_t end_addr;
+  long long unsigned int file_offset;
+  uint64_t dev_major;
+  uint64_t dev_minor;
+  uint64_t inode;
+} mod_info;
+
+// Module info, whether to check mount namespace, payload
 // Callback returning a negative value indicates to stop the iteration
-typedef int (*bcc_procutils_modulecb)(const char *, uint64_t, uint64_t,
-                                      uint64_t, bool, void *);
+typedef int (*bcc_procutils_modulecb)(mod_info *, int, void *);
+
 // Symbol name, address, payload
 typedef void (*bcc_procutils_ksymcb)(const char *, uint64_t, void *);
 
@@ -42,6 +53,9 @@
 // Returns -1 on error, and 0 on success
 int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback,
                               void *payload);
+
+int _procfs_maps_each_module(FILE *procmaps, int pid,
+                             bcc_procutils_modulecb callback, void *payload);
 // Iterate over all non-data Kernel symbols.
 // Returns -1 on error, and 0 on success
 int bcc_procutils_each_ksym(bcc_procutils_ksymcb callback, void *payload);
diff --git a/src/cc/bcc_syms.cc b/src/cc/bcc_syms.cc
index 96d431e..9fea636 100644
--- a/src/cc/bcc_syms.cc
+++ b/src/cc/bcc_syms.cc
@@ -20,6 +20,7 @@
 #include <linux/elf.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <cstdio>
@@ -102,7 +103,7 @@
 }
 
 ProcSyms::ProcSyms(int pid, struct bcc_symbol_option *option)
-    : pid_(pid), procstat_(pid), mount_ns_instance_(new ProcMountNS(pid_)) {
+    : pid_(pid), procstat_(pid) {
   if (option)
     std::memcpy(&symbol_option_, option, sizeof(bcc_symbol_option));
   else
@@ -122,9 +123,8 @@
 }
 
 void ProcSyms::load_exe() {
-  ProcMountNSGuard g(mount_ns_instance_.get());
   std::string exe = ebpf::get_pid_exe(pid_);
-  Module module(exe.c_str(), mount_ns_instance_.get(), &symbol_option_);
+  Module module(exe.c_str(), exe.c_str(), &symbol_option_);
 
   if (module.type_ != ModuleType::EXEC)
     return;
@@ -143,35 +143,33 @@
 
 void ProcSyms::refresh() {
   modules_.clear();
-  mount_ns_instance_.reset(new ProcMountNS(pid_));
   load_modules();
   procstat_.reset();
 }
 
-int ProcSyms::_add_module(const char *modname, uint64_t start, uint64_t end,
-                          uint64_t offset, bool check_mount_ns, void *payload) {
+int ProcSyms::_add_module(mod_info *mod, int enter_ns, void *payload) {
   ProcSyms *ps = static_cast<ProcSyms *>(payload);
+  std::string ns_relative_path = tfm::format("/proc/%d/root%s", ps->pid_, mod->name);
+  const char *modpath = enter_ns && ps->pid_ != -1 ? ns_relative_path.c_str() : mod->name;
   auto it = std::find_if(
       ps->modules_.begin(), ps->modules_.end(),
-      [=](const ProcSyms::Module &m) { return m.name_ == modname; });
+      [=](const ProcSyms::Module &m) { return m.name_ == mod->name; });
   if (it == ps->modules_.end()) {
     auto module = Module(
-        modname, check_mount_ns ? ps->mount_ns_instance_.get() : nullptr,
-        &ps->symbol_option_);
+        mod->name, modpath, &ps->symbol_option_);
 
     // pid/maps doesn't account for file_offset of text within the ELF.
     // It only gives the mmap offset. We need the real offset for symbol
     // lookup.
     if (module.type_ == ModuleType::SO) {
-      ProcMountNSGuard g(ps->mount_ns_instance_.get());
-      if (bcc_elf_get_text_scn_info(modname, &module.elf_so_addr_,
+      if (bcc_elf_get_text_scn_info(modpath, &module.elf_so_addr_,
                                     &module.elf_so_offset_) < 0) {
-        fprintf(stderr, "WARNING: Couldn't find .text section in %s\n", modname);
-        fprintf(stderr, "WARNING: BCC can't handle sym look ups for %s", modname);
+        fprintf(stderr, "WARNING: Couldn't find .text section in %s\n", modpath);
+        fprintf(stderr, "WARNING: BCC can't handle sym look ups for %s", modpath);
       }
     }
 
-    if (!bcc_is_perf_map(modname) || module.type_ != ModuleType::UNKNOWN)
+    if (!bcc_is_perf_map(modpath) || module.type_ != ModuleType::UNKNOWN)
       // Always add the module even if we can't read it, so that we could
       // report correct module name. Unless it's a perf map that we only
       // add readable ones.
@@ -179,7 +177,7 @@
     else
       return 0;
   }
-  it->ranges_.emplace_back(start, end, offset);
+  it->ranges_.emplace_back(mod->start_addr, mod->end_addr, mod->file_offset);
   // perf-PID map is added last. We try both inside the Process's mount
   // namespace + chroot, and in global /tmp. Make sure we only add one.
   if (it->type_ == ModuleType::PERF_MAP)
@@ -242,15 +240,14 @@
   return false;
 }
 
-ProcSyms::Module::Module(const char *name, ProcMountNS *mount_ns,
-                         struct bcc_symbol_option *option)
+ProcSyms::Module::Module(const char *name, const char *path,
+    struct bcc_symbol_option *option)
     : name_(name),
+      path_(path),
       loaded_(false),
-      mount_ns_(mount_ns),
       symbol_option_(option),
       type_(ModuleType::UNKNOWN) {
-  ProcMountNSGuard g(mount_ns_);
-  int elf_type = bcc_elf_get_type(name_.c_str());
+  int elf_type = bcc_elf_get_type(path_.c_str());
   // The Module is an ELF file
   if (elf_type >= 0) {
     if (elf_type == ET_EXEC)
@@ -260,9 +257,9 @@
     return;
   }
   // Other symbol files
-  if (bcc_is_valid_perf_map(name_.c_str()) == 1)
+  if (bcc_is_valid_perf_map(path_.c_str()) == 1)
     type_ = ModuleType::PERF_MAP;
-  else if (bcc_elf_is_vdso(name_.c_str()) == 1)
+  else if (bcc_elf_is_vdso(path_.c_str()) == 1)
     type_ = ModuleType::VDSO;
 
   // Will be stored later
@@ -286,12 +283,10 @@
   if (type_ == ModuleType::UNKNOWN)
     return;
 
-  ProcMountNSGuard g(mount_ns_);
-
   if (type_ == ModuleType::PERF_MAP)
-    bcc_perf_map_foreach_sym(name_.c_str(), _add_symbol, this);
+    bcc_perf_map_foreach_sym(path_.c_str(), _add_symbol, this);
   if (type_ == ModuleType::EXEC || type_ == ModuleType::SO)
-    bcc_elf_foreach_sym(name_.c_str(), _add_symbol, symbol_option_, this);
+    bcc_elf_foreach_sym(path_.c_str(), _add_symbol, symbol_option_, this);
   if (type_ == ModuleType::VDSO)
     bcc_elf_foreach_vdso_sym(_add_symbol, this);
 
@@ -547,27 +542,60 @@
   return bsym->resolve_addr(build_id, trace->offset, sym) ? 0 : -1;
 }
 
-struct mod_st {
+struct mod_search {
   const char *name;
+  uint64_t inode;
+  uint64_t dev_major;
+  uint64_t dev_minor;
+  uint64_t addr;
+  uint8_t inode_match_only;
+
   uint64_t start;
   uint64_t file_offset;
 };
 
-static int _find_module(const char *modname, uint64_t start, uint64_t end,
-                        uint64_t offset, bool, void *p) {
-  struct mod_st *mod = (struct mod_st *)p;
-  if (!strcmp(modname, mod->name)) {
-    mod->start = start;
-    mod->file_offset = offset;
-    return -1;
+int _bcc_syms_find_module(mod_info *info, int enter_ns, void *p) {
+  struct mod_search *mod = (struct mod_search *)p;
+  // use inode + dev to determine match if inode set
+  if (mod->inode) {
+    if (mod->inode != info->inode)
+      return 0;
+
+    // look at comment on USDT::set_probe_matching_kludge
+    // in api/BPF.h for explanation of why this might be
+    // necessary
+    if (mod->inode_match_only)
+      goto file_match;
+
+    if(mod->dev_major == info->dev_major
+        && mod->dev_minor == info->dev_minor)
+      goto file_match;
+
+    return 0;
   }
+
+  // fallback to name match
+  if (!strcmp(info->name, mod->name))
+    goto file_match;
+
   return 0;
+
+file_match:
+  mod->start = info->start_addr;
+  mod->file_offset = info->file_offset;
+  return -1;
 }
 
 int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address,
-                            uint64_t *global) {
-  struct mod_st mod = {module, 0x0};
-  if (bcc_procutils_each_module(pid, _find_module, &mod) < 0 ||
+                            uint8_t inode_match_only, uint64_t *global) {
+  struct stat s;
+  if (stat(module, &s))
+    return -1;
+
+  struct mod_search mod = {module, s.st_ino, major(s.st_dev), minor(s.st_dev),
+                           address, inode_match_only,
+                           0x0, 0x0};
+  if (bcc_procutils_each_module(pid, _bcc_syms_find_module, &mod) < 0 ||
       mod.start == 0x0)
     return -1;
 
@@ -645,8 +673,11 @@
   }
   if (sym->module == NULL)
     return -1;
-
-  ProcMountNSGuard g(pid);
+  if (pid != 0 && pid != -1) {
+    char *temp = (char*)sym->module;
+    sym->module = strdup(tfm::format("/proc/%d/root%s", pid, sym->module).c_str());
+    free(temp);
+  }
 
   sym->name = symname;
   sym->offset = addr;
diff --git a/src/cc/bcc_syms.h b/src/cc/bcc_syms.h
index c213f10..12b3cfa 100644
--- a/src/cc/bcc_syms.h
+++ b/src/cc/bcc_syms.h
@@ -22,6 +22,7 @@
 
 #include <stdint.h>
 #include "linux/bpf.h"
+#include "bcc_proc.h"
 
 struct bcc_symbol {
   const char *name;
@@ -65,8 +66,9 @@
                               const char *name, uint64_t *addr);
 void bcc_symcache_refresh(void *resolver);
 
+int _bcc_syms_find_module(struct mod_info *info, int enter_ns, void *p);
 int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address,
-                            uint64_t *global);
+                            uint8_t inode_match_only, uint64_t *global);
 
 /*bcc APIs for build_id stackmap support*/
 void *bcc_buildsymcache_new(void);
diff --git a/src/cc/libbpf.c b/src/cc/libbpf.c
index 5de772b..9054bef 100644
--- a/src/cc/libbpf.c
+++ b/src/cc/libbpf.c
@@ -941,7 +941,7 @@
                               const char *event_type, pid_t pid, int maxactive)
 {
   int kfd = -1, res = -1, ns_fd = -1;
-  char ev_alias[128];
+  char ev_alias[256];
   bool is_kprobe = strncmp("kprobe", event_type, 6) == 0;
 
   snprintf(buf, PATH_MAX, "/sys/kernel/debug/tracing/%s_events", event_type);
diff --git a/src/cc/ns_guard.cc b/src/cc/ns_guard.cc
deleted file mode 100644
index c5baf5a..0000000
--- a/src/cc/ns_guard.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2017 Facebook, Inc.
- * Copyright (c) 2017 VMware, 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 <fcntl.h>
-#include <sched.h>
-#include <sys/stat.h>
-#include <string>
-
-#include "ns_guard.h"
-
-// TODO: Remove this when CentOS 6 support is not needed anymore
-#include "setns.h"
-
-ProcMountNS::ProcMountNS(int pid) : target_ino_(0) {
-  if (pid < 0)
-    return;
-
-  std::string target_path = "/proc/" + std::to_string(pid) + "/ns/mnt";
-  ebpf::FileDesc target_fd(open(target_path.c_str(), O_RDONLY));
-  ebpf::FileDesc self_fd(open("/proc/self/ns/mnt", O_RDONLY));
-
-  if (self_fd < 0 || target_fd < 0)
-    return;
-
-  struct stat self_stat, target_stat;
-  if (fstat(self_fd, &self_stat) != 0)
-    return;
-  if (fstat(target_fd, &target_stat) != 0)
-    return;
-
-  target_ino_ = target_stat.st_ino;
-  if (self_stat.st_ino == target_stat.st_ino)
-    // Both current and target Process are in same mount namespace
-    return;
-
-  self_fd_ = std::move(self_fd);
-  target_fd_ = std::move(target_fd);
-}
-
-ProcMountNSGuard::ProcMountNSGuard(ProcMountNS *mount_ns)
-    : mount_ns_instance_(nullptr), mount_ns_(mount_ns), entered_(false) {
-  init();
-}
-
-ProcMountNSGuard::ProcMountNSGuard(int pid)
-    : mount_ns_instance_(pid > 0 ? new ProcMountNS(pid) : nullptr),
-      mount_ns_(mount_ns_instance_.get()),
-      entered_(false) {
-  init();
-}
-
-void ProcMountNSGuard::init() {
-  if (!mount_ns_ || mount_ns_->self() < 0 || mount_ns_->target() < 0)
-    return;
-
-  if (setns(mount_ns_->target(), CLONE_NEWNS) == 0)
-    entered_ = true;
-}
-
-ProcMountNSGuard::~ProcMountNSGuard() {
-  if (mount_ns_ && entered_ && mount_ns_->self() >= 0)
-    setns(mount_ns_->self(), CLONE_NEWNS);
-}
diff --git a/src/cc/ns_guard.h b/src/cc/ns_guard.h
deleted file mode 100644
index ce4b61b..0000000
--- a/src/cc/ns_guard.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2017 Facebook, Inc.
- * Copyright (c) 2017 VMware, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <sys/types.h>
-
-#include "file_desc.h"
-
-class ProcMountNSGuard;
-
-// ProcMountNS opens an fd corresponding to the current mount namespace and the
-// mount namespace of the target process.
-// The fds will remain uninitialized (<0) if the open fails, or if the current
-// and target namespaces are identical.
-class ProcMountNS {
- public:
-  explicit ProcMountNS(int pid);
-  int self() const { return self_fd_; }
-  int target() const { return target_fd_; }
-  ino_t target_ino() const { return target_ino_; }
-
- private:
-  ebpf::FileDesc self_fd_;
-  ebpf::FileDesc target_fd_;
-  ino_t target_ino_;
-};
-
-// ProcMountNSGuard switches to the target mount namespace and restores the
-// original upon going out of scope.
-class ProcMountNSGuard {
- public:
-  explicit ProcMountNSGuard(ProcMountNS *mount_ns);
-  explicit ProcMountNSGuard(int pid);
-
-  ~ProcMountNSGuard();
-
- private:
-  void init();
-
-  std::unique_ptr<ProcMountNS> mount_ns_instance_;
-  ProcMountNS *mount_ns_;
-  bool entered_;
-};
diff --git a/src/cc/syms.h b/src/cc/syms.h
index b533741..ac7e8fb 100644
--- a/src/cc/syms.h
+++ b/src/cc/syms.h
@@ -23,9 +23,9 @@
 #include <unordered_set>
 #include <vector>
 
+#include "bcc_proc.h"
 #include "bcc_syms.h"
 #include "file_desc.h"
-#include "ns_guard.h"
 
 class ProcStat {
   std::string procfs_;
@@ -98,13 +98,12 @@
           : start(s), end(e), file_offset(f) {}
     };
 
-    Module(const char *name, ProcMountNS *mount_ns,
-           struct bcc_symbol_option *option);
+    Module(const char *name, const char *path, struct bcc_symbol_option *option);
 
     std::string name_;
+    std::string path_;
     std::vector<Range> ranges_;
     bool loaded_;
-    ProcMountNS *mount_ns_;
     bcc_symbol_option *symbol_option_;
     ModuleType type_;
 
@@ -130,13 +129,11 @@
   int pid_;
   std::vector<Module> modules_;
   ProcStat procstat_;
-  std::unique_ptr<ProcMountNS> mount_ns_instance_;
   bcc_symbol_option symbol_option_;
 
   static int _add_load_sections(uint64_t v_addr, uint64_t mem_sz,
                                 uint64_t file_offset, void *payload);
-  static int _add_module(const char *, uint64_t, uint64_t, uint64_t, bool,
-                         void *);
+  static int _add_module(mod_info *, int, void *);
   void load_exe();
   void load_modules();
 
diff --git a/src/cc/usdt.h b/src/cc/usdt.h
index f1dac48..428bc35 100644
--- a/src/cc/usdt.h
+++ b/src/cc/usdt.h
@@ -20,7 +20,7 @@
 #include <unordered_map>
 #include <vector>
 
-#include "ns_guard.h"
+#include "bcc_proc.h"
 #include "syms.h"
 #include "vendor/optional.hpp"
 
@@ -196,11 +196,11 @@
   std::vector<Location> locations_;
 
   optional<int> pid_;
-  ProcMountNS *mount_ns_;
   std::unordered_map<std::string, bool> object_type_map_; // bin_path => is shared lib?
 
   optional<std::string> attached_to_;
   optional<uint64_t> attached_semaphore_;
+  uint8_t mod_match_inode_only_;
 
   std::string largest_arg_type(size_t arg_n);
 
@@ -212,7 +212,7 @@
 
 public:
   Probe(const char *bin_path, const char *provider, const char *name,
-        uint64_t semaphore, const optional<int> &pid, ProcMountNS *ns);
+        uint64_t semaphore, const optional<int> &pid, uint8_t mod_match_inode_only = 0);
 
   size_t num_locations() const { return locations_.size(); }
   size_t num_arguments() const { return locations_.front().arguments_.size(); }
@@ -251,29 +251,30 @@
 
   optional<int> pid_;
   optional<ProcStat> pid_stat_;
-  std::unique_ptr<ProcMountNS> mount_ns_instance_;
   std::string cmd_bin_path_;
   bool loaded_;
 
   static void _each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
                           void *p);
-  static int _each_module(const char *modpath, uint64_t, uint64_t, uint64_t,
-                          bool, void *p);
+  static int _each_module(mod_info *, int enter_ns, void *p);
 
   void add_probe(const char *binpath, const struct bcc_elf_usdt *probe);
   std::string resolve_bin_path(const std::string &bin_path);
 
+private:
+  uint8_t mod_match_inode_only_;
+
 public:
-  Context(const std::string &bin_path);
-  Context(int pid);
-  Context(int pid, const std::string &bin_path);
+  Context(const std::string &bin_path, uint8_t mod_match_inode_only = 0);
+  Context(int pid, uint8_t mod_match_inode_only = 0);
+  Context(int pid, const std::string &bin_path,
+          uint8_t mod_match_inode_only = 0);
   ~Context();
 
   optional<int> pid() const { return pid_; }
   bool loaded() const { return loaded_; }
   size_t num_probes() const { return probes_.size(); }
   const std::string & cmd_bin_path() const { return cmd_bin_path_; }
-  ino_t inode() const { return mount_ns_instance_->target_ino(); }
 
   Probe *get(const std::string &probe_name);
   Probe *get(const std::string &provider_name, const std::string &probe_name);
diff --git a/src/cc/usdt/usdt.cc b/src/cc/usdt/usdt.cc
index 09b204e..ab51cb5 100644
--- a/src/cc/usdt/usdt.cc
+++ b/src/cc/usdt/usdt.cc
@@ -54,17 +54,18 @@
 }
 
 Probe::Probe(const char *bin_path, const char *provider, const char *name,
-             uint64_t semaphore, const optional<int> &pid, ProcMountNS *ns)
+             uint64_t semaphore, const optional<int> &pid,
+             uint8_t mod_match_inode_only)
     : bin_path_(bin_path),
       provider_(provider),
       name_(name),
       semaphore_(semaphore),
       pid_(pid),
-      mount_ns_(ns) {}
+      mod_match_inode_only_(mod_match_inode_only)
+      {}
 
 bool Probe::in_shared_object(const std::string &bin_path) {
     if (object_type_map_.find(bin_path) == object_type_map_.end()) {
-      ProcMountNSGuard g(mount_ns_);
       return (object_type_map_[bin_path] = bcc_elf_is_shared_obj(bin_path.c_str()));
     }
     return object_type_map_[bin_path];
@@ -74,7 +75,7 @@
                                    const uint64_t addr) {
   if (in_shared_object(bin_path)) {
     return (pid_ &&
-            !bcc_resolve_global_addr(*pid_, bin_path.c_str(), addr, global));
+            !bcc_resolve_global_addr(*pid_, bin_path.c_str(), addr, mod_match_inode_only_, global));
   }
 
   *global = addr;
@@ -235,15 +236,19 @@
   ctx->add_probe(binpath, probe);
 }
 
-int Context::_each_module(const char *modpath, uint64_t, uint64_t, uint64_t,
-                          bool, void *p) {
+int Context::_each_module(mod_info *mod, int enter_ns, void *p) {
   Context *ctx = static_cast<Context *>(p);
+
+  std::string path = mod->name;
+  if (ctx->pid_ && *ctx->pid_ != -1 && enter_ns) {
+    path = tfm::format("/proc/%d/root%s", *ctx->pid_, path);
+  }
+
   // 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);
+  if (ctx->modules_.insert(path).second /*inserted new?*/) {
+    bcc_elf_foreach_usdt(path.c_str(), _each_probe, p);
   }
   return 0;
 }
@@ -257,8 +262,9 @@
   }
 
   probes_.emplace_back(
-      new Probe(binpath, probe->provider, probe->name, probe->semaphore, pid_,
-	mount_ns_instance_.get()));
+    new Probe(binpath, probe->provider, probe->name, probe->semaphore, pid_,
+              mod_match_inode_only_)
+  );
   probes_.back()->add_location(probe->pc, binpath, probe->arg_fmt);
 }
 
@@ -273,6 +279,10 @@
     ::free(which_so);
   }
 
+  if (!result.empty() && pid_ && *pid_ != -1) {
+    result = tfm::format("/proc/%d/root%s", *pid_, result);
+  }
+
   return result;
 }
 
@@ -348,8 +358,8 @@
   }
 }
 
-Context::Context(const std::string &bin_path)
-    : mount_ns_instance_(new ProcMountNS(-1)), loaded_(false) {
+Context::Context(const std::string &bin_path, uint8_t mod_match_inode_only)
+    : loaded_(false), mod_match_inode_only_(mod_match_inode_only) {
   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) {
@@ -361,8 +371,9 @@
     probe->finalize_locations();
 }
 
-Context::Context(int pid) : pid_(pid), pid_stat_(pid),
-  mount_ns_instance_(new ProcMountNS(pid)), loaded_(false) {
+Context::Context(int pid, uint8_t mod_match_inode_only)
+    : pid_(pid), pid_stat_(pid), loaded_(false),
+    mod_match_inode_only_(mod_match_inode_only) {
   if (bcc_procutils_each_module(pid, _each_module, this) == 0) {
     cmd_bin_path_ = ebpf::get_pid_exe(pid);
     if (cmd_bin_path_.empty())
@@ -374,16 +385,13 @@
     probe->finalize_locations();
 }
 
-Context::Context(int pid, const std::string &bin_path)
-    : pid_(pid), pid_stat_(pid),
-      mount_ns_instance_(new ProcMountNS(pid)), loaded_(false) {
+Context::Context(int pid, const std::string &bin_path,
+                 uint8_t mod_match_inode_only)
+    : pid_(pid), pid_stat_(pid), loaded_(false),
+      mod_match_inode_only_(mod_match_inode_only) {
   std::string full_path = resolve_bin_path(bin_path);
   if (!full_path.empty()) {
-    int res;
-    {
-      ProcMountNSGuard g(mount_ns_instance_.get());
-      res = bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this);
-    }
+    int res = bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this);
     if (res == 0) {
       cmd_bin_path_ = ebpf::get_pid_exe(pid);
       if (cmd_bin_path_.empty())
@@ -410,16 +418,13 @@
   if (!path) {
     ctx = new USDT::Context(pid);
   } else {
-    {
-      ProcMountNSGuard g(new ProcMountNS(pid));
-      struct stat buffer;
-      if (strlen(path) >= 1 && path[0] != '/') {
-        fprintf(stderr, "HINT: Binary path should be absolute.\n\n");
-        return nullptr;
-      } else if (stat(path, &buffer) == -1) {
-        fprintf(stderr, "HINT: Specified binary doesn't exist.\n\n");
-        return nullptr;
-      }
+    struct stat buffer;
+    if (strlen(path) >= 1 && path[0] != '/') {
+      fprintf(stderr, "HINT: Binary path should be absolute.\n\n");
+      return nullptr;
+    } else if (stat(path, &buffer) == -1) {
+      fprintf(stderr, "HINT: Specified binary doesn't exist.\n\n");
+      return nullptr;
     }
     ctx = new USDT::Context(pid, path);
   }
@@ -469,7 +474,7 @@
   stream << USDT::USDT_PROGRAM_HEADER;
   // Generate genargs codes for an array of USDT Contexts.
   //
-  // Each mnt_point + cmd_bin_path + probe_provider + probe_name
+  // Each 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++) {
@@ -478,8 +483,8 @@
     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();
+        auto pid = ctx->pid();
+        std::string key = ctx->cmd_bin_path() + "*" + p->provider() + "*" + p->name();
         if (generated_probes.find(key) != generated_probes.end())
           continue;
         if (!p->usdt_getarg(stream))
diff --git a/tests/cc/CMakeLists.txt b/tests/cc/CMakeLists.txt
index 66e484d..f4eb399 100644
--- a/tests/cc/CMakeLists.txt
+++ b/tests/cc/CMakeLists.txt
@@ -4,6 +4,7 @@
 include_directories(${CMAKE_SOURCE_DIR}/src/cc)
 include_directories(${CMAKE_SOURCE_DIR}/src/cc/api)
 include_directories(${CMAKE_SOURCE_DIR}/src/cc/libbpf/include/uapi)
+include_directories(${CMAKE_SOURCE_DIR}/tests/python/include)
 
 add_executable(test_static test_static.c)
 target_link_libraries(test_static bcc-static)
@@ -27,12 +28,10 @@
 	test_usdt_args.cc
 	test_usdt_probes.cc
 	utils.cc)
+file(COPY dummy_proc_map.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+add_library(usdt_test_lib SHARED usdt_test_lib.c)
 
-target_link_libraries(test_libbcc bcc-shared dl)
+target_link_libraries(test_libbcc bcc-shared dl usdt_test_lib)
 add_test(NAME test_libbcc COMMAND ${TEST_WRAPPER} c_test_all sudo ${CMAKE_CURRENT_BINARY_DIR}/test_libbcc)
 
-find_path(SDT_HEADER NAMES "sys/sdt.h")
-if (SDT_HEADER)
-	target_compile_definitions(test_libbcc PRIVATE HAVE_SDT_HEADER=1)
-endif()
 endif(ENABLE_USDT)
diff --git a/tests/cc/dummy_proc_map.txt b/tests/cc/dummy_proc_map.txt
new file mode 100644
index 0000000..4ff28c8
--- /dev/null
+++ b/tests/cc/dummy_proc_map.txt
@@ -0,0 +1,45 @@
+00400000-007c8000 r-xp 00000000 00:1b 11644523                           /opt/some/path/tobinary/bin/binary
+7f151476e000-7f1514779000 r-xp 00000000 00:1b 72809479                   /some/other/path/tolibs/lib/libnss_files-2.26.so
+7f1514779000-7f1514978000 ---p 0000b000 00:1b 72809479                   /some/other/path/tolibs/lib/libnss_files-2.26.so
+7f1514978000-7f1514979000 r--p 0000a000 00:1b 72809479                   /some/other/path/tolibs/lib/libnss_files-2.26.so
+7f1514979000-7f151497a000 rw-p 0000b000 00:1b 72809479                   /some/other/path/tolibs/lib/libnss_files-2.26.so
+7f1515b7e000-7f1515b7f000 rw-p 00009000 00:1b 72809418                   /some/other/path/tolibs/lib/libcrypt-2.26.so
+7f1515bad000-7f1515baf000 r-xp 00000000 00:1b 72809526                   /some/other/path/tolibs/lib/libutil-2.26.so
+7f1515baf000-7f1515dae000 ---p 00002000 00:1b 72809526                   /some/other/path/tolibs/lib/libutil-2.26.so
+7f1515dae000-7f1515daf000 r--p 00001000 00:1b 72809526                   /some/other/path/tolibs/lib/libutil-2.26.so
+7f1515daf000-7f1515db0000 rw-p 00002000 00:1b 72809526                   /some/other/path/tolibs/lib/libutil-2.26.so
+7f1515db0000-7f151601c000 r-xp 00000000 00:1b 72809420                   /some/other/path/tolibs/lib/libcrypto.so.1.1
+7f151601c000-7f151621c000 ---p 0026c000 00:1b 72809420                   /some/other/path/tolibs/lib/libcrypto.so.1.1
+7f151621c000-7f151623a000 r--p 0026c000 00:1b 72809420                   /some/other/path/tolibs/lib/libcrypto.so.1.1
+7f151623a000-7f1516244000 rw-p 0028a000 00:1b 72809420                   /some/other/path/tolibs/lib/libcrypto.so.1.1
+7f1516247000-7f15162ac000 r-xp 00000000 00:1b 72809514                   /some/other/path/tolibs/lib/libssl.so.1.1
+7f15162ac000-7f15164ab000 ---p 00065000 00:1b 72809514                   /some/other/path/tolibs/lib/libssl.so.1.1
+7f15164ab000-7f15164af000 r--p 00064000 00:1b 72809514                   /some/other/path/tolibs/lib/libssl.so.1.1
+7f15164af000-7f15164b5000 rw-p 00068000 00:1b 72809514                   /some/other/path/tolibs/lib/libssl.so.1.1
+7f15164b5000-7f15164cf000 r-xp 00000000 00:1b 72809538                   /some/other/path/tolibs/lib/libz.so.1.2.8
+7f15164cf000-7f15166ce000 ---p 0001a000 00:1b 72809538                   /some/other/path/tolibs/lib/libz.so.1.2.8
+7f15166ce000-7f15166cf000 r--p 00019000 00:1b 72809538                   /some/other/path/tolibs/lib/libz.so.1.2.8
+7f15166cf000-7f15166d0000 rw-p 0001a000 00:1b 72809538                   /some/other/path/tolibs/lib/libz.so.1.2.8
+7f15166d0000-7f15166d1000 r-xp 0001b064 00:1b 72809538                   /some/other/path/tolibs/lib/libz.so.1.2.8
+7f15168d4000-7f1516a23000 r-xp 00000000 00:1b 72809463                   /some/other/path/tolibs/lib/libm-2.26.so
+7f1516a23000-7f1516c22000 ---p 0014f000 00:1b 72809463                   /some/other/path/tolibs/lib/libm-2.26.so
+7f1516c22000-7f1516c23000 r--p 0014e000 00:1b 72809463                   /some/other/path/tolibs/lib/libm-2.26.so
+7f1516c23000-7f1516c24000 rw-p 0014f000 00:1b 72809463                   /some/other/path/tolibs/lib/libm-2.26.so
+7f1516c24000-7f1516c3e000 r-xp 00000000 00:1b 72809495                   /some/other/path/tolibs/lib/libpthread-2.26.so
+7f1516c3e000-7f1516e3d000 ---p 0001a000 00:1b 72809495                   /some/other/path/tolibs/lib/libpthread-2.26.so
+7f1516e3d000-7f1516e3e000 r--p 00019000 00:1b 72809495                   /some/other/path/tolibs/lib/libpthread-2.26.so
+7f1516e3e000-7f1516e3f000 rw-p 0001a000 00:1b 72809495                   /some/other/path/tolibs/lib/libpthread-2.26.so
+7f1516e43000-7f1516e6a000 r-xp 00000000 00:1b 72809393                   /some/other/path/tolibs/lib/ld-2.26.so
+7f1517010000-7f1517011000 rw-s 00000000 00:05 1117877022                 /dev/zero (deleted)
+7f1517011000-7f1517012000 rw-s 00000000 00:05 1117877021                 /dev/zero (deleted)
+7f1517012000-7f1517013000 rw-s 00000000 00:05 1117877020                 /dev/zero (deleted)
+7f1517013000-7f151701c000 rw-s 00000000 00:05 1117899207                 /dev/zero (deleted)
+7f151701c000-7f1517069000 rw-p 00000000 00:00 0
+7f1517069000-7f151706a000 r--p 00026000 00:1b 72809393                   /some/other/path/tolibs/lib/ld-2.26.so
+7f151706a000-7f151706b000 rw-p 00027000 00:1b 72809393                   /some/other/path/tolibs/lib/ld-2.26.so
+7f151706b000-7f151706c000 rw-p 00000000 00:00 0
+7ffd5070d000-7ffd5073d000 rwxp 00000000 00:00 0                          [stack]
+7ffd5073d000-7ffd5073e000 rw-p 00000000 00:00 0
+7ffd507d7000-7ffd507da000 r--p 00000000 00:00 0                          [vvar]
+7ffd507da000-7ffd507dc000 r-xp 00000000 00:00 0                          [vdso]
+ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
diff --git a/tests/cc/test_c_api.cc b/tests/cc/test_c_api.cc
index 60804a0..31dc5ec 100644
--- a/tests/cc/test_c_api.cc
+++ b/tests/cc/test_c_api.cc
@@ -439,6 +439,133 @@
   munmap(map_addr, map_sz);
 }
 
+// must match exactly the defitinion of mod_search in bcc_syms.cc
+struct mod_search {
+  const char *name;
+  uint64_t inode;
+  uint64_t dev_major;
+  uint64_t dev_minor;
+  uint64_t addr;
+  uint8_t inode_match_only;
+
+  uint64_t start;
+  uint64_t file_offset;
+};
+
+TEST_CASE("searching for modules in /proc/[pid]/maps", "[c_api]") {
+  FILE *dummy_maps = fopen("dummy_proc_map.txt", "r");
+  REQUIRE(dummy_maps != NULL);
+
+  SECTION("name match") {
+    fseek(dummy_maps, 0, SEEK_SET);
+
+    struct mod_search search;
+    memset(&search, 0, sizeof(struct mod_search));
+    search.name = "/some/other/path/tolibs/lib/libutil-2.26.so";
+    search.addr = 0x1;
+    int res =  _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module,
+                                        &search);
+    REQUIRE(res == 0);
+    REQUIRE(search.start == 0x7f1515bad000);
+  }
+
+  SECTION("expected failure to match (name only search)") {
+    fseek(dummy_maps, 0, SEEK_SET);
+
+    struct mod_search search;
+    memset(&search, 0, sizeof(struct mod_search));
+    search.name = "/lib/that/isnt/in/maps/libdoesntexist.so";
+    search.addr = 0x1;
+    int res =  _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module,
+                                        &search);
+    REQUIRE(res == -1);
+  }
+
+  SECTION("inode+dev match, names different") {
+    fseek(dummy_maps, 0, SEEK_SET);
+
+    struct mod_search search;
+    memset(&search, 0, sizeof(struct mod_search));
+    search.name = "/proc/5/root/some/other/path/tolibs/lib/libz.so.1.2.8";
+    search.inode = 72809538;
+    search.dev_major = 0x00;
+    search.dev_minor = 0x1b;
+    search.addr = 0x2;
+    int res =  _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module,
+                                        &search);
+    REQUIRE(res == 0);
+    REQUIRE(search.start == 0x7f15164b5000);
+  }
+
+  SECTION("inode+dev don't match, names same") {
+    fseek(dummy_maps, 0, SEEK_SET);
+
+    struct mod_search search;
+    memset(&search, 0, sizeof(struct mod_search));
+    search.name = "/some/other/path/tolibs/lib/libutil-2.26.so";
+    search.inode = 9999999;
+    search.dev_major = 0x42;
+    search.dev_minor = 0x1b;
+    search.addr = 0x2;
+    int res =  _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module,
+                                        &search);
+    REQUIRE(res == -1);
+  }
+
+  SECTION("inodes match, dev_major/minor don't, expected failure") {
+    fseek(dummy_maps, 0, SEEK_SET);
+
+    struct mod_search search;
+    memset(&search, 0, sizeof(struct mod_search));
+    search.name = "/some/other/path/tolibs/lib/libutil-2.26.so";
+    search.inode = 72809526;
+    search.dev_major = 0x11;
+    search.dev_minor = 0x11;
+    search.addr = 0x2;
+    int res =  _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module,
+                                        &search);
+    REQUIRE(res == -1);
+  }
+
+  SECTION("inodes match, dev_major/minor don't, match inode only") {
+    fseek(dummy_maps, 0, SEEK_SET);
+
+    struct mod_search search;
+    memset(&search, 0, sizeof(struct mod_search));
+    search.name = "/some/other/path/tolibs/lib/libutil-2.26.so";
+    search.inode = 72809526;
+    search.dev_major = 0x11;
+    search.dev_minor = 0x11;
+    search.addr = 0x2;
+    search.inode_match_only = 1;
+    int res =  _procfs_maps_each_module(dummy_maps, 42, _bcc_syms_find_module,
+                                        &search);
+    REQUIRE(res == 0);
+    REQUIRE(search.start == 0x7f1515bad000);
+  }
+
+  fclose(dummy_maps);
+}
+
+TEST_CASE("resolve global addr in libc in this process", "[c_api]") {
+  int pid = getpid();
+  char *sopath = bcc_procutils_which_so("c", pid);
+  uint64_t local_addr = 0x15;
+  uint64_t global_addr;
+
+  struct mod_search search;
+  memset(&search, 0, sizeof(struct mod_search));
+  search.name = sopath;
+
+  int res = bcc_procutils_each_module(pid, _bcc_syms_find_module,
+                                      &search);
+  REQUIRE(res == 0);
+  REQUIRE(search.start != 0);
+
+  res = bcc_resolve_global_addr(pid, sopath, local_addr, 0, &global_addr);
+  REQUIRE(res == 0);
+  REQUIRE(global_addr == (search.start + local_addr - search.file_offset));
+}
 
 TEST_CASE("get online CPUs", "[c_api]") {
 	std::vector<int> cpus = ebpf::get_online_cpus();
diff --git a/tests/cc/test_usdt_probes.cc b/tests/cc/test_usdt_probes.cc
index 3e42633..71c75a1 100644
--- a/tests/cc/test_usdt_probes.cc
+++ b/tests/cc/test_usdt_probes.cc
@@ -22,19 +22,24 @@
 #include "usdt.h"
 #include "api/BPF.h"
 
-#ifdef HAVE_SDT_HEADER
 /* required to insert USDT probes on this very executable --
  * we're gonna be testing them live! */
-#include <sys/sdt.h>
+#include "folly/tracing/StaticTracepoint.h"
 
 static int a_probed_function() {
   int an_int = 23 + getpid();
   void *a_pointer = malloc(4);
-  DTRACE_PROBE2(libbcc_test, sample_probe_1, an_int, a_pointer);
+  FOLLY_SDT(libbcc_test, sample_probe_1, an_int, a_pointer);
   free(a_pointer);
   return an_int;
 }
 
+extern "C" int lib_probed_function();
+
+int call_shared_lib_func() {
+  return lib_probed_function();
+}
+
 TEST_CASE("test finding a probe in our own process", "[usdt]") {
   USDT::Context ctx(getpid());
   REQUIRE(ctx.num_probes() >= 1);
@@ -43,7 +48,8 @@
     auto probe = ctx.get("sample_probe_1");
     REQUIRE(probe);
 
-    REQUIRE(probe->in_shared_object(probe->bin_path()) == false);
+    if(probe->in_shared_object(probe->bin_path()))
+        return;
     REQUIRE(probe->name() == "sample_probe_1");
     REQUIRE(probe->provider() == "libbcc_test");
     REQUIRE(probe->bin_path().find("/test_libbcc") != std::string::npos);
@@ -83,7 +89,15 @@
     res = bpf.detach_usdt(u);
     REQUIRE(res.code() == 0);
 }
-#endif  // HAVE_SDT_HEADER
+
+TEST_CASE("test find a probe in our process' shared libs with c++ API", "[usdt]") {
+  ebpf::BPF bpf;
+  ebpf::USDT u(::getpid(), "libbcc_test", "sample_lib_probe_1", "on_event");
+
+  auto res = bpf.init("int on_event() { return 0; }", {}, {u});
+  REQUIRE(res.msg() == "");
+  REQUIRE(res.code() == 0);
+}
 
 class ChildProcess {
   pid_t pid_;
diff --git a/tests/cc/usdt_test_lib.c b/tests/cc/usdt_test_lib.c
new file mode 100644
index 0000000..799ad9b
--- /dev/null
+++ b/tests/cc/usdt_test_lib.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "folly/tracing/StaticTracepoint.h"
+
+int lib_probed_function() {
+  int an_int = 42 + getpid();
+  FOLLY_SDT(libbcc_test, sample_lib_probe_1, an_int);
+  return an_int;
+}