]> sourceware.org Git - systemtap.git/commitdiff
PR11508: stap --ldd option for automagic shared library scanning
authorFrank Ch. Eigler <fche@elastic.org>
Fri, 16 Apr 2010 20:38:41 +0000 (16:38 -0400)
committerFrank Ch. Eigler <fche@elastic.org>
Fri, 16 Apr 2010 20:40:32 +0000 (16:40 -0400)
* session.h, main.cxx (unwindsym_ldd): New flag.
* translate.cxx (add_unwindsym_ldd): New function to perform ldd scan.
* NEWS, stap.1.in: Document it.
* testsuite/buildok/thirtythree.stp: Lightly test it.

NEWS
main.cxx
session.h
stap.1.in
testsuite/buildok/thirtythree.stp [new file with mode: 0755]
translate.cxx

diff --git a/NEWS b/NEWS
index 3450fde9f6e46c8c5b6aa8da287e9933e166a249..2d17834e71303e29845f6f6719493ba6cb85cf5a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,9 @@
 * What's new
 
+- The new "--ldd" option automatically adds any additional shared
+  libraries needed by probed or -d-listed userspace binaries to the -d
+  list, to enable symbolic backtracing through them.
+
 - A new family of set_kernel_* functions make it easier for gurus to write
   new values at arbitrary memory addresses.
 
index 28d3bc8aa695cec47cdbdc8fd11036ed7b905747..05b5fc4fac5dfbd384e9be8f2fefbebae0a06687 100644 (file)
--- a/main.cxx
+++ b/main.cxx
@@ -139,6 +139,7 @@ usage (systemtap_session& s, int exitcode)
       clog << "              " << syms[i] << endl;
   }
   clog
+    << "   --ldd      add unwind/symbol data for all referenced OBJECT files." << endl
     << "   -t         collect probe timing information" << endl
 #ifdef HAVE_LIBSQLITE3
     << "   -q         generate information on tapset coverage" << endl
@@ -613,6 +614,7 @@ main (int argc, char * const argv [])
   s.unprivileged = false;
   s.omit_werror = false;
   s.compatible = VERSION; // XXX: perhaps also process GIT_SHAID if available?
+  s.unwindsym_ldd = false;
   bool client_options = false;
   string client_options_disallowed;
 
@@ -704,6 +706,7 @@ main (int argc, char * const argv [])
 #define LONG_OPT_POISON_CACHE 12
 #define LONG_OPT_CLEAN_CACHE 13
 #define LONG_OPT_COMPATIBLE 14
