This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH] 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: jistone at redhat dot com, "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- Date: Fri, 14 Jun 2013 19:16:49 -0700
- Subject: [PATCH] PR11096: Add support for the "module" argument to @var
- References: <51AD24EB dot 5000708 at redhat 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 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