]> sourceware.org Git - systemtap.git/commitdiff
implement iterate_over_callees()
authorJonathan Lebon <jlebon@redhat.com>
Tue, 28 Jan 2014 19:51:07 +0000 (14:51 -0500)
committerJonathan Lebon <jlebon@redhat.com>
Wed, 19 Feb 2014 15:33:05 +0000 (10:33 -0500)
In this patch, we actually implement iterate_over_callees(). This
function looks for inlined subroutines or GNU_call_sites and calls the
callback for each found. The query_callee() callback for now simply does
a query_statement on the callee found. Logic is added in the next patch
to limit probing to calls from caller only.

dwflpp.cxx
dwflpp.h
tapsets.cxx

index b2f072a76900c4ae064916c98cd4dc5768e8dfe1..2730096fcf1233fd9d5298ba2bef4ddf86f44a3c 100644 (file)
@@ -1787,6 +1787,210 @@ dwflpp::iterate_over_labels (Dwarf_Die *begin_die,
   while (dwarf_siblingof (&die, &die) == 0);
 }
 
+// Mini 'query-like' struct to help us navigate callbacks during
+// external function resolution
+struct external_function_query {
+  dwflpp* dw;
+  const char* name;
+  Dwarf_Die die;
+  Dwarf_Addr addr;
+  bool resolved;
+  external_function_query(dwflpp* dw, const char* name):
+    dw(dw), name(name), resolved(false) {}
+};
+
+int
+dwflpp::external_function_cu_callback (Dwarf_Die* cu, void *arg)
+{
+  external_function_query * efq = static_cast<external_function_query *>(arg);
+  efq->dw->focus_on_cu(cu);
+  return efq->dw->iterate_over_functions(external_function_func_callback,
+                                         efq, efq->name);
+}
+
+int
+dwflpp::external_function_func_callback (Dwarf_Die* func, void * arg)
+{
+  external_function_query * efq = static_cast<external_function_query *>(arg);
+  Dwarf_Attribute external;
+  Dwarf_Addr func_addr;
+  if (dwarf_attr_integrate(func, DW_AT_external, &external) != NULL &&
+      dwarf_lowpc(func, &func_addr) == 0)
+    {
+      efq->die = *func;
+      efq->addr = func_addr;
+      efq->resolved = true;
+      return DWARF_CB_ABORT; // we found it so stop here
+    }
+  return DWARF_CB_OK;
+}
+
+void
+dwflpp::iterate_over_callees (Dwarf_Die *begin_die,
+                              const string& sym,
+                              long recursion_depth,
+                              dwarf_query *q,
+                              void (* callback)(const char *,
+                                                const char *,
+                                                int,
+                                                Dwarf_Die *,
+                                                Dwarf_Addr,
+                                                stack<Dwarf_Addr>*,
+                                                dwarf_query *),
+                              stack<Dwarf_Addr> *callers)
+{
+  get_module_dwarf();
+
+  Dwarf_Die die, import;
+
+  // DIE of abstract_origin found in die
+  Dwarf_Die origin;
+
+  // callee's entry pc (= where we'll probe)
+  Dwarf_Addr func_addr;
+
+  // caller's unwind pc during call (to match against bt for filtering)
+  Dwarf_Addr caller_uw_addr;
+
+  Dwarf_Attribute attr;
+
+  int dline;
+  const char *name, *file;
+  if (dwarf_child(begin_die, &die) != 0)
+    return;  // die without children, bail out.
+
+  bool free_callers = false;
+  if (callers == NULL) /* first call */
+    {
+      callers = new stack<Dwarf_Addr>();
+      free_callers = true;
+    }
+
+  do
+    {
+      bool inlined = false;
+      switch (dwarf_tag(&die))
+        {
+        case DW_TAG_inlined_subroutine:
+          inlined = true;
+        case DW_TAG_GNU_call_site:
+          name = dwarf_diename (&die);
+          if (!name)
+            continue;
+          if (name != sym)
+            {
+              if (!name_has_wildcard(sym))
+                continue;
+              if (!function_name_matches_pattern(name, sym))
+                continue;
+            }
+
+          /* In both cases (call sites and inlines), we want the
+           * abstract_origin. The difference is that in inlines, the addr is
+           * in the die itself, whereas for call sites, the addr is in the
+           * abstract_origin's die.
+           *     Note that in the case of inlines, we're only calling back
+           * for that inline instance, not all. This is what we want, since
+           * it will only be triggered when 'called' from the target func,
+           * which is something we have to emulate for non-inlined funcs
+           * (which is the purpose of the caller_uw_addr below) */
+          if (dwarf_attr_die(&die, DW_AT_abstract_origin, &origin) == NULL)
+            continue;
+
+          // the low_pc of the die in either cases is the pc that would
+          // show up in a backtrace (inlines are a special case in which
+          // the die's low_pc is also the abstract_origin's low_pc = the
+          // 'start' of the inline instance)
+          if (dwarf_lowpc(&die, &caller_uw_addr) != 0)
+            continue;
+
+          if (inlined)
+            func_addr = caller_uw_addr;
+          else if (dwarf_lowpc(&origin, &func_addr) != 0)
+            {
+              // function doesn't have a low_pc, is it external?
+              if (dwarf_attr_integrate(&origin, DW_AT_external,
+                                       &attr) != NULL)
+                {
+                  // let's iterate over the CUs and find it. NB: it's
+                  // possible we could have also done this by creating a
+                  // probe point with .exported tacked on and rerunning it
+                  // through derive_probe(). But since we're already on the
+                  // dwflpp side of things, and we already have access to
+                  // everything we need, let's try to be self-sufficient.
+
+                  // remember old focus
+                  Dwarf_Die *old_cu = cu;
+
+                  external_function_query efq(this, dwarf_linkage_name(&origin) ?: name);
+                  iterate_over_cus(external_function_cu_callback, &efq, false);
+
+                  // restore focus
+                  cu = old_cu;
+
+                  if (!efq.resolved) // did we resolve it?
+                    continue;
+
+                  func_addr = efq.addr;
+                  origin = efq.die;
+                }
+              // non-external function without low_pc, jump ship
+              else continue;
+            }
+
+          // We now have the addr to probe in func_addr, and the DIE
+          // from which to obtain file/line info in origin
+
+          // Get the file/line number for this callee
+          file = dwarf_decl_file (&origin) ?: "<unknown source>";
+          dwarf_decl_line (&origin, &dline);
+
+          // add as a caller to match against
+          if (!inlined)
+            callers->push(caller_uw_addr);
+
+          callback(name, file, dline, inlined ? &die : &origin,
+                   func_addr, callers, q);
+
+          // If it's a tail call, print a warning that it may not be caught
+          if (!inlined
+              && dwarf_attr_integrate(&die, DW_AT_GNU_tail_call, &attr) != NULL)
+            sess.print_warning (_F("Callee \"%s\" in function \"%s\" is a tail call: "
+                                   ".callee probe may not fire. Try placing the probe "
+                                   "directly on the callee function instead.", name,
+                                   dwarf_diename(begin_die)));
+
+          if (recursion_depth > 1) // .callees(N)
+            iterate_over_callees(inlined ? &die : &origin,
+                                 sym, recursion_depth-1, q,
+                                 callback, callers);
+
+          if (!inlined)
+            callers->pop();
+          break;
+
+        case DW_TAG_subprogram:
+          break; // don't leave our filtered func
+
+        case DW_TAG_imported_unit:
+          // Iterate over the children of the imported unit as if they
+          // were inserted in place.
+          if (dwarf_attr_die(&die, DW_AT_import, &import))
+            iterate_over_callees (&import, sym, recursion_depth, q, callback, callers);
+          break;
+
+        default:
+          if (dwarf_haschildren (&die))
+            iterate_over_callees (&die, sym, recursion_depth, q, callback, callers);
+          break;
+        }
+    }
+  while (dwarf_siblingof (&die, &die) == 0);
+
+  if (free_callers && callers != NULL)
+    delete callers;
+}
+
 
 void
 dwflpp::collect_srcfiles_matching (string const & pattern,
index 672101d71e15b8943819062f05fe87370dad6368..839cfb79cbc29141b6cedf6e7f10416a2113920a 100644 (file)
--- a/dwflpp.h
+++ b/dwflpp.h
@@ -253,6 +253,19 @@ struct dwflpp
                                               Dwarf_Addr,
                                               dwarf_query *));
 
+  void iterate_over_callees (Dwarf_Die *begin_die,
+                             const std::string& sym,
+                             long recursion_depth,
+                             dwarf_query *q,
+                             void (* callback)(const char *,
+                                               const char *,
+                                               int,
+                                               Dwarf_Die *,
+                                               Dwarf_Addr,
+                                               std::stack<Dwarf_Addr>*,
+                                               dwarf_query *),
+                             std::stack<Dwarf_Addr>*callers=NULL);
+
   int iterate_over_notes (void *object,
                          void (*callback)(void *object, int type,
                                           const char *data, size_t len));
@@ -377,6 +390,8 @@ private:
 
   static int mod_function_caching_callback (Dwarf_Die* func, void *arg);
   static int cu_function_caching_callback (Dwarf_Die* func, void *arg);
+  static int external_function_cu_callback (Dwarf_Die* cu, void *arg);
+  static int external_function_func_callback (Dwarf_Die* func, void *arg);
 
   bool has_single_line_record (dwarf_query * q, char const * srcfile, int lineno);
 
index 9eba9f93b05c7b1b290cdd45321ba7b0210aec78..94db2469c3477fc244d3377e07b09b494a697d5e 100644 (file)
@@ -37,6 +37,7 @@
 #include <sstream>
 #include <stdexcept>
 #include <vector>
+#include <stack>
 #include <cstdarg>
 #include <cassert>
 #include <iomanip>
@@ -1569,6 +1570,19 @@ query_label (string const & func,
     }
 }
 
+static void
+query_callee (char const * callee,
+              char const * file,
+              int line,
+              Dwarf_Die *callee_die,
+              Dwarf_Addr callee_addr,
+              stack<Dwarf_Addr> *callers,
+              dwarf_query * q)
+{
+  assert (q->has_function_str);
+  query_statement(callee, file, line, callee_die, callee_addr, q);
+}
+
 static void
 query_inline_instance_info (inline_instance_info & ii,
                            dwarf_query * q)
@@ -1906,6 +1920,38 @@ query_cu (Dwarf_Die * cudie, void * arg)
             q->dw.iterate_over_labels (&i->die, q->label_val, i->name,
                                        q, query_label);
         }
+      else if (q->has_callee || q->has_callees_num)
+        {
+          // .callee(str) --> str, .callees[(N)] --> "*"
+          string callee_val = q->has_callee ? q->callee_val : "*";
+          long callees_num_val = q->has_callees_num ? q->callees_num_val : 1;
+
+          // NB: We filter functions that do not match the file here rather than
+          // in query_callee because we only want the filtering to apply to the
+          // first level, not to callees that are recursed into if
+          // callees_num_val > 1.
+          for (func_info_map_t::iterator i = q->filtered_functions.begin();
+               i != q->filtered_functions.end(); ++i)
+            {
+              if (q->spec_type != function_alone &&
+                  q->filtered_srcfiles.count(i->decl_file) == 0)
+                continue;
+              q->dw.iterate_over_callees (&i->die, callee_val,
+                                          callees_num_val,
+                                          q, query_callee);
+            }
+
+          for (inline_instance_map_t::iterator i = q->filtered_inlines.begin();
+               i != q->filtered_inlines.end(); ++i)
+            {
+              if (q->spec_type != function_alone &&
+                  q->filtered_srcfiles.count(i->decl_file) == 0)
+                continue;
+              q->dw.iterate_over_callees (&i->die, callee_val,
+                                          callees_num_val,
+                                          q, query_callee);
+            }
+        }
       else
         {
           // Otherwise, simply probe all resolved functions.
This page took 0.048346 seconds and 5 git commands to generate.