summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Ch. Eigler <fche@elastic.org>2010-02-23 20:31:00 -0500
committerFrank Ch. Eigler <fche@elastic.org>2010-02-23 20:31:00 -0500
commit30263a7389d5c2712536b74656193708bbc64d49 (patch)
treeb10b72dd3db455abf95a8ab21c540069c0143860
parentd6c273473f7bea4d696c95ca48d55ca26e25ab2f (diff)
downloadsystemtap-steved-30263a7389d5c2712536b74656193708bbc64d49.tar.gz
systemtap-steved-30263a7389d5c2712536b74656193708bbc64d49.tar.xz
systemtap-steved-30263a7389d5c2712536b74656193708bbc64d49.zip
PR11005: @defined($tvar) predicate, part 1
* staptree.h (defined_op): New class. * all files: Extend all visitors as appropriate, mostly dummy/pass-through implementation. * parse.cxx (parse_target_symbol): New function, factored out of parse_symbol(). (parse_define_op): New function. * NEWS: Mention it. * parse.h: Corresponding changes. * tapsets.cxx (var_expanding_visitor::visit_defined_op): Implement @defined() semantics. (dwarf_var_expanding_visitor::visit_target_symbol): Adjust. * tapset-utrace.c (visit_target_symbol_arg): Avoid crashes on $argZZZ. * tapsets.cxx (sdt_var_expanding_visitor): Ditto. * semok/thirtysix.stp: New test.
-rw-r--r--NEWS4
-rw-r--r--elaborate.cxx26
-rw-r--r--elaborate.h1
-rw-r--r--parse.cxx117
-rw-r--r--parse.h4
-rw-r--r--staptree.cxx49
-rw-r--r--staptree.h22
-rw-r--r--tapset-utrace.cxx10
-rw-r--r--tapsets.cxx98
-rw-r--r--tapsets.h4
-rwxr-xr-xtestsuite/semok/thirtysix.stp19
-rw-r--r--translate.cxx12
12 files changed, 302 insertions, 64 deletions
diff --git a/NEWS b/NEWS
index 8d9fe2bf..85619f8e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,9 @@
* What's new
+- A new predicate @defined is available for testing whether a
+ particular $variable/expression is resolvable at translate time:
+ probe foo { if (@defined($bar)) log ("$bar is available here") }
+
- Adjacent string literals are glued together, making this
construct valid:
probe process("/usr" @1 "/bin").function("*") { ... }
diff --git a/elaborate.cxx b/elaborate.cxx
index 88a856c2..127e0bda 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -1243,6 +1243,7 @@ semantic_pass_symbols (systemtap_session& s)
}
+
// Keep unread global variables for probe end value display.
void add_global_var_display (systemtap_session& s)
{
@@ -2609,6 +2610,7 @@ struct void_statement_reducer: public update_visitor
void visit_print_format (print_format* e);
void visit_target_symbol (target_symbol* e);
void visit_cast_op (cast_op* e);
+ void visit_defined_op (defined_op* e);
// these are a bit hairy to grok due to the intricacies of indexables and
// stats, so I'm chickening out and skipping them...
@@ -2947,6 +2949,23 @@ void_statement_reducer::visit_cast_op (cast_op* e)
}
+void
+void_statement_reducer::visit_defined_op (defined_op* e)
+{
+ // When the result of a @defined operation isn't needed, just elide
+ // it entirely. Its operand $expression must already be
+ // side-effect-free.
+
+ if (session.verbose>2)
+ clog << "Eliding unused check " << *e->tok << endl;
+
+ relaxed_p = false;
+ e = 0;
+ provide (e);
+}
+
+
+
void semantic_pass_opt5 (systemtap_session& s, bool& relaxed_p)
{
// Let's simplify statements with unused computed values.
@@ -3576,6 +3595,13 @@ typeresolution_info::visit_target_symbol (target_symbol* e)
void
+typeresolution_info::visit_defined_op (defined_op* e)
+{
+ throw semantic_error("unexpected @defined", e->tok);
+}
+
+
+void
typeresolution_info::visit_cast_op (cast_op* e)
{
// Like target_symbol, a cast_op shouldn't survive this far
diff --git a/elaborate.h b/elaborate.h
index b97d2ca8..d5ec34d9 100644
--- a/elaborate.h
+++ b/elaborate.h
@@ -103,6 +103,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_defined_op (defined_op* e);
};
diff --git a/parse.cxx b/parse.cxx
index f99a440b..b867f98d 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -2360,12 +2360,8 @@ parser::parse_value ()
else if (t->type == tok_operator && t->content == "&")
{
next ();
- t = peek ();
- if (t->type != tok_identifier ||
- (t->content != "@cast" && t->content[0] != '$'))
- throw parse_error ("expected @cast or $var");
-
- target_symbol *ts = static_cast<target_symbol*>(parse_symbol());
+ t = next ();
+ target_symbol *ts = parse_target_symbol (t);
ts->addressof = true;
return ts;
}
@@ -2426,8 +2422,7 @@ parser::parse_indexable ()
// var, indexable[index], func(parms), printf("...", ...), $var, $var->member, @stat_op(stat)
-expression*
-parser::parse_symbol ()
+expression* parser::parse_symbol ()
{
hist_op *hop = NULL;
symbol *sym = NULL;
@@ -2440,40 +2435,12 @@ parser::parse_symbol ()
// now scrutinize this identifier for the various magic forms of identifier
// (printf, @stat_op, and $var...)
- if (name == "@cast")
- {
- // type-punning time
- cast_op *cop = new cast_op;
- cop->tok = t;
- cop->base_name = name;
- expect_op("(");
- cop->operand = parse_expression ();
- expect_op(",");
- expect_unknown(tok_string, cop->type);
- // types never start with "struct<space>" or "union<space>",
- // so gobble it up.
- if (cop->type.compare(0, 7, "struct ") == 0)
- cop->type = cop->type.substr(7);
- if (cop->type.compare(0, 6, "union ") == 0)
- cop->type = cop->type.substr(6);
- if (peek_op (","))
- {
- next();
- expect_unknown(tok_string, cop->module);
- }
- expect_op(")");
- parse_target_symbol_components(cop);
-
- // if there aren't any dereferences, then the cast is pointless
- if (cop->components.empty())
- {
- expression *op = cop->operand;
- delete cop;
- return op;
- }
- return cop;
- }
+ if (name == "@cast" || (name.size()>0 && name[0] == '$'))
+ return parse_target_symbol (t);
+ if (name == "@defined")
+ return parse_defined_op (t);
+
else if (name.size() > 0 && name[0] == '@')
{
stat_op *sop = new stat_op;
@@ -2577,16 +2544,6 @@ parser::parse_symbol ()
return fmt;
}
- else if (name.size() > 0 && name[0] == '$')
- {
- // target_symbol time
- target_symbol *tsym = new target_symbol;
- tsym->tok = t;
- tsym->base_name = name;
- parse_target_symbol_components(tsym);
- return tsym;
- }
-
else if (peek_op ("(")) // function call
{
next ();
@@ -2674,6 +2631,64 @@ parser::parse_symbol ()
}
+// Parse a @cast or $var. Given head token has already been consumed.
+target_symbol* parser::parse_target_symbol (const token* t)
+{
+ if (t->type == tok_identifier && t->content == "@cast")
+ {
+ cast_op *cop = new cast_op;
+ cop->tok = t;
+ cop->base_name = t->content;
+ expect_op("(");
+ cop->operand = parse_expression ();
+ expect_op(",");
+ expect_unknown(tok_string, cop->type);
+ // types never start with "struct<space>" or "union<space>",
+ // so gobble it up.
+ if (cop->type.compare(0, 7, "struct ") == 0)
+ cop->type = cop->type.substr(7);
+ if (cop->type.compare(0, 6, "union ") == 0)
+ cop->type = cop->type.substr(6);
+ if (peek_op (","))
+ {
+ next();
+ expect_unknown(tok_string, cop->module);
+ }
+ expect_op(")");
+ parse_target_symbol_components(cop);
+ return cop;
+ }
+
+ if (t->type == tok_identifier && t->content[0]=='$')
+ {
+ // target_symbol time
+ target_symbol *tsym = new target_symbol;
+ tsym->tok = t;
+ tsym->base_name = t->content;
+ parse_target_symbol_components(tsym);
+ return tsym;
+ }
+
+ throw parse_error ("expected @cast or $var");
+}
+
+
+// Parse a @defined(). Given head token has already been consumed.
+expression* parser::parse_defined_op (const token* t)
+{
+ defined_op* dop = new defined_op;
+ dop->tok = t;
+ expect_op("(");
+ string nm;
+ // no need for parse_hist_op... etc., as @defined takes only target_symbols as its operand.
+ const token* tt = expect_ident (nm);
+ dop->operand = parse_target_symbol (tt);
+ expect_op(")");
+ return dop;
+}
+
+
+
void
parser::parse_target_symbol_components (target_symbol* e)
{
diff --git a/parse.h b/parse.h
index 0ff8664c..8f34442a 100644
--- a/parse.h
+++ b/parse.h
@@ -1,5 +1,5 @@
// -*- C++ -*-
-// Copyright (C) 2005-2007 Red Hat Inc.
+// Copyright (C) 2005-2010 Red Hat Inc.
// Copyright (C) 2007 Bull S.A.S
//
// This file is part of systemtap, and is free software. You can
@@ -188,6 +188,8 @@ private: // nonterminals
continue_statement* parse_continue_statement ();
indexable* parse_indexable ();
const token *parse_hist_op_or_bare_name (hist_op *&hop, std::string &name);
+ target_symbol *parse_target_symbol (const token* t);
+ expression* parse_defined_op (const token* t);
expression* parse_expression ();
expression* parse_assignment ();
expression* parse_ternary ();
diff --git a/staptree.cxx b/staptree.cxx
index 7a335fc3..9a69d11b 100644
--- a/staptree.cxx
+++ b/staptree.cxx
@@ -332,6 +332,12 @@ void cast_op::print (ostream& o) const
}
+void defined_op::print (ostream& o) const
+{
+ o << "@defined(" << *operand << ")";
+}
+
+
void vardecl::print (ostream& o) const
{
o << name;
@@ -1300,6 +1306,14 @@ cast_op::visit (visitor* u)
u->visit_cast_op(this);
}
+
+void
+defined_op::visit (visitor* u)
+{
+ u->visit_defined_op(this);
+}
+
+
void
arrayindex::visit (visitor* u)
{
@@ -1675,6 +1689,13 @@ traversing_visitor::visit_cast_op (cast_op* e)
}
void
+traversing_visitor::visit_defined_op (defined_op* e)
+{
+ e->operand->visit (this);
+}
+
+
+void
traversing_visitor::visit_arrayindex (arrayindex* e)
{
for (unsigned i=0; i<e->indexes.size(); i++)
@@ -1789,6 +1810,14 @@ varuse_collecting_visitor::visit_cast_op (cast_op *e)
}
void
+varuse_collecting_visitor::visit_defined_op (defined_op *e)
+{
+ // XXX
+ functioncall_traversing_visitor::visit_defined_op (e);
+}
+
+
+void
varuse_collecting_visitor::visit_print_format (print_format* e)
{
// NB: Instead of being top-level statements, "print" and "printf"
@@ -2181,6 +2210,13 @@ throwing_visitor::visit_cast_op (cast_op* e)
}
void
+throwing_visitor::visit_defined_op (defined_op* e)
+{
+ throwone (e->tok);
+}
+
+
+void
throwing_visitor::visit_arrayindex (arrayindex* e)
{
throwone (e->tok);
@@ -2423,6 +2459,13 @@ update_visitor::visit_cast_op (cast_op* e)
}
void
+update_visitor::visit_defined_op (defined_op* e)
+{
+ replace (e->operand);
+ provide (e);
+}
+
+void
update_visitor::visit_arrayindex (arrayindex* e)
{
replace (e->base);
@@ -2660,6 +2703,12 @@ deep_copy_visitor::visit_cast_op (cast_op* e)
}
void
+deep_copy_visitor::visit_defined_op (defined_op* e)
+{
+ update_visitor::visit_defined_op(new defined_op(*e));
+}
+
+void
deep_copy_visitor::visit_arrayindex (arrayindex* e)
{
update_visitor::visit_arrayindex(new arrayindex(*e));
diff --git a/staptree.h b/staptree.h
index a654a7b4..5bd69124 100644
--- a/staptree.h
+++ b/staptree.h
@@ -1,5 +1,5 @@
// -*- C++ -*-
-// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2010 Red Hat Inc.
// Copyright (C) 2006 Intel Corporation.
//
// This file is part of systemtap, and is free software. You can
@@ -254,7 +254,7 @@ struct target_symbol: public symbol
bool addressof;
std::string base_name;
std::vector<component> components;
- std::string probe_context_var;
+ std::string probe_context_var; // NB: this being set implies that target_symbol is *resolved*
semantic_error* saved_conversion_error;
target_symbol(): addressof(false), saved_conversion_error (0) {}
void print (std::ostream& o) const;
@@ -276,6 +276,14 @@ struct cast_op: public target_symbol
};
+struct defined_op: public expression
+{
+ target_symbol *operand;
+ void print (std::ostream& o) const;
+ void visit (visitor* u);
+};
+
+
struct arrayindex: public expression
{
std::vector<expression*> indexes;
@@ -718,6 +726,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_defined_op (defined_op* e) = 0;
};
@@ -759,6 +768,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_defined_op (defined_op* e);
};
@@ -801,6 +811,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_defined_op (defined_op* e);
bool side_effect_free ();
bool side_effect_free_wrt (const std::set<vardecl*>& vars);
@@ -851,6 +862,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_defined_op (defined_op* e);
};
// A visitor similar to a traversing_visitor, but with the ability to rewrite
@@ -865,7 +877,7 @@ struct update_visitor: public visitor
{
src->visit(this);
assert(!targets.empty());
- dst = static_cast<T*>(targets.top());
+ dst = static_cast<T*>(targets.top()); // XXX: danger will robinson: not typesafe!
targets.pop();
assert(clearok || dst);
}
@@ -874,7 +886,7 @@ struct update_visitor: public visitor
template <typename T> void provide (T* src)
{
- targets.push(static_cast<void*>(src));
+ targets.push(static_cast<void*>(src)); // XXX: not typesafe!
}
template <typename T> void replace (T*& src, bool clearok=false)
@@ -917,6 +929,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_defined_op (defined_op* e);
private:
std::stack<void *> targets;
@@ -972,6 +985,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_defined_op (defined_op* e);
};
#endif // STAPTREE_H
diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx
index 73f52c4d..4820ecf9 100644
--- a/tapset-utrace.cxx
+++ b/tapset-utrace.cxx
@@ -483,7 +483,15 @@ utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
else // $argN
{
string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
- int argnum = lex_cast<int>(argnum_s);
+ int argnum = 0;
+ try
+ {
+ argnum = lex_cast<int>(argnum_s);
+ }
+ catch (const runtime_error& f) // non-integral $arg suffix: e.g. $argKKKSDF
+ {
+ throw semantic_error ("invalid syscall argument number (1-6)", e->tok);
+ }
e->assert_no_components("utrace");
diff --git a/tapsets.cxx b/tapsets.cxx
index 9184e288..097cddc8 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -1911,6 +1911,58 @@ var_expanding_visitor::visit_assignment (assignment* e)
void
+var_expanding_visitor::visit_defined_op (defined_op* e)
+{
+ bool resolved = true;
+
+ defined_ops.push (e);
+ try {
+ // NB: provide<>/require<> are NOT typesafe. So even though a defined_op is
+ // defined with a target_symbol* operand, a subsidiary call may attempt to
+ // rewrite it to a general expression* instead, and require<> happily
+ // casts to/from void*, causing possible memory corruption. We use
+ // expression* here, being the general case of rewritten $variable.
+ expression *foo1 = e->operand;
+ foo1 = require (foo1);
+
+ // NB: we have some curious cases to consider here, depending on what
+ // various visit_target_symbol() implementations do for successful or
+ // erroneous resolutions.
+ //
+ // dwarf stuff: success: rewrites to a function; failure: retains target_symbol, sets saved_conversion_error
+ //
+ // sdt-kprobes sdt.h: success: string or functioncall; failure: semantic_error
+ //
+ // sdt-uprobes: success: string or no op; failure: no op; expect derived/synthetic
+ // dwarf probe to take care of it.
+ // But this is rather unhelpful. So we rig the sdt_var_expanding_visitor
+ // to pass through @defined() to the synthetic dwarf probe.
+ //
+ // utrace: success: rewrites to function; failure: semantic_error
+ //
+ // procfs: success: sets probe_context_var; failure: semantic_error
+
+ target_symbol* foo2 = dynamic_cast<target_symbol*> (foo1);
+ if (foo2 && foo2->saved_conversion_error) // failing dwarf
+ resolved = false;
+ else if (foo2 && foo2->probe_context_var != "") // successful procfs etc.
+ resolved = true;
+ else if (foo2) // unresolved but otherwise unremarked, for catching at translate time
+ resolved = false;
+ else // resolved, rewritten to some other expression type
+ resolved = true;
+ } catch (const semantic_error& e) {
+ resolved = false;
+ }
+ defined_ops.pop ();
+
+ literal_number* ln = new literal_number (resolved ? 1 : 0);
+ ln->tok = e->tok;
+ provide (ln);
+}
+
+
+void
dwarf_var_expanding_visitor::visit_target_symbol_saved_return (target_symbol* e)
{
// Get the full name of the target symbol.
@@ -2361,14 +2413,19 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e)
{
assert(e->base_name.size() > 0 && e->base_name[0] == '$');
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
bool lvalue = is_active_lvalue(e);
if (lvalue && !q.sess.guru_mode)
throw semantic_error("write to target variable not permitted", e->tok);
+ // XXX: process $context vars should be writeable
// See if we need to generate a new probe to save/access function
// parameters from a return probe. PR 1382.
if (q.has_return
+ && !defined_being_checked
&& e->base_name != "$return" // not the special return-value variable handled below
&& e->base_name != "$$return") // nor the other special variable handled below
{
@@ -2439,7 +2496,9 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e)
}
catch (const semantic_error& er)
{
- if (!q.sess.skip_badvars)
+ // NB: with --skip-badvars, @defined() still wins in that it may expand to 0
+ // for nonexistent $variables.
+ if (!q.sess.skip_badvars || defined_being_checked)
{
// We suppress this error message, and pass the unresolved
// target_symbol to the next pass. We hope that this value ends
@@ -3636,6 +3695,7 @@ struct sdt_var_expanding_visitor: public var_expanding_visitor
int arg_count;
void visit_target_symbol (target_symbol* e);
+ void visit_defined_op (defined_op* e);
};
void
@@ -3651,13 +3711,24 @@ sdt_var_expanding_visitor::visit_target_symbol (target_symbol *e)
provide(myname);
return;
}
- else if (e->base_name.find("$arg") == string::npos || ! have_reg_args)
+
+ if (e->base_name.find("$arg") == string::npos || ! have_reg_args)
{
+ // NB: uprobes-based sdt.h; $argFOO gets resolved later.
+ // XXX: We don't even know the arg_count in this case.
provide(e);
return;
}
- int argno = lex_cast<int>(e->base_name.substr(4));
+ int argno = 0;
+ try
+ {
+ argno = lex_cast<int>(e->base_name.substr(4));
+ }
+ catch (const runtime_error& f) // non-integral $arg suffix: e.g. $argKKKSDF
+ {
+ throw semantic_error ("invalid argument number", e->tok);
+ }
if (argno < 1 || argno > arg_count)
throw semantic_error ("invalid argument number", e->tok);
@@ -3717,6 +3788,18 @@ sdt_var_expanding_visitor::visit_target_symbol (target_symbol *e)
}
+// See var_expanding_visitor::visit_defined_op for a background on
+// this callback,
+void
+sdt_var_expanding_visitor::visit_defined_op (defined_op *e)
+{
+ if (! have_reg_args) // for uprobes, pass @defined through to dwarf synthetic probe's own var-expansion
+ provide (e);
+ else
+ var_expanding_visitor::visit_defined_op (e);
+}
+
+
struct sdt_query : public base_query
{
sdt_query(probe * base_probe, probe_point * base_loc,
@@ -3793,6 +3876,12 @@ sdt_query::handle_query_module()
break;
}
}
+
+ // XXX: This loses any connection to the original base_probe.
+ // Consider creating a fake derived_probe_point class, kind of
+ // like the alias_* ones in elaborate.cxx, so that probe-base
+ // relationships can be maintained. Or extend struct-probe
+ // with a base pointer. PR10831.
probe *new_base = new probe(*base_probe);
probe_point *new_location = new probe_point(*base_loc);
convert_location(new_base, new_location);
@@ -3807,7 +3896,8 @@ sdt_query::handle_query_module()
// Expand the local variables in the probe body
sdt_var_expanding_visitor svv (module_val, probe_name,
- probe_arg, have_reg_args);
+ probe_arg, // XXX: whoa, isn't this 'arg_count'?
+ have_reg_args);
svv.replace (new_base->body);
unsigned i = results.size();
diff --git a/tapsets.h b/tapsets.h
index 115f2ccf..bdaf3201 100644
--- a/tapsets.h
+++ b/tapsets.h
@@ -1,5 +1,5 @@
// -*- C++ -*-
-// Copyright (C) 2005, 2009 Red Hat Inc.
+// Copyright (C) 2005-2010 Red Hat Inc.
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
@@ -49,9 +49,11 @@ struct var_expanding_visitor: public update_visitor
{
static unsigned tick;
std::stack<functioncall**> target_symbol_setter_functioncalls;
+ std::stack<defined_op*> defined_ops;
var_expanding_visitor() {}
void visit_assignment (assignment* e);
+ void visit_defined_op (defined_op* e);
};
#endif // TAPSETS_H
diff --git a/testsuite/semok/thirtysix.stp b/testsuite/semok/thirtysix.stp
new file mode 100755
index 00000000..4f27d9bf
--- /dev/null
+++ b/testsuite/semok/thirtysix.stp
@@ -0,0 +1,19 @@
+#! stap -p4
+
+probe kernel.function("sys_open") { println(@defined($foobar)) } # invalid
+probe kernel.function("sys_open") { println(@defined($mode)) } # valid
+probe kernel.function("sys_open").return { println(@defined($foobar)) } # invalid
+probe kernel.function("sys_open").return { println(@defined($mode)) } # valid
+probe kernel.trace("*")? { println(@defined($rw)) } # valid and invalid in places
+probe kernel.mark("*")? { println(@defined($foo)) } # invalid
+probe kernel.mark("*")? { println(@defined($name)) } # valid
+probe process("stap").mark("*")? { println(@defined($arg1)) } # valid
+probe process("stap").mark("*")? { println(@defined($nosuchvar)) } # invalid
+probe procfs("file").read { println(@defined($nosuchvar)) } # invalid
+probe procfs("file").write { println(@defined($value)) } # valid
+%( CONFIG_UTRACE == "y" %?
+probe process("stap").syscall { println(@defined($arg1)) } # valid
+probe process("stap").syscall { println(@defined($argZZ)) } # invalid
+probe process("stap").syscall.return { println(@defined($nosuchvar)) } # invalid
+probe process("stap").syscall.return { println(@defined($syscall)) } # valid
+%)
diff --git a/translate.cxx b/translate.cxx
index b813ae83..c1d1383a 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -1,5 +1,5 @@
// translation pass
-// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2010 Red Hat Inc.
// Copyright (C) 2005-2008 Intel Corporation.
//
// This file is part of systemtap, and is free software. You can
@@ -156,6 +156,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_defined_op (defined_op* e);
};
// A shadow visitor, meant to generate temporary variable declarations
@@ -3545,7 +3546,14 @@ c_unparser::visit_target_symbol (target_symbol* e)
void
c_unparser::visit_cast_op (cast_op* e)
{
- throw semantic_error("cannot translate general cast expression", e->tok);
+ throw semantic_error("cannot translate general @cast expression", e->tok);
+}
+
+
+void
+c_unparser::visit_defined_op (defined_op* e)
+{
+ throw semantic_error("cannot translate general @defined expression", e->tok);
}