diff options
Diffstat (limited to 'tapsets.cxx')
-rw-r--r-- | tapsets.cxx | 1097 |
1 files changed, 678 insertions, 419 deletions
diff --git a/tapsets.cxx b/tapsets.cxx index c8bc4d35..b1ca2998 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -290,8 +290,10 @@ symbol_table void add_symbol(const char *name, bool weak, Dwarf_Addr addr, Dwarf_Addr *high_addr); enum info_status read_symbols(FILE *f, const string& path); - enum info_status read_from_elf_file(const string& path); - enum info_status read_from_text_file(const string& path); + enum info_status read_from_elf_file(const string& path, + const systemtap_session &sess); + enum info_status read_from_text_file(const string& path, + const systemtap_session &sess); enum info_status get_from_elf(); void prepare_section_rejection(Dwfl_Module *mod); bool reject_section(GElf_Word section); @@ -351,6 +353,10 @@ struct dwarf_derived_probe: public derived_probe void join_group (systemtap_session& s); void emit_probe_local_init(translator_output * o); + string args; + void saveargs(Dwarf_Die* scope_die); + void printargs(std::ostream &o) const; + // Pattern registration helpers. static void register_statement_variants(match_node * root, dwarf_builder * dw, @@ -393,6 +399,10 @@ struct uprobe_derived_probe: public derived_probe Dwarf_Addr addr, bool return_p); + string args; + void saveargs(Dwarf_Die* scope_die); + void printargs(std::ostream &o) const; + void printsig (std::ostream &o) const; void join_group (systemtap_session& s); }; @@ -811,10 +821,11 @@ dwarf_query::query_module_symtab() fi = sym_table->get_func_containing_address(addr); if (!fi) { - cerr << "Warning: address " - << hex << addr << dec - << " out of range for module " - << dw.module_name; + if (! sess.suppress_warnings) + cerr << "Warning: address " + << hex << addr << dec + << " out of range for module " + << dw.module_name; return; } if (!null_die(&fi->die)) @@ -823,10 +834,11 @@ dwarf_query::query_module_symtab() // the indicated function, but query_module_dwarf() didn't // match addr to any compilation unit, so addr must be // above that cu's address range. - cerr << "Warning: address " - << hex << addr << dec - << " maps to no known compilation unit in module " - << dw.module_name; + if (! sess.suppress_warnings) + cerr << "Warning: address " + << hex << addr << dec + << " maps to no known compilation unit in module " + << dw.module_name; return; } query_func_info(fi->addr, *fi, this); @@ -1408,6 +1420,9 @@ query_cu (Dwarf_Die * cudie, void * arg) << " does not match the beginning of a statement"; if (address_line) msg << " (try 0x" << hex << lineaddr << ")"; + else + msg << " (no line info found for '" << q->dw.cu_name + << "', in module '" << q->dw.module_name << "')"; if (! q->sess.guru_mode) throw semantic_error(msg.str()); else if (! q->sess.suppress_warnings) @@ -1699,6 +1714,8 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor dwarf_var_expanding_visitor(dwarf_query & q, Dwarf_Die *sd, Dwarf_Addr a): q(q), scope_die(sd), addr(a), add_block(NULL), add_probe(NULL), visited(false) {} + 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_cast_op (cast_op* e); }; @@ -1758,415 +1775,432 @@ var_expanding_visitor::visit_assignment (assignment* e) void -dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) -{ - assert(e->base_name.size() > 0 && e->base_name[0] == '$'); - visited = true; - - bool lvalue = is_active_lvalue(e); - if (lvalue && !q.sess.guru_mode) - throw semantic_error("write to target variable not permitted", e->tok); - - // See if we need to generate a new probe to save/access function - // parameters from a return probe. PR 1382. - if (q.has_return - && e->base_name != "$return" // not the special return-value variable handled below - && e->base_name != "$$return") // nor the other special variable handled below +dwarf_var_expanding_visitor::visit_target_symbol_saved_return (target_symbol* e) +{ + // Get the full name of the target symbol. + stringstream ts_name_stream; + e->print(ts_name_stream); + string ts_name = ts_name_stream.str(); + + // Check and make sure we haven't already seen this target + // variable in this return probe. If we have, just return our + // last replacement. + map<string, symbol *>::iterator i = return_ts_map.find(ts_name); + if (i != return_ts_map.end()) { - if (lvalue) - throw semantic_error("write to target variable not permitted in .return probes", e->tok); - - // Get the full name of the target symbol. - stringstream ts_name_stream; - e->print(ts_name_stream); - string ts_name = ts_name_stream.str(); - - // Check and make sure we haven't already seen this target - // variable in this return probe. If we have, just return our - // last replacement. - map<string, symbol *>::iterator i = return_ts_map.find(ts_name); - if (i != return_ts_map.end()) - { - provide (i->second); - return; - } - - // We've got to do several things here to handle target - // variables in return probes. + provide (i->second); + return; + } - // (1) Synthesize two global arrays. One is the cache of the - // target variable and the other contains a thread specific - // nesting level counter. The arrays will look like - // this: - // - // _dwarf_tvar_{name}_{num} - // _dwarf_tvar_{name}_{num}_ctr + // We've got to do several things here to handle target + // variables in return probes. - string aname = (string("_dwarf_tvar_") - + e->base_name.substr(1) - + "_" + lex_cast<string>(tick++)); - vardecl* vd = new vardecl; - vd->name = aname; - vd->tok = e->tok; - q.sess.globals.push_back (vd); + // (1) Synthesize two global arrays. One is the cache of the + // target variable and the other contains a thread specific + // nesting level counter. The arrays will look like + // this: + // + // _dwarf_tvar_{name}_{num} + // _dwarf_tvar_{name}_{num}_ctr + + string aname = (string("_dwarf_tvar_") + + e->base_name.substr(1) + + "_" + lex_cast<string>(tick++)); + vardecl* vd = new vardecl; + vd->name = aname; + vd->tok = e->tok; + q.sess.globals.push_back (vd); + + string ctrname = aname + "_ctr"; + vd = new vardecl; + vd->name = ctrname; + vd->tok = e->tok; + q.sess.globals.push_back (vd); + + // (2) Create a new code block we're going to insert at the + // beginning of this probe to get the cached value into a + // temporary variable. We'll replace the target variable + // reference with the temporary variable reference. The code + // will look like this: + // + // _dwarf_tvar_tid = tid() + // _dwarf_tvar_{name}_{num}_tmp + // = _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, + // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] + // delete _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, + // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]--] + // if (! _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]) + // delete _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid] + + // (2a) Synthesize the tid temporary expression, which will look + // like this: + // + // _dwarf_tvar_tid = tid() + symbol* tidsym = new symbol; + tidsym->name = string("_dwarf_tvar_tid"); + tidsym->tok = e->tok; - string ctrname = aname + "_ctr"; - vd = new vardecl; - vd->name = ctrname; - vd->tok = e->tok; - q.sess.globals.push_back (vd); - - // (2) Create a new code block we're going to insert at the - // beginning of this probe to get the cached value into a - // temporary variable. We'll replace the target variable - // reference with the temporary variable reference. The code - // will look like this: - // - // _dwarf_tvar_tid = tid() - // _dwarf_tvar_{name}_{num}_tmp - // = _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, - // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] - // delete _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, - // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]--] - // if (! _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]) - // delete _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid] - - // (2a) Synthesize the tid temporary expression, which will look - // like this: - // - // _dwarf_tvar_tid = tid() - symbol* tidsym = new symbol; - tidsym->name = string("_dwarf_tvar_tid"); - tidsym->tok = e->tok; - - if (add_block == NULL) - { - add_block = new block; - add_block->tok = e->tok; - - // Synthesize a functioncall to grab the thread id. - functioncall* fc = new functioncall; - fc->tok = e->tok; - fc->function = string("tid"); - - // Assign the tid to '_dwarf_tvar_tid'. - assignment* a = new assignment; - a->tok = e->tok; - a->op = "="; - a->left = tidsym; - a->right = fc; - - expr_statement* es = new expr_statement; - es->tok = e->tok; - es->value = a; - add_block->statements.push_back (es); - } + if (add_block == NULL) + { + add_block = new block; + add_block->tok = e->tok; - // (2b) Synthesize an array reference and assign it to a - // temporary variable (that we'll use as replacement for the - // target variable reference). It will look like this: - // - // _dwarf_tvar_{name}_{num}_tmp - // = _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, - // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] - - arrayindex* ai_tvar_base = new arrayindex; - ai_tvar_base->tok = e->tok; - - symbol* sym = new symbol; - sym->name = aname; - sym->tok = e->tok; - ai_tvar_base->base = sym; - - ai_tvar_base->indexes.push_back(tidsym); - - // We need to create a copy of the array index in its current - // state so we can have 2 variants of it (the original and one - // that post-decrements the second index). - arrayindex* ai_tvar = new arrayindex; - arrayindex* ai_tvar_postdec = new arrayindex; - *ai_tvar = *ai_tvar_base; - *ai_tvar_postdec = *ai_tvar_base; - - // Synthesize the - // "_dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]" used as the - // second index into the array. - arrayindex* ai_ctr = new arrayindex; - ai_ctr->tok = e->tok; - - sym = new symbol; - sym->name = ctrname; - sym->tok = e->tok; - ai_ctr->base = sym; - ai_ctr->indexes.push_back(tidsym); - ai_tvar->indexes.push_back(ai_ctr); - - symbol* tmpsym = new symbol; - tmpsym->name = aname + "_tmp"; - tmpsym->tok = e->tok; + // Synthesize a functioncall to grab the thread id. + functioncall* fc = new functioncall; + fc->tok = e->tok; + fc->function = string("tid"); + // Assign the tid to '_dwarf_tvar_tid'. assignment* a = new assignment; a->tok = e->tok; a->op = "="; - a->left = tmpsym; - a->right = ai_tvar; + a->left = tidsym; + a->right = fc; expr_statement* es = new expr_statement; es->tok = e->tok; es->value = a; - add_block->statements.push_back (es); + } - // (2c) Add a post-decrement to the second array index and - // delete the array value. It will look like this: - // - // delete _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, - // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]--] - - post_crement* pc = new post_crement; - pc->tok = e->tok; - pc->op = "--"; - pc->operand = ai_ctr; - ai_tvar_postdec->indexes.push_back(pc); - - delete_statement* ds = new delete_statement; - ds->tok = e->tok; - ds->value = ai_tvar_postdec; - - add_block->statements.push_back (ds); - - // (2d) Delete the counter value if it is 0. It will look like - // this: - // if (! _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]) - // delete _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid] - - ds = new delete_statement; - ds->tok = e->tok; - ds->value = ai_ctr; - - unary_expression *ue = new unary_expression; - ue->tok = e->tok; - ue->op = "!"; - ue->operand = ai_ctr; - - if_statement *ifs = new if_statement; - ifs->tok = e->tok; - ifs->condition = ue; - ifs->thenblock = ds; - ifs->elseblock = NULL; - - add_block->statements.push_back (ifs); - - // (3) We need an entry probe that saves the value for us in the - // global array we created. Create the entry probe, which will - // look like this: - // - // probe kernel.function("{function}") { - // _dwarf_tvar_tid = tid() - // _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, - // ++_dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] - // = ${param} - // } - - if (add_probe == NULL) + // (2b) Synthesize an array reference and assign it to a + // temporary variable (that we'll use as replacement for the + // target variable reference). It will look like this: + // + // _dwarf_tvar_{name}_{num}_tmp + // = _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, + // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] + + arrayindex* ai_tvar_base = new arrayindex; + ai_tvar_base->tok = e->tok; + + symbol* sym = new symbol; + sym->name = aname; + sym->tok = e->tok; + ai_tvar_base->base = sym; + + ai_tvar_base->indexes.push_back(tidsym); + + // We need to create a copy of the array index in its current + // state so we can have 2 variants of it (the original and one + // that post-decrements the second index). + arrayindex* ai_tvar = new arrayindex; + arrayindex* ai_tvar_postdec = new arrayindex; + *ai_tvar = *ai_tvar_base; + *ai_tvar_postdec = *ai_tvar_base; + + // Synthesize the + // "_dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]" used as the + // second index into the array. + arrayindex* ai_ctr = new arrayindex; + ai_ctr->tok = e->tok; + + sym = new symbol; + sym->name = ctrname; + sym->tok = e->tok; + ai_ctr->base = sym; + ai_ctr->indexes.push_back(tidsym); + ai_tvar->indexes.push_back(ai_ctr); + + symbol* tmpsym = new symbol; + tmpsym->name = aname + "_tmp"; + tmpsym->tok = e->tok; + + assignment* a = new assignment; + a->tok = e->tok; + a->op = "="; + a->left = tmpsym; + a->right = ai_tvar; + + expr_statement* es = new expr_statement; + es->tok = e->tok; + es->value = a; + + add_block->statements.push_back (es); + + // (2c) Add a post-decrement to the second array index and + // delete the array value. It will look like this: + // + // delete _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, + // _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]--] + + post_crement* pc = new post_crement; + pc->tok = e->tok; + pc->op = "--"; + pc->operand = ai_ctr; + ai_tvar_postdec->indexes.push_back(pc); + + delete_statement* ds = new delete_statement; + ds->tok = e->tok; + ds->value = ai_tvar_postdec; + + add_block->statements.push_back (ds); + + // (2d) Delete the counter value if it is 0. It will look like + // this: + // if (! _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]) + // delete _dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid] + + ds = new delete_statement; + ds->tok = e->tok; + ds->value = ai_ctr; + + unary_expression *ue = new unary_expression; + ue->tok = e->tok; + ue->op = "!"; + ue->operand = ai_ctr; + + if_statement *ifs = new if_statement; + ifs->tok = e->tok; + ifs->condition = ue; + ifs->thenblock = ds; + ifs->elseblock = NULL; + + add_block->statements.push_back (ifs); + + // (3) We need an entry probe that saves the value for us in the + // global array we created. Create the entry probe, which will + // look like this: + // + // probe kernel.function("{function}") { + // _dwarf_tvar_tid = tid() + // _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, + // ++_dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] + // = ${param} + // } + + if (add_probe == NULL) + { + add_probe = new probe; + add_probe->tok = e->tok; + + // We need the name of the current probe point, minus the + // ".return" (or anything after it, such as ".maxactive(N)"). + // Create a new probe point, copying all the components, + // stopping when we see the ".return" component. + probe_point* pp = new probe_point; + for (unsigned c = 0; c < q.base_loc->components.size(); c++) { - add_probe = new probe; - add_probe->tok = e->tok; - - // We need the name of the current probe point, minus the - // ".return" (or anything after it, such as ".maxactive(N)"). - // Create a new probe point, copying all the components, - // stopping when we see the ".return" component. - probe_point* pp = new probe_point; - for (unsigned c = 0; c < q.base_loc->components.size(); c++) - { - if (q.base_loc->components[c]->functor == "return") - break; - else - pp->components.push_back(q.base_loc->components[c]); - } - pp->tok = e->tok; - pp->optional = q.base_loc->optional; - add_probe->locations.push_back(pp); - - add_probe->body = new block; - add_probe->body->tok = e->tok; - - // Synthesize a functioncall to grab the thread id. - functioncall* fc = new functioncall; - fc->tok = e->tok; - fc->function = string("tid"); - - // Assign the tid to '_dwarf_tvar_tid'. - assignment* a = new assignment; - a->tok = e->tok; - a->op = "="; - a->left = tidsym; - a->right = fc; - - expr_statement* es = new expr_statement; - es->tok = e->tok; - es->value = a; - add_probe->body = new block(add_probe->body, es); - - vardecl* vd = new vardecl; - vd->tok = e->tok; - vd->name = tidsym->name; - vd->type = pe_long; - vd->set_arity(0); - add_probe->locals.push_back(vd); - } + if (q.base_loc->components[c]->functor == "return") + break; + else + pp->components.push_back(q.base_loc->components[c]); + } + pp->tok = e->tok; + pp->optional = q.base_loc->optional; + add_probe->locations.push_back(pp); - // Save the value, like this: - // _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, - // ++_dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] - // = ${param} - arrayindex* ai_tvar_preinc = new arrayindex; - *ai_tvar_preinc = *ai_tvar_base; + add_probe->body = new block; + add_probe->body->tok = e->tok; - pre_crement* preinc = new pre_crement; - preinc->tok = e->tok; - preinc->op = "++"; - preinc->operand = ai_ctr; - ai_tvar_preinc->indexes.push_back(preinc); + // Synthesize a functioncall to grab the thread id. + functioncall* fc = new functioncall; + fc->tok = e->tok; + fc->function = string("tid"); - a = new assignment; + // Assign the tid to '_dwarf_tvar_tid'. + assignment* a = new assignment; a->tok = e->tok; a->op = "="; - a->left = ai_tvar_preinc; - a->right = e; + a->left = tidsym; + a->right = fc; - es = new expr_statement; + expr_statement* es = new expr_statement; es->tok = e->tok; es->value = a; - add_probe->body = new block(add_probe->body, es); - // (4) Provide the '_dwarf_tvar_{name}_{num}_tmp' variable to - // our parent so it can be used as a substitute for the target - // symbol. - provide (tmpsym); - - // (5) Remember this replacement since we might be able to reuse - // it later if the same return probe references this target - // symbol again. - return_ts_map[ts_name] = tmpsym; - return; + vardecl* vd = new vardecl; + vd->tok = e->tok; + vd->name = tidsym->name; + vd->type = pe_long; + vd->set_arity(0); + add_probe->locals.push_back(vd); } - if (e->base_name == "$$vars" - || e->base_name == "$$parms" - || e->base_name == "$$locals" - || (q.has_return && (e->base_name == "$$return"))) - { - Dwarf_Die *scopes; - if (dwarf_getscopes_die (scope_die, &scopes) == 0) - return; - - if (e->addressof) - throw semantic_error("cannot take address of context variable", e->tok); - - target_symbol *tsym = new target_symbol; - print_format* pf = new print_format; - - // Convert $$parms to sprintf of a list of parms and active local vars - // which we recursively evaluate - - // NB: we synthesize a new token here rather than reusing - // e->tok, because print_format::print likes to use - // its tok->content. - token* pf_tok = new token; - pf_tok->location = e->tok->location; - pf_tok->type = tok_identifier; - pf_tok->content = "sprint"; + // Save the value, like this: + // _dwarf_tvar_{name}_{num}[_dwarf_tvar_tid, + // ++_dwarf_tvar_{name}_{num}_ctr[_dwarf_tvar_tid]] + // = ${param} + arrayindex* ai_tvar_preinc = new arrayindex; + *ai_tvar_preinc = *ai_tvar_base; + + pre_crement* preinc = new pre_crement; + preinc->tok = e->tok; + preinc->op = "++"; + preinc->operand = ai_ctr; + ai_tvar_preinc->indexes.push_back(preinc); + + a = new assignment; + a->tok = e->tok; + a->op = "="; + a->left = ai_tvar_preinc; + a->right = e; + + es = new expr_statement; + es->tok = e->tok; + es->value = a; + + add_probe->body = new block(add_probe->body, es); + + // (4) Provide the '_dwarf_tvar_{name}_{num}_tmp' variable to + // our parent so it can be used as a substitute for the target + // symbol. + provide (tmpsym); + + // (5) Remember this replacement since we might be able to reuse + // it later if the same return probe references this target + // symbol again. + return_ts_map[ts_name] = tmpsym; +} - pf->tok = pf_tok; - pf->print_to_stream = false; - pf->print_with_format = true; - pf->print_with_delim = false; - pf->print_with_newline = false; - pf->print_char = false; - if (q.has_return && (e->base_name == "$$return")) +void +dwarf_var_expanding_visitor::visit_target_symbol_context (target_symbol* e) +{ + Dwarf_Die *scopes; + if (dwarf_getscopes_die (scope_die, &scopes) == 0) + return; + auto_free free_scopes(scopes); + + target_symbol *tsym = new target_symbol; + print_format* pf = new print_format; + + // Convert $$parms to sprintf of a list of parms and active local vars + // which we recursively evaluate + + // NB: we synthesize a new token here rather than reusing + // e->tok, because print_format::print likes to use + // its tok->content. + token* pf_tok = new token; + pf_tok->location = e->tok->location; + pf_tok->type = tok_identifier; + pf_tok->content = "sprint"; + + pf->tok = pf_tok; + pf->print_to_stream = false; + pf->print_with_format = true; + pf->print_with_delim = false; + pf->print_with_newline = false; + pf->print_char = false; + + if (q.has_return && (e->base_name == "$$return")) + { + tsym->tok = e->tok; + tsym->base_name = "$return"; + + // Ignore any variable that isn't accessible. + tsym->saved_conversion_error = 0; + expression *texp = tsym; + replace (texp); // NB: throws nothing ... + if (tsym->saved_conversion_error) // ... but this is how we know it happened. { - tsym->tok = e->tok; - tsym->base_name = "$return"; - - // Ignore any variable that isn't accessible. - tsym->saved_conversion_error = 0; - expression *texp = tsym; - texp = require (texp); // NB: throws nothing ... - if (tsym->saved_conversion_error) // ... but this is how we know it happened. - { - } - else - { - pf->raw_components += "return"; - pf->raw_components += "=%#x "; - pf->args.push_back(texp); - } } else { - // non-.return probe: support $$parms, $$vars, $$locals - Dwarf_Die result; - if (dwarf_child (&scopes[0], &result) == 0) - do + pf->raw_components += "return"; + pf->raw_components += "=%#x "; + pf->args.push_back(texp); + } + } + else + { + // non-.return probe: support $$parms, $$vars, $$locals + Dwarf_Die result; + if (dwarf_child (&scopes[0], &result) == 0) + do + { + switch (dwarf_tag (&result)) { - switch (dwarf_tag (&result)) - { - case DW_TAG_variable: - if (e->base_name == "$$parms") - continue; - break; - case DW_TAG_formal_parameter: - if (e->base_name == "$$locals") - continue; - break; - - default: - continue; - } + case DW_TAG_variable: + if (e->base_name == "$$parms") + continue; + break; + case DW_TAG_formal_parameter: + if (e->base_name == "$$locals") + continue; + break; + + default: + continue; + } - const char *diename = dwarf_diename (&result); - if (! diename) continue; + const char *diename = dwarf_diename (&result); + if (! diename) continue; - tsym->tok = e->tok; - tsym->base_name = "$"; - tsym->base_name += diename; + tsym->tok = e->tok; + tsym->base_name = "$"; + tsym->base_name += diename; - // Ignore any variable that isn't accessible. - tsym->saved_conversion_error = 0; - expression *texp = tsym; - texp = require (texp); // NB: throws nothing ... - if (tsym->saved_conversion_error) // ... but this is how we know it happened. - { - if (q.sess.verbose>2) - { - for (semantic_error *c = tsym->saved_conversion_error; - c != 0; - c = c->chain) { - clog << "variable location problem: " << c->what() << endl; - } - } - - pf->raw_components += diename; - pf->raw_components += "=? "; - } - else + // Ignore any variable that isn't accessible. + tsym->saved_conversion_error = 0; + expression *texp = tsym; + replace (texp); // NB: throws nothing ... + if (tsym->saved_conversion_error) // ... but this is how we know it happened. + { + if (q.sess.verbose>2) { - pf->raw_components += diename; - pf->raw_components += "=%#x "; - pf->args.push_back(texp); + for (semantic_error *c = tsym->saved_conversion_error; + c != 0; + c = c->chain) { + clog << "variable location problem: " << c->what() << endl; + } } + + pf->raw_components += diename; + pf->raw_components += "=? "; } - while (dwarf_siblingof (&result, &result) == 0); - } + else + { + pf->raw_components += diename; + pf->raw_components += "=%#x "; + pf->args.push_back(texp); + } + } + while (dwarf_siblingof (&result, &result) == 0); + } - pf->components = print_format::string_to_components(pf->raw_components); - provide (pf); + pf->components = print_format::string_to_components(pf->raw_components); + provide (pf); +} + +void +dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) +{ + assert(e->base_name.size() > 0 && e->base_name[0] == '$'); + visited = true; + + bool lvalue = is_active_lvalue(e); + if (lvalue && !q.sess.guru_mode) + throw semantic_error("write to target variable not permitted", e->tok); + + // See if we need to generate a new probe to save/access function + // parameters from a return probe. PR 1382. + if (q.has_return + && e->base_name != "$return" // not the special return-value variable handled below + && e->base_name != "$$return") // nor the other special variable handled below + { + if (lvalue) + throw semantic_error("write to target variable not permitted in .return probes", e->tok); + + visit_target_symbol_saved_return(e); + return; + } + + if (e->base_name == "$$vars" + || e->base_name == "$$parms" + || e->base_name == "$$locals" + || (q.has_return && (e->base_name == "$$return"))) + { + if (lvalue) + throw semantic_error("cannot write to context variable", e->tok); + + if (e->addressof) + throw semantic_error("cannot take address of context variable", e->tok); + + visit_target_symbol_context(e); return; } @@ -2213,7 +2247,6 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) // quietly. provide (e); semantic_error* saveme = new semantic_error (er); // copy it - saveme->tok1 = e->tok; // XXX: token not passed to q.dw code generation routines // NB: we can have multiple errors, since a $target variable // may be expanded in several different contexts: // function ("*") { $var } @@ -2238,6 +2271,18 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) fdecl->name = fname; fdecl->body = ec; + + // Any non-literal indexes need to be passed in too. + for (unsigned i = 0; i < e->components.size(); ++i) + if (e->components[i].type == target_symbol::comp_expression_array_index) + { + vardecl *v = new vardecl; + v->type = pe_long; + v->name = "index" + lex_cast<string>(i); + v->tok = e->tok; + fdecl->formal_args.push_back(v); + } + if (lvalue) { // Modify the fdecl so it carries a single pe_long formal @@ -2262,6 +2307,11 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) n->function = fname; n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session + // Any non-literal indexes need to be passed in too. + for (unsigned i = 0; i < e->components.size(); ++i) + if (e->components[i].type == target_symbol::comp_expression_array_index) + n->args.push_back(require(e->components[i].expr_index)); + if (lvalue) { // Provide the functioncall to our parent, so that it can be @@ -2494,6 +2544,17 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) v1->tok = e->tok; fdecl->formal_args.push_back(v1); + // Any non-literal indexes need to be passed in too. + for (unsigned i = 0; i < e->components.size(); ++i) + if (e->components[i].type == target_symbol::comp_expression_array_index) + { + vardecl *v = new vardecl; + v->type = pe_long; + v->name = "index" + lex_cast<string>(i); + v->tok = e->tok; + fdecl->formal_args.push_back(v); + } + if (lvalue) { // Modify the fdecl so it carries a second pe_long formal @@ -2522,6 +2583,11 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session n->args.push_back(e->operand); + // Any non-literal indexes need to be passed in too. + for (unsigned i = 0; i < e->components.size(); ++i) + if (e->components[i].type == target_symbol::comp_expression_array_index) + n->args.push_back(require(e->components[i].expr_index)); + if (lvalue) { // Provide the functioncall to our parent, so that it can be @@ -2604,7 +2670,7 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, if (!null_die(scope_die)) { dwarf_var_expanding_visitor v (q, scope_die, dwfl_addr); - this->body = v.require (this->body); + v.replace (this->body); this->access_vars = v.visited; // If during target-variable-expanding the probe, we added a new block @@ -2622,6 +2688,10 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, } // else - null scope_die - $target variables will produce an error during translate phase + // Save the local variables for listing mode + if (q.sess.listing_mode_vars) + saveargs(scope_die); + // Reset the sole element of the "locations" vector as a // "reverse-engineered" form of the incoming (q.base_loc) probe // point. This allows a user to see what function / file / line @@ -2686,6 +2756,66 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname, } +static bool dwarf_type_name(Dwarf_Die& type_die, string& c_type); + +void +dwarf_derived_probe::saveargs(Dwarf_Die* scope_die) +{ + Dwarf_Die *scopes; + if (!null_die(scope_die) && dwarf_getscopes_die (scope_die, &scopes) == 0) + return; + auto_free free_scopes(scopes); + + stringstream argstream; + string type_name; + Dwarf_Attribute type_attr; + Dwarf_Die type_die; + + if (has_return && + dwarf_attr_integrate (scope_die, DW_AT_type, &type_attr) && + dwarf_formref_die (&type_attr, &type_die) && + dwarf_type_name(type_die, type_name)) + argstream << " $return:" << type_name; + + Dwarf_Die arg; + if (dwarf_child (&scopes[0], &arg) == 0) + do + { + switch (dwarf_tag (&arg)) + { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + break; + + default: + continue; + } + + const char *arg_name = dwarf_diename (&arg); + if (!arg_name) + continue; + + type_name.clear(); + if (!dwarf_attr_integrate (&arg, DW_AT_type, &type_attr) || + !dwarf_formref_die (&type_attr, &type_die) || + !dwarf_type_name(type_die, type_name)) + continue; + + argstream << " $" << arg_name << ":" << type_name; + } + while (dwarf_siblingof (&arg, &arg) == 0); + + args = argstream.str(); +} + + +void +dwarf_derived_probe::printargs(std::ostream &o) const +{ + o << args; +} + + void dwarf_derived_probe::register_statement_variants(match_node * root, dwarf_builder * dw, @@ -2896,7 +3026,18 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->line() << "];"; common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp"); s.op->newline() << "c->regs = regs;"; + + // Make it look like the IP is set as it wouldn't have been replaced + // by a breakpoint instruction when calling real probe handler. Reset + // IP regs on return, so we don't confuse kprobes. PR10458 + s.op->newline() << "{"; + s.op->indent(1); + s.op->newline() << "unsigned long kprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "REG_IP(regs) = (unsigned long) inst->addr;"; s.op->newline() << "(*sdp->ph) (c);"; + s.op->newline() << "REG_IP(regs) = kprobes_ip;"; + s.op->newline(-1) << "}"; + common_probe_entryfn_epilogue (s.op); s.op->newline() << "return 0;"; s.op->newline(-1) << "}"; @@ -2919,7 +3060,18 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s) common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp"); s.op->newline() << "c->regs = regs;"; s.op->newline() << "c->pi = inst;"; // for assisting runtime's backtrace logic + + // Make it look like the IP is set as it wouldn't have been replaced + // by a breakpoint instruction when calling real probe handler. Reset + // IP regs on return, so we don't confuse kprobes. PR10458 + s.op->newline() << "{"; + s.op->indent(1); + s.op->newline() << "unsigned long kprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "REG_IP(regs) = (unsigned long) inst->rp->kp.addr;"; s.op->newline() << "(*sdp->ph) (c);"; + s.op->newline() << "REG_IP(regs) = kprobes_ip;"; + s.op->newline(-1) << "}"; + common_probe_entryfn_epilogue (s.op); s.op->newline() << "return 0;"; s.op->newline(-1) << "}"; @@ -3166,7 +3318,7 @@ sdt_var_expanding_visitor::visit_target_symbol (target_symbol *e) cast->type = probe_name + "_arg" + lex_cast<string>(argno); cast->module = process_name; - provide(cast); + cast->visit(this); } @@ -3247,7 +3399,7 @@ sdt_query::handle_query_module() sdt_var_expanding_visitor svv (module_val, probe_name, probe_arg, have_reg_args, probe_type == utrace_type); - new_base->body = svv.require (new_base->body); + svv.replace (new_base->body); unsigned i = results.size(); @@ -3693,7 +3845,8 @@ symbol_table::read_symbols(FILE *f, const string& path) // that gives us raw addresses -- which we need for modules -- whereas // nm provides the address relative to the beginning of the section. enum info_status -symbol_table::read_from_elf_file(const string &path) +symbol_table::read_from_elf_file(const string &path, + const systemtap_session &sess) { FILE *f; string cmd = string("/usr/bin/nm -n --defined-only ") + path; @@ -3708,7 +3861,7 @@ symbol_table::read_from_elf_file(const string &path) enum info_status status = read_symbols(f, path); if (pclose(f) != 0) { - if (status == info_present) + if (status == info_present && ! sess.suppress_warnings) cerr << "Warning: nm cannot read symbol table from " << path; return info_absent; } @@ -3716,13 +3869,15 @@ symbol_table::read_from_elf_file(const string &path) } enum info_status -symbol_table::read_from_text_file(const string& path) +symbol_table::read_from_text_file(const string& path, + const systemtap_session &sess) { FILE *f = fopen(path.c_str(), "r"); if (!f) { - cerr << "Warning: cannot read symbol table from " - << path << " -- " << strerror (errno); + if (! sess.suppress_warnings) + cerr << "Warning: cannot read symbol table from " + << path << " -- " << strerror (errno); return info_absent; } enum info_status status = read_symbols(f, path); @@ -3870,12 +4025,13 @@ module_info::get_symtab(dwarf_query *q) sym_table = new symbol_table(this); if (!elf_path.empty()) { - if (name == TOK_KERNEL && !sess.kernel_symtab_path.empty()) + if (name == TOK_KERNEL && !sess.kernel_symtab_path.empty() + && ! sess.suppress_warnings) cerr << "Warning: reading symbol table from " << elf_path << " -- ignoring " << sess.kernel_symtab_path - << endl ;; + << endl; symtab_status = sym_table->get_from_elf(); } else @@ -3891,7 +4047,7 @@ module_info::get_symtab(dwarf_query *q) else { symtab_status = - sym_table->read_from_text_file(sess.kernel_symtab_path); + sym_table->read_from_text_file(sess.kernel_symtab_path, sess); if (symtab_status == info_present) { sess.sym_kprobes_text_start = @@ -4000,7 +4156,7 @@ uprobe_derived_probe::uprobe_derived_probe (const string& function, if (!null_die(scope_die)) { dwarf_var_expanding_visitor v (q, scope_die, dwfl_addr); // XXX: user-space deref's! - this->body = v.require (this->body); + v.replace (this->body); // If during target-variable-expanding the probe, we added a new block // of code, add it to the start of the probe. @@ -4018,6 +4174,10 @@ uprobe_derived_probe::uprobe_derived_probe (const string& function, } // else - null scope_die - $target variables will produce an error during translate phase + // Save the local variables for listing mode + if (q.sess.listing_mode_vars) + saveargs(scope_die); + // Reset the sole element of the "locations" vector as a // "reverse-engineered" form of the incoming (q.base_loc) probe // point. This allows a user to see what function / file / line @@ -4092,6 +4252,67 @@ uprobe_derived_probe::uprobe_derived_probe (probe *base, void +uprobe_derived_probe::saveargs(Dwarf_Die* scope_die) +{ + // same as dwarf_derived_probe::saveargs + + Dwarf_Die *scopes; + if (!null_die(scope_die) && dwarf_getscopes_die (scope_die, &scopes) == 0) + return; + auto_free free_scopes(scopes); + + stringstream argstream; + string type_name; + Dwarf_Attribute type_attr; + Dwarf_Die type_die; + + if (return_p && + dwarf_attr_integrate (scope_die, DW_AT_type, &type_attr) && + dwarf_formref_die (&type_attr, &type_die) && + dwarf_type_name(type_die, type_name)) + argstream << " $return:" << type_name; + + Dwarf_Die arg; + if (dwarf_child (&scopes[0], &arg) == 0) + do + { + switch (dwarf_tag (&arg)) + { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + break; + + default: + continue; + } + + const char *arg_name = dwarf_diename (&arg); + if (!arg_name) + continue; + + type_name.clear(); + if (!dwarf_attr_integrate (&arg, DW_AT_type, &type_attr) || + !dwarf_formref_die (&type_attr, &type_die) || + !dwarf_type_name(type_die, type_name)) + continue; + + argstream << " $" << arg_name << ":" << type_name; + } + while (dwarf_siblingof (&arg, &arg) == 0); + + args = argstream.str(); +} + + +void +uprobe_derived_probe::printargs(std::ostream &o) const +{ + // same as dwarf_derived_probe::printargs + o << args; +} + + +void uprobe_derived_probe::printsig (ostream& o) const { // Same as dwarf_derived_probe. @@ -4222,7 +4443,18 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->newline() << "if (sup->spec_index < 0 ||" << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen s.op->newline() << "c->regs = regs;"; + + // Make it look like the IP is set as it would in the actual user + // task when calling real probe handler. Reset IP regs on return, so + // we don't confuse uprobes. PR10458 + s.op->newline() << "{"; + s.op->indent(1); + s.op->newline() << "unsigned long uprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "REG_IP(regs) = inst->vaddr;"; s.op->newline() << "(*sups->ph) (c);"; + s.op->newline() << "REG_IP(regs) = uprobes_ip;"; + s.op->newline(-1) << "}"; + common_probe_entryfn_epilogue (s.op); s.op->newline(-1) << "}"; @@ -4234,7 +4466,18 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s) << "sup->spec_index >= " << probes.size() << ") return;"; // XXX: should not happen // XXX: kretprobes saves "c->pi = inst;" too s.op->newline() << "c->regs = regs;"; + + // Make it look like the IP is set as it would in the actual user + // task when calling real probe handler. Reset IP regs on return, so + // we don't confuse uprobes. PR10458 + s.op->newline() << "{"; + s.op->indent(1); + s.op->newline() << "unsigned long uprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "REG_IP(regs) = inst->rp->u.vaddr;"; s.op->newline() << "(*sups->ph) (c);"; + s.op->newline() << "REG_IP(regs) = uprobes_ip;"; + s.op->newline(-1) << "}"; + common_probe_entryfn_epilogue (s.op); s.op->newline(-1) << "}"; @@ -4723,7 +4966,18 @@ kprobe_derived_probe_group::emit_module_decls (systemtap_session& s) s.op->line() << "];"; common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp"); s.op->newline() << "c->regs = regs;"; + + // Make it look like the IP is set as it wouldn't have been replaced + // by a breakpoint instruction when calling real probe handler. Reset + // IP regs on return, so we don't confuse kprobes. PR10458 + s.op->newline() << "{"; + s.op->indent(1); + s.op->newline() << "unsigned long kprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "REG_IP(regs) = (unsigned long) inst->addr;"; s.op->newline() << "(*sdp->ph) (c);"; + s.op->newline() << "REG_IP(regs) = kprobes_ip;"; + s.op->newline(-1) << "}"; + common_probe_entryfn_epilogue (s.op); s.op->newline() << "return 0;"; s.op->newline(-1) << "}"; @@ -4746,7 +5000,18 @@ kprobe_derived_probe_group::emit_module_decls (systemtap_session& s) common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp"); s.op->newline() << "c->regs = regs;"; s.op->newline() << "c->pi = inst;"; // for assisting runtime's backtrace logic + + // Make it look like the IP is set as it wouldn't have been replaced + // by a breakpoint instruction when calling real probe handler. Reset + // IP regs on return, so we don't confuse kprobes. PR10458 + s.op->newline() << "{"; + s.op->indent(1); + s.op->newline() << "unsigned long kprobes_ip = REG_IP(c->regs);"; + s.op->newline() << "REG_IP(regs) = (unsigned long) inst->rp->kp.addr;"; s.op->newline() << "(*sdp->ph) (c);"; + s.op->newline() << "REG_IP(regs) = kprobes_ip;"; + s.op->newline(-1) << "}"; + common_probe_entryfn_epilogue (s.op); s.op->newline() << "return 0;"; s.op->newline(-1) << "}"; @@ -5037,19 +5302,8 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) } // make sure we're not dereferencing base types - if (!e->components.empty() && !arg->isptr) - switch (e->components[0].first) - { - case target_symbol::comp_literal_array_index: - throw semantic_error("tracepoint variable '" + e->base_name - + "' may not be used as array", e->tok); - case target_symbol::comp_struct_member: - throw semantic_error("tracepoint variable '" + e->base_name - + "' may not be used as a structure", e->tok); - default: - throw semantic_error("invalid use of tracepoint variable '" - + e->base_name + "'", e->tok); - } + if (!arg->isptr) + e->assert_no_components("tracepoint"); // we can only write to dereferenced fields, and only if guru mode is on bool lvalue = is_active_lvalue(e); @@ -5098,7 +5352,6 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) // up not being referenced after all, so it can be optimized out // quietly. semantic_error* saveme = new semantic_error (er); // copy it - saveme->tok1 = e->tok; // XXX: token not passed to dw code generation routines // NB: we can have multiple errors, since a target variable // may be expanded in several different contexts: // trace ("*") { $foo->bar } @@ -5115,6 +5368,17 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) v1->tok = e->tok; fdecl->formal_args.push_back(v1); + // Any non-literal indexes need to be passed in too. + for (unsigned i = 0; i < e->components.size(); ++i) + if (e->components[i].type == target_symbol::comp_expression_array_index) + { + vardecl *v = new vardecl; + v->type = pe_long; + v->name = "index" + lex_cast<string>(i); + v->tok = e->tok; + fdecl->formal_args.push_back(v); + } + if (lvalue) { // Modify the fdecl so it carries a pe_long formal @@ -5142,10 +5406,16 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e) n->function = fname; n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session - // make the original a bare target symbol for the tracepoint value, - // which will be passed into the dwarf dereferencing code - e->components.clear(); - n->args.push_back(require(e)); + // make a copy of the original as a bare target symbol for the tracepoint + // value, which will be passed into the dwarf dereferencing code + target_symbol* e2 = deep_copy_visitor::deep_copy(e); + e2->components.clear(); + n->args.push_back(require(e2)); + + // Any non-literal indexes need to be passed in too. + for (unsigned i = 0; i < e->components.size(); ++i) + if (e->components[i].type == target_symbol::comp_expression_array_index) + n->args.push_back(require(e->components[i].expr_index)); if (lvalue) { @@ -5170,18 +5440,7 @@ tracepoint_var_expanding_visitor::visit_target_symbol_context (target_symbol* e) if (is_active_lvalue (e)) throw semantic_error("write to tracepoint '" + e->base_name + "' not permitted", e->tok); - if (!e->components.empty()) - switch (e->components[0].first) - { - case target_symbol::comp_literal_array_index: - throw semantic_error("tracepoint '" + e->base_name + "' may not be used as array", - e->tok); - case target_symbol::comp_struct_member: - throw semantic_error("tracepoint '" + e->base_name + "' may not be used as a structure", - e->tok); - default: - throw semantic_error("invalid tracepoint '" + e->base_name + "' use", e->tok); - } + e->assert_no_components("tracepoint"); if (e->base_name == "$$name") { @@ -5287,7 +5546,7 @@ tracepoint_derived_probe::tracepoint_derived_probe (systemtap_session& s, // Now expand the local variables in the probe body tracepoint_var_expanding_visitor v (dw, name, args); - this->body = v.require (this->body); + v.replace (this->body); if (sess.verbose > 2) clog << "tracepoint-based " << name << " tracepoint='" << tracepoint_name |