+#define LONG_OPT_LDD 15
       // NB: also see find_hash(), usage(), switch stmt below, stap.1 man page
       static struct option long_options[] = {
         { "kelf", 0, &long_opt, LONG_OPT_KELF },
@@ -726,6 +729,7 @@ main (int argc, char * const argv [])
         { "poison-cache", 0, &long_opt, LONG_OPT_POISON_CACHE },
         { "clean-cache", 0, &long_opt, LONG_OPT_CLEAN_CACHE },
         { "compatible", 1, &long_opt, LONG_OPT_COMPATIBLE },
+        { "ldd", 0, &long_opt, LONG_OPT_LDD },
         { NULL, 0, NULL, 0 }
       };
       int grc = getopt_long (argc, argv, "hVvtp:I:e:o:R:r:a:m:kgPc:x:D:bs:uqwl:d:L:FS:B:W",
@@ -1048,6 +1052,10 @@ main (int argc, char * const argv [])
               s.compatible = optarg;
               break;
 
+            case LONG_OPT_LDD:
+              s.unwindsym_ldd = true;
+              break;
+
             default:
               // NOTREACHED unless one added a getopt option but not a corresponding switch/case:
               cerr << "Unhandled long argument id " << long_opt << endl;
index ee1762a27ac5c670afe57465a406eba4716f4e22..8370d0d7abe4270f0df55717b9d767dbbdf27fb7 100644 (file)
--- a/session.h
+++ b/session.h
@@ -212,6 +212,7 @@ struct systemtap_session
 
   // List of libdwfl module names to extract symbol/unwind data for.
   std::set<std::string> unwindsym_modules;
+  bool unwindsym_ldd;
   struct module_cache* module_cache;
 
   // NB: It is very important for all of the above (and below) fields
index 0fc052ff6e62ac30c0fbd06622542bbc09c8a9a7..822aad067c7f0443c67f777cad2ff86941fef32f 100644 (file)
--- a/stap.1.in
+++ b/stap.1.in
@@ -182,6 +182,12 @@ Add symbol/unwind information for the given module into the kernel object
 module.  This may enable symbolic tracebacks from those modules/programs,
 even if they do not have an explicit probe placed into them.
 .TP
+.BI \-\-ldd
+Add symbol/unwind information for all shared libraries suspected by
+ldd to be necessary for user-space binaries being probe or listed with
+the \-d option.  Caution: this can make the probe modules considerably
+larger.
+.TP
 .BI \-o " FILE"
 Send standard output to named file. In bulk mode, percpu files will
 start with FILE_ (FILE_cpu with -F) followed by the cpu number.
diff --git a/testsuite/buildok/thirtythree.stp b/testsuite/buildok/thirtythree.stp
new file mode 100755 (executable)
index 0000000..cd704bd
--- /dev/null
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+stap --ldd -vvv -p4 -e 'probe begin {}' -t -d /bin/ls
+
+
index 6fb335f62134111ec6ebf5a4138c8ce011bd50db..14a1c4ba493069ce017d805c2530a4d3834a2847 100644 (file)
@@ -24,6 +24,7 @@
 #include <string>
 #include <cassert>
 #include <cstring>
+#include <cerrno>
 
 extern "C" {
 #include <elfutils/libdwfl.h>
@@ -5105,6 +5106,69 @@ dump_unwindsyms (Dwfl_Module *m,
 // them with the runtime.
 void emit_symbol_data_done (unwindsym_dump_context*, systemtap_session&);
 
+
+void
+add_unwindsym_ldd (systemtap_session &s)
+{
+  std::set<std::string> added;
+
+  // NB: This is not entirely safe.  It may be possible to create a
+  // handcrafted executable that sends ldd off to neverland, or even
+  // to execute the thing.
+  if (geteuid() == 0 && !s.suppress_warnings)
+    s.print_warning("/usr/bin/ldd may not be safe to run on untrustworthy executables");
+
+  for (std::set<std::string>::iterator it = s.unwindsym_modules.begin();
+       it != s.unwindsym_modules.end();
+       it++)
+    {
+      string modname = *it;
+      assert (modname.length() != 0);
+      if (! is_user_module (modname)) continue;
+
+      string ldd_command = "/usr/bin/ldd " + modname;
+      if (s.verbose > 2)
+        clog << "Running '" << ldd_command << "'" << endl;
+
+      FILE *fp = popen (ldd_command.c_str(), "r");
+      if (fp == 0)
+        clog << ldd_command << " failed: " << strerror(errno) << endl;
+      else
+        {
+          while (1)
+            {
+              char linebuf[256];
+              char *soname = 0;
+              char *shlib = 0;
+              char *addr = 0;
+
+              char *line = fgets (linebuf, 256, fp);
+              if (line == 0) break; // EOF or error
+
+              int nf = sscanf (line, "%as => %as %as", &soname, & shlib, &addr);
+              if (nf != 3) continue; // fewer than expected fields
+
+              if (added.find (shlib) == added.end())
+                {
+                  (void) addr; // don't bother print this one
+                  if (s.verbose > 2)
+                    clog << "Added -d '" << shlib << "' due to '" << soname << "'" << endl;
+                  added.insert (shlib);
+                }
+
+              free (soname);
+              free (shlib);
+              free (addr);
+            }
+          pclose (fp);
+        }
+    }
+  
+  s.unwindsym_modules.insert (added.begin(), added.end());
+}
+
+
+
 void
 emit_symbol_data (systemtap_session& s)
 {
@@ -5114,6 +5178,11 @@ emit_symbol_data (systemtap_session& s)
 
   ofstream kallsyms_out ((s.tmpdir + "/" + symfile).c_str());
 
+  // step 0: run ldd on any user modules if requested
+  if (s.unwindsym_ldd)
+    add_unwindsym_ldd (s);
+  // NB: do this before the ctx.unwindsym_modules copy is taken
+
   unwindsym_dump_context ctx = { s, kallsyms_out, 0, ~0, s.unwindsym_modules };
 
   // Micro optimization, mainly to speed up tiny regression tests
This page took 0.07126 seconds and 5 git commands to generate.