diff options
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | elaborate.cxx | 26 | ||||
-rw-r--r-- | elaborate.h | 1 | ||||
-rw-r--r-- | parse.cxx | 117 | ||||
-rw-r--r-- | parse.h | 4 | ||||
-rw-r--r-- | staptree.cxx | 49 | ||||
-rw-r--r-- | staptree.h | 22 | ||||
-rw-r--r-- | tapset-utrace.cxx | 10 | ||||
-rw-r--r-- | tapsets.cxx | 98 | ||||
-rw-r--r-- | tapsets.h | 4 | ||||
-rwxr-xr-x | testsuite/semok/thirtysix.stp | 19 | ||||
-rw-r--r-- | translate.cxx | 12 |
12 files changed, 302 insertions, 64 deletions
@@ -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 4220fa8f..4e5c63c8 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) { @@ -2607,6 +2608,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... @@ -2945,6 +2947,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. @@ -3800,6 +3819,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); }; @@ -2356,12 +2356,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; } @@ -2422,8 +2418,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; @@ -2436,40 +2431,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; @@ -2573,16 +2540,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 (); @@ -2670,6 +2627,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) { @@ -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 a31112cb..b6ee3fdf 100644 --- a/staptree.cxx +++ b/staptree.cxx @@ -344,6 +344,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; @@ -1312,6 +1318,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) { @@ -1687,6 +1701,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++) @@ -1801,6 +1822,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" @@ -2193,6 +2222,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); @@ -2435,6 +2471,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); @@ -2672,6 +2715,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)); @@ -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; @@ -720,6 +728,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; }; @@ -761,6 +770,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); }; @@ -803,6 +813,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); @@ -853,6 +864,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 @@ -867,7 +879,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); } @@ -876,7 +888,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) @@ -919,6 +931,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; @@ -974,6 +987,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 62191c85..fcb22e03 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -1935,6 +1935,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. @@ -2385,14 +2437,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 { @@ -2463,7 +2520,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 @@ -3660,6 +3719,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 @@ -3675,13 +3735,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); @@ -3741,6 +3812,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, @@ -3817,6 +3900,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); @@ -3831,7 +3920,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(); @@ -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,11 +49,13 @@ struct var_expanding_visitor: public update_visitor { static unsigned tick; std::stack<functioncall**> target_symbol_setter_functioncalls; + std::stack<defined_op*> defined_ops; std::set<std::string> valid_ops; std::string *op; 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); } |