public inbox for systemtap@sourceware.org
 help / color / mirror / Atom feed
* Accessing user-space global variables in timer.profile?
@ 2013-04-18  0:55 agentzh
  2013-04-22 15:54 ` David Smith
  0 siblings, 1 reply; 23+ messages in thread
From: agentzh @ 2013-04-18  0:55 UTC (permalink / raw)
  To: systemtap

Hello!

I'm wondering if it is possible to get @var() working the in
timer.profile context, just like @cast().

I've tried using @var("var@file") in a timer.profile handler directly,
but getting the error

    Bad $context variable being substituted with literal 0

Maybe we can specify the path to the executable file as a second
argument to @var(), just as in @cast()?

IMHO, it is very useful if we can get access to user-space global
variables of a particular running process in timer.profile when pid()
returns the expected pid.

Could anyone help me here? Thanks in advance!

Best regards,
-agentzh

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

* Re: Accessing user-space global variables in timer.profile?
  2013-04-18  0:55 Accessing user-space global variables in timer.profile? agentzh
@ 2013-04-22 15:54 ` David Smith
  2013-04-23  0:39   ` agentzh
  0 siblings, 1 reply; 23+ messages in thread
From: David Smith @ 2013-04-22 15:54 UTC (permalink / raw)
  To: agentzh; +Cc: systemtap

On 04/17/2013 07:55 PM, agentzh wrote:
> Hello!
> 
> I'm wondering if it is possible to get @var() working the in
> timer.profile context, just like @cast().
> 
> I've tried using @var("var@file") in a timer.profile handler directly,
> but getting the error
> 
>     Bad $context variable being substituted with literal 0
> 
> Maybe we can specify the path to the executable file as a second
> argument to @var(), just as in @cast()?
> 
> IMHO, it is very useful if we can get access to user-space global
> variables of a particular running process in timer.profile when pid()
> returns the expected pid.
> 
> Could anyone help me here? Thanks in advance!

I believe I see what you are asking here, but I'm afraid it just isn't
possible. When we're in a process.* probe, probes run in the context of
a process, so you can access that process' state and memory. When we're
in a timer.* probe, probes don't really run in the context of any
particular user process, they run in kernel context. At that point, any
specific user process can't really be accessed. That specific user
process might have been swapped out for instance.

So, how to do what you want and be able to sample a user-space global
variable at a timed interval? If the program has any timers itself, the
easiest solution would be to probe the program's timer function.

-- 
David Smith
dsmith@redhat.com
Red Hat
http://www.redhat.com
256.217.0141 (direct)
256.837.0057 (fax)

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

* Re: Accessing user-space global variables in timer.profile?
  2013-04-22 15:54 ` David Smith
@ 2013-04-23  0:39   ` agentzh
  2013-04-23  1:23     ` Josh Stone
  0 siblings, 1 reply; 23+ messages in thread
From: agentzh @ 2013-04-23  0:39 UTC (permalink / raw)
  To: David Smith; +Cc: systemtap

Hello!

On Mon, Apr 22, 2013 at 8:54 AM, David Smith wrote:
>
> I believe I see what you are asking here, but I'm afraid it just isn't
> possible. When we're in a process.* probe, probes run in the context of
> a process, so you can access that process' state and memory. When we're
> in a timer.* probe, probes don't really run in the context of any
> particular user process, they run in kernel context. At that point, any
> specific user process can't really be accessed. That specific user
> process might have been swapped out for instance.
>

Because I only sample a process with relatively high CPU usage, the
odds of swapping out the pages that I'm interested in should be really
small.

And I'm fine with getting a runtime warning when the memory page is
indeed swapped out, for example.

We are already able to fetch the current userspace backtraces in the
timer.profile context, for example. And it should be an excellent
addition if we can access more data like global variables in the
current user process.

> So, how to do what you want and be able to sample a user-space global
> variable at a timed interval? If the program has any timers itself, the
> easiest solution would be to probe the program's timer function.
>

Thank you for the suggestion but the values of my global variables
change *very* frequently and probing on every calls that may change
the variables is just a bit too expensive for me because I'm doing
online profiling in a production environment where reducing the
instrumentation cost means a lot.

Thanks!
-agentzh

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

* Re: Accessing user-space global variables in timer.profile?
  2013-04-23  0:39   ` agentzh
@ 2013-04-23  1:23     ` Josh Stone
  2013-04-23 19:08       ` agentzh
  2013-05-30  4:00       ` agentzh
  0 siblings, 2 replies; 23+ messages in thread
From: Josh Stone @ 2013-04-23  1:23 UTC (permalink / raw)
  To: agentzh; +Cc: David Smith, systemtap

On 04/22/2013 05:39 PM, agentzh wrote:
> Hello!
> 
> On Mon, Apr 22, 2013 at 8:54 AM, David Smith wrote:
>>
>> I believe I see what you are asking here, but I'm afraid it just isn't
>> possible. When we're in a process.* probe, probes run in the context of
>> a process, so you can access that process' state and memory. When we're
>> in a timer.* probe, probes don't really run in the context of any
>> particular user process, they run in kernel context. At that point, any
>> specific user process can't really be accessed. That specific user
>> process might have been swapped out for instance.
>>
> 
> Because I only sample a process with relatively high CPU usage, the
> odds of swapping out the pages that I'm interested in should be really
> small.
> 
> And I'm fine with getting a runtime warning when the memory page is
> indeed swapped out, for example.
> 
> We are already able to fetch the current userspace backtraces in the
> timer.profile context, for example. And it should be an excellent
> addition if we can access more data like global variables in the
> current user process.

At least with backtraces, we only have to know where modules are loaded,
which we can track at runtime.  For variables, we need to know more at
compile time, including debuginfo which covers it.

So we'll need a @var which at least hints which module holds it, as
described in http://sourceware.org/bugzilla/show_bug.cgi?id=11096#c5

That's talking about functions, but timer.profile is similarly context-free.

Once we have @var-module support internally, then we can also play games
with defaulting the module, using the target object for most process.*
probes (as @var does now), but perhaps using the -c CMD executable for
@var used in functions and context-free probes.

Hope that makes sense.  So it's something feasible, I think, but we
can't do it just yet.

Josh

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

* Re: Accessing user-space global variables in timer.profile?
  2013-04-23  1:23     ` Josh Stone
@ 2013-04-23 19:08       ` agentzh
  2013-05-30  4:00       ` agentzh
  1 sibling, 0 replies; 23+ messages in thread
From: agentzh @ 2013-04-23 19:08 UTC (permalink / raw)
  To: Josh Stone; +Cc: David Smith, systemtap

Hello!

On Mon, Apr 22, 2013 at 6:23 PM, Josh Stone wrote:
>
> So we'll need a @var which at least hints which module holds it, as
> described in http://sourceware.org/bugzilla/show_bug.cgi?id=11096#c5
>
> That's talking about functions, but timer.profile is similarly context-free.
>

Yes! This is exactly what I'm looking for :)

> Once we have @var-module support internally, then we can also play games
> with defaulting the module, using the target object for most process.*
> probes (as @var does now), but perhaps using the -c CMD executable for
> @var used in functions and context-free probes.
>

I'm fine with explicitly specifying the module name in @var() since
I'm generating the stap script on-the-fly with Perl. But this is
feature is a good plus anyway :)

> Hope that makes sense.  So it's something feasible, I think, but we
> can't do it just yet.
>

Good to know it is doable :)

Thanks!
-agentzh

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

* Re: Accessing user-space global variables in timer.profile?
  2013-04-23  1:23     ` Josh Stone
  2013-04-23 19:08       ` agentzh
@ 2013-05-30  4:00       ` agentzh
  2013-06-01  7:46         ` agentzh
  2013-06-03 22:41         ` Josh Stone
  1 sibling, 2 replies; 23+ messages in thread
From: agentzh @ 2013-05-30  4:00 UTC (permalink / raw)
  To: Josh Stone; +Cc: David Smith, systemtap

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

Hello!

On Mon, Apr 22, 2013 at 6:23 PM, Josh Stone wrote:
> So we'll need a @var which at least hints which module holds it, as
> described in http://sourceware.org/bugzilla/show_bug.cgi?id=11096#c5
>
> That's talking about functions, but timer.profile is similarly context-free.
>

