This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] PR11096: Add support for the "module" argument to @var


From: "Yichun Zhang (agentzh)" <agentzh@gmail.com>

The notation @var("varname@cuname", "module") is now supported and @var
can thus effectively be used almost everywhere like the context of stap
functions or probe timer.profile.

Just like @cast, multiple module names can be specified by seperating
them with ":", for example, "module1:module2:module3". And they will be
attempted in turn until a match is found.

Refactored the code by introducing atvar_op as suggested by Josh Stone
to make the implementation cleaner. The field "target_name" has been
moved from target_symbol to atvar_op but not cu_name because moving the
latter requires too many modifications in dwflpp.

* parse.cxx: Add support for the optional "module" parameter to the
  parser.
* staptree.h: Remove the "target_name" field from target_symbol and make
  sym_name() virtual. Define atvar_op which inherits target_symbol. Add
  method visit_atvar_op to the visitor classes.
* staptree.cxx: Define visit_atvar_op for the visitor classes. Define
  methods of atvar_op.
* tapsets.cxx: Define visit_atvar_op for dwarf_var_expanding_visitor,
  sdt_uprobe_var_expanding_visitor, and
  tracepoint_var_expanding_visitor. Define dwarf_atvar_expanding_visitor
  to run in series with dwarf_cast_expanding_visitor in
  dwarf_derived_probe. Add dwarf_atvar_query to handle the DWARF queres
  of dwarf_atvar_expanding_visitor. Postpone the processing of @var with
  either cu name or module name or both to dwarf_atvar_expanding_visitor
  in order to eliminate code duplication.
* elaborate.h: Declare visit_atvar_op for typeresolution_info.
* elaborate.cxx: Define visit_atvar_op for symbol_fetcher and
  typeresolution_info.
* translate.cxx: Define visit_atvar_op for c_unparser.
* testsuite/systemtap.base/: Add many more test cases for @var.
* .gitignore: Ignore executable files generated by the @var tests.

Signed-off-by: Yichun Zhang (agentzh) <agentzh@gmail.com>
---
 .gitignore                                        |   4 +
 elaborate.cxx                                     |  41 +++
 elaborate.h                                       |   1 +
 parse.cxx                                         |  28 +-
 staptree.cxx                                      |  63 +++-
 staptree.h                                        |  15 +-
 tapsets.cxx                                       | 333 +++++++++++++++++-----
 testsuite/systemtap.base/at_var_func.exp          |  30 ++
 testsuite/systemtap.base/at_var_func.stp          |  19 ++
 testsuite/systemtap.base/at_var_lvalue.c          |  29 ++
 testsuite/systemtap.base/at_var_lvalue.exp        |  21 ++
 testsuite/systemtap.base/at_var_lvalue.stp        |  25 ++
 testsuite/systemtap.base/at_var_mark_func.exp     |  26 ++
 testsuite/systemtap.base/at_var_mark_func.stp     |  24 ++
 testsuite/systemtap.base/at_var_timer_profile.c   |  27 ++
 testsuite/systemtap.base/at_var_timer_profile.exp |  31 ++
 testsuite/systemtap.base/at_var_timer_profile.stp |  23 ++
 testsuite/systemtap.base/global_var_kernel.exp    |   5 +-
 testsuite/systemtap.base/global_var_kernel.stp    |   7 +
 translate.cxx                                     |   8 +
 20 files changed, 669 insertions(+), 91 deletions(-)
 create mode 100644 testsuite/systemtap.base/at_var_func.exp
 create mode 100644 testsuite/systemtap.base/at_var_func.stp
 create mode 100644 testsuite/systemtap.base/at_var_lvalue.c
 create mode 100644 testsuite/systemtap.base/at_var_lvalue.exp
 create mode 100644 testsuite/systemtap.base/at_var_lvalue.stp
 create mode 100644 testsuite/systemtap.base/at_var_mark_func.exp
 create mode 100644 testsuite/systemtap.base/at_var_mark_func.stp
 create mode 100644 testsuite/systemtap.base/at_var_timer_profile.c
 create mode 100644 testsuite/systemtap.base/at_var_timer_profile.exp
 create mode 100644 testsuite/systemtap.base/at_var_timer_profile.stp

diff --git a/.gitignore b/.gitignore
index 95de57b..f0d11e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,7 @@ lib-elfutils
 stamp-elfutils
 dtrace
 stappaths.7
+testsuite/at_var
+testsuite/at_var_func
+testsuite/at_var_timer_profile
+testsuite/at_var_lvalue
diff --git a/elaborate.cxx b/elaborate.cxx
index c53e9f7..a4424b8 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -1085,6 +1085,11 @@ struct symbol_fetcher
     e->base->visit (this);
   }
 
