This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH v2 1/1] PR11096: Add support for the "module" argument to @var
- From: "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- To: systemtap at sourceware dot org
- Cc: Josh Stone <jistone at redhat dot com>, "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- Date: Mon, 24 Jun 2013 00:51:46 -0700
- Subject: [PATCH v2 1/1] PR11096: Add support for the "module" argument to @var
- References: <51BFB294 dot 7070006 at redhat dot com> <1372060306-25547-1-git-send-email-agentzh at gmail dot com>
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