Okay, I've come up with a (naive) patch to implement
@var("somevar@somefile", "somemodule") for stap user functions and
context-free probe handlers (see below). It works for me on Fedora 17,
both in a function or in timer.profile.

There's some minor things that I'm not very sure though:

1. It may not be a good idea to expand such @var in
dwarf_cast_expanding_visitor. Maybe I should add another ad-hoc
visitor like dwarf_var_early_expanding_visitor?

2. There's some code duplication between this early @var expansion and
the existing dwarf_var_expanding_visitor. Maybe we could merge them
two or just abstract out some common logic?

Because I'm very new to the systemtap code base, I'd highly appreciate
any suggestions or other comments!

Thanks!
-agentzh

From 6a8bbb53e3252e1cfe47e8c17dd6e3301adcab69 Mon Sep 17 00:00:00 2001
From: "agentzh (Yichun Zhang)" <agentzh@gmail.com>
Date: Wed, 29 May 2013 20:49:06 -0700
Subject: [PATCH] Allow use of @var("somevar@somefile", "somemodule") in stap
 functions and context-free probes.

---
 parse.cxx   |   5 ++
 staptree.h  |   3 +-
 tapsets.cxx | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 173 insertions(+), 1 deletion(-)

diff --git a/parse.cxx b/parse.cxx
index bbdc2ad..dfcc1c3 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -3661,6 +3661,11 @@ target_symbol* parser::parse_target_symbol
(const token* t)
  tsym->cu_name = tsym->target_name.substr(found_at + 1);
       else
  tsym->cu_name = "";
+      if (peek_op (","))
+        {
+          swallow ();
+          expect_unknown(tok_string, tsym->module);
+        }
       expect_op(")");
       parse_target_symbol_components(tsym);
       tsym->addressof = addressof;
diff --git a/staptree.h b/staptree.h
index 8eac23d..8913151 100644
--- a/staptree.h
+++ b/staptree.h
@@ -270,6 +270,7 @@ struct target_symbol: public symbol
   bool addressof;
   std::string target_name;
   std::string cu_name;
+  std::string module;
   std::vector<component> components;
   semantic_error* saved_conversion_error; // hand-made linked list
   target_symbol(): addressof(false), saved_conversion_error (0) {}
@@ -288,7 +289,7 @@ 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;
   void print (std::ostream& o) const;
   void visit (visitor* u);
 };
diff --git a/tapsets.cxx b/tapsets.cxx
index 6bede4c..a56b778 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -3953,6 +3953,7 @@ struct dwarf_cast_expanding_visitor: public
var_expanding_visitor
     s(s), db(db) {}
   void visit_cast_op (cast_op* e);
   void filter_special_modules(string& module);
+  void visit_target_symbol(target_symbol* e);
 };


@@ -4161,6 +4162,171 @@ void
dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
 }


+struct dwarf_var_query: public base_query
+{
+  target_symbol& e;
+  const bool userspace_p;
+  functioncall*& result;
+  vector<Dwarf_Die> *scopes;
+  unsigned& tick;
+
+  dwarf_var_query(dwflpp& dw, const string& module, target_symbol& e,
+                  const bool userspace_p, functioncall*& result,
+                  unsigned& tick):
+    base_query(dw, module), e(e),
+    userspace_p(userspace_p), 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 getscopes(target_symbol *e);
+  ~dwarf_var_query() { if (scopes) delete scopes; }
+};
+
+
+void
+dwarf_var_query::handle_query_module()
+{
+  string fname = (string("_dwarf_tvar_get")
+                  + "_" + e.sym_name()
+                  + "_" + lex_cast(tick++));
+
+  getscopes(&e);
+
+  if (scopes->size() == 0)
+    return;
+
+  this->dw.focus_on_cu(&(scopes->at(0)));
+
+  exp_type type = pe_long;
+  string code = this->dw.literal_stmt_for_local(*scopes, 0, e.sym_name(),
+                                                &e, 0, type);
+
+  result = synthetic_embedded_deref_call(this->sess, fname, code, type,
+                                         userspace_p, 0, &e);
+}
+
+
+void
+dwarf_var_query::getscopes(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(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);
+
+  //cerr << "Found CU name: " << cu_name << endl;
+
+  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_cast_expanding_visitor::visit_target_symbol(target_symbol* e)
+{
+  //cerr << "accessing target_symbol " << e->sym_name() << endl;
+
+  if (is_active_lvalue(e)
+      || e->name != "@var"
+      || e->target_name.empty()
+      || e->module.empty()
+      || (!e->components.empty()
+          && e->components.back().type == target_symbol::comp_pretty_print))
+    {
+      provide(e);
+      return;
+    }
+
+  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];
+      filter_special_modules(module);
+
+      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_var_query q (*dw, module, *e, userspace_p, result, tick);
+      dw->iterate_over_modules(&query_module, &q);
+    }
+
+  if (!result)
+    {
+      provide(e);
+      return;
+    }
+
+  result->visit (this);
+}
+
+
 void
 dwarf_derived_probe::printsig (ostream& o) const
 {
--
1.7.11.7

[-- Attachment #2: var_in_funcs_and_ctx_free_probes.patch --]
[-- Type: application/octet-stream, Size: 6360 bytes --]

From 6a8bbb53e3252e1cfe47e8c17dd6e3301adcab69 Mon Sep 17 00:00:00 2001
From: "agentzh (Yichun Zhang)" <agentzh@gmail.com>
Date: Wed, 29 May 2013 20:49:06 -0700
Subject: [PATCH] Allow use of @var("somevar@somefile", "somemodule") in stap
 functions and context-free probes.

---
 parse.cxx   |   5 ++
 staptree.h  |   3 +-
 tapsets.cxx | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 173 insertions(+), 1 deletion(-)

diff --git a/parse.cxx b/parse.cxx
index bbdc2ad..dfcc1c3 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -3661,6 +3661,11 @@ target_symbol* parser::parse_target_symbol (const token* t)
 	tsym->cu_name = tsym->target_name.substr(found_at + 1);
       else
 	tsym->cu_name = "";
+      if (peek_op (","))
+        {
+          swallow ();
+          expect_unknown(tok_string, tsym->module);
+        }
       expect_op(")");
       parse_target_symbol_components(tsym);
       tsym->addressof = addressof;
diff --git a/staptree.h b/staptree.h
index 8eac23d..8913151 100644
--- a/staptree.h
+++ b/staptree.h
@@ -270,6 +270,7 @@ struct target_symbol: public symbol
   bool addressof;
   std::string target_name;
   std::string cu_name;
+  std::string module;
   std::vector<component> components;
   semantic_error* saved_conversion_error; // hand-made linked list
   target_symbol(): addressof(false), saved_conversion_error (0) {}
@@ -288,7 +289,7 @@ 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;
   void print (std::ostream& o) const;
   void visit (visitor* u);
 };
diff --git a/tapsets.cxx b/tapsets.cxx
index 6bede4c..a56b778 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -3953,6 +3953,7 @@ struct dwarf_cast_expanding_visitor: public var_expanding_visitor
     s(s), db(db) {}
   void visit_cast_op (cast_op* e);
   void filter_special_modules(string& module);
+  void visit_target_symbol(target_symbol* e);
 };
 
 
@@ -4161,6 +4162,171 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
 }
 
 
