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 v2 1/1] 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, probe timer.profile, and probe kernel.trace().

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 fields "target_name" and
"cu_name" have been moved from target_symbol to its subclassses atvar_op
and/or cast_op.

@var now searches all the CUs that matches the "cuname" specified for the
variable. And when "cuname" is missing, @var just searches all the CUs.

Added quite a few test cases for @var. The at_var_lvalue.exp and
at_var_timer_profile.exp are currently failing with the dyninst runtime
because that runtime lacks proper support for using @var as lvalue and
probe timer.profile. Also, the at_var_pie.exp test file for testing
accessing globals in PIE executables via @var is also failing because
accessing globals in PIE or DSO via @var does not work for @var on git
master either.

* 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.
  void_statement_reducer.
* elaborate.cxx: Define visit_atvar_op for symbol_fetcher,
  typeresolution_info, and void_statement_reducer.
* translate.cxx: Define visit_atvar_op for c_unparser.
* testsuite/systemtap.base/: Add many more test cases for @var.

Signed-off-by: Yichun Zhang (agentzh) <agentzh@gmail.com>
---
 dwflpp.cxx                                         |  12 +-
 elaborate.cxx                                      |  48 ++++
 elaborate.h                                        |   1 +
 parse.cxx                                          |  29 +-
 staptree.cxx                                       |  76 +++++-
 staptree.h                                         |  19 +-
 tapsets.cxx                                        | 296 ++++++++++++++++-----
 testsuite/systemtap.base/at_var.exp                |   3 +
 testsuite/systemtap.base/at_var_cu.exp             |  56 ++++
 testsuite/systemtap.base/at_var_cu.stp             |  31 +++
 testsuite/systemtap.base/at_var_cu_1.c             |  12 +
 testsuite/systemtap.base/at_var_cu_2.c             |  13 +
 testsuite/systemtap.base/at_var_cu_3.c             |  13 +
 testsuite/systemtap.base/at_var_func.exp           |  48 ++++
 testsuite/systemtap.base/at_var_func.stp           |  21 ++
 testsuite/systemtap.base/at_var_lvalue.c           |  29 ++
 testsuite/systemtap.base/at_var_lvalue.exp         |  34 +++
 testsuite/systemtap.base/at_var_lvalue.stp         |  25 ++
 testsuite/systemtap.base/at_var_mark.exp           |   5 +-
 testsuite/systemtap.base/at_var_mark_func.exp      |  26 ++
 testsuite/systemtap.base/at_var_mark_func.stp      |  24 ++
 testsuite/systemtap.base/at_var_pie.exp            |  45 ++++
 testsuite/systemtap.base/at_var_timer_profile.c    |  27 ++
 testsuite/systemtap.base/at_var_timer_profile.exp  |  46 ++++
 testsuite/systemtap.base/at_var_timer_profile.stp  |  23 ++
 testsuite/systemtap.base/at_var_tracepoint.exp     |  10 +
 testsuite/systemtap.base/at_var_tracepoint.stp     |   9 +
 .../systemtap.base/at_var_unresolved_lvalue.exp    |  42 +++
 .../systemtap.base/at_var_unresolved_lvalue.stp    |   5 +
 testsuite/systemtap.base/at_var_void_stmt.c        |  27 ++
 testsuite/systemtap.base/at_var_void_stmt.exp      |  49 ++++
 testsuite/systemtap.base/at_var_void_stmt.stp      |  13 +
 testsuite/systemtap.base/global_var_kernel.exp     |   5 +-
 testsuite/systemtap.base/global_var_kernel.stp     |   7 +
 translate.cxx                                      |   8 +
 35 files changed, 1034 insertions(+), 103 deletions(-)
 create mode 100644 testsuite/systemtap.base/at_var_cu.exp
 create mode 100644 testsuite/systemtap.base/at_var_cu.stp
 create mode 100644 testsuite/systemtap.base/at_var_cu_1.c
 create mode 100644 testsuite/systemtap.base/at_var_cu_2.c
 create mode 100644 testsuite/systemtap.base/at_var_cu_3.c
 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_pie.exp
 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
 create mode 100644 testsuite/systemtap.base/at_var_tracepoint.exp
 create mode 100644 testsuite/systemtap.base/at_var_tracepoint.stp
 create mode 100644 testsuite/systemtap.base/at_var_unresolved_lvalue.exp
 create mode 100644 testsuite/systemtap.base/at_var_unresolved_lvalue.stp
 create mode 100644 testsuite/systemtap.base/at_var_void_stmt.c
 create mode 100644 testsuite/systemtap.base/at_var_void_stmt.exp
 create mode 100644 testsuite/systemtap.base/at_var_void_stmt.stp

