cc: Resolve library names using loaded libraries
authorPaul Chaignon <paul.chaignon@gmail.com>
Sun, 25 Dec 2016 18:43:41 +0000 (19:43 +0100)
committerPaul Chaignon <paul.chaignon@gmail.com>
Sun, 15 Jan 2017 07:33:29 +0000 (08:33 +0100)
To resolve library names, bcc_procutils_which_so leverages mapped
libraries of the targeted process, if one is given. Uses the kernel's
/proc/$pid/maps

src/cc/BPF.cc
src/cc/bcc_proc.c
src/cc/bcc_proc.h
src/cc/bcc_syms.cc
src/cc/bcc_syms.h
src/cc/usdt.cc
src/lua/bcc/libbcc.lua
src/lua/bcc/sym.lua
src/python/bcc/__init__.py
src/python/bcc/libbcc.py
tests/cc/test_c_api.cc

index 4a7ca2c..f2d6ab6 100644 (file)
@@ -462,7 +462,7 @@ StatusTuple BPF::check_binary_symbol(const std::string& binary_path,
                                      const std::string& symbol,
                                      uint64_t symbol_addr, bcc_symbol* output) {
   int res = bcc_resolve_symname(binary_path.c_str(), symbol.c_str(),
-                                symbol_addr, output);
+                                symbol_addr, 0, output);
   if (res < 0)
     return StatusTuple(
         -1, "Unable to find offset for binary %s symbol %s address %lx",
index f4c870f..99b24b6 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdint.h>
 #include <ctype.h>
 #include <stdio.h>
+#include <math.h>
 
 #include "bcc_perf_map.h"
 #include "bcc_proc.h"
@@ -196,6 +197,8 @@ static struct ld_lib {
   int flags;
 } * lib_cache;
 
+static char libpath[4096];
+
 static int read_cache1(const char *ld_map) {
   struct ld_cache1 *ldcache = (struct ld_cache1 *)ld_map;
   const char *ldstrings =
@@ -307,7 +310,47 @@ static bool match_so_flags(int flags) {
   return true;
 }
 
-const char *bcc_procutils_which_so(const char *libname) {
+static bool which_so_in_process(const char* libname, int pid) {
+  int ret, found = false;
+  char endline[4096], *mapname = NULL, *newline;
+  char mappings_file[128];
+  const size_t search_len = strlen(libname) + strlen("/lib.");
+  char search1[search_len + 1];
+  char search2[search_len + 1];
+
+  sprintf(mappings_file, "/proc/%ld/maps", (long)pid);
+  FILE *fp = fopen(mappings_file, "r");
+  if (!fp)
+    return NULL;
+
+  snprintf(search1, search_len + 1, "/lib%s.", libname);
+  snprintf(search2, search_len + 1, "/lib%s-", libname);
+
+  do {
+    ret = fscanf(fp, "%*x-%*x %*s %*x %*s %*d");
+    if (!fgets(endline, sizeof(endline), fp))
+      break;
+
+    mapname = endline;
+    newline = strchr(endline, '\n');
+    if (newline)
+      newline[0] = '\0';
+
+    while (isspace(mapname[0])) mapname++;
+
+    if (strstr(mapname, ".so") && (strstr(mapname, search1) ||
+                                   strstr(mapname, search2))) {
+      found = true;
+      memcpy(libpath, mapname, strlen(mapname));
+      break;
+    }
+  } while (ret != EOF);
+
+  fclose(fp);
+  return found;
+}
+
+const char *bcc_procutils_which_so(const char *libname, int pid) {
   const size_t soname_len = strlen(libname) + strlen("lib.so");
   char soname[soname_len + 1];
   int i;
@@ -315,6 +358,9 @@ const char *bcc_procutils_which_so(const char *libname) {
   if (strchr(libname, '/'))
     return libname;
 
+  if (pid && which_so_in_process(libname, pid))
+    return libpath;
+
   if (lib_cache_count < 0)
     return NULL;
 
@@ -327,8 +373,9 @@ const char *bcc_procutils_which_so(const char *libname) {
 
   for (i = 0; i < lib_cache_count; ++i) {
     if (!strncmp(lib_cache[i].libname, soname, soname_len) &&
-        match_so_flags(lib_cache[i].flags))
+        match_so_flags(lib_cache[i].flags)) {
       return lib_cache[i].path;
+    }
   }
   return NULL;
 }
index 5e04ff2..e7607f9 100644 (file)
@@ -27,7 +27,7 @@ extern "C" {
 typedef int (*bcc_procutils_modulecb)(const char *, uint64_t, uint64_t, void *);
 typedef void (*bcc_procutils_ksymcb)(const char *, uint64_t, void *);
 
-const char *bcc_procutils_which_so(const char *libname);
+const char *bcc_procutils_which_so(const char *libname, int pid);
 char *bcc_procutils_which(const char *binpath);
 int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback,
                               void *payload);
index 8a100f3..2dc575b 100644 (file)
@@ -304,7 +304,7 @@ int bcc_foreach_symbol(const char *module, SYM_CB cb) {
 }
 
 int bcc_resolve_symname(const char *module, const char *symname,
-                        const uint64_t addr, struct bcc_symbol *sym) {
+                        const uint64_t addr, int pid, struct bcc_symbol *sym) {
   uint64_t load_addr;
 
   sym->module = NULL;
@@ -317,7 +317,7 @@ int bcc_resolve_symname(const char *module, const char *symname,
   if (strchr(module, '/')) {
     sym->module = module;
   } else {
-    sym->module = bcc_procutils_which_so(module);
+    sym->module = bcc_procutils_which_so(module, pid);
   }
 
   if (sym->module == NULL)
index 6d8e2ad..7ce2dd7 100644 (file)
@@ -43,7 +43,7 @@ int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address,
 int bcc_foreach_symbol(const char *module, SYM_CB cb);
 int bcc_find_symbol_addr(struct bcc_symbol *sym);
 int bcc_resolve_symname(const char *module, const char *symname,
-                        const uint64_t addr, struct bcc_symbol *sym);
+                        const uint64_t addr, int pid, struct bcc_symbol *sym);
 #ifdef __cplusplus
 }
 #endif
index 4f1b00a..095e1ce 100644 (file)
@@ -223,7 +223,7 @@ std::string Context::resolve_bin_path(const std::string &bin_path) {
   if (char *which = bcc_procutils_which(bin_path.c_str())) {
     result = which;
     ::free(which);
-  } else if (const char *which_so = bcc_procutils_which_so(bin_path.c_str())) {
+  } else if (const char *which_so = bcc_procutils_which_so(bin_path.c_str(), 0)) {
     result = which_so;
   }
 
index 2628445..1f4cf4e 100644 (file)
@@ -109,7 +109,7 @@ struct bcc_symbol {
 };
 
 int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr,
-               struct bcc_symbol *sym);
+               int pid, struct bcc_symbol *sym);
 void *bcc_symcache_new(int pid);
 int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym);
 void bcc_symcache_refresh(void *resolver);
index 8bd6d3b..023e4f3 100644 (file)
@@ -32,7 +32,7 @@ end
 
 local function check_path_symbol(module, symname, addr)
   local sym = SYM()
-  if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, sym) < 0 then
+  if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, 0, sym) < 0 then
     if sym[0].module == nil then
       error("could not find library '%s' in the library path" % module)
     else