+  void visit_atvar_op (atvar_op *e)
+  {
+    sym = e;
+  }
+
   void visit_cast_op (cast_op* e)
   {
     sym = e;
@@ -4587,6 +4592,42 @@ typeresolution_info::visit_target_symbol (target_symbol* e)
 
 
 void
+typeresolution_info::visit_atvar_op (atvar_op* e)
+{
+  // This occurs only if an @var() was not resolved over in
+  // tapset.cxx land, that error was properly suppressed, and the
+  // later unused-expression-elimination pass didn't get rid of it
+  // either.  So we have an @var() that is believed to be of
+  // genuine use, yet unresolved by the provider.
+
+  if (session.verbose > 2)
+    {
+      clog << _("Resolution problem with ");
+      if (current_function)
+        {
+          clog << "function " << current_function->name << endl;
+          current_function->body->print (clog);
+          clog << endl;
+        }
+      else if (current_probe)
+        {
+          clog << "probe " << *current_probe->sole_location() << endl;
+          current_probe->body->print (clog);
+          clog << endl;
+        }
+      else
+        //TRANSLATORS: simply saying not an issue with a probe or function
+        clog << _("other") << endl;
+    }
+
+  if (e->saved_conversion_error)
+    throw (* (e->saved_conversion_error));
+  else
+    throw semantic_error(_("unresolved @var() expression"), e->tok);
+}
+
+
+void
 typeresolution_info::visit_defined_op (defined_op* e)
 {
   throw semantic_error(_("unexpected @defined"), e->tok);
diff --git a/elaborate.h b/elaborate.h
index 6ac5b80..c0c27bb 100644
--- a/elaborate.h
+++ b/elaborate.h
@@ -124,6 +124,7 @@ struct typeresolution_info: public visitor
   void visit_stat_op (stat_op* e);
   void visit_hist_op (hist_op* e);
   void visit_cast_op (cast_op* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
   void visit_perf_op (perf_op* e);
diff --git a/parse.cxx b/parse.cxx
index bbdc2ad..b2669eb 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -3642,7 +3642,6 @@ target_symbol* parser::parse_target_symbol (const token* t)
       target_symbol *tsym = new target_symbol;
       tsym->tok = t;
       tsym->name = t->content;
-      tsym->target_name = "";
       tsym->cu_name = "";
       parse_target_symbol_components(tsym);
       tsym->addressof = addressof;
@@ -3651,20 +3650,27 @@ target_symbol* parser::parse_target_symbol (const token* t)
 
   if (t->type == tok_operator && t->content == "@var")
     {
-      target_symbol *tsym = new target_symbol;
-      tsym->tok = t;
-      tsym->name = t->content;
+      atvar_op *aop = new atvar_op;
+      aop->tok = t;
+      aop->name = t->content;
       expect_op("(");
-      expect_unknown(tok_string, tsym->target_name);
-      size_t found_at = tsym->target_name.find("@");
+      expect_unknown(tok_string, aop->target_name);
+      size_t found_at = aop->target_name.find("@");
       if (found_at != string::npos)
-	tsym->cu_name = tsym->target_name.substr(found_at + 1);
+        aop->cu_name = aop->target_name.substr(found_at + 1);
       else
-	tsym->cu_name = "";
+        aop->cu_name = "";
+      if (peek_op (","))
+        {
+          swallow ();
+          expect_unknown (tok_string, aop->module);
+        }
+      else
+        aop->module = "";
       expect_op(")");
-      parse_target_symbol_components(tsym);
-      tsym->addressof = addressof;
-      return tsym;
+      parse_target_symbol_components(aop);
+      aop->addressof = addressof;
+      return aop;
     }
 
   throw parse_error (_("expected @cast, @var or $var"));
diff --git a/staptree.cxx b/staptree.cxx
index e1be9e3..6f93954 100644
--- a/staptree.cxx
+++ b/staptree.cxx
@@ -281,19 +281,22 @@ void target_symbol::chain (const semantic_error &er)
   this->saved_conversion_error = e;
 }
 
+
 string target_symbol::sym_name ()
 {
-  if (name == "@var")
-    {
-      if (cu_name == "")
-	return target_name;
-      else
-	return target_name.substr(0, target_name.length() - cu_name.length() - 1);
-    }
+  return name.substr(1);
+}
+
+
+string atvar_op::sym_name ()
+{
+  if (cu_name == "")
+    return target_name;
   else
-    return name.substr(1);
+    return target_name.substr(0, target_name.length() - cu_name.length() - 1);
 }
 
+
 // ------------------------------------------------------------------------
 // parse tree printing
 
@@ -416,8 +419,16 @@ void target_symbol::print (ostream& o) const
   if (addressof)
     o << "&";
   o << name;
-  if (name == "@var")
-    o << "(\"" << target_name << "\")";
+  for (unsigned i = 0; i < components.size(); ++i)
+    o << components[i];
+}
+
+
+void atvar_op::print (ostream& o) const
+{
+  if (addressof)
+    o << "&";
+  o << name << "(\"" << target_name << "\")";
   for (unsigned i = 0; i < components.size(); ++i)
     o << components[i];
 }
@@ -1495,6 +1506,13 @@ cast_op::visit (visitor* u)
 
 
 void
+atvar_op::visit (visitor* u)
+{
+  u->visit_atvar_op(this);
+}
+
+
+void
 defined_op::visit (visitor* u)
 {
   u->visit_defined_op(this);
@@ -1831,6 +1849,12 @@ traversing_visitor::visit_cast_op (cast_op* e)
 }
 
 void
+traversing_visitor::visit_atvar_op (atvar_op* e)
+{
+  e->visit_components (this);
+}
+
+void
 traversing_visitor::visit_defined_op (defined_op* e)
 {
   e->operand->visit (this);
@@ -2462,6 +2486,12 @@ throwing_visitor::visit_target_symbol (target_symbol* e)
 }
 
 void
+throwing_visitor::visit_atvar_op (atvar_op* e)
+{
+  throwone (e->tok);
+}
+
+void
 throwing_visitor::visit_cast_op (cast_op* e)
 {
   throwone (e->tok);
@@ -2754,6 +2784,13 @@ update_visitor::visit_cast_op (cast_op* e)
 }
 
 void
+update_visitor::visit_atvar_op (atvar_op* e)
+{
+  e->visit_components (this);
+  provide (e);
+}
+
+void
 update_visitor::visit_defined_op (defined_op* e)
 {
   replace (e->operand);
@@ -3010,6 +3047,12 @@ deep_copy_visitor::visit_cast_op (cast_op* e)
 }
 
 void
+deep_copy_visitor::visit_atvar_op (atvar_op* e)
+{
+  update_visitor::visit_atvar_op(new atvar_op(*e));
+}
+
+void
 deep_copy_visitor::visit_defined_op (defined_op* e)
 {
   update_visitor::visit_defined_op(new defined_op(*e));
diff --git a/staptree.h b/staptree.h
index 8eac23d..16455d8 100644
--- a/staptree.h
+++ b/staptree.h
@@ -268,12 +268,11 @@ struct target_symbol: public symbol
     };
 
   bool addressof;
-  std::string target_name;
   std::string cu_name;
   std::vector<component> components;
   semantic_error* saved_conversion_error; // hand-made linked list
   target_symbol(): addressof(false), saved_conversion_error (0) {}
-  std::string sym_name ();
+  virtual std::string sym_name ();
   void chain (const semantic_error& er);
   void print (std::ostream& o) const;
   void visit (visitor* u);
@@ -293,6 +292,13 @@ struct cast_op: public target_symbol
   void visit (visitor* u);
 };
 