+struct dwarf_var_query: public base_query
+{
+  target_symbol& e;
+  const bool userspace_p;
+  functioncall*& result;
+  vector<Dwarf_Die> *scopes;
+  unsigned& tick;
+
+  dwarf_var_query(dwflpp& dw, const string& module, target_symbol& e,
+                  const bool userspace_p, functioncall*& result,
+                  unsigned& tick):
+    base_query(dw, module), e(e),
+    userspace_p(userspace_p), 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 getscopes(target_symbol *e);
+  ~dwarf_var_query() { if (scopes) delete scopes; }
+};
+
+
+void
+dwarf_var_query::handle_query_module()
+{
+  string fname = (string("_dwarf_tvar_get")
+                  + "_" + e.sym_name()
+                  + "_" + lex_cast(tick++));
+
+  getscopes(&e);
+
+  if (scopes->size() == 0)
+    return;
+
+  this->dw.focus_on_cu(&(scopes->at(0)));
+
+  exp_type type = pe_long;
+  string code = this->dw.literal_stmt_for_local(*scopes, 0, e.sym_name(),
+                                                &e, 0, type);
+
+  result = synthetic_embedded_deref_call(this->sess, fname, code, type,
+                                         userspace_p, 0, &e);
+}
+
+
+void
+dwarf_var_query::getscopes(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(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);
+
+  //cerr << "Found CU name: " << cu_name << endl;
+
+  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_cast_expanding_visitor::visit_target_symbol(target_symbol* e)
+{
+  //cerr << "accessing target_symbol " << e->sym_name() << endl;
+
+  if (is_active_lvalue(e)
+      || e->name != "@var"
+      || e->target_name.empty()
+      || e->module.empty()
+      || (!e->components.empty()
+          && e->components.back().type == target_symbol::comp_pretty_print))
+    {
+      provide(e);
+      return;
+    }
+
+  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];
+      filter_special_modules(module);
+
+      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_var_query q (*dw, module, *e, userspace_p, result, tick);
+      dw->iterate_over_modules(&query_module, &q);
+    }
+
+  if (!result)
+    {
+      provide(e);
+      return;
+    }
+
+  result->visit (this);
+}
+
+
 void
 dwarf_derived_probe::printsig (ostream& o) const
 {
-- 
1.7.11.7


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

* Re: Accessing user-space global variables in timer.profile?
  2013-05-30  4:00       ` agentzh
@ 2013-06-01  7:46         ` agentzh
  2013-06-03 23:19           ` Josh Stone
  2013-06-03 22:41         ` Josh Stone
  1 sibling, 1 reply; 23+ messages in thread
From: agentzh @ 2013-06-01  7:46 UTC (permalink / raw)
  To: Josh Stone; +Cc: David Smith, systemtap

Hello!

On Wed, May 29, 2013 at 9:00 PM, agentzh wrote:
> Okay, I've come up with a (naive) patch to implement
> @var("somevar@somefile", "somemodule") for stap user functions and
> context-free probe handlers (see below). It works for me on Fedora 17,
> both in a function or in timer.profile.
>

Okay, based on this new feature, I've just written a systemtap-based
tool named pl-sample-bt that can sample and aggregate Perl-land
backtraces for a specified perl process at the Linux system ticks:

    https://github.com/agentzh/perl-systemtap-toolkit#pl-sample-bt

Its output can then be used to render "Perl-land Flame Graphs" by
Brendan Gregg's FlameGraph tool chain, for example:

    http://agentzh.org/misc/flamegraph/perl-test-nginx-socket.svg

Essentially, pl-sample-bt emulates dtrace's jstack() thing for Perl 5.
And I just ported the Perl_pp_caller function in the perl 5 core over
to the stap language. The basic approach may well apply to other high
level languages' VMs :)

Best regards,
-agentzh

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

* Re: Accessing user-space global variables in timer.profile?
  2013-05-30  4:00       ` agentzh
  2013-06-01  7:46         ` agentzh
@ 2013-06-03 22:41         ` Josh Stone
  2013-06-03 23:21           ` Josh Stone
  2013-06-03 23:52           ` Accessing user-space global variables in timer.profile? Josh Stone
  1 sibling, 2 replies; 23+ messages in thread
From: Josh Stone @ 2013-06-03 22:41 UTC (permalink / raw)
  To: agentzh; +Cc: David Smith, systemtap

On 05/29/2013 09:00 PM, agentzh wrote:
> Hello!
> 
> On Mon, Apr 22, 2013 at 6:23 PM, Josh Stone wrote:
>> So we'll need a @var which at least hints which module holds it, as
>> described in http://sourceware.org/bugzilla/show_bug.cgi?id=11096#c5
>>
>> That's talking about functions, but timer.profile is similarly context-free.
>>
> 
> Okay, I've come up with a (naive) patch to implement
> @var("somevar@somefile", "somemodule") for stap user functions and
> context-free probe handlers (see below). It works for me on Fedora 17,
> both in a function or in timer.profile.
> 
> There's some minor things that I'm not very sure though:
> 
> 1. It may not be a good idea to expand such @var in
> dwarf_cast_expanding_visitor. Maybe I should add another ad-hoc
> visitor like dwarf_var_early_expanding_visitor?

Yeah, it will be small, just as dwarf_cast_expanding_visitor is small,
but let's keep them separate.  The session::code_filters is made for
this, even though @cast is the only one there so far.

I'm not sure what's "early" about this though.  I do see we have an
unfortunate collision against existing dwarf_var_expanding_visitor --
maybe call this as atvar, e.g. dwarf_atvar_expanding_visitor and
dwarf_atvar_query.  (FWIW even I don't think "atvar" is a *great* name,
but "var" is just too general.)

> 2. There's some code duplication between this early @var expansion and
> the existing dwarf_var_expanding_visitor. Maybe we could merge them
> two or just abstract out some common logic?

I think that could be helped a little by deferring the current @var
expansion into your new visitor.  This would be much like how the
dwarf_var_expanding_visitor::visit_cast_op() only sets the module name,
so dwarf_cast_expanding_visitor has the right context later.

> Because I'm very new to the systemtap code base, I'd highly appreciate
> any suggestions or other comments!

On the note of deferring as @cast does, I also think that @var should
really be its own target_symbol subclass, rather than being a weird
special-case flavor of target_symbol as it is now.  I know this is not
your invention, but "target_name", "cu_name", and now "module" are all
specific to @var needs.  It's a bigger change to make a new atvar_op, as
many tree visitors will need to be expanded, but I think it may be
cleaner overall.

...
> +void
> +dwarf_var_query::getscopes(target_symbol *e)

I can see this is copied from dwarf_var_expanding_visitor::getcuscope.
If we let dwarf_var_expanding_visitor defer @var processing until your
visitor, then yours becomes the only copy needed.  (Or equivalently,
just rename the existing function into your visitor class

...
> +void
> +dwarf_cast_expanding_visitor::visit_target_symbol(target_symbol* e)
> +{

I see this similar to dwarf_cast_expanding_visitor::visit_cast_op.  So
first it could be dwarf_atvar_expanding_visitor::visit_atvar_op as I
said.  The duplication here is not too bad, but a few tweaks:

> +  //cerr << "accessing target_symbol " << e->sym_name() << endl;
> +
> +  if (is_active_lvalue(e)
> +      || e->name != "@var"
> +      || e->target_name.empty()
> +      || e->module.empty()
> +      || (!e->components.empty()
> +          && e->components.back().type == target_symbol::comp_pretty_print))
> +    {
> +      provide(e);
> +      return;
> +    }

I'm not sure why you're skipping lvalues - does the existing @var
prevent writes?  And why punt on pretty_print?

Also, module.empty() might as well default to "kernel", the same way
that cast_op does.

> +
> +  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];
> +      filter_special_modules(module);

The "special" modules don't apply here.  This is where @cast actually
compiles new modules from headers to get type debuginfo.  That doesn't
make sense for @var, as we'd never ever see that module loaded to get
access to any of its variables.

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

* Re: Accessing user-space global variables in timer.profile?
  2013-06-01  7:46         ` agentzh
@ 2013-06-03 23:19           ` Josh Stone
  2013-06-07 23:23             ` agentzh
  0 siblings, 1 reply; 23+ messages in thread
From: Josh Stone @ 2013-06-03 23:19 UTC (permalink / raw)
  To: agentzh; +Cc: David Smith, systemtap

On 06/01/2013 12:46 AM, agentzh wrote:
> Hello!
> 
> On Wed, May 29, 2013 at 9:00 PM, agentzh wrote:
>> Okay, I've come up with a (naive) patch to implement
>> @var("somevar@somefile", "somemodule") for stap user functions and
>> context-free probe handlers (see below). It works for me on Fedora 17,
>> both in a function or in timer.profile.
>>
> 
> Okay, based on this new feature, I've just written a systemtap-based
> tool named pl-sample-bt that can sample and aggregate Perl-land
> backtraces for a specified perl process at the Linux system ticks:
> 
>     https://github.com/agentzh/perl-systemtap-toolkit#pl-sample-bt
> 
> Its output can then be used to render "Perl-land Flame Graphs" by
> Brendan Gregg's FlameGraph tool chain, for example:
> 
>     http://agentzh.org/misc/flamegraph/perl-test-nginx-socket.svg
> 
> Essentially, pl-sample-bt emulates dtrace's jstack() thing for Perl 5.
> And I just ported the Perl_pp_caller function in the perl 5 core over
> to the stap language. The basic approach may well apply to other high
> level languages' VMs :)

Very cool!  In fact, if we can tease the script portions of this away
from perl-generation, it would be great to make an example script out of
this.  Hey, I'm sure if you're probing perl, then you're perfectly
comfortable writing perl to drive stap, but we need to be kinder in the
examples we present. :)