index 0304da1..e1363ad 100644 (file)
@@ -554,11 +554,12 @@ class BPF(object):
 
 
     @classmethod
-    def _check_path_symbol(cls, module, symname, addr):
+    def _check_path_symbol(cls, module, symname, addr, pid):
         sym = bcc_symbol()
         psym = ct.pointer(sym)
+        c_pid = 0 if pid == -1 else pid
         if lib.bcc_resolve_symname(module.encode("ascii"),
-                symname.encode("ascii"), addr or 0x0, psym) < 0:
+                symname.encode("ascii"), addr or 0x0, c_pid, psym) < 0:
             if not sym.module:
                 raise Exception("could not find library %s" % module)
             raise Exception("could not determine address of symbol %s" % symname)
@@ -566,7 +567,7 @@ class BPF(object):
 
     @staticmethod
     def find_library(libname):
-        res = lib.bcc_procutils_which_so(libname.encode("ascii"))
+        res = lib.bcc_procutils_which_so(libname.encode("ascii"), 0)
         return res if res is None else res.decode()
 
     @staticmethod
@@ -736,7 +737,8 @@ class BPF(object):
 
         Libraries can be given in the name argument without the lib prefix, or
         with the full path (/usr/lib/...). Binaries can be given only with the
-        full path (/bin/sh).
+        full path (/bin/sh). If a PID is given, the uprobe will attach to the
+        version of the library used by the process.
 
         Example: BPF(text).attach_uprobe("c", "malloc")
                  BPF(text).attach_uprobe("/usr/bin/python", "main")
