]> sourceware.org Git - systemtap.git/commitdiff
PR14489: Revamp stapdyn probe metadata
authorJosh Stone <jistone@redhat.com>
Fri, 28 Sep 2012 00:55:05 +0000 (17:55 -0700)
committerJosh Stone <jistone@redhat.com>
Fri, 28 Sep 2012 00:55:05 +0000 (17:55 -0700)
Rather than having a fixed data structure for stapdyn to read from the
module, now stapdyn queries the module dynamically for its data.  Thus,
we dlopen the module within stapdyn, then dlsym a few query functions
and use those to enumerate all of the probe data.

* runtime/dyninst/stapdyn.h: Add functions for metadata.
* runtime/dyninst/uprobes.h: New, define module internal datastructures.
* runtime/dyninst/uprobes.c: Implement the metadata functions.
* tapsets.cxx: Generate probe metadat in the new datastructures.
* stapdyn/stapdyn.cxx: Query probes using the new functions.
* stapdyn/Makefile.am: No longer need -lelf for stapdyn.
* stapdyn/Makefile.in: Regenerate.

runtime/dyninst/stapdyn.h
runtime/dyninst/uprobes.c
runtime/dyninst/uprobes.h [new file with mode: 0644]
stapdyn/Makefile.am
stapdyn/Makefile.in
stapdyn/stapdyn.cxx
tapsets.cxx

index b2183116f64099aa5d482ceeeb3dd9f16a1b05a7..5beb0a55212aab6db6b6fbe97960df95ee5f399d 100644 (file)
@@ -19,14 +19,31 @@ extern "C" {
 
 
 /* These are declarations of all interfaces that stapdyn may call in the
- * module, either directly or via dyninst in the mutatee.  */
+ * module, either directly or via dyninst in the mutatee.  To maintain
+ * compatibility as much as possible, function signatures should not be
+ * changed between releases, only deprecated/renamed as necessary.
+ */
+
+
+/* STAP 2.0 : */
 
 extern int stp_dyninst_session_init(void);
 extern void stp_dyninst_session_exit(void);
 
+extern uint64_t stp_dyninst_target_count(void);
+extern const char* stp_dyninst_target_path(uint64_t index);
+
+extern uint64_t stp_dyninst_probe_count(void);
+extern uint64_t stp_dyninst_probe_target(uint64_t index);
+extern uint64_t stp_dyninst_probe_offset(uint64_t index);
+extern uint64_t stp_dyninst_probe_semaphore(uint64_t index);
+
 extern int enter_dyninst_uprobe(uint64_t index, struct pt_regs *regs);
 
 
+/* STAP 2.x : */
+
+
 #ifdef __cplusplus
 }
 #endif
index 628f27f23ad5fd362dd9dd905865060b38885721..bf54e5740ed57bc417810d5530705b6b654f1412 100644 (file)
 
 /* STAPDU: SystemTap Dyninst Uprobes */
 
-/* A target has the dyninst view of what we want to probe.
+/* These functions implement the ABI in stapdyn.h
  *
- * NB: This becomes an ABI in the module with stapdyn, so be cautious.  It
- * might be better to go with a more flexible format - perhaps JSON?
+ * NB: tapsets.cxx will generate two arrays used here:
+ *   struct stapdu_target stapdu_targets[];
+ *   struct stapdu_probe stapdu_probes[];
  */
-struct stapdu_target {
-       char filename[240];
-       uint64_t offset; /* the probe offset within the file */
-       uint64_t sdt_sem_offset; /* the semaphore offset from process->base */
-};
 
 
-/* A consumer has the runtime information for a probe.  */
-struct stapdu_consumer {
-       struct stap_probe * const probe;
-};
+uint64_t stp_dyninst_target_count(void)
+{
+       return ARRAY_SIZE(stapdu_targets);
+}
+
+const char* stp_dyninst_target_path(uint64_t index)
+{
+       if (index >= stp_dyninst_target_count())
+               return NULL;
+       return stapdu_targets[index].path;
+}
+
+
+uint64_t stp_dyninst_probe_count(void)
+{
+       return ARRAY_SIZE(stapdu_probes);
+}
+
+uint64_t stp_dyninst_probe_target(uint64_t index)
+{
+       if (index >= stp_dyninst_probe_count())
+               return (uint64_t)-1;
+       return stapdu_probes[index].target;
+}
+
+uint64_t stp_dyninst_probe_offset(uint64_t index)
+{
+       if (index >= stp_dyninst_probe_count())
+               return (uint64_t)-1;
+       return stapdu_probes[index].offset;
+}
+
+uint64_t stp_dyninst_probe_semaphore(uint64_t index)
+{
+       if (index >= stp_dyninst_probe_count())
+               return (uint64_t)-1;
+       return stapdu_probes[index].semaphore;
+}
 
 
 #endif /* _UPROBES_DYNINST_C_ */