diff --git a/dwflpp.cxx b/dwflpp.cxx
index bb0d34a..cfaf5af 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -2270,7 +2270,7 @@ dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
     {
       stringstream alternatives;
       print_locals (scopes, alternatives);
-      if (e->cu_name == "")
+      if (pc)
         throw semantic_error (_F("unable to find local '%s', [man error::dwarf] dieoffset %s in %s, near pc %s %s %s %s (%s)",
                                  local.c_str(),
                                  lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
@@ -2291,7 +2291,7 @@ dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
                                  module_name.c_str(),
                                  (scope_die == NULL) ? "" : _("in"),
                                  (dwarf_diename(scope_die) ?: "<unknown>"),
-                                 e->cu_name.c_str(),
+                                 cu_name().c_str(),
                                  (alternatives.str() == ""
                                   ? (_("<no alternatives>"))
 				  : (_("alternatives:")
@@ -2333,7 +2333,7 @@ dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
     }
 
   // Global vars don't need (cannot use) frame base in location descriptor.
-  if (e->cu_name != "")
+  if (pc == 0)
     return NULL;
 
   /* We start out walking the "lexical scopes" as returned by
@@ -2438,7 +2438,7 @@ dwflpp::translate_location(struct obstack *pool,
   // pc is in the dw address space of the current module, which is what
   // c_translate_location expects. get_cfa_ops wants the global dwfl address.
   // cfa_ops only make sense for locals.
-  if (e->cu_name == "")
+  if (pc)
     {
       Dwarf_Addr addr = pc + module_bias;
       cfa_ops = get_cfa_ops (addr);
@@ -3013,13 +3013,13 @@ dwflpp::literal_stmt_for_local (vector<Dwarf_Die>& scopes,
 
   if (sess.verbose>2)
     {
-      if (e->cu_name == "")
+      if (pc)
         clog << _F("finding location for local '%s' near address %#" PRIx64
                    ", module bias %#" PRIx64 "\n", local.c_str(), pc,
 	           module_bias);
       else
         clog << _F("finding location for global '%s' in CU '%s'\n",
-		   local.c_str(), e->cu_name.c_str());
+		   local.c_str(), cu_name().c_str());
     }
 
 
diff --git a/elaborate.cxx b/elaborate.cxx
index c53e9f7..925bce5 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;
@@ -2960,6 +2965,7 @@ struct void_statement_reducer: public update_visitor
   void visit_functioncall (functioncall* e);
   void visit_print_format (print_format* e);
   void visit_target_symbol (target_symbol* e);
+  void visit_atvar_op (atvar_op* e);
   void visit_cast_op (cast_op* e);
   void visit_defined_op (defined_op* e);
 
@@ -3252,6 +3258,12 @@ void_statement_reducer::visit_print_format (print_format* e)
 }
 
 void
+void_statement_reducer::visit_atvar_op (atvar_op* e)
+{
+  visit_target_symbol (e);
+}
+
+void
 void_statement_reducer::visit_target_symbol (target_symbol* e)
 {
   // When target_symbol isn't needed, it's just as good to
@@ -4587,6 +4599,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..7114d4d 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -3642,8 +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;
       return tsym;
@@ -3651,20 +3649,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..82ff5fc 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);
@@ -2016,6 +2040,19 @@ varuse_collecting_visitor::visit_target_symbol (target_symbol *e)
   functioncall_traversing_visitor::visit_target_symbol (e);
 }
 
+
+void
+varuse_collecting_visitor::visit_atvar_op (atvar_op *e)
+{
+  // Similar to visit_target_symbol
+
+  if (is_active_lvalue (e))
+    embedded_seen = true;
+
+  functioncall_traversing_visitor::visit_atvar_op (e);
+}
+
+
 void
 varuse_collecting_visitor::visit_cast_op (cast_op *e)
 {
@@ -2462,6 +2499,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 +2797,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 +3060,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..927ba02 100644
--- a/staptree.h
+++ b/staptree.h
@@ -268,12 +268,10 @@ 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);
@@ -288,11 +286,18 @@ std::ostream& operator << (std::ostream& o, const target_symbol::component& c);
 struct cast_op: public target_symbol
 {
   expression *operand;
-  std::string type_name, module;
+  std::string type_name, cu_name, module;
   void print (std::ostream& o) const;
   void visit (visitor* u);
 };
 
+struct atvar_op: public target_symbol
+{
+  std::string target_name, cu_name, module;
+  virtual std::string sym_name ();
+  void print (std::ostream& o) const;
+  void visit (visitor* u);
+};
 
 struct defined_op: public expression
 {
@@ -791,6 +796,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 +844,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);
@@ -888,6 +895,7 @@ struct varuse_collecting_visitor: public functioncall_traversing_visitor
   void visit_post_crement (post_crement *e);
   void visit_foreach_loop (foreach_loop *s);
   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..b65dfef 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,186 @@ 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;
+  unsigned& tick;
+  const string cu_name_pattern;
+
+  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),
+    tick(tick), cu_name_pattern(string("*/") + e.cu_name) {}
+
+  void handle_query_module ();
+  void query_library (const char *) {}
+  void query_plt (const char *entry, size_t addr) {}
+  static int atvar_query_cu (Dwarf_Die *cudie, void *data);
+};
+
+
+int
+dwarf_atvar_query::atvar_query_cu (Dwarf_Die * cudie, void * data)
+{
+  dwarf_atvar_query * q = static_cast<dwarf_atvar_query *>(data);
+
+  if (! q->e.cu_name.empty())
+    {
+      const char *die_name = dwarf_diename(cudie);
+
+      if (strcmp(die_name, q->e.cu_name.c_str()) != 0 // Perfect match
+          && fnmatch(q->cu_name_pattern.c_str(), die_name, 0) != 0)
+        {
+          return DWARF_CB_OK;
+        }
+    }
+
+  try
+    {
+      vector<Dwarf_Die>  scopes(1, *cudie);
+
+      q->dw.focus_on_cu (cudie);
+
+      if (! q->e.components.empty() &&
+          q->e.components.back().type == target_symbol::comp_pretty_print)
+        {
+          dwarf_pretty_print dpp (q->dw, scopes, 0, q->e.sym_name(),
+                                  q->userspace_p, q->e);
+          q->result = dpp.expand();
+          return DWARF_CB_ABORT;
+        }
+
+      exp_type type = pe_long;
+      string code = q->dw.literal_stmt_for_local (scopes, 0, q->e.sym_name(),
+                                                  &q->e, q->lvalue, type);
+
+      if (code.empty())
+        return DWARF_CB_OK;
+
+      string fname = (string(q->lvalue ? "_dwarf_tvar_set"
+                                       : "_dwarf_tvar_get")
+                      + "_" + q->e.sym_name()
+                      + "_" + lex_cast(q->tick++));
+
+      q->result = synthetic_embedded_deref_call (q->sess, fname, code, type,
+                                                 q->userspace_p, q->lvalue,
+                                                 &q->e);
+    }
+  catch (const semantic_error& er)
+    {
+      // NB: we can have multiple errors, since a @var
+      // may be attempted using several different modules
+      // or CUs.
+      q->e.chain (er);
+      return DWARF_CB_OK;
+    }
+
+  if (q->result) {
+      return DWARF_CB_ABORT;
+  }
+
+  return DWARF_CB_OK;
+}
+
+
+void
+dwarf_atvar_query::handle_query_module ()
+{
+
+  dw.iterate_over_cus(atvar_query_cu, this, false);
+}
+
+
+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 +4864,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 +5620,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 +6105,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 +6121,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
@@ -9212,14 +9376,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,7 +9392,6 @@ tracepoint_var_expanding_visitor::visit_target_symbol (target_symbol* e)
 }
 
 
-
 tracepoint_derived_probe::tracepoint_derived_probe (systemtap_session& s,
                                                     dwflpp& dw, Dwarf_Die& func_die,
                                                     const string& tracepoint_name,
@@ -10037,6 +10197,8 @@ tracepoint_builder::build(systemtap_session& s,
   string tracepoint;
   assert(get_param (parameters, TOK_TRACE, tracepoint));
 
+  s.unwindsym_modules.insert ("kernel");  // for @var
+
   tracepoint_query q(*dw, tracepoint, base, location, finished_results);
   dw->iterate_over_modules(&query_module, &q);
 }
diff --git a/testsuite/systemtap.base/at_var.exp b/testsuite/systemtap.base/at_var.exp
index 806ce03..88755fd 100644
--- a/testsuite/systemtap.base/at_var.exp
+++ b/testsuite/systemtap.base/at_var.exp
@@ -32,3 +32,6 @@ if { $res != "" } {
 }
 
 stap_run3 $test $srcdir/$subdir/$test.stp -c ./${test}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
diff --git a/testsuite/systemtap.base/at_var_cu.exp b/testsuite/systemtap.base/at_var_cu.exp
new file mode 100644
index 0000000..d62ba3a
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu.exp
@@ -0,0 +1,56 @@
+set test "at_var_cu"
+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") and @var("var@cu") search
+# in all the CUs that look like a match for the pattern "cu".
+
+set ::result_string {foo: @var("counter", @1): 0
+foo: @var("counter@at_var_cu_2.c", @1): 0
+foo: @var("counter@at_var_cu_3.c", @1): 0
+foo: @var("counter@at_var_cu*.c", @1): 0
+bar: @var("counter", @1): 7
+bar: @var("counter@at_var_cu_2.c", @1): 7
+bar: @var("counter@at_var_cu_3.c", @1): 0
+bar: @var("counter@at_var_cu*.c", @1): 7
+baz: @var("counter", @1): 8
+baz: @var("counter@at_var_cu_2.c", @1): 8
+baz: @var("counter@at_var_cu_3.c", @1): 0
+baz: @var("counter@at_var_cu*.c", @1): 8
+bah: @var("counter", @1): 8
+bah: @var("counter@at_var_cu_2.c", @1): 8
+bah: @var("counter@at_var_cu_3.c", @1): 3
+bah: @var("counter@at_var_cu*.c", @1): 8
+bah': @var("counter@at_var_cu*.c"): 8}
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+
+set sources \
+    "$testpath/${test}_1.c $testpath/${test}_2.c $testpath/${test}_3.c"
+
+set res [target_compile "$sources" $exefile executable \
+    "additional_flags=-O2 additional_flags=-g"]
+
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    fail "unable to compile ${test}.c"
+}
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./$test \
+            $exefile --runtime=$runtime
+
+    } elseif {[uprobes_p]} {
+        stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile
+
+    } else {
+        untested "$test"
+    }
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
diff --git a/testsuite/systemtap.base/at_var_cu.stp b/testsuite/systemtap.base/at_var_cu.stp
new file mode 100644
index 0000000..5ec4f0c
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu.stp
@@ -0,0 +1,31 @@
+function out(ctx) {
+  printf("%s: @var(\"counter\", @1): %d\n", ctx, @var("counter", @1))
+  printf("%s: @var(\"counter@at_var_cu_2.c\", @1): %d\n", ctx,
+         @var("counter@at_var_cu_2.c", @1))
+  printf("%s: @var(\"counter@at_var_cu_3.c\", @1): %d\n", ctx,
+         @var("counter@at_var_cu_3.c", @1))
+  printf("%s: @var(\"counter@at_var_cu*.c\", @1): %d\n", ctx,
+         @var("counter@at_var_cu*.c", @1))
+}
+
+probe process.function("foo")
+{
+  out("foo")
+}
+
+probe process.function("bar")
+{
+  out("bar")
+}
+
+probe process.function("baz")
+{
+  out("baz")
+}
+
+probe process.function("bah")
+{
+  out("bah")
+  printf("bah': @var(\"counter@at_var_cu*.c\"): %d\n",
+         @var("counter@at_var_cu*.c"))
+}
diff --git a/testsuite/systemtap.base/at_var_cu_1.c b/testsuite/systemtap.base/at_var_cu_1.c
new file mode 100644
index 0000000..7fa9d50
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu_1.c
@@ -0,0 +1,12 @@
+extern void foo(void);
+extern void bar(void);
+
+int
+main(void)
+{
+  foo();
+  bar();
+  baz();
+  bah();
+  return 0;
+}
diff --git a/testsuite/systemtap.base/at_var_cu_2.c b/testsuite/systemtap.base/at_var_cu_2.c
new file mode 100644
index 0000000..5c9fb43
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu_2.c
@@ -0,0 +1,13 @@
+static int counter;
+
+void
+foo(void)
+{
+  counter = 7;
+}
+
+void
+bar(void)
+{
+  counter++;
+}
diff --git a/testsuite/systemtap.base/at_var_cu_3.c b/testsuite/systemtap.base/at_var_cu_3.c
new file mode 100644
index 0000000..1c75641
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu_3.c
@@ -0,0 +1,13 @@
+static int counter;
+
+void
+baz(void)
+{
+  counter = 3;
+}
+
+void
+bah(void)
+{
+  counter--;
+}
diff --git a/testsuite/systemtap.base/at_var_func.exp b/testsuite/systemtap.base/at_var_func.exp
new file mode 100644
index 0000000..f515bab
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_func.exp
@@ -0,0 +1,48 @@
+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")->bar): NO
+@defined(@var("foo", @3)->bar): NO
+@defined(@var("foo@blah.c", @1)->bar): NO}
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_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"
+}
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./$test \
+            $exefile "$stap_path:$exefile:$staprun_path" \
+            "$stap_path:$staprun_path" --runtime=$runtime
+
+    } elseif {[uprobes_p]} {
+        stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile \
+            "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path"
+
+    } else {
+        untested "$test"
+    }
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
diff --git a/testsuite/systemtap.base/at_var_func.stp b/testsuite/systemtap.base/at_var_func.stp
new file mode 100644
index 0000000..3da6536
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_func.stp
@@ -0,0 +1,21 @@
+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\")->bar): %s\n",
+         @defined(@var("foo", "badmodule")->bar) ? "YES" : "NO")
+  printf("@defined(@var(\"foo\", @3)->bar): %s\n",
+         @defined(@var("foo", @3)->bar) ? "YES" : "NO")
+  printf("@defined(@var(\"foo@blah.c\", @1)->bar): %s\n",
+         @defined(@var("foo@blah.c", @1)->bar) ? "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..0329b30
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_lvalue.exp
@@ -0,0 +1,34 @@
+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 }
+
+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"
+}
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./${test} -g $exefile \
+            --runtime=$runtime
+
+    } elseif {[uprobes_p]} {
+        stap_run3 $test $srcdir/$subdir/$test.stp -c ./${test} -g $exefile
+
+    } else {
+        untested "$test"
+    }
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
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.exp b/testsuite/systemtap.base/at_var_mark.exp
index 5200ded..a3b1199 100644
--- a/testsuite/systemtap.base/at_var_mark.exp
+++ b/testsuite/systemtap.base/at_var_mark.exp
@@ -5,7 +5,7 @@ set stap_path $env(SYSTEMTAP_PATH)/stap
 
 # Check @var, 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\")\}'"