+struct atvar_op: public target_symbol
+{
+  std::string target_name, module;
+  virtual std::string sym_name ();
+  void print (std::ostream& o) const;
+  void visit (visitor* u);
+};
 
 struct defined_op: public expression
 {
@@ -791,6 +797,7 @@ struct visitor
   virtual void visit_stat_op (stat_op* e) = 0;
   virtual void visit_hist_op (hist_op* e) = 0;
   virtual void visit_cast_op (cast_op* e) = 0;
+  virtual void visit_atvar_op (atvar_op* e) = 0;
   virtual void visit_defined_op (defined_op* e) = 0;
   virtual void visit_entry_op (entry_op* e) = 0;
   virtual void visit_perf_op (perf_op* e) = 0;
@@ -838,6 +845,7 @@ struct traversing_visitor: public visitor
   void visit_stat_op (stat_op* e);
   void visit_hist_op (hist_op* e);
   void visit_cast_op (cast_op* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
   void visit_perf_op (perf_op* e);
@@ -943,6 +951,7 @@ struct throwing_visitor: public visitor
   void visit_stat_op (stat_op* e);
   void visit_hist_op (hist_op* e);
   void visit_cast_op (cast_op* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
   void visit_perf_op (perf_op* e);
@@ -1021,6 +1030,7 @@ struct update_visitor: public visitor
   virtual void visit_stat_op (stat_op* e);
   virtual void visit_hist_op (hist_op* e);
   virtual void visit_cast_op (cast_op* e);
+  virtual void visit_atvar_op (atvar_op* e);
   virtual void visit_defined_op (defined_op* e);
   virtual void visit_entry_op (entry_op* e);
   virtual void visit_perf_op (perf_op* e);
@@ -1079,6 +1089,7 @@ struct deep_copy_visitor: public update_visitor
   virtual void visit_stat_op (stat_op* e);
   virtual void visit_hist_op (hist_op* e);
   virtual void visit_cast_op (cast_op* e);
+  virtual void visit_atvar_op (atvar_op* e);
   virtual void visit_defined_op (defined_op* e);
   virtual void visit_entry_op (entry_op* e);
   virtual void visit_perf_op (perf_op* e);
diff --git a/tapsets.cxx b/tapsets.cxx
index 6a785e8..38b457c 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -2280,11 +2280,11 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
   void visit_target_symbol_saved_return (target_symbol* e);
   void visit_target_symbol_context (target_symbol* e);
   void visit_target_symbol (target_symbol* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_cast_op (cast_op* e);
   void visit_entry_op (entry_op* e);
   void visit_perf_op (perf_op* e);
 private:
-  vector<Dwarf_Die>& getcuscope(target_symbol *e);
   vector<Dwarf_Die>& getscopes(target_symbol *e);
 };
 
@@ -3686,11 +3686,28 @@ dwarf_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
 
 
 void
+dwarf_var_expanding_visitor::visit_atvar_op (atvar_op *e)
+{
+  if (e->module.empty() && e->cu_name.empty())
+    {
+      // process like any other local
+      // e->sym_name() will do the right thing
+      visit_target_symbol(e);
+      return;
+    }
+
+  // Fill in our current module context if needed
+  if (e->module.empty())
+    e->module = q.dw.module_name;
+
+  var_expanding_visitor::visit_atvar_op(e);
+}
+
+
+void
 dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e)
 {
-  assert(e->name.size() > 0
-	 && ((e->name[0] == '$' && e->target_name == "")
-	      || (e->name == "@var" && e->target_name != "")));
+  assert(e->name.size() > 0 && (e->name[0] == '$' || e->name == "@var"));
   visited = true;
   bool defined_being_checked = (defined_ops.size() > 0 && (defined_ops.top()->operand == e));
   // In this mode, we avoid hiding errors or generating extra code such as for .return saved $vars
@@ -3864,65 +3881,10 @@ dwarf_var_expanding_visitor::visit_perf_op (perf_op *e)
     throw semantic_error(_F("perf counter '%s' not defined", e_lit_val.c_str()));
 }
 
-vector<Dwarf_Die>&
-dwarf_var_expanding_visitor::getcuscope(target_symbol *e)
-{
-  Dwarf_Off cu_off = 0;
-  const char *cu_name = NULL;
-
-  string prefixed_srcfile = string("*/") + e->cu_name;
-
-  Dwarf_Off off = 0;
-  size_t cuhl;
-  Dwarf_Off noff;
-  Dwarf_Off module_bias;
-  Dwarf *dw = dwfl_module_getdwarf(q.dw.module, &module_bias);
-  while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0)
-    {
-      Dwarf_Die die_mem;
-      Dwarf_Die *die;
-      die = dwarf_offdie (dw, off + cuhl, &die_mem);
-
-      /* We are not interested in partial units. */
-      if (dwarf_tag (die) == DW_TAG_compile_unit)
-	{
-	  const char *die_name = dwarf_diename (die);
-	  if (strcmp (die_name, e->cu_name.c_str()) == 0) // Perfect match.
-	    {
-	      cu_name = die_name;
-	      cu_off = off + cuhl;
-	      break;
-	    }
-
-	  if (fnmatch(prefixed_srcfile.c_str(), die_name, 0) == 0)
-	    if (cu_name == NULL || strlen (die_name) < strlen (cu_name))
-	      {
-		cu_name = die_name;
-		cu_off = off + cuhl;
-	      }
-	}
-      off = noff;
-    }
-
-  if (cu_name == NULL)
-    throw semantic_error ("unable to find CU '" + e->cu_name + "'"
-			  + " while searching for '" + e->target_name + "'",
-			  e->tok);
-
-  vector<Dwarf_Die> *cu_scope = new vector<Dwarf_Die>;
-  Dwarf_Die cu_die;
-  dwarf_offdie (dw, cu_off, &cu_die);
-  cu_scope->push_back(cu_die);
-  return *cu_scope;
-}
 
 vector<Dwarf_Die>&
 dwarf_var_expanding_visitor::getscopes(target_symbol *e)
 {
-  // "static globals" can only be found in the top-level CU.
-  if (e->name == "@var" && e->cu_name != "")
-    return this->getcuscope(e);
-
   if (scopes.empty())
     {
       if(scope_die != NULL)
@@ -4162,6 +4124,217 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
 }
 
 
+struct dwarf_atvar_expanding_visitor: public var_expanding_visitor
+{
+  systemtap_session& s;
+  dwarf_builder& db;
+
+  dwarf_atvar_expanding_visitor(systemtap_session& s, dwarf_builder& db):
+    s(s), db(db) {}
+  void visit_atvar_op (atvar_op* e);
+};
+
+
+struct dwarf_atvar_query: public base_query
+{
+  atvar_op& e;
+  const bool userspace_p, lvalue;
+  functioncall*& result;
+  vector<Dwarf_Die> *scopes;
+  unsigned& tick;
+
+  dwarf_atvar_query(dwflpp& dw, const string& module, atvar_op& e,
+                    const bool userspace_p, const bool lvalue,
+                    functioncall*& result,
+                    unsigned& tick):
+    base_query(dw, module), e(e),
+    userspace_p(userspace_p), lvalue(lvalue), result(result),
+    scopes(NULL), tick(tick) {}
+
+  void handle_query_module ();
+  void query_library (const char *) {}
+  void query_plt (const char *entry, size_t addr) {}
+  void get_cu_scope (atvar_op *e);
+  ~dwarf_atvar_query () { if (scopes) delete scopes; }
+};
+
+
+void
+dwarf_atvar_query::handle_query_module ()
+{
+  string code;
+  exp_type type;
+
+  try
+    {
+      get_cu_scope (&e);
+
+      this->dw.focus_on_cu (&(scopes->at(0)));
+
+      if (! e.components.empty() &&
+          e.components.back().type == target_symbol::comp_pretty_print)
+        {
+          dwarf_pretty_print dpp (this->dw, *scopes, 0,
+                                  e.sym_name(),
+                                  userspace_p, e);
+          result = dpp.expand();
+          return;
+        }
+
+      type = pe_long;
+      code = this->dw.literal_stmt_for_local (*scopes, 0, e.sym_name(),
+                                              &e, lvalue, type);
+    }
+  catch (const semantic_error& er)
+    {
+      // NB: we can have multiple errors, since a @var
+      // may be attempted using several different modules:
+      //     @var(ptr, "type", "module1:module2:...")
+      e.chain (er);
+    }
+
+  if (code.empty())
+    return;
+
+  string fname = (string(lvalue ? "_dwarf_tvar_set" : "_dwarf_tvar_get")
+                  + "_" + e.sym_name()
+                  + "_" + lex_cast(tick++));
+
+  result = synthetic_embedded_deref_call (this->sess, fname, code, type,
+                                          userspace_p, lvalue, &e);
+}
+
+
+void
+dwarf_atvar_query::get_cu_scope (atvar_op *e)
+{
+  Dwarf_Off cu_off = 0;
+  const char *cu_name = NULL;
+
+  string prefixed_srcfile = string("*/") + e->cu_name;
+
+  Dwarf_Off off = 0;
+  size_t cuhl;
+  Dwarf_Off noff;
+  Dwarf_Off module_bias;
+  Dwarf *dw = dwfl_module_getdwarf(this->dw.module, &module_bias);
+  while (dwarf_nextcu(dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0)
+    {
+      Dwarf_Die die_mem;
+      Dwarf_Die *die;
+      die = dwarf_offdie(dw, off + cuhl, &die_mem);
+
+      /* We are not interested in partial units. */
+      if (dwarf_tag(die) == DW_TAG_compile_unit)
+        {
+          const char *die_name = dwarf_diename(die);
+          if (e->cu_name.empty()
+              || strcmp(die_name, e->cu_name.c_str()) == 0) // Perfect match.
+            {
+              cu_name = die_name;
+              cu_off = off + cuhl;
+              break;
+            }
+
+          if (fnmatch(prefixed_srcfile.c_str(), die_name, 0) == 0)
+            if (cu_name == NULL || strlen(die_name) < strlen (cu_name))
+              {
+                cu_name = die_name;
+                cu_off = off + cuhl;
+              }
+        }
+      off = noff;
+    }
+
+  if (cu_name == NULL)
+    throw semantic_error ("unable to find CU '" + e->cu_name + "'"
+                          + " while searching for '" + e->target_name + "'",
+                          e->tok);
+
+  if (scopes)
+    delete scopes;
+
+  scopes = new vector<Dwarf_Die>;
+  Dwarf_Die cu_die;
+  dwarf_offdie(dw, cu_off, &cu_die);
+  scopes->push_back(cu_die);
+}
+
+
+void
+dwarf_atvar_expanding_visitor::visit_atvar_op (atvar_op* e)
+{
+  const bool lvalue = is_active_lvalue(e);
+  if (lvalue && !s.guru_mode)
+    throw semantic_error(_("write to @var variable not permitted; "
+                           "need stap -g"), e->tok);
+
+  if (e->module.empty())
+    e->module = "kernel";
+
+  functioncall* result = NULL;
+
+  // split the module string by ':' for alternatives
+  vector<string> modules;
+  tokenize(e->module, modules, ":");
+  bool userspace_p = false;
+  for (unsigned i = 0; !result && i < modules.size(); ++i)
+    {
+      string& module = modules[i];
+
+      dwflpp* dw;
+      try
+        {
+          userspace_p = is_user_module(module);
+          if (!userspace_p)
+            {
+              // kernel or kernel module target
+              dw = db.get_kern_dw(s, module);
+            }
+          else
+            {
+              module = find_executable(module, "", s.sysenv);
+              dw = db.get_user_dw(s, module);
+            }
+        }
+      catch (const semantic_error& er)
+        {
+          /* ignore and go to the next module */
+          continue;
+        }
+
+      dwarf_atvar_query q (*dw, module, *e, userspace_p, lvalue, result, tick);
+      dw->iterate_over_modules(&query_module, &q);
+
+      if (result)
+        {
+          try
+            {
+              if (lvalue)
+                {
+                  // Provide the functioncall to our parent, so that it can be
+                  // used to substitute for the assignment node immediately above
+                  // us.
+                  assert(!target_symbol_setter_functioncalls.empty());
+                  *(target_symbol_setter_functioncalls.top()) = result;
+                }
+
+              result->visit(this);
+            }
+          catch (const semantic_error& er)
+            {
+              e->chain (er);
+              provide (e);
+            }
+
+          return;
+        }
+    }
+
+  provide(e);
+}
+
+
 void
 dwarf_derived_probe::printsig (ostream& o) const
 {
@@ -4722,6 +4895,9 @@ dwarf_derived_probe::register_patterns(systemtap_session& s)
   update_visitor *filter = new dwarf_cast_expanding_visitor(s, *dw);
   s.code_filters.push_back(filter);
 
+  filter = new dwarf_atvar_expanding_visitor(s, *dw);
+  s.code_filters.push_back(filter);
+
   register_function_and_statement_variants(s, root->bind(TOK_KERNEL), dw, pr_privileged);
   register_function_and_statement_variants(s, root->bind_str(TOK_MODULE), dw, pr_privileged);
   root->bind(TOK_KERNEL)->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE)
@@ -5475,6 +5651,7 @@ struct sdt_uprobe_var_expanding_visitor: public var_expanding_visitor
   void visit_target_symbol (target_symbol* e);
   void visit_target_symbol_arg (target_symbol* e);
   void visit_target_symbol_context (target_symbol* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_cast_op (cast_op* e);
 };
 
@@ -5959,8 +6136,7 @@ sdt_uprobe_var_expanding_visitor::visit_target_symbol (target_symbol* e)
   try
     {
       assert(e->name.size() > 0
-	     && ((e->name[0] == '$' && e->target_name == "")
-		 || (e->name == "@var" && e->target_name != "")));
+             && (e->name[0] == '$' || e->name == "@var"));
 
       if (e->name == "$$name" || e->name == "$$provider" || e->name == "$$parms" || e->name == "$$vars")
         visit_target_symbol_context (e);
@@ -5976,6 +6152,25 @@ sdt_uprobe_var_expanding_visitor::visit_target_symbol (target_symbol* e)
 
 
 void
+sdt_uprobe_var_expanding_visitor::visit_atvar_op (atvar_op* e)
+{
+  if (e->module.empty() && e->cu_name.empty())
+    {
+      // process like any other local
+      // e->sym_name() will do the right thing
+      visit_target_symbol(e);
+      return;
+    }
+
+  // Fill in our current module context if needed
+  if (e->module.empty())
+    e->module = process_name;
+
+  var_expanding_visitor::visit_atvar_op(e);
+}
+
+
+void
 sdt_uprobe_var_expanding_visitor::visit_cast_op (cast_op* e)
 {
   // Fill in our current module context if needed
@@ -9037,6 +9232,7 @@ struct tracepoint_var_expanding_visitor: public var_expanding_visitor
   void visit_target_symbol (target_symbol* e);
   void visit_target_symbol_arg (target_symbol* e);
   void visit_target_symbol_context (target_symbol* e);
+  void visit_atvar_op (atvar_op* e);
 };
 
 
@@ -9212,14 +9408,11 @@ tracepoint_var_expanding_visitor::visit_target_symbol (target_symbol* e)
 {
   try
     {
-      assert(e->name.size() > 0
-	     && ((e->name[0] == '$' && e->target_name == "")
-		 || (e->name == "@var" && e->target_name != "")));
+      assert(e->name.size() > 0 && e->name[0] == '$');
 
       if (e->name == "$$name" || e->name == "$$parms" || e->name == "$$vars")
         visit_target_symbol_context (e);
-      else if (e->name == "@var")
-	throw semantic_error(_("cannot use @var DWARF variables in tracepoints"), e->tok);
+
       else
         visit_target_symbol_arg (e);
     }
@@ -9231,6 +9424,14 @@ tracepoint_var_expanding_visitor::visit_target_symbol (target_symbol* e)
 }
 
 
+void
+tracepoint_var_expanding_visitor::visit_atvar_op (atvar_op* e)
+{
+  assert(e->name == "@var" && e->target_name != "");
+  throw semantic_error(_("cannot use @var DWARF variables in tracepoints"),
+                       e->tok);
+}
+
 
 tracepoint_derived_probe::tracepoint_derived_probe (systemtap_session& s,
                                                     dwflpp& dw, Dwarf_Die& func_die,
diff --git a/testsuite/systemtap.base/at_var_func.exp b/testsuite/systemtap.base/at_var_func.exp
new file mode 100644
index 0000000..382e1ae
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_func.exp
@@ -0,0 +1,30 @@
+set test "at_var_func"
+set testpath "$srcdir/$subdir"
+set exefile "[pwd]/$test"
+set stap_path $env(SYSTEMTAP_PATH)/stap
+set staprun_path $env(SYSTEMTAP_PATH)/staprun
+
+# Test that @var("var@cu", "module") work in contextless stap functions.
+# Also ensure that the module argument can be multiple module names
+# separated by colon, e.g., "module1:module2:module3".
+# Also test that unresolved @var() can be properly caught by @defined().
+
+set ::result_string {@var("foo", @1)->bar: 42
+@var("foo@at_var.c", @1)->bar: 42
+@var("foo@at_var.c", @2)->bar: 42
+@var("foo", @1)$: {.bar=42}
+@var("foo", @1)$$: {.bar=42}
+@defined(@var("foo", "badmodle")): NO
+@defined(@var("foo", @3)): NO}
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+if {! [uprobes_p]} { untested "$test"; return }
+
+set res [target_compile ${testpath}/at_var.c $exefile executable "additional_flags=-O2 additional_flags=-g"]
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    fail "unable to compile ${test}.c"
+}
+
+stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path"
diff --git a/testsuite/systemtap.base/at_var_func.stp b/testsuite/systemtap.base/at_var_func.stp
new file mode 100644
index 0000000..1d00d82
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_func.stp
@@ -0,0 +1,19 @@
+function f()
+{
+  printf("@var(\"foo\", @1)->bar: %d\n", @var("foo", @1)->bar);
+  printf("@var(\"foo@at_var.c\", @1)->bar: %d\n",
+         @var("foo@at_var.c", @1)->bar);
+  printf("@var(\"foo@at_var.c\", @2)->bar: %d\n",
+         @var("foo@at_var.c", @2)->bar);
+  printf("@var(\"foo\", @1)$: %s\n", @var("foo", @1)$);
+  printf("@var(\"foo\", @1)$$: %s\n", @var("foo", @1)$$);
+  printf("@defined(@var(\"foo\", \"badmodle\")): %s\n",
+         @defined(@var("foo", "badmodule")) ? "YES" : "NO")
+  printf("@defined(@var(\"foo\", @3)): %s\n",
+         @defined(@var("foo", @3)) ? "YES" : "NO")
+}
+
+probe process.function("sub")
+{
+  f()
+}
diff --git a/testsuite/systemtap.base/at_var_lvalue.c b/testsuite/systemtap.base/at_var_lvalue.c
new file mode 100644
index 0000000..9d14e80
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_lvalue.c
@@ -0,0 +1,29 @@
+#include <sys/time.h>
+
+struct foo
+{
+  int bar;
+};
+
+static struct foo foo;
+
+void sub(const char *file)
+{
+  struct timeval times[2];
+  times[0].tv_sec = 1;
+  times[0].tv_usec = 2;
+  times[1].tv_sec = 3;
+  times[1].tv_usec = 4;
+  utimes (file, times);
+  foo.bar -= 2; /* 40 */
+}
+
+int
+main (int argc, char **argv)
+{
+  foo.bar = 41 + argc; /* 42 */
+  sub(argv[0]);
+  sub(argv[0]);
+  sub(argv[0]);
+  return 0;
+}
diff --git a/testsuite/systemtap.base/at_var_lvalue.exp b/testsuite/systemtap.base/at_var_lvalue.exp
new file mode 100644
index 0000000..d6bf522
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_lvalue.exp
@@ -0,0 +1,21 @@
+set test "at_var_lvalue"
+set testpath "$srcdir/$subdir"
+set exefile "[pwd]/$test"
+
+# Test that @var("var") can be used as an lvalue in guru mode.
+set ::result_string {0: @var("foo")->bar: 41
+0: @var("foo@at_var_lvalue.c")->bar: 40
+1: @var("foo@at_var_lvalue.c", @1)->bar: 37
+2: @var("foo@at_var_lvalue.c", @1)->bar: 34}
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+if {! [uprobes_p]} { untested "$test"; return }
+
+set res [target_compile ${testpath}/${test}.c ${test} executable "additional_flags=-O2 additional_flags=-g"]
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    fail "unable to compile ${test}.c"
+}
+
+stap_run3 $test $srcdir/$subdir/$test.stp -c ./${test} -g $exefile
diff --git a/testsuite/systemtap.base/at_var_lvalue.stp b/testsuite/systemtap.base/at_var_lvalue.stp
new file mode 100644
index 0000000..14fdd41
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_lvalue.stp
@@ -0,0 +1,25 @@
+global i = 0
+
+function f(n)
+{
+  @var("foo@at_var_lvalue.c", @1)->bar =
+      @var("foo@at_var_lvalue.c", @1)->bar - 1
+  printf("%d: @var(\"foo@at_var_lvalue.c\", @1)->bar: %d\n", n,
+         @var("foo@at_var_lvalue.c", @1)->bar)
+}
+
+probe process.function("sub")
+{
+  if (i == 0) {
+      @var("foo")->bar = @var("foo")->bar - 1
+      printf("0: @var(\"foo\")->bar: %d\n", @var("foo")->bar)
+      @var("foo@at_var_lvalue.c")->bar = @var("foo@at_var_lvalue.c")->bar - 1
+      printf("0: @var(\"foo@at_var_lvalue.c\")->bar: %d\n",
+             @var("foo@at_var_lvalue.c")->bar)
+
+  } else {
+      f(i)
+  }
+
+  i++
+}
diff --git a/testsuite/systemtap.base/at_var_mark_func.exp b/testsuite/systemtap.base/at_var_mark_func.exp
new file mode 100644
index 0000000..dcecc86
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_mark_func.exp
@@ -0,0 +1,26 @@
+set test "at_var_mark_func"
+set testpath "$srcdir/$subdir"
+
+set stap_path $env(SYSTEMTAP_PATH)/stap
+
+# Check @var in contextless stap functions, even when @var
+# doesn't exist, works in process.mark probes.
+set output_string "pass:yes:0\r\n"
+set invoke "$stap_path -p4 -e 'probe begin \{ log(\"start\")\}'"
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+if {! [uprobes_p]} { untested "$test"; return }
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run "${test} ($runtime)" wait_5_secs $output_string \
+            --runtime=$runtime ${testpath}/${test}.stp -c $invoke
+
+    } elseif {[uprobes_p]} {
+        stap_run ${test} wait_5_secs $output_string \
+            ${testpath}/${test}.stp $stap_path -c $invoke
+    } else {
+        untested "$test"
+    }
+}
diff --git a/testsuite/systemtap.base/at_var_mark_func.stp b/testsuite/systemtap.base/at_var_mark_func.stp
new file mode 100644
index 0000000..353a302
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_mark_func.stp
@@ -0,0 +1,24 @@
+global more_addr = 0;
+
+function foo(name)
+{
+  if (more_addr == 0)
+    {
+      log("systemtap starting probe");
+      more_addr = @var("morehelp@session.cxx", @1);
+    }
+  else
+    {
+      log("systemtap ending probe");
+      name = substr(name, 0, 4);
+      correct = @defined(@var("no_such_var_really_not", @1)) ? "no" : "yes";
+      diff = more_addr - @var("morehelp@session.cxx", @1);
+      printf("%s:%s:%d\n", name, correct, diff);
+      exit();
+    }
+}
+
+probe process.mark("pass*")
+{
+  foo($$name)
+}
diff --git a/testsuite/systemtap.base/at_var_timer_profile.c b/testsuite/systemtap.base/at_var_timer_profile.c
new file mode 100644
index 0000000..91e9f23
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_timer_profile.c
@@ -0,0 +1,27 @@
+#include <unistd.h>
+#include <stdlib.h>
+
+struct foo
+{
+  int bar;
+};
+
+static struct foo foo;
+
+int
+sub(int a)
+{
+  int i, j;
+  for (i = 0; i < 1000; i++)
+    for (j = 0; j < 1000; j++)
+      a += i + j + rand();
+  return a;
+}
+
+int
+main (int argc, char **argv)
+{
+  foo.bar = 41 + argc; /* 42 */
+  sub(argc);
+  return 0;
+}
diff --git a/testsuite/systemtap.base/at_var_timer_profile.exp b/testsuite/systemtap.base/at_var_timer_profile.exp
new file mode 100644
index 0000000..5632c35
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_timer_profile.exp
@@ -0,0 +1,31 @@
+set test "at_var_timer_profile"
+set testpath "$srcdir/$subdir"
+set exefile "[pwd]/$test"
+set stap_path $env(SYSTEMTAP_PATH)/stap
+set staprun_path $env(SYSTEMTAP_PATH)/staprun
+
+# Test that @var("var@cu", "module") work in the context of
+# timer.profile.
+# Also ensure that the module argument can be multiple module names
+# separated by colon, e.g., "module1:module2:module3".
+# Also test that unresolved @var() can be properly caught by @defined().
+
+set ::result_string {@var("foo", @1)->bar: 42
+@var("foo@at_var_timer_profile.c", @1)->bar: 42
+@var("foo@at_var_timer_profile.c", @2)->bar: 42
+@var("foo", @1)$: {.bar=42}
+@var("foo", @1)$$: {.bar=42}
+@defined(@var("foo", "badmodle")): NO
+@defined(@var("foo", @3)): NO}
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+if {! [uprobes_p]} { untested "$test"; return }
+
+set res [target_compile ${testpath}/$test.c $exefile executable "additional_flags=-O2 additional_flags=-g"]
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    fail "unable to compile ${test}.c"
+}
+
+stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path"
diff --git a/testsuite/systemtap.base/at_var_timer_profile.stp b/testsuite/systemtap.base/at_var_timer_profile.stp
new file mode 100644
index 0000000..479f749
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_timer_profile.stp
@@ -0,0 +1,23 @@
+global active = 0
+
+probe process.function("sub")
+{
+  active = 1
+}
+
+probe timer.profile {
+  if (active && pid() == target()) {
+      printf("@var(\"foo\", @1)->bar: %d\n", @var("foo", @1)->bar);
+      printf("@var(\"foo@at_var_timer_profile.c\", @1)->bar: %d\n",
+             @var("foo@at_var_timer_profile.c", @1)->bar);
+      printf("@var(\"foo@at_var_timer_profile.c\", @2)->bar: %d\n",
+             @var("foo@at_var_timer_profile.c", @2)->bar);
+      printf("@var(\"foo\", @1)$: %s\n", @var("foo", @1)$);
+      printf("@var(\"foo\", @1)$$: %s\n", @var("foo", @1)$$);
+      printf("@defined(@var(\"foo\", \"badmodle\")): %s\n",
+             @defined(@var("foo", "badmodule")) ? "YES" : "NO")
+      printf("@defined(@var(\"foo\", @3)): %s\n",
+             @defined(@var("foo", @3)) ? "YES" : "NO")
+      exit()
+  }
+}
diff --git a/testsuite/systemtap.base/global_var_kernel.exp b/testsuite/systemtap.base/global_var_kernel.exp
index f6b3c9a..a8e5010 100644
--- a/testsuite/systemtap.base/global_var_kernel.exp
+++ b/testsuite/systemtap.base/global_var_kernel.exp
@@ -1,8 +1,9 @@
 set test "global_var_kernel"
 set testpath "$srcdir/$subdir"
 