diff --git a/runtime/dyninst/uprobes.h b/runtime/dyninst/uprobes.h
new file mode 100644 (file)
index 0000000..0a89cbb
--- /dev/null
@@ -0,0 +1,34 @@
+/* -*- linux-c -*-
+ * Common data types for dyninst-based uprobes
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software.  You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#ifndef _UPROBES_DYNINST_H_
+#define _UPROBES_DYNINST_H_
+
+/* STAPDU: SystemTap Dyninst Uprobes */
+
+
+/* A stapdu_target describes the files to probe */
+struct stapdu_target {
+       const char * const path;
+       /* buildid? */
+};
+
+
+/* A stapdu_probe has the individual information for a probe.  */
+struct stapdu_probe {
+       uint64_t target; /* the target index for this probe */
+       uint64_t offset; /* the probe offset within the file */
+       uint64_t semaphore; /* the sdt semaphore offset within the file */
+       struct stap_probe * const probe;
+};
+
+
+#endif /* _UPROBES_DYNINST_H_ */
+
index a54fb19ee4399d6c1aabb454ceb421e8ee24ccd8..5d5cbca86c659e6ae409e44fbb32a13d5996b098 100644 (file)
@@ -30,7 +30,7 @@ noinst_PROGRAMS += dynsdt
 stapdyn_SOURCES = stapdyn.cxx dynutil.cxx ../util.cxx
 stapdyn_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@
 stapdyn_LDFLAGS = $(AM_LDFLAGS) @DYNINST_LDFLAGS@
-stapdyn_LDADD = -ldl -lelf $(dyninst_LIBS)
+stapdyn_LDADD = -ldl $(dyninst_LIBS)
 
 dynsdt_SOURCES = dynsdt.cxx dynutil.cxx ../util.cxx
 dynsdt_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@
index 1700dcb561abf5adc3c2d84e4b81bf360357c7dd..f57e32965b21d23bee910bf5187b46f373128152 100644 (file)
@@ -293,7 +293,7 @@ AM_LDFLAGS = @PIELDFLAGS@ $(am__append_2)
 @HAVE_DYNINST_TRUE@stapdyn_SOURCES = stapdyn.cxx dynutil.cxx ../util.cxx
 @HAVE_DYNINST_TRUE@stapdyn_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@
 @HAVE_DYNINST_TRUE@stapdyn_LDFLAGS = $(AM_LDFLAGS) @DYNINST_LDFLAGS@
-@HAVE_DYNINST_TRUE@stapdyn_LDADD = -ldl -lelf $(dyninst_LIBS)
+@HAVE_DYNINST_TRUE@stapdyn_LDADD = -ldl $(dyninst_LIBS)
 @HAVE_DYNINST_TRUE@dynsdt_SOURCES = dynsdt.cxx dynutil.cxx ../util.cxx
 @HAVE_DYNINST_TRUE@dynsdt_CXXFLAGS = $(AM_CXXFLAGS) @DYNINST_CXXFLAGS@
 @HAVE_DYNINST_TRUE@dynsdt_LDFLAGS = $(AM_LDFLAGS) @DYNINST_LDFLAGS@
index 04a2d5f75752f254ea73df5790f57cb8d2cf96a5..90e8686ffe69d823583c928b54988765051dd286 100644 (file)
@@ -7,8 +7,6 @@ extern "C" {
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <getopt.h>
-#include <gelf.h>
-#include <libelf.h>
 #include <libgen.h>
 #include <limits.h>
 #include <sys/stat.h>
@@ -41,6 +39,36 @@ using namespace std;
 
 static const char* program = "stapdyn";
 
+
+struct stapdyn_uprobe_probe {
+    uint64_t index, offset, semaphore;
+    stapdyn_uprobe_probe(uint64_t index, uint64_t offset, uint64_t semaphore):
+      index(index), offset(offset), semaphore(semaphore) {}
+};
+
+
+struct stapdyn_uprobe_target {
+    string path;
+    vector<stapdyn_uprobe_probe> probes;
+    stapdyn_uprobe_target(const char* path): path(path) {}
+};
+
+
+template <typename T> void
+set_dlsym(T*& pointer, void* handle, const char* symbol, bool required=true)
+{
+  const char* err = dlerror(); // clear previous errors
+  pointer = reinterpret_cast<T*>(dlsym(handle, symbol));
+  if (required)
+    {
+      if ((err = dlerror()))
+        throw runtime_error(err);
+      if (pointer == NULL)
+        throw runtime_error(string(symbol) + " is NULL");
+    }
+}
+
+
 static void __attribute__ ((noreturn))
 usage (int rc)
 {
@@ -65,24 +93,16 @@ call_inferior_function(BPatch_process *app, const string& name)
     }
 }
 