If I read it correctly, the dynamic pieces of this script are mostly
just to avoid repeating yourself - is that right?  I think with a
parameter for $perl_path, you can do the rest with macros.  Maybe a
second parameter for the $minor_version variations.

I certainly won't force you to change, but it would make a nice in-tree
showcase for @var-module support. :)

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

* Re: Accessing user-space global variables in timer.profile?
  2013-06-03 22:41         ` Josh Stone
@ 2013-06-03 23:21           ` Josh Stone
  2013-06-07 23:20             ` agentzh
  2013-06-15  2:18             ` [PATCH] PR11096: Add support for the "module" argument to @var Yichun Zhang (agentzh)
  2013-06-03 23:52           ` Accessing user-space global variables in timer.profile? Josh Stone
  1 sibling, 2 replies; 23+ messages in thread
From: Josh Stone @ 2013-06-03 23:21 UTC (permalink / raw)
  To: agentzh; +Cc: David Smith, systemtap

(resending this followup, because my client glitched and I don't see
that this made it to the list...)

On 06/03/2013 03:41 PM, Josh Stone wrote:
>> 2. There's some code duplication between this early @var expansion and
>> the existing dwarf_var_expanding_visitor. Maybe we could merge them
>> two or just abstract out some common logic?
> 
> I think that could be helped a little by deferring the current @var
> expansion into your new visitor.  This would be much like how the
> dwarf_var_expanding_visitor::visit_cast_op() only sets the module name,
> so dwarf_cast_expanding_visitor has the right context later.

Ugh, now it occurs to me that this suggestion only makes sense for @var
globals, where there's a cu_name and no module name.  For plain
@var("name") it could be a local variable, and this really does need to
be processed on the spot in dwarf_var_expanding_visitor.

Here's a possible outline to accomplish that with separate atvar_op:

* Make target_symbol::sym_name virtual, and make this one deal only with
the plain ->name case.

* Add the atvar_op::sym_name override which does target_name munging.

* Add something like:

  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;
  }

* Then dwarf_atvar_query/visitor can handle the globals that make it
through.


Hope I'm not over-engineering this... what do you think?

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

* Re: Accessing user-space global variables in timer.profile?
  2013-06-03 22:41         ` Josh Stone
  2013-06-03 23:21           ` Josh Stone
@ 2013-06-03 23:52           ` Josh Stone
  1 sibling, 0 replies; 23+ messages in thread
From: Josh Stone @ 2013-06-03 23:52 UTC (permalink / raw)
  To: agentzh; +Cc: David Smith, systemtap

On 06/03/2013 03:41 PM, Josh Stone wrote:
>> 2. There's some code duplication between this early @var expansion and
>> the existing dwarf_var_expanding_visitor. Maybe we could merge them
>> two or just abstract out some common logic?
> 
> I think that could be helped a little by deferring the current @var
> expansion into your new visitor.  This would be much like how the
> dwarf_var_expanding_visitor::visit_cast_op() only sets the module name,
> so dwarf_cast_expanding_visitor has the right context later.

Ugh, now it occurs to me that this suggestion only makes sense for @var
globals, where there's a cu_name and no module name.  For plain
@var("name") it could be a local variable, and this really does need to
be processed on the spot in dwarf_var_expanding_visitor.

Here's a possible outline to accomplish that with separate atvar_op:

* Make target_symbol::sym_name virtual, and make this one deal only with
the plain ->name case.

* Add the atvar_op::sym_name override which does target_name munging.

* Add something like:

  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;
  }

* Then dwarf_atvar_query/visitor can handle the globals that make it
through.


Hope I'm not over-engineering this... what do you think?

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

* Re: Accessing user-space global variables in timer.profile?
  2013-06-03 23:21           ` Josh Stone
@ 2013-06-07 23:20             ` agentzh
  2013-06-15  2:18             ` [PATCH] PR11096: Add support for the "module" argument to @var Yichun Zhang (agentzh)
  1 sibling, 0 replies; 23+ messages in thread
From: agentzh @ 2013-06-07 23:20 UTC (permalink / raw)
  To: Josh Stone; +Cc: David Smith, systemtap

Hello Josh!

On Mon, Jun 3, 2013 at 6:21 PM, Josh Stone <jistone@redhat.com> wrote:
> Ugh, now it occurs to me that this suggestion only makes sense for @var
> globals, where there's a cu_name and no module name.  For plain
> @var("name") it could be a local variable, and this really does need to
> be processed on the spot in dwarf_var_expanding_visitor.
>

Yeah, it makes sense :)

> Here's a possible outline to accomplish that with separate atvar_op:
>
[...]
>
> Hope I'm not over-engineering this... what do you think?

Thank you for your suggestions! They look good to me. I'll rewrite my
patch in the next few days accordingly :)

Thanks!
-agentzh

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

* Re: Accessing user-space global variables in timer.profile?
  2013-06-03 23:19           ` Josh Stone
@ 2013-06-07 23:23             ` agentzh
  0 siblings, 0 replies; 23+ messages in thread
From: agentzh @ 2013-06-07 23:23 UTC (permalink / raw)
  To: Josh Stone; +Cc: David Smith, systemtap

Hello Josh!

On Mon, Jun 3, 2013 at 6:19 PM, Josh Stone wrote:
> Very cool!

Glad you like it :)

>  In fact, if we can tease the script portions of this away
> from perl-generation, it would be great to make an example script out of
> this.  Hey, I'm sure if you're probing perl, then you're perfectly
> comfortable writing perl to drive stap, but we need to be kinder in the
> examples we present. :)
>

Fair enough :)

> If I read it correctly, the dynamic pieces of this script are mostly
> just to avoid repeating yourself - is that right?  I think with a
> parameter for $perl_path, you can do the rest with macros.  Maybe a
> second parameter for the $minor_version variations.
>

Yes, that's correct.

> I certainly won't force you to change, but it would make a nice in-tree
> showcase for @var-module support. :)

I'm glad to maintain a perl-free in-tree version of this tool. I can
make it into a separate patch if you'd accept it :)

Thanks!
-agentzh

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

* [PATCH] PR11096: Add support for the "module" argument to @var
  2013-06-03 23:21           ` Josh Stone
  2013-06-07 23:20             ` agentzh
@ 2013-06-15  2:18             ` Yichun Zhang (agentzh)
  2013-06-18  1:06               ` Josh Stone
  1 sibling, 1 reply; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-15  2:18 UTC (permalink / raw)
  To: systemtap; +Cc: jistone, Yichun Zhang (agentzh)

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

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

* Re: [PATCH] PR11096: Add support for the "module" argument to @var
  2013-06-15  2:18             ` [PATCH] PR11096: Add support for the "module" argument to @var Yichun Zhang (agentzh)
@ 2013-06-18  1:06               ` Josh Stone
  2013-06-24  7:52                 ` [PATCH v2 0/1] " Yichun Zhang (agentzh)
  0 siblings, 1 reply; 23+ messages in thread
From: Josh Stone @ 2013-06-18  1:06 UTC (permalink / raw)
  To: Yichun Zhang (agentzh); +Cc: systemtap

On 06/14/2013 07:16 PM, Yichun Zhang (agentzh) wrote:
> 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.

Thanks, the patch looks very good!  I have just a few comments.

Too bad about cu_name -- perhaps that's a cleanup for another day.  I do
have a few suggestions for that though.


> 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

If these are really needed, they should go in testsuite/.gitignore.  But
generally, most of our testcases clean up after themselves.  Some tests
leave the files around in verbose mode, which is fine.