-# Check the righ "kernel/time.c" is picked up.
-set output_string "sys_tz = {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}\r\n"
+# Check the righ "kernel/time.c" is picked up (both in the syscall probe
+# context and the stap function context.
+set output_string "sys_tz = {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}\r\nf: {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}"
 
 # Only run on make installcheck
 if {! [installtest_p]} { untested "$test"; return }
diff --git a/testsuite/systemtap.base/global_var_kernel.stp b/testsuite/systemtap.base/global_var_kernel.stp
index f128e34..5eb22c7 100644
--- a/testsuite/systemtap.base/global_var_kernel.stp
+++ b/testsuite/systemtap.base/global_var_kernel.stp
@@ -1,8 +1,15 @@
 # Test the correct kernel/time.c CU is selected.
+
+function f()
+{
+  printf("f: %s\n", @var("sys_tz@kernel/time.c")$$);
+}
+
 probe syscall.open
 {
   log("systemtap starting probe");
   log("systemtap ending probe");
   printf("sys_tz = %s\n", @var("sys_tz@kernel/time.c")$$);
+  f()
   exit();
 }
diff --git a/translate.cxx b/translate.cxx
index e14b492..f2ae600 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -202,6 +202,7 @@ struct c_unparser: public unparser, public visitor
   void visit_stat_op (stat_op* e);
   void visit_hist_op (hist_op* e);
   void visit_cast_op (cast_op* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_defined_op (defined_op* e);
   void visit_entry_op (entry_op* e);
   void visit_perf_op (perf_op* e);
@@ -4407,6 +4408,13 @@ c_unparser::visit_target_symbol (target_symbol* e)
 
 
 void
+c_unparser::visit_atvar_op (atvar_op* e)
+{
+  throw semantic_error(_("cannot translate general @var expression"), e->tok);
+}
+
+
+void
 c_unparser::visit_cast_op (cast_op* e)
 {
   throw semantic_error(_("cannot translate general @cast expression"), e->tok);
-- 
1.7.11.7


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]