-// XXX: copied from runtime/dyninst/uprobes-dyninst.c
-struct stapdu_target {
-       char filename[240];
-       uint64_t offset; /* the probe offset within the file */
-       uint64_t sdt_sem_offset; /* the semaphore offset from process->base */
-};
-
 static void
-instrument_uprobes(BPatch_process *app, Elf_Data* data)
+instrument_uprobes(BPatch_process *app,
+                   const vector<stapdyn_uprobe_target>& targets)
 {
-  if (!app || !data) return;
+  if (!app || targets.empty())
+    return;
 
   BPatch_image* image = app->getImage();
-  if (!image) return;
-
-  const stapdu_target* targets = static_cast<const stapdu_target*>(data->d_buf);
-  size_t ntargets = data->d_size / sizeof(stapdu_target);
-  if (!ntargets) return;
+  if (!image)
+    return;
 
   map<string, BPatch_object*> file_objects;
   vector<BPatch_object *> objects;
@@ -101,110 +121,126 @@ instrument_uprobes(BPatch_process *app, Elf_Data* data)
   BPatch_function& enter_function = *functions[0];
 
   app->beginInsertionSet();
-  for (size_t i = 0; i < ntargets; ++i)
+  for (uint64_t i = 0; i < targets.size(); ++i)
     {
-      const stapdu_target* target = &targets[i];
-      clog << "found target " << target->filename
-           << " 0x" << hex << target->offset << dec << endl;
+      const stapdyn_uprobe_target& target = targets[i];
 
-      BPatch_object* object = file_objects[target->filename];
+      BPatch_object* object = file_objects[target.path];
       if (!object)
         {
-          clog << "no object named " << target->filename << endl;
+          clog << "no object named " << target.path << endl;
           continue;
         }
+      clog << "found target " << target.path << ", inserting "
+           << target.probes.size() << " probes" << endl;
 
-      Dyninst::Address address = object->fileOffsetToAddr(target->offset);
-      if (address == BPatch_object::E_OUT_OF_BOUNDS)
+      for (uint64_t j = 0; j < target.probes.size(); ++j)
         {
-          clog << "couldn't convert 0x" << hex << target->offset << dec
-               << " in " << target->filename << " to an address" << endl;
-          continue;
+          const stapdyn_uprobe_probe& probe = target.probes[j];
+
+          Dyninst::Address address = object->fileOffsetToAddr(probe.offset);
+          if (address == BPatch_object::E_OUT_OF_BOUNDS)
+            {
+              clog << "couldn't convert " << lex_cast_hex(probe.offset)
+                   << " in " << target.path << " to an address" << endl;
+              continue;
+            }
+
+          vector<BPatch_point*> points;
+          object->findPoints(address, points);
+          if (points.empty())
+            {
+              clog << "couldn't find instrumentation point for address "
+                   << lex_cast_hex(address) << " at offset "
+                   << lex_cast_hex(probe.offset) << " in " << target.path << endl;
+              continue;
+            }
+
+          BPatch_Vector<BPatch_snippet *> args;
+          args.push_back(new BPatch_constExpr((int64_t)probe.index));
+          args.push_back(new BPatch_constExpr((void*)NULL)); // pt_regs
+          BPatch_funcCallExpr call(enter_function, args);
+          app->insertSnippet(call, points);
+
+          // XXX write the semaphore too!
         }
-
-      vector<BPatch_point*> points;
-      object->findPoints(address, points);
-      if (points.empty())
-        {
-          clog << "couldn't find instrumentation point 0x" << hex << target->offset << dec
-               << " in " << target->filename << endl;
-          continue;
-        }
-
-      BPatch_Vector<BPatch_snippet *> args;
-      args.push_back(new BPatch_constExpr((int64_t)i)); // probe index
-      args.push_back(new BPatch_constExpr((void*)NULL)); // pt_regs
-      BPatch_funcCallExpr call(enter_function, args);
-      app->insertSnippet(call, points);
-
-      // XXX write the semaphore too!
     }
   app->finalizeInsertionSet(false);
 }
 