> +void
> +dwarf_atvar_query::handle_query_module ()
> +{
> +  string code;
> +  exp_type type;
> +
> +  try
> +    {
> +      get_cu_scope (&e);

It seems inflexible to me that get_cu_scope() is trying to find just one
best-matching CU, which is the only one that will get searched for the
variable.

I think it would be better to try *all* CUs that look like a match.
This should be pretty easy by pushing the code generation into a deeper
loop through dw.iterate_over_cus.

Take a look at how tracepoint_query_cu() is used, for example.  That one
doesn't care about the actual cu_name, but that's easy enough to add for
@var.

Consider also that we could allow @var("foo", "module"), having no
cu_name specified by the user, so it would iterate over them all.

> +
> +      this->dw.focus_on_cu (&(scopes->at(0)));

This is a hint to me how dwflpp can stop using e->cu_name.  With this
focus, dw.cu_name() will let dwflpp tell itself the right filename.

The other dwflpp uses I see of e->cu_name are trying to differentiate
globals vs. locals.  I think this is better served by the fact that
you're passing pc==0 for the global case.

> +
> +      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);
> +    }

Basically, this whole try-catch should move within the cu iteration.


> +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);
> +}

This doesn't need to be an error anymore, as a tracepoint probe should
now be fine reading a global from a module.  So I don't think
tracepoints need any visit_atvar_op - dwarf_atvar_visitor will reach
this and do the right thing, just as for timer.profile, etc.


> +# Only run on make installcheck and uprobes present.
> +if {! [installtest_p]} { untested "$test"; return }
> +if {! [uprobes_p]} { untested "$test"; return }

First, I'm really glad to see that you've provided tests!

I think all your tests should work in --runtime=dyninst mode too.  We
don't have full module-tracking there (e.g. PR15052), but as long as the
variable is in a non-PIE executable, I think it should get absolute
addresses that will work fine.  See for instance how at_var_mark.exp
loops over the available runtimes.

Using @var on PIE and/or DSO might be another good test, to make sure
relocation-tracking works with @var-module too.


Finally, while you were pretty thorough with visitor coverage, I found a
couple that should have visit_atvar_op:

- void_statement_reducer: this is an optimizer, and visit_atvar_op can
probably just call up to visit_target_op.

- varuse_collecting_visitor: this needs to care for lvalue @var, so that
unresolved cases don't get optimized away.  I see that your
at_var_lvalue.exp works fine, because it's resolved, but consider:

> $ stap -g -e 'probe begin { @var("foo") = 1; println(pp()); exit() }'
> WARNING: Eliding side-effect-free expression : operator '@var' at <input>:1:15
>  source: probe begin { @var("foo") = 1; println(pp()); exit() }
>                        ^
> begin

That's not actually side-effect-free, and should not be elided.  This
should get a semko test to make sure it fails.


Thanks,
Josh

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

* [PATCH v2 0/1] PR11096: Add support for the "module" argument to @var
  2013-06-18  1:06               ` Josh Stone
@ 2013-06-24  7:52                 ` Yichun Zhang (agentzh)
  2013-06-24  7:53                   ` [PATCH v2 1/1] " Yichun Zhang (agentzh)
  0 siblings, 1 reply; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-24  7:52 UTC (permalink / raw)
  To: systemtap; +Cc: Josh Stone, Yichun Zhang (agentzh)

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

v2:
* Move cu_name from target_symbol to its subclasses cast_op and atvar_op.
* Revert the changes in .gitignore.
* Cleanup executable files at the end of the test files for @var when the
  verbse level is 0.
* Now @var attempts all the CUs that look like a match for the specified
  CU name patter. When no CU name is specified by the user, all the CUs
  will be attempted for the variable until a hit is found. Also add (passing)
  test file at_var_cu.exp for it.
* Add support for @var in tracepoint probe handlers and add (passing) test
  file at_var_tracepoint.exp for it.
* Allow the @var tests run for other runtimes like dyninst.
* Add new test file at_var_pie.exp for using @var to access globas in PIE.
  the tests are currently failing because even the current master does not
  really support using @var for globals in PIE nor DSO even when the module
  argument is not specified. So no regressions here.
* Define visit_atvar_op for void_statement_reducer and add (passing)
  test file at_var_void_stmt.exp for it.
* Define visit_atvar_op for varuse_collecting_visitor which fixes
  the bogus "Eliding side-effect-free expression" warning in v1 when
  the lvalue @var cannot be resolved. Add the (passing) test file
  at_var_unresolved_lvalue.exp for this case.

Thanks Josh Stone for all the suggestions.

Yichun Zhang (agentzh) (1):
  PR11096: Add support for the "module" argument to @var

 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

-- 
1.7.11.7

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

* [PATCH v2 1/1] PR11096: Add support for the "module" argument to @var
  2013-06-24  7:52                 ` [PATCH v2 0/1] " Yichun Zhang (agentzh)
@ 2013-06-24  7:53                   ` Yichun Zhang (agentzh)
  2013-06-25  1:16                     ` Josh Stone
  0 siblings, 1 reply; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-24  7:53 UTC (permalink / raw)
  To: systemtap; +Cc: Josh Stone, Yichun Zhang (agentzh)

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

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

* Re: [PATCH v2 1/1] PR11096: Add support for the "module" argument to @var
  2013-06-24  7:53                   ` [PATCH v2 1/1] " Yichun Zhang (agentzh)
@ 2013-06-25  1:16                     ` Josh Stone
  2013-06-26  5:42                       ` [PATCH v3 0/1] " Yichun Zhang (agentzh)
  0 siblings, 1 reply; 23+ messages in thread
From: Josh Stone @ 2013-06-25  1:16 UTC (permalink / raw)
  To: Yichun Zhang (agentzh); +Cc: systemtap

On 06/24/2013 12:51 AM, Yichun Zhang (agentzh) wrote:
> 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.

*separating

> 
> 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.

*subclasses

> 
> @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.

You can use setup_kfail right before running the part that is known to
fail.  Reference PR 15540 for at_var_timer_profile, 15052 for PIE/DSO on
dyninst.  The kernel-mode PIE/DSO should be fixed by my suggestion below
for unwind symbols and vma tracking.  The lvalue should be better with
my PR15673 fix.


> @@ -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);
>  };

I can't find anywhere that @cast needs cu_name.  You agreed on IRC, so
let's leave this one out.


>  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;

Slight tweak, I think actually the module should be set before the
visit_target_symbol call too.  That way if it doesn't get resolved
locally, the atvar visitor will still know what module context to use,
where it can walk over all CUs.


> +  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;
> +    }

While this is usually how we handle errors, it gets crazy if there are a
lot of CUs.  For example, try:

  stap -ve 'probe begin { println(@var("foo")) }'

I think we may have to just squash this specific "unable to find" error,
and instead create a more generic error at the module level.


> +      dwarf_atvar_query q (*dw, module, *e, userspace_p, lvalue, result, tick);
> +      dw->iterate_over_modules(&query_module, &q);
> +
> +      if (result)

If we squash the CU-level errors above, here with !result is where we
can create a module-level error.

> +        {
> +          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);
> +            }

I don't think this needs a try-catch.  The lvalue trick won't throw, and
result is only visited to reach things like array indexes we didn't
touch yet.  If any of those throw (and they shouldn't) it wouldn't make
sense to chain into e.


> @@ -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);
>  }

While this may have solved your particular testcase, I believe what it
actually needs is the @var itself to insert whatever module its using.

This should solve @var in PIE and DSO too - they just need the relevant
module added to this set.  And for userspace there's also one more
requirement to enable the vma tracker.

So remove that kernel hack, and then adding this seems to work:

@@ -4277,6 +4276,10 @@ dwarf_atvar_expanding_visitor::visit_atvar_op

       if (result)
         {
+          s.unwindsym_modules.insert(module);
+          if (userspace_p && !s.runtime_usermode_p())
+            enable_vma_tracker(s);
+


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

* [PATCH v3 0/1] PR11096: Add support for the "module" argument to @var
  2013-06-25  1:16                     ` Josh Stone
@ 2013-06-26  5:42                       ` Yichun Zhang (agentzh)
  2013-06-26  5:43                         ` [PATCH v3 1/1] " Yichun Zhang (agentzh)
  0 siblings, 1 reply; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-26  5:42 UTC (permalink / raw)
  To: systemtap; +Cc: Josh Stone, Yichun Zhang (agentzh)

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