+set invoke "$stap_path -e 'probe begin \{ exit() \}'"
 
 # Only run on make installcheck and uprobes present.
 if {! [installtest_p]} { untested "$test"; return }
@@ -17,7 +17,8 @@ foreach runtime [get_runtime_list] {
 
     } elseif {[uprobes_p]} {
 	stap_run ${test} wait_5_secs $output_string \
-	    ${testpath}/${test}.stp -c $invoke 
+	    ${testpath}/${test}.stp -c $invoke
+
     } else {
 	untested "$test"
     }
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..9335229
--- /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 -e 'probe begin \{ exit() \}'"
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run "${test} ($runtime)" wait_5_secs $output_string \
+            ${testpath}/${test}.stp "$stap_path" -c $invoke --runtime=$runtime
+
+    } 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_pie.exp b/testsuite/systemtap.base/at_var_pie.exp
new file mode 100644
index 0000000..1f49386
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_pie.exp
@@ -0,0 +1,45 @@
+set test "at_var_pie"
+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 with PIE.
+
+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")->bar): NO
+@defined(@var("foo", @3)->bar): NO
+@defined(@var("foo@blah.c", @1)->bar): NO}
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+
+set res [target_compile ${testpath}/at_var.c $exefile executable \
+    "additional_flags=-O2 additional_flags=-g additional_flags=-fPIE additional_flags=-pie"]
+
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    fail "unable to compile ${test}.c"
+}
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run3 "$test ($runtime)" $srcdir/$subdir/at_var_func.stp -c ./$test \
+            $exefile "$stap_path:$exefile:$staprun_path" \
+            "$stap_path:$staprun_path" --runtime=$runtime
+
+    } elseif {[uprobes_p]} {
+        stap_run3 $test $srcdir/$subdir/at_var_func.stp -c ./$test $exefile \
+            "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path"
+
+    } else {
+        untested "$test"
+    }
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
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..fbfdf50
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_timer_profile.exp
@@ -0,0 +1,46 @@
+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 }
+
+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"
+}
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+        stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./$test $exefile \
+            "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path" \
+            --runtime=$runtime
+
+    } elseif {[uprobes_p]} {
+        stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile \
+            "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path"
+
+    } else {
+        untested "$test"
+    }
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
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/at_var_tracepoint.exp b/testsuite/systemtap.base/at_var_tracepoint.exp
new file mode 100644
index 0000000..5dc6837
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_tracepoint.exp
@@ -0,0 +1,10 @@
+set test "at_var_tracepoint"
+set testpath "$srcdir/$subdir"
+
+# Check @var() is usable in the kernel tracepoint probes.
+set output_string "sys_tz = {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}"
+
+# Only run on make installcheck
+if {! [installtest_p]} { untested "$test"; return }
+
+stap_run ${test} no_load $output_string ${testpath}/${test}.stp
diff --git a/testsuite/systemtap.base/at_var_tracepoint.stp b/testsuite/systemtap.base/at_var_tracepoint.stp
new file mode 100644
index 0000000..e1b2e22
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_tracepoint.stp
@@ -0,0 +1,9 @@
+# Test global vars are usable in the kernel tracepoint probes.
+
+probe kernel.trace("sched_switch")
+{
+  log("systemtap starting probe");
+  log("systemtap ending probe");
+  printf("sys_tz = %s\n", @var("sys_tz@kernel/time.c")$$);
+  exit();
+}
diff --git a/testsuite/systemtap.base/at_var_unresolved_lvalue.exp b/testsuite/systemtap.base/at_var_unresolved_lvalue.exp
new file mode 100644
index 0000000..3074a9e
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_unresolved_lvalue.exp
@@ -0,0 +1,42 @@
+set test "at_var_unresolved_lvalue"
+set testpath "$srcdir/$subdir"
+
+# Check the void statement reducer with @var.
+set output_string ".*?enter func sub\r\n"
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_p]} { untested "$test"; return }
+
+set res [target_compile ${testpath}/at_var.c $test executable \
+    "additional_flags=-O2 additional_flags=-g"]
+
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    fail "unable to compile ${test}.c"
+}
+
+spawn stap $srcdir/$subdir/$test.stp -c ./$test $test -g
+set ok 0
+
+expect {
+    -timeout 180
+
+    -re {^semantic error: unresolved @var\(\) expression: operator '@var'} {
+        incr ok; exp_continue
+    }
+
+    -re {^[^\r\n]*\r\n} { exp_continue }
+
+    timeout { fail "$test (timeout)" }
+    eof { }
+}
+wait
+
+if {$ok == 1} {
+    pass "$test"
+} {
+    fail "$test"
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
diff --git a/testsuite/systemtap.base/at_var_unresolved_lvalue.stp b/testsuite/systemtap.base/at_var_unresolved_lvalue.stp
new file mode 100644
index 0000000..56fa268
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_unresolved_lvalue.stp
@@ -0,0 +1,5 @@
+probe process.function("sub")
+{
+  @var("no_such_variable", @1) = 3
+  exit()
+}
diff --git a/testsuite/systemtap.base/at_var_void_stmt.c b/testsuite/systemtap.base/at_var_void_stmt.c
new file mode 100644
index 0000000..e8d5f08
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_void_stmt.c
@@ -0,0 +1,27 @@
+#include <sys/time.h>
+
+struct foo
+{
+  int  bar[1];
+};
+
+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[0] -= 2; /* 40 */
+}
+
+int
+main (int argc, char **argv)
+{
+  foo.bar[0] = 41 + argc; /* 42 */
+  sub(argv[0]);
+  return 0;
+}
diff --git a/testsuite/systemtap.base/at_var_void_stmt.exp b/testsuite/systemtap.base/at_var_void_stmt.exp
new file mode 100644
index 0000000..3065ae3
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_void_stmt.exp
@@ -0,0 +1,49 @@
+set test "at_var_void_stmt"
+set testpath "$srcdir/$subdir"
+
+# Check the void statement reducer with @var.
+set output_string ".*?enter func sub\r\n"
+
+# Only run on make installcheck and uprobes present.
+if {! [installtest_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"
+}
+
+spawn stap -vvv $srcdir/$subdir/$test.stp -c ./$test $test
+set ok 0
+set logs 0
+
+expect {
+    -timeout 180
+
+    -re {^Eliding unused target symbol operator '@var'} {
+        incr logs; exp_continue
+    }
+
+    -re {^Eliding side-effect-free singleton block operator '@var'} {
+        incr logs; exp_continue
+    }
+
+    -re {^count = 3\r\n} { incr ok; exp_continue; }
+
+    -re {^[^\r\n]*\r\n} { exp_continue }
+
+    timeout { fail "$test (timeout)" }
+    eof { }
+}
+wait
+
+if {$ok == 1 && $logs == 2} {
+    pass "$test ($ok, $logs)"
+} {
+    fail "$test ($ok, $logs)"
+}
+
+# Cleanup
+if { $verbose == 0 } { catch { exec rm -f $test } }
diff --git a/testsuite/systemtap.base/at_var_void_stmt.stp b/testsuite/systemtap.base/at_var_void_stmt.stp
new file mode 100644
index 0000000..c62557d
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_void_stmt.stp
@@ -0,0 +1,13 @@
+global count = 1
+
+function foo() {
+  count += 2
+  return 0
+}
+
+probe process.function("sub")
+{
+  @var("foo", @1)->bar[foo()]
+  printf("count = %d\n", count)
+  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]