@@ -753,7 +755,7 @@ class BPF(object):
                                    group_fd=group_fd)
             return
 
-        (path, addr) = BPF._check_path_symbol(name, sym, addr)
+        (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
 
         self._check_probe_quota(1)
         fn = self.load_func(fn_name, BPF.KPROBE)
@@ -776,7 +778,7 @@ class BPF(object):
         """
 
         name = str(name)
-        (path, addr) = BPF._check_path_symbol(name, sym, addr)
+        (path, addr) = BPF._check_path_symbol(name, sym, addr, -1)
         ev_name = "p_%s_0x%x" % (self._probe_repl.sub("_", path), addr)
         if ev_name not in self.open_uprobes:
             raise Exception("Uprobe %s is not attached" % ev_name)
@@ -805,7 +807,7 @@ class BPF(object):
             return
 
         name = str(name)
-        (path, addr) = BPF._check_path_symbol(name, sym, addr)
+        (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
 
         self._check_probe_quota(1)
         fn = self.load_func(fn_name, BPF.KPROBE)
@@ -828,7 +830,7 @@ class BPF(object):
         """
 
         name = str(name)
-        (path, addr) = BPF._check_path_symbol(name, sym, addr)
+        (path, addr) = BPF._check_path_symbol(name, sym, addr, -1)
         ev_name = "r_%s_0x%x" % (self._probe_repl.sub("_", path), addr)
         if ev_name not in self.open_uprobes:
             raise Exception("Uretprobe %s is not attached" % ev_name)
index f03a1b7..2ba9867 100644 (file)
@@ -131,11 +131,11 @@ class bcc_symbol(ct.Structure):
         ]
 
 lib.bcc_procutils_which_so.restype = ct.c_char_p
-lib.bcc_procutils_which_so.argtypes = [ct.c_char_p]
+lib.bcc_procutils_which_so.argtypes = [ct.c_char_p, ct.c_int]
 
 lib.bcc_resolve_symname.restype = ct.c_int
 lib.bcc_resolve_symname.argtypes = [
-    ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.POINTER(bcc_symbol)]
+    ct.c_char_p, ct.c_char_p, ct.c_ulonglong, ct.c_int, ct.POINTER(bcc_symbol)]
 
 _SYM_CB_TYPE = ct.CFUNCTYPE(ct.c_int, ct.c_char_p, ct.c_ulonglong)
 lib.bcc_foreach_symbol.restype = ct.c_int
index af0bd3a..276301b 100644 (file)
 using namespace std;
 
 TEST_CASE("shared object resolution", "[c_api]") {
-  const char *libm = bcc_procutils_which_so("m");
+  const char *libm = bcc_procutils_which_so("m", 0);
   REQUIRE(libm);
   REQUIRE(libm[0] == '/');
   REQUIRE(string(libm).find("libm.so") != string::npos);
 }
 
+TEST_CASE("shared object resolution using loaded libraries", "[c_api]") {
+  const char *libelf = bcc_procutils_which_so("elf", getpid());
+  REQUIRE(libelf);
+  REQUIRE(libelf[0] == '/');
+  REQUIRE(string(libelf).find("libelf") != string::npos);
+}
+
 TEST_CASE("binary resolution with `which`", "[c_api]") {
   char *ld = bcc_procutils_which("ld");
   REQUIRE(ld);
@@ -57,12 +64,21 @@ TEST_CASE("list all kernel symbols", "[c_api]") {
 TEST_CASE("resolve symbol name in external library", "[c_api]") {
   struct bcc_symbol sym;
 
-  REQUIRE(bcc_resolve_symname("c", "malloc", 0x0, &sym) == 0);
+  REQUIRE(bcc_resolve_symname("c", "malloc", 0x0, 0, &sym) == 0);
   REQUIRE(string(sym.module).find("libc.so") != string::npos);
   REQUIRE(sym.module[0] == '/');
   REQUIRE(sym.offset != 0);
 }
 
+TEST_CASE("resolve symbol name in external library using loaded libraries", "[c_api]") {
+  struct bcc_symbol sym;
+
+  REQUIRE(bcc_resolve_symname("bcc", "bcc_procutils_which", 0x0, getpid(), &sym) == 0);
+  REQUIRE(string(sym.module).find("libbcc.so") != string::npos);
+  REQUIRE(sym.module[0] == '/');
+  REQUIRE(sym.offset != 0);
+}
+
 extern "C" int _a_test_function(const char *a_string) {
   int i;
   for (i = 0; a_string[i]; ++i)