v3:
* Fix typos in the commit message.
* Mark PR numbers in test files at_var_timer_profile.exp and at_var_pie.exp.
* Remove the useless field "cu_name" from cast_op.
* Fix @var("name") that does not match any local variables: we did not
  scan all the CUs for a match in globals in the current module. Add a
  corresponding test case to the test file at_var_cu.exp.
* Fix the issue that too many error messages (per CU) are printed when @var
  fails to resolve. Now we only print one error message for each of the
  modules. Add test file at_var_unresolved.exp for this case.
* Remove a inappropriate try-catch block in
  dwarf_atvar_expanding_visitor::visit_atvar_op.
* Fix the issue when accessing global variables in PIE and DSO via @var.

Yichun Zhang (agentzh) (1):
  PR11096: Add support for the "module" argument to @var

 dwflpp.cxx                                         |  12 +-
 elaborate.cxx                                      |  48 ++++
 elaborate.h                                        |   1 +
 parse.cxx                                          |  29 +-
 staptree.cxx                                       |  76 +++++-
 staptree.h                                         |  17 +-
 tapsets.cxx                                        | 300 ++++++++++++++++-----
 testsuite/systemtap.base/at_var.exp                |   3 +
 testsuite/systemtap.base/at_var_cu.exp             |  57 ++++
 testsuite/systemtap.base/at_var_cu.stp             |  33 +++
 testsuite/systemtap.base/at_var_cu_1.c             |  14 +
 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            |  50 ++++
 testsuite/systemtap.base/at_var_timer_profile.c    |  27 ++
 testsuite/systemtap.base/at_var_timer_profile.exp  |  51 ++++
 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 +
 testsuite/systemtap.base/at_var_unresolved.exp     |  67 +++++
 .../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 +
 36 files changed, 1119 insertions(+), 102 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.exp
 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

-- 
1.7.11.7

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

* [PATCH v3 1/1] PR11096: Add support for the "module" argument to @var
  2013-06-26  5:42                       ` [PATCH v3 0/1] " Yichun Zhang (agentzh)
@ 2013-06-26  5:43                         ` Yichun Zhang (agentzh)
  2013-06-26 22:40                           ` Josh Stone
  0 siblings, 1 reply; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-26  5:43 UTC (permalink / raw)
  To: systemtap; +Cc: Josh Stone, Yichun Zhang (agentzh)

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 separating
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 subclass atvar_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.

Accessing global variables in PIE and DSO via @var now works for the
default (kernel) runtime.

Thanks Josh Stone for reviewing this patch and providing a lot of
invaluable suggestions.

* 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                                         |  17 +-
 tapsets.cxx                                        | 300 ++++++++++++++++-----
 testsuite/systemtap.base/at_var.exp                |   3 +
 testsuite/systemtap.base/at_var_cu.exp             |  57 ++++
 testsuite/systemtap.base/at_var_cu.stp             |  33 +++
 testsuite/systemtap.base/at_var_cu_1.c             |  14 +
 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            |  50 ++++
 testsuite/systemtap.base/at_var_timer_profile.c    |  27 ++
 testsuite/systemtap.base/at_var_timer_profile.exp  |  51 ++++
 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 +
 testsuite/systemtap.base/at_var_unresolved.exp     |  67 +++++
 .../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 +
 36 files changed, 1119 insertions(+), 102 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.exp
 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 be1150f..c106b73 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -3646,8 +3646,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;
@@ -3655,20 +3653,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..2a68ce7 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);
@@ -293,6 +291,13 @@ struct cast_op: public target_symbol
   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 623b4c9..c67beaf 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,33 @@ 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())
+    {
+      // Fill in the module name so that even if there is no match among
+      // local variables, dwarf_atvar_expanding_visitor can still scan
+      // all the CUs in the current module for a global variable match.
+      e->module = q.dw.module_name;
+
+      // 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 +3886,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 +4129,187 @@ 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)
+    {
+      // Here we suppress the error because we often just have too many
+      // when scanning all the CUs.
+      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)
+        {
+          s.unwindsym_modules.insert(module);
+          if (userspace_p && !s.runtime_usermode_p())
+            enable_vma_tracker(s);
+
+          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);
+          return;
+        }
+
+      /* Unable to find the variable in the current module, so we chain
+       * an error in atvar_op */
+      semantic_error  er(_F("unable to find global '%s' in %s, %s %s",
+                            e->sym_name().c_str(), module.c_str(),
+                            e->cu_name.empty() ? "" : _("in"),
+                            e->cu_name.c_str()));
+      e->chain (er);
+    }
+
+  provide(e);
+}
+
+
 void
 dwarf_derived_probe::printsig (ostream& o) const
 {
@@ -4722,6 +4870,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 +5626,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 +6111,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 +6127,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
@@ -9226,14 +9396,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);
     }
@@ -9245,7 +9412,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,
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..6546824
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu.exp
@@ -0,0 +1,57 @@
+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
+bah': @var("main_global"): 5}
+
+# 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..bc6ead9
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu.stp
@@ -0,0 +1,33 @@
+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"))
+  printf("bah': @var(\"main_global\"): %d\n",
+         @var("main_global"))
+}
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..094d40b
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_cu_1.c
@@ -0,0 +1,14 @@
+extern void foo(void);
+extern void bar(void);
+
+static int main_global = 5;
+
+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..2cabb8f
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_pie.exp
@@ -0,0 +1,50 @@
+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 != ""} {
+        if {$runtime == "dyninst"} {
+            # dyninst lacks support for relocations as required by PIE
+            setup_kfail 15052 "*-*-*"
+        }
+
+        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..215270d
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_timer_profile.exp
@@ -0,0 +1,51 @@
+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 != ""} {
+        if {$runtime == "dyninst"} {
+            # dyninst lacks support for probe timer.profile
+            setup_kfail 15540 "*-*-*"
+        }
+
+        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.exp b/testsuite/systemtap.base/at_var_unresolved.exp
new file mode 100644
index 0000000..10db59d
--- /dev/null
+++ b/testsuite/systemtap.base/at_var_unresolved.exp
@@ -0,0 +1,67 @@
+set test "at_var_unresolved"
+
+# Ensure that there is only one error message for an unresolved @var.
+
+# Only run on make installcheck
+if {! [installtest_p]} { untested "$test"; return }
+
+# Case 1: Test @var("name"):
+
+spawn stap -e "probe begin { println(@var(\"var_really_not_exist\")) exit() }"
+set errs 0
+
+expect {
+    -timeout 180
+
+    -re {^semantic error: unable to find global 'var_really_not_exist'.*? in kernel} {
+        incr errs; exp_continue
+    }
+
+    -re {^sematnic error: } {
+        incr errs; exp_continue
+    }
+
+    -re {^[^\r\n]*\r\n} { exp_continue }
+
+    timeout { fail "$test (timeout)" }
+    eof { }
+}
+wait
+
+if {$errs == 1} {
+    pass "$test ($errs)"
+} {
+    fail "$test ($errs)"
+}
+
+
+# Case 2: Test @var("name@cuname"):
+
+set errs 0
+
+spawn stap -e "probe begin { println(@var(\"var_really_not_exist@*.c\")) exit() }"
+set ok 0
+
+expect {
+    -timeout 180
+
+    -re {^semantic error: unable to find global 'var_really_not_exist'.*? in kernel, in \*\.c} {
+        incr ok; exp_continue
+    }
+
+    -re {^sematnic error: } {
+        incr errs; exp_continue
+    }
+
+    -re {^[^\r\n]*\r\n} { exp_continue }
+
+    timeout { fail "$test (timeout)" }
+    eof { }
+}
+wait
+
+if {$ok == 1 && $errs == 0} {
+    pass "$test (cuname: $ok, $errs)"
+} {
+    fail "$test (cuname: $ok, $errs)"
+}
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..1306797
--- /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 unresolved @var used as lvalue.
+
+# 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 $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: unable to find global 'no_such_variable' in at_var_unresolved_lvalue} {
+        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

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

* Re: [PATCH v3 1/1] PR11096: Add support for the "module" argument to @var
  2013-06-26  5:43                         ` [PATCH v3 1/1] " Yichun Zhang (agentzh)
@ 2013-06-26 22:40                           ` Josh Stone
  2013-06-27 19:35                             ` Yichun Zhang (agentzh)
  0 siblings, 1 reply; 23+ messages in thread
