public inbox for systemtap@sourceware.org
 help / color / mirror / Atom feed
* PATCH add more syntax for shared library probing
@ 2011-03-23 15:12 Stan Cox
  2011-03-24 16:50 ` Frank Ch. Eigler
  0 siblings, 1 reply; 2+ messages in thread
From: Stan Cox @ 2011-03-23 15:12 UTC (permalink / raw)
  To: systemtap

[-- Attachment #1: Type: text/plain, Size: 2187 bytes --]

Comments on this patch to add more syntax for shared library probing?
Example:
1. library wildcard for function in libc.so
  stap -c "/usr/bin/printf '%s\n' 'ABCD'" -e 'probe 
process("/usr/bin/printf").library("*").function("__snprintf_chk") {printf("at 
printf begin\n")} probe 
process("/usr/bin/printf").library("*").function("__snprintf_chk").return 
{printf("at printf end\n")} probe 
process("/usr/bin/printf").library("*").statement("__snprintf_chk@snprintf_chk.c+2") 
{printf("in printf body\n")}'
ABCD
at printf begin
in printf body
at printf end

2. Similar for -L.  Perhaps filling in the wildcard with the matched library 
would be useful.
stap -L  'process("/usr/bin/printf").library("*").function("__snprintf_chk")'
process("/usr/bin/printf").library("*").function("__snprintf_chk")? $s:char* 
$maxlen:size_t $flags:int $slen:size_t $format:char const* $arg:va_list $done:int

3. Probe in a user SO
stap -c ./sdt_misc_uprobe_shared.x -e 'probe process 
("./sdt_misc_uprobe_shared.x").library("*").statement("bar@sdt_misc.c+9") ? 
{printf ("In bar i=%d\n",$i)}'
In bar i=2

	* dwflpp.cxx (iterate_over_libraries): New.  Just calls
	iterate_over_libraries_dynamic with the Dwfl_Module.

	(iterate_over_libraries_dynamic): Iterate through SHT_DYNAMIC for
	the Dwfl_Module.  Save DT_RPATH/DT_RUNPATH for library search.
	DT_NEEDED/DT_SONAME are potentially SOs to search.  Insure we
	haven't already seen it, call the callback, then find the SOs
	that this so references via iterate_over_libraries_module.  The
	system default library paths are explicitly appended to runpath.

	(iterate_over_libraries_module): Get the Dwfl_Module for this SO
	via dwfl_getmodules which just callbacks iterate_over_libraries_dynamic.

	* util.cxx (find_executable):  Add rpath and runpath default parameters.
	Search: 1. LD_RPATH 2. LD_LIBRARY_PATH 3. LD_RUNPATH 4. system libraries

	* tapsets.cxx (base_query): If this is a library then save the
	executable path.
	(query_module): For a library, call query_library_callback via
	iterate_over_libraries.
	(query_library_callback): New. Calls query_library.
	(query_library): Substitute the library name into the chain then
	derive_probes.



[-- Attachment #2: ,git.diff --]
[-- Type: text/plain, Size: 12575 bytes --]

diff --git a/dwflpp.cxx b/dwflpp.cxx
index 70bf07c..f47df3f 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -1021,8 +1021,6 @@ dwflpp::iterate_over_notes (void *object, void (*callback)(void *object, int typ
   if (elf_getshdrstrndx (elf, &shstrndx))
     return elf_errno();
 
-  GElf_Addr base = (GElf_Addr) -1;
-
   Elf_Scn *scn = NULL;
 
   vector<Dwarf_Die> notes;
@@ -1037,9 +1035,6 @@ dwflpp::iterate_over_notes (void *object, void (*callback)(void *object, int typ
 	case SHT_NOTE:
 	  if (!(shdr.sh_flags & SHF_ALLOC))
 	    {
-	      if (base == (GElf_Addr) -1)
-		base = 0;
-
 	      Elf_Data *data = elf_getdata (scn, NULL);
 	      size_t next;
 	      GElf_Nhdr nhdr;
@@ -1057,6 +1052,125 @@ dwflpp::iterate_over_notes (void *object, void (*callback)(void *object, int typ
 }
 
 
+/* For each entry in the .dynamic section in the current module call 'callback', use
+ * 'data' for the dynamic entry buffer return the entry type and 'object' in case
+ * 'callback' is a method */
+
+typedef struct {Dwfl *dwfl; base_query *q; void (*callback)(void *object, int type, const char *arg);} iol_data;
+static void iterate_over_libraries_module (const string& fname, iol_data *iold);
+set<string> libs_found;
+
+static int
+iterate_over_libraries_dynamic(Dwfl_Module *mod, void **, const char *name, Dwarf_Addr addr, void *arg)
+{
+  Dwarf_Addr bias;
+  Elf* elf = (dwarf_getelf (dwfl_module_getdwarf (mod, &bias))
+              ?: dwfl_module_getelf (mod, &bias));
+  size_t shstrndx;
+  iol_data *iold = (iol_data*)arg;
+  if (elf_getshdrstrndx (elf, &shstrndx))
+    return elf_errno();
+
+  Elf_Scn *scn = NULL;
+
+  while ((scn = elf_nextscn (elf, scn)) != NULL)
+    {
+      GElf_Shdr shdr;
+      if (gelf_getshdr (scn, &shdr) == NULL)
+        continue;
+      if (shdr.sh_type == SHT_DYNAMIC)
+        {
+          string rpath;
+          string runpath;
+          Elf_Data *data = elf_getdata (scn, NULL);
+          if (data == NULL)
+            return 0;
+
+          for (unsigned cnt = 0; cnt < shdr.sh_size / shdr.sh_entsize; ++cnt)
+            {
+              GElf_Dyn dynmem;
+              GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dynmem);
+              if (dyn == NULL)
+                break;
+              switch (dyn->d_tag)
+              {
+              case DT_RPATH:
+                rpath = (const char*)elf_strptr (elf, shdr.sh_link, dyn->d_un.d_val);
+                break;
+              case DT_RUNPATH:
+                runpath = (const char*)elf_strptr (elf, shdr.sh_link, dyn->d_un.d_val);
+                break;
+              }
+            }
+          for (unsigned cnt = 0; cnt < shdr.sh_size / shdr.sh_entsize; ++cnt)
+            {
+              GElf_Dyn dynmem;
+              GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dynmem);
+              string fname;
+              if (dyn == NULL)
+                break;
+              switch (dyn->d_tag)
+              {
+              case DT_NEEDED:
+              case DT_SONAME:
+                fname = (const char*)elf_strptr (elf, shdr.sh_link, dyn->d_un.d_val);
+                if (libs_found.find(fname) != libs_found.end())
+                  continue;
+                libs_found.insert(fname);
+                runpath = runpath + "/lib64:/usr/lib64:/lib:/usr/lib";
+                fname = find_executable(fname, "LD_LIBRARY_PATH", rpath, runpath);
+                (iold->callback) (iold->q, dyn->d_tag, fname.c_str());
+                iterate_over_libraries_module (fname, iold);
+              }
+            }
+        }
+    }
+  return 0;
+}
+
+
+static void
+iterate_over_libraries_module (const string& fname, iol_data *iold)
+{
+  int fd;
+
+  if ((fd = open (fname.c_str(), O_RDONLY)) < 0)
+    {
+      return;
+    }
+   static const Dwfl_Callbacks callbacks =
+     {
+       dwfl_build_id_find_elf,
+       dwfl_standard_find_debuginfo,
+       dwfl_offline_section_address,
+       NULL
+     };
+   Dwfl *dwfl = dwfl_begin (&callbacks);
+   if (dwfl == NULL)
+     return;
+  if (dwfl_report_offline (dwfl, fname.c_str(), fname.c_str(), fd) == NULL)
+    return;
+  else
+    {
+      dwfl_report_end (dwfl, NULL, NULL);
+
+      /* Process the one or more modules gleaned from this file.  */
+      //      struct process_dwflmod_args a = { .fd = fd, .only_one = only_one };
+      dwfl_getmodules (dwfl, &iterate_over_libraries_dynamic, iold, 0);
+    }
+  dwfl_end (dwfl);
+}
+
+
+void
+dwflpp::iterate_over_libraries(void (*callback)(void *object, int type, const char *arg), base_query *q)
+{
+  libs_found.erase(libs_found.begin(),libs_found.end());
+  iol_data iol_data = {dwfl_ptr.get()->dwfl, q, callback};
+  iterate_over_libraries_dynamic (module, NULL, this->module_name.c_str(), 0, &iol_data);
+}
+
+
 // This little test routine represents an unfortunate breakdown in
 // abstraction between dwflpp (putatively, a layer right on top of
 // elfutils), and dwarf_query (interpreting a systemtap probe point).
diff --git a/dwflpp.h b/dwflpp.h
index 5735f4e..fbbf2a2 100644
--- a/dwflpp.h
+++ b/dwflpp.h
@@ -251,6 +251,10 @@ struct dwflpp
 			  void (*callback)(void *object, int type,
 					   const char *data, size_t len));
 
+  void iterate_over_libraries (void (*callback)(void *object,
+      int type, const char *data), base_query *data);
+
+
   GElf_Shdr * get_section(std::string section_name, GElf_Shdr *shdr_mem,
                           Elf **elf_ret=NULL);
 
diff --git a/tapsets.cxx b/tapsets.cxx
index ceb78b8..0405c86 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -504,6 +504,9 @@ struct base_query
 			       string const & k, long & v);
   static bool get_number_param(literal_map_t const & params,
 			       string const & k, Dwarf_Addr & v);
+  static void query_library_callback (void *object, int type, const char *data);
+  virtual void query_library (int type, const char *data) = 0;
+
 
   // Extracted parameters.
   bool has_kernel;
@@ -532,13 +535,18 @@ base_query::base_query(dwflpp & dw, literal_map_t const & params):
       string library_name;
       has_process = get_string_param(params, TOK_PROCESS, module_val);
       has_library = get_string_param (params, TOK_LIBRARY, library_name);
-      if (has_library)
-	{
-	  path = find_executable (module_val);
-	  module_val = find_executable (library_name, "LD_LIBRARY_PATH");
-	}
-      else if (has_process)
+      if (has_process)
         module_val = find_executable (module_val);
+      if (has_library)
+        {
+          if (! contains_glob_chars (library_name))
+            {
+              path = module_val;
+              module_val = find_executable (library_name, "LD_LIBRARY_PATH");
+            }
+          else
+            path = library_name;
+        }
     }
 
   assert (has_kernel || has_process || has_module);
@@ -619,6 +627,7 @@ struct dwarf_query : public base_query
   virtual void handle_query_module();
   void query_module_dwarf();
   void query_module_symtab();
+  void query_library (int type, const char *data);
 
   void add_probe_point(string const & funcname,
 		       char const * filename,
@@ -1919,8 +1928,12 @@ query_module (Dwfl_Module *mod,
             q->sess.sym_stext = lookup_symbol_address (mod, "_stext");
         }
 
-      // Finally, search the module for matches of the probe point.
-      q->handle_query_module();
+      if (q->has_library && contains_glob_chars (q->path))
+        // handle .library(GLOB)
+        q->dw.iterate_over_libraries(&q->query_library_callback, q);
+      else
+        // search the module for matches of the probe point.
+        q->handle_query_module();
 
 
       // If we know that there will be no more matches, abort early.
@@ -1937,6 +1950,43 @@ query_module (Dwfl_Module *mod,
 }
 
 
+void
+base_query::query_library_callback (void *q, int type, const char *data)
+{
+  base_query *me = (base_query*)q;
+  me->query_library (type, data);
+}
+
+
+void
+dwarf_query::query_library (int type, const char *library)
+{
+  if (type ==  DT_NEEDED
+      && dw.function_name_matches_pattern(library, user_lib))
+    {
+      string library_path = find_executable (library, "LD_LIBRARY_PATH");
+      probe_point* specific_loc = new probe_point(*base_loc);
+      specific_loc->optional = true;
+      vector<probe_point::component*> derived_comps;
+
+      vector<probe_point::component*>::iterator it;
+      for (it = specific_loc->components.begin();
+          it != specific_loc->components.end(); ++it)
+        if ((*it)->functor == TOK_LIBRARY)
+          derived_comps.push_back(new probe_point::component(TOK_LIBRARY,
+              new literal_string(library_path)));
+        else
+          derived_comps.push_back(*it);
+      probe_point* derived_loc = new probe_point(*specific_loc);
+      derived_loc->components = derived_comps;
+      probe *new_base = base_probe->create_alias(derived_loc, specific_loc);
+      derive_probes(sess, new_base, results);
+      if (sess.verbose > 2)
+        clog << _("module=") << library_path;
+    }
+}
+
+
 // This would more naturally fit into elaborate.cxx:semantic_pass_symbols,
 // but the needed declaration for module_cache is not available there.
 // Nor for that matter in session.cxx.  Only in this CU is that field ever
@@ -3453,6 +3503,7 @@ struct dwarf_cast_query : public base_query
     userspace_p(userspace_p), result(result) {}
 
   void handle_query_module();
+  void query_library (int type, const char *data) {};
 };
 
 
@@ -5307,6 +5358,7 @@ struct sdt_query : public base_query
             dwflpp & dw, literal_map_t const & params,
             vector<derived_probe *> & results);
 
+  void query_library (int type, const char *data) {};
   void handle_query_module();
 
 private:
@@ -5954,9 +6006,7 @@ dwarf_builder::build(systemtap_session & sess,
       dw = get_kern_dw(sess, module_name);
     }
   else if (get_param (parameters, TOK_PROCESS, module_name))
-    {
-      string library_name;
-
+      {
       // PR6456  process("/bin/*")  glob handling
       if (contains_glob_chars (module_name))
         {
@@ -6113,11 +6163,9 @@ dwarf_builder::build(systemtap_session & sess,
          script_file.close();
       }
 
-      if (get_param (parameters, TOK_LIBRARY, library_name))
-	{
-	  module_name = find_executable (library_name, "LD_LIBRARY_PATH");
-	  user_lib = module_name;
-	}
+      get_param (parameters, TOK_LIBRARY, user_lib);
+      if (user_lib.length() && ! contains_glob_chars (user_lib))
+        module_name = find_executable (user_lib, "LD_LIBRARY_PATH");
       else
 	module_name = user_path; // canonicalize it
 
@@ -8479,6 +8527,7 @@ struct tracepoint_query : public base_query
   void handle_query_module();
   int handle_query_cu(Dwarf_Die * cudie);
   int handle_query_func(Dwarf_Die * func);
+  void query_library (int type, const char *data) {};
 
   static int tracepoint_query_cu (Dwarf_Die * cudie, void * arg);
   static int tracepoint_query_func (Dwarf_Die * func, base_query * query);
diff --git a/util.cxx b/util.cxx
index a1dc8c0..24be913 100644
--- a/util.cxx
+++ b/util.cxx
@@ -321,7 +321,8 @@ tokenize(const string& str, vector<string>& tokens,
 // same policy as execvp().  A program name not containing a slash
 // will be searched along the $PATH.
 
-string find_executable(const string& name, const string& env_path)
+string find_executable(const string& name, const string& env_path,
+    const string& rpath, const string& runpath)
 {
   string retpath;
 
@@ -336,8 +337,12 @@ string find_executable(const string& name, const string& env_path)
     }
   else // Nope, search $PATH.
     {
-      char *path = getenv(env_path.c_str());
-      if (path)
+      string path = getenv(env_path.c_str());
+      if (rpath.length() != 0 && runpath.length() == 0)
+        path = rpath + ":" + path;
+      if (runpath.length() != 0)
+        path = path + ":" + runpath;
+      if (path.length())
         {
           // Split PATH up.
           vector<string> dirs;
diff --git a/util.h b/util.h
index bc79dd5..074ed34 100644
--- a/util.h
+++ b/util.h
@@ -23,7 +23,9 @@ std::string getmemusage ();
 void tokenize(const std::string& str, std::vector<std::string>& tokens,
 	      const std::string& delimiters);
 std::string find_executable(const std::string& name,
-			    const std::string& env_path = "PATH");
+			    const std::string& env_path = "PATH",
+			    const std::string& rpath = "",
+			    const std::string& runpath = "");
 const std::string cmdstr_quoted(const std::string& cmd);
 const std::string cmdstr_join(const std::vector<std::string>& cmds);
 std::string git_revision(const std::string& path);

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: PATCH add more syntax for shared library probing
  2011-03-23 15:12 PATCH add more syntax for shared library probing Stan Cox
@ 2011-03-24 16:50 ` Frank Ch. Eigler
  0 siblings, 0 replies; 2+ messages in thread
From: Frank Ch. Eigler @ 2011-03-24 16:50 UTC (permalink / raw)
  To: Stan Cox; +Cc: systemtap


scox wrote:

> [...]

Looks good!

> 2. Similar for -L.  Perhaps filling in the wildcard with the matched
> library would be useful.

Yup.  pp() etc. should get the fully resolved name.

> [...]
> +/* For each entry in the .dynamic section in the current module call 'callback', use
> + * 'data' for the dynamic entry buffer return the entry type and 'object' in case
> + * 'callback' is a method */
> [...]

This ld.so-emulation code looks promising, but the risk of giving
results inconsistent with actual ld.so would have to be guarded
against.  Have you considered PR12560's alternate approach?

- FChE

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-03-24 16:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-23 15:12 PATCH add more syntax for shared library probing Stan Cox
2011-03-24 16:50 ` Frank Ch. Eigler

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).