diff options
Diffstat (limited to 'tapsets.cxx')
-rw-r--r-- | tapsets.cxx | 98 |
1 files changed, 94 insertions, 4 deletions
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(); |