From: Josh Stone @ 2013-06-26 22:40 UTC (permalink / raw)
  To: Yichun Zhang (agentzh); +Cc: systemtap

I have just two small comments, which you can probably just fix without
further review, then I think this is ready to push!

>  void
> +dwarf_var_expanding_visitor::visit_atvar_op (atvar_op *e)
> +{
> +  if (e->module.empty() && e->cu_name.empty())
> +    {
> +      // Fill in the module name so that even if there is no match among
> +      // local variables, dwarf_atvar_expanding_visitor can still scan
> +      // all the CUs in the current module for a global variable match.
> +      e->module = q.dw.module_name;

Add a similar e->module update in sdt_uprobe_var_expanding_visitor.
That's superfluous when an sdt module does have debuginfo, since it will
end up in this dwarf visitor eventually.  But SDT can technically get
resolved without debuginfo, and we don't want any @var from this context
to default to "kernel" in that case.  (It will still fail without
debuginfo, but it should fail with a correct message.)

> +      /* Unable to find the variable in the current module, so we chain
> +       * an error in atvar_op */
> +      semantic_error  er(_F("unable to find global '%s' in %s, %s %s",
> +                            e->sym_name().c_str(), module.c_str(),
> +                            e->cu_name.empty() ? "" : _("in"),
> +                            e->cu_name.c_str()));

This message looks awkward for the empty-cu case, e.g.

[...]unable to find global 'foo' in kernel,  : operator '@var'[...]

I suggest changing "%s, %s %s" to "%s%s%s", and "in" to ", in ".


Thanks,
Josh

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

* Re: [PATCH v3 1/1] PR11096: Add support for the "module" argument to @var
  2013-06-26 22:40                           ` Josh Stone
@ 2013-06-27 19:35                             ` Yichun Zhang (agentzh)
  2013-06-27 23:31                               ` Yichun Zhang (agentzh)
  0 siblings, 1 reply; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-27 19:35 UTC (permalink / raw)
  To: Josh Stone; +Cc: systemtap

Hello Josh!

On Wed, Jun 26, 2013 at 3:40 PM, Josh Stone wrote:
> I have just two small comments, which you can probably just fix without
> further review, then I think this is ready to push!
>

Thank you for your suggestions! I've fixed these and also added some
tests for them. Below is the diff against my local branch with my v3
patch applied. Please have a look :)

BTW, the newly added sdt_global_var.exp test file also uncovers PR15688:

    http://sourceware.org/bugzilla/show_bug.cgi?id=15688

I've marked the corresponding subtests with the PR number.

Thanks!
-agentzh

diff --git a/tapsets.cxx b/tapsets.cxx
index c67beaf..05dbd9a 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -3688,23 +3688,18 @@
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())
-    {
-      // Fill in the module name so that even if there is no match among
-      // local variables, dwarf_atvar_expanding_visitor can still scan
-      // all the CUs in the current module for a global variable match.
-      e->module = q.dw.module_name;
+  // Fill in our current module context if needed
+  if (e->module.empty())
+    e->module = q.dw.module_name;

+  if (e->module == q.dw.module_name && 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);
 }

@@ -4299,9 +4294,9 @@ dwarf_atvar_expanding_visitor::visit_atvar_op
(atvar_op* e)

       /* Unable to find the variable in the current module, so we chain
        * an error in atvar_op */
-      semantic_error  er(_F("unable to find global '%s' in %s, %s %s",
+      semantic_error  er(_F("unable to find global '%s' in %s%s%s",
                             e->sym_name().c_str(), module.c_str(),
-                            e->cu_name.empty() ? "" : _("in"),
+                            e->cu_name.empty() ? "" : _(", in "),
                             e->cu_name.c_str()));
       e->chain (er);
     }
@@ -6129,13 +6124,7 @@
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;
-    }
+  need_debug_info = true;

   // Fill in our current module context if needed
   if (e->module.empty())
diff --git a/testsuite/systemtap.base/at_var_func.exp
b/testsuite/systemtap.base/at_var_func.exp
index f515bab..cf12d6e 100644
--- a/testsuite/systemtap.base/at_var_func.exp
+++ b/testsuite/systemtap.base/at_var_func.exp
@@ -16,7 +16,8 @@ set ::result_string {@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}
+@defined(@var("foo@blah.c", @1)->bar): NO
+@var("foo")->bar: 42}

 # Only run on make installcheck and uprobes present.
 if {! [installtest_p]} { untested "$test"; return }
diff --git a/testsuite/systemtap.base/at_var_func.stp
b/testsuite/systemtap.base/at_var_func.stp
index 3da6536..5d12656 100644
--- a/testsuite/systemtap.base/at_var_func.stp
+++ b/testsuite/systemtap.base/at_var_func.stp
@@ -18,4 +18,5 @@ function f()
 probe process.function("sub")
 {
   f()
+  printf("@var(\"foo\")->bar: %d\n", @var("foo")->bar);
 }
diff --git a/testsuite/systemtap.base/at_var_mark.exp
b/testsuite/systemtap.base/at_var_mark.exp
index a3b1199..7b2074f 100644
--- a/testsuite/systemtap.base/at_var_mark.exp
+++ b/testsuite/systemtap.base/at_var_mark.exp
@@ -4,7 +4,7 @@ set testpath "$srcdir/$subdir"
 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 output_string "s = \\d+\r\n.*pass:yes:0\r\n"
 set invoke "$stap_path -e 'probe begin \{ exit() \}'"

 # Only run on make installcheck and uprobes present.
diff --git a/testsuite/systemtap.base/at_var_mark.stp
b/testsuite/systemtap.base/at_var_mark.stp
index fe47bf9..0481cb0 100644
--- a/testsuite/systemtap.base/at_var_mark.stp
+++ b/testsuite/systemtap.base/at_var_mark.stp
@@ -5,11 +5,12 @@ probe process.mark("pass*")
   if (more_addr == 0)
     {
       log("systemtap starting probe");
+      log("systemtap ending probe");
       more_addr = @var("morehelp@session.cxx");
+      printf("s = %d\n", @var("s"))
     }
   else
     {
-      log("systemtap ending probe");
       name = substr($$name, 0, 4);
       correct = @defined(@var("no_such_var_really_not")) ? "no" : "yes";
       diff = more_addr - @var("morehelp@session.cxx");
diff --git a/testsuite/systemtap.base/at_var_pie.exp
b/testsuite/systemtap.base/at_var_pie.exp
index 2cabb8f..9433571 100644
--- a/testsuite/systemtap.base/at_var_pie.exp
+++ b/testsuite/systemtap.base/at_var_pie.exp
@@ -13,7 +13,8 @@ set ::result_string {@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}
+@defined(@var("foo@blah.c", @1)->bar): NO
+@var("foo")->bar: 42}

 # Only run on make installcheck and uprobes present.
 if {! [installtest_p]} { untested "$test"; return }
diff --git a/testsuite/systemtap.base/at_var_unresolved.exp
b/testsuite/systemtap.base/at_var_unresolved.exp
index 10db59d..8247224 100644
--- a/testsuite/systemtap.base/at_var_unresolved.exp
+++ b/testsuite/systemtap.base/at_var_unresolved.exp
@@ -13,7 +13,7 @@ set errs 0
 expect {
     -timeout 180

-    -re {^semantic error: unable to find global
'var_really_not_exist'.*? in kernel} {
+    -re {^semantic error: unable to find global
'var_really_not_exist'.*? in kernel: operator '@var'} {
         incr errs; exp_continue
     }

diff --git a/testsuite/systemtap.base/sdt.c b/testsuite/systemtap.base/sdt.c
index 39f4e88..58ea705 100644
--- a/testsuite/systemtap.base/sdt.c
+++ b/testsuite/systemtap.base/sdt.c
@@ -1,8 +1,11 @@
 #include "sys/sdt.h"

+int my_global_var = 56;
+
 static void call0(void)
 {
   STAP_PROBE(test, mark_z);
+  my_global_var++;
 }

 static void call1(int a)
diff --git a/testsuite/systemtap.base/sdt_global_var.exp
b/testsuite/systemtap.base/sdt_global_var.exp
new file mode 100644
index 0000000..e29148c
--- /dev/null
+++ b/testsuite/systemtap.base/sdt_global_var.exp
@@ -0,0 +1,122 @@
+set test "sdt_global_var"
+
+# Test accessing global variables in SDT probes.
+set result_string10 "z: @(\"my_global_var\") = 56
+z: \$my_global_var = 56
+j: @(\"my_global_var\") = 57
+j: \$my_global_var = 57"
+set result_string12 "
+l: @(\"my_global_var\") = 57
+l: \$my_global_var = 57"
+
+set extra_flags {{additional_flags=-O2} {additional_flags=-fPIE
additional_flags=-pie additional_flags=-O2}}
+set extra_mssgs {-O2}
+
+set pbtype_flags {{""} {additional_flags=-DSTAP_SDT_V2}}
+set pbtype_mssgs {{uprobe} {V2_uprobe}}
+
+proc sdt_stap_run { TEST_NAME TEST_FILE FAIL args } {
+    foreach runtime [get_runtime_list] {
+ set full_test_name $TEST_NAME
+ if {$runtime != ""} {
+            if {$runtime == "dyninst" && [regexp "\-pie" $TEST_NAME]} {
+                setup_kfail 15052 "*-*-*"
+            }
+
+    lappend full_test_name "($runtime)"
+    set cmd [concat stap --runtime=$runtime $TEST_FILE $args]
+ } elseif {[uprobes_p]} {
+            if {[regexp "\-pie" $TEST_NAME]} {
+                setup_kfail 15688 "*-*-*"
+            }
+
+    set cmd [concat stap $TEST_FILE $args]
+ } else {
+    untested $full_test_name
+    continue
+ }
+ send_log "executing: $cmd\n"
+ catch {eval exec $cmd} res
+
+ set skip 0
+ set n 0
+ set expected [split $::result_string "\n"]
+ foreach line [split $res "\n"] {
+    if {![string equal $line [lindex $expected $n]]} {
+ $FAIL "$full_test_name"
+ send_log "line [expr $n + 1]: expected \"[lindex $expected $n]\"\n"
+ send_log "Got \"$line\"\n"
+ set skip 1
+ break
+    }
+    incr n
+ }
+ if {$skip != 0} {
+    continue
+ }
+ if {[expr $n == [llength $expected]]} {
+    pass "$full_test_name"
+ } else {
+    $FAIL "$full_test_name"
+    send_log "too few lines of output, got $n, expected [llength $expected]\n"
+ }
+    }
+}
+
+# Iterate pbtype_flags
+for {set p 0} {$p < [llength $pbtype_flags]} {incr p} {
+set pbtype_flag [lindex $pbtype_flags $p]
+set pbtype_mssg [lindex $pbtype_mssgs $p]
+
+# Iterate extra_flags, trying the cases with and without PIE
+for {set i 0} {$i < [llength $extra_flags]} {incr i} {
+set extra_flag [lindex $extra_flags $i]
+set extra_mssg [lindex $extra_mssgs $i]
+set testprog "sdt.c.exe.$i"
+
+set test_flags "additional_flags=-g"
+set test_flags "$test_flags [sdt_includes]"
+set test_flags "$test_flags additional_flags=-Wall"
+set test_flags "$test_flags additional_flags=-Wextra"
+set test_flags "$test_flags additional_flags=-Werror $pbtype_flag"
+
+set saveidx 0
+
+set res [target_compile $srcdir/$subdir/sdt.c $testprog executable
"$test_flags $extra_flag"]
+if { $res != "" } {
+    verbose "target_compile failed: $res" 2
+    if {[string first "unrecognized command line option" $res] == -1} {
+      fail "compiling sdt.c $extra_mssg $pbtype_mssg"
+    } else {
+      untested "compiling sdt.c $extra_mssg $pbtype_mssg"
+    }
+    untested "$test $extra_mssg $pbtype_mssg"
+    continue
+} else {
+    pass "compiling sdt.c $extra_mssg $pbtype_mssg"
+}
+
+if {[installtest_p]} {
+    if {[regexp "^(ppc64|s390x)$" $::tcl_platform(machine)] \
+    && [regexp "^(-O)" $extra_mssg]} {
+ # register name and constant is ambiguous in ppc/s390 asm syntax
+ # thus constant folding can throw the result off
+ set FAIL xfail
+    } else {
+ set FAIL fail
+    }
+    set ::result_string $result_string10
+    if { $pbtype_mssg == "uprobe" } {
+ append ::result_string $result_string12
+    }
+    sdt_stap_run "$test $extra_mssg $pbtype_mssg $extra_flag" \
+ -w $FAIL $srcdir/$subdir/$test.stp $testprog -c ./$testprog
+} else {
+    untested "$test $extra_mssg $pbtype_mssg"
+}
+catch {exec rm -f $testprog}
+
+} ; # for {set i 0} {$i < [llength $extra_flags]}
+
+} ; # for {set i 0} {$i < [llength $pbtype_flags]}
+
diff --git a/testsuite/systemtap.base/sdt_global_var.stp
b/testsuite/systemtap.base/sdt_global_var.stp
new file mode 100644
index 0000000..c259f34
--- /dev/null
+++ b/testsuite/systemtap.base/sdt_global_var.stp
@@ -0,0 +1,17 @@
+probe process(@1).mark("mark_z")
+{
+  printf("z: @(\"my_global_var\") = %d\n", @var("my_global_var"));
+  printf("z: $my_global_var = %d\n", $my_global_var);
+}
+
+probe process(@1).mark("mark_j")
+{
+  printf("j: @(\"my_global_var\") = %d\n", @var("my_global_var"));
+  printf("j: $my_global_var = %d\n", $my_global_var);
+}
+
+probe process(@1).mark("mark_l") ?
+{
+  printf("l: @(\"my_global_var\") = %d\n", @var("my_global_var"));
+  printf("l: $my_global_var = %d\n", $my_global_var);
+}

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

* Re: [PATCH v3 1/1] PR11096: Add support for the "module" argument to @var
  2013-06-27 19:35                             ` Yichun Zhang (agentzh)
@ 2013-06-27 23:31                               ` Yichun Zhang (agentzh)
  0 siblings, 0 replies; 23+ messages in thread
From: Yichun Zhang (agentzh) @ 2013-06-27 23:31 UTC (permalink / raw)
  To: Josh Stone; +Cc: systemtap

Hello!

On Thu, Jun 27, 2013 at 12:34 PM, Yichun Zhang (agentzh) wrote:
>
> Thank you for your suggestions! I've fixed these and also added some
> tests for them. Below is the diff against my local branch with my v3
> patch applied. Please have a look :)
>

This patch has already been committed as bd1fcbad91 :)

Thanks!
-agentzh

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

end of thread, other threads:[~2013-06-27 23:31 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-04-18  0:55 Accessing user-space global variables in timer.profile? agentzh
2013-04-22 15:54 ` David Smith
2013-04-23  0:39   ` agentzh
2013-04-23  1:23     ` Josh Stone
2013-04-23 19:08       ` agentzh
2013-05-30  4:00       ` agentzh
2013-06-01  7:46         ` agentzh
2013-06-03 23:19           ` Josh Stone
2013-06-07 23:23             ` agentzh
2013-06-03 22:41         ` Josh Stone
2013-06-03 23:21           ` Josh Stone
2013-06-07 23:20             ` agentzh
2013-06-15  2:18             ` [PATCH] PR11096: Add support for the "module" argument to @var Yichun Zhang (agentzh)
2013-06-18  1:06               ` Josh Stone
2013-06-24  7:52                 ` [PATCH v2 0/1] " Yichun Zhang (agentzh)
2013-06-24  7:53                   ` [PATCH v2 1/1] " Yichun Zhang (agentzh)
2013-06-25  1:16                     ` Josh Stone
2013-06-26  5:42                       ` [PATCH v3 0/1] " Yichun Zhang (agentzh)
2013-06-26  5:43                         ` [PATCH v3 1/1] " Yichun Zhang (agentzh)
2013-06-26 22:40                           ` Josh Stone
2013-06-27 19:35                             ` Yichun Zhang (agentzh)
2013-06-27 23:31                               ` Yichun Zhang (agentzh)
2013-06-03 23:52           ` Accessing user-space global variables in timer.profile? Josh Stone

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