-static void
-find_uprobes(BPatch_process *app, const string& module)
+static vector<stapdyn_uprobe_target>
+find_uprobes(void* module)
 {
-  int fd = -1;
-  size_t shstrndx;
-  Elf* elf = NULL;
-  Elf_Scn* scn = NULL;
+  vector<stapdyn_uprobe_target> targets;
+
+  typeof(&stp_dyninst_target_count) target_count = NULL;
+  typeof(&stp_dyninst_target_path) target_path = NULL;
+
+  typeof(&stp_dyninst_probe_count) probe_count = NULL;
+  typeof(&stp_dyninst_probe_target) probe_target = NULL;
+  typeof(&stp_dyninst_probe_offset) probe_offset = NULL;
+  typeof(&stp_dyninst_probe_semaphore) probe_semaphore = NULL;
 
-  fd = open(module.c_str(), O_RDONLY);
-  if (fd < 0)
+  set_dlsym(target_count, module, "stp_dyninst_target_count", false);
+  if (target_count == NULL) // no uprobes
+    return targets;
+
+  try
     {
-      clog << "can't open " << module << endl;
-      goto out;
+      // if target_count exists, the rest of these should too
+      set_dlsym(target_path, module, "stp_dyninst_target_path");
+      set_dlsym(probe_count, module, "stp_dyninst_probe_count");
+      set_dlsym(probe_target, module, "stp_dyninst_probe_target");
+      set_dlsym(probe_offset, module, "stp_dyninst_probe_offset");
+      set_dlsym(probe_semaphore, module, "stp_dyninst_probe_semaphore");
     }
-
-  elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
-  if (!elf)
+  catch (runtime_error& e)
     {
-      clog << "can't read elf " << module << endl;
-      goto out_fd;
+      clog << program << ": dlsym " << e.what() << endl;
+      return targets;
     }
 
-  elf_getshdrstrndx (elf, &shstrndx);
+  const uint64_t ntargets = target_count();
+  for (uint64_t i = 0; i < ntargets; ++i)
+    targets.push_back(stapdyn_uprobe_target(target_path(i)));
+
+  const uint64_t nprobes = probe_count();
+  for (uint64_t i = 0; i < nprobes; ++i)
+    {
+      uint64_t ti = probe_target(i);
+      stapdyn_uprobe_probe p(i, probe_offset(i), probe_semaphore(i));
+      if (ti < ntargets)
+        targets[ti].probes.push_back(p);
+    }
 
-  scn = NULL;
-  while ((scn = elf_nextscn(elf, scn)))
+#if 0 // verbose debug
+  for (uint64_t i = 0; i < ntargets; ++i)
     {
-      GElf_Shdr shdr;
-      if (gelf_getshdr (scn, &shdr) == NULL)
-       continue;
-      if (shdr.sh_type != SHT_PROGBITS)
-       continue;
-      if (!(shdr.sh_flags & SHF_ALLOC))
-       continue;
-      if (!strcmp(".stap_dyninst", elf_strptr(elf, shstrndx, shdr.sh_name)))
-        instrument_uprobes(app, elf_rawdata(scn, NULL));
+      stapdyn_uprobe_target& t = targets[i];
+      clog << "target " << t.path << " has "
+        << t.probes.size() << " probes" << endl;
+      for (uint64_t j = 0; j < t.probes.size(); ++j)
+        clog << "  offset:" << (void*)t.probes[i].offset
+          << " semaphore:" << t.probes[i].semaphore << endl;
     }
+#endif
 
-  elf_end(elf);
-out_fd:
-  close(fd);
-out:
-  ;
+  return targets;
 }
 
 static int
 run_simple_module(void* module)
 {
-  typedef typeof(stp_dyninst_session_init) init_t;
-  typedef typeof(stp_dyninst_session_exit) exit_t;
-
-  const char* err = dlerror(); // clear previous errors
-  init_t *session_init = (init_t*)dlsym(module, "stp_dyninst_session_init");
-  if ((err = dlerror()))
+  typeof(&stp_dyninst_session_init) session_init = NULL;
+  typeof(&stp_dyninst_session_exit) session_exit = NULL;
+  try
     {
-      clog << program << ": dlsym " << err << endl;
-      return 1;
+      set_dlsym(session_init, module, "stp_dyninst_session_init");
+      set_dlsym(session_exit, module, "stp_dyninst_session_exit");
     }
-
-  exit_t *session_exit = (exit_t*)dlsym(module, "stp_dyninst_session_exit");
-  if ((err = dlerror()))
+  catch (runtime_error& e)
     {
-      clog << program << ": dlsym " << err << endl;
+      clog << program << ": dlsym " << e.what() << endl;
       return 1;
     }
 
@@ -296,7 +332,7 @@ main(int argc, char * const argv[])
 
   app->loadLibrary(module);
 
-  find_uprobes(app, module);
+  instrument_uprobes(app, find_uprobes(dlmodule));
 
   call_inferior_function(app, "stp_dyninst_session_init");
 
index 65e6974d2b7283bfb0bf75b43bdd8955c78d1aae..f55f73ed1e6693d8a067cad8332d15117b01778f 100644 (file)
@@ -7701,49 +7701,61 @@ uprobe_derived_probe_group::emit_module_dyninst_decls (systemtap_session& s)
   if (probes.empty()) return;
   s.op->newline() << "/* ---- dyninst uprobes ---- */";
   emit_module_maxuprobes (s);
-  s.op->newline() << "#include \"dyninst/uprobes.c\"";
+  s.op->newline() << "#include \"dyninst/uprobes.h\"";
 
-  // Discover and declare targets for dyninst to use
-  s.op->newline() << "const struct stapdu_target "
-                  << "stap_dyninst_uprobe_targets[] "
-                  << "__attribute__ ((section (\".stap_dyninst\"))) "
-                  << "= {";
+  // Assign "task-finder" numbers as we build up the stapdu_target table.
+  // This means we process probes[] in two passes.
+  map <string,unsigned> module_index;
+  unsigned module_index_ctr = 0;
+
+  // Discover and declare file targets
+  s.op->newline() << "static const struct stapdu_target stapdu_targets[] = {";
   s.op->indent(1);
   for (unsigned i=0; i<probes.size(); i++)
     {
       uprobe_derived_probe *p = probes[i];
-      s.op->newline() << "{";
-      // XXX pid/procname filtering?
-      s.op->line() << " .filename=" << lex_cast_qstring(p->module) << ",";
-      s.op->line() << " .offset=(loff_t)0x" << hex << p->addr << dec << "ULL,";
-      if (p->sdt_semaphore_addr)
-        s.op->line() << " .sdt_sem_offset=(loff_t)0x"
-                     << hex << p->sdt_semaphore_addr << dec << "ULL,";
-      s.op->line() << " },";
+      string pbmkey = make_pbm_key (p);
+      if (module_index.find (pbmkey) == module_index.end())
+        {
+          module_index[pbmkey] = module_index_ctr++;
+
+          s.op->newline() << "{";
+          // NB: it's essential that make_pbm_key() use all of and
+          // only the same fields as we're about to emit.
+          s.op->line() << " .path=" << lex_cast_qstring(p->module) << ",";
+          // XXX pid/procname filtering?
+          s.op->line() << " },";
+        }
     }
   s.op->newline(-1) << "};";
   s.op->assert_0_indent();
 
-  // Declare the actual probes for the runtime to use
-  s.op->newline() << "static struct stapdu_consumer "
-                  << "stap_dyninst_uprobe_consumers[] = {";
+  // Declare the individual probes
+  s.op->newline() << "static struct stapdu_probe stapdu_probes[] = {";
   s.op->indent(1);
   for (unsigned i=0; i<probes.size(); i++)
     {
       uprobe_derived_probe *p = probes[i];
+      unsigned index = module_index[make_pbm_key(p)];
       s.op->newline() << "{";
+      s.op->line() << " .target=" << index << ",";
+      s.op->line() << " .offset=" << lex_cast_hex(p->addr) << "ULL,";
+      if (p->sdt_semaphore_addr)
+        s.op->line() << " .semaphore="
+                     << lex_cast_hex(p->sdt_semaphore_addr) << "ULL,";
       s.op->line() << " .probe=" << common_probe_init (p) << ",";
       s.op->line() << " },";
     }
   s.op->newline(-1) << "};";
   s.op->assert_0_indent();
 
+  s.op->newline() << "#include \"dyninst/uprobes.c\"";
+
   // Write the probe handler.
   // NB: not static, so dyninst can find it
   s.op->newline() << "int enter_dyninst_uprobe "
                   << "(uint64_t index, struct pt_regs *regs) {";
-  s.op->newline(1) << "struct stapdu_consumer *sup = "
-                   << "&stap_dyninst_uprobe_consumers[index];";
+  s.op->newline(1) << "struct stapdu_probe *sup = &stapdu_probes[index];";
   common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "sup->probe",
                                  "stp_probe_type_uprobe");
   s.op->newline() << "c->uregs = regs;";
This page took 0.047547 seconds and 5 git commands to generate.