elaborate.cxx - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include "config.h"
#include "elaborate.h"
#include "translate.h"
#include "parse.h"
#include "tapsets.h"
#include "session.h"
#include "util.h"
#include "task_finder.h"
#include "stapregex.h"
extern "C" {
#include <sys/utsname.h>
#include <fnmatch.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
}
#include <algorithm>
#include <fstream>
#include <map>
#include <cassert>
#include <set>
#include <vector>
#include <algorithm>
#include <iterator>
#include <climits>
using namespace std;
expression* add_condition (expression* a, expression* b)
{
if (!a && !b) return 0;
if (! a) return deep_copy_visitor::deep_copy(b);
if (! b) return deep_copy_visitor::deep_copy(a);
logical_and_expr la;
la.op = "&&";
la.left = a;
la.right = b;
la.tok = a->tok; return deep_copy_visitor::deep_copy(& la);
}
derived_probe::derived_probe (probe *p, probe_point *l, bool rewrite_loc):
base (p), base_pp(l), group(NULL), sdt_semaphore_addr(0),
session_index((unsigned)-1)
{
assert (p);
this->tok = p->tok;
this->privileged = p->privileged;
this->body = deep_copy_visitor::deep_copy(p->body);
assert (l);
if (rewrite_loc)
l = new probe_point(*l);
this->locations.push_back (l);
}
void
derived_probe::printsig (ostream& o) const
{
probe::printsig (o);
printsig_nested (o);
}
void
derived_probe::printsig_nested (ostream& o) const
{
ios::fmtflags f = o.flags (ios::internal);
if (f & ios::internal)
{
o << " <- ";
base->printsig (o);
}
else
{
o << " /* <- ";
base->printsig (o);
o << " */";
}
(void) o.flags (f);
}
void
derived_probe::collect_derivation_chain (std::vector<probe*> &probes_list) const
{
probes_list.push_back(const_cast<derived_probe*>(this));
base->collect_derivation_chain(probes_list);
}
void
derived_probe::collect_derivation_pp_chain (std::vector<probe_point*> &pp_list) const
{
pp_list.push_back(const_cast<probe_point*>(this->sole_location()));
base->collect_derivation_pp_chain(pp_list);
}
string
derived_probe::derived_locations ()
{
ostringstream o;
vector<probe_point*> reference_point;
collect_derivation_pp_chain(reference_point);
if (reference_point.size() > 0)
for(unsigned i=1; i<reference_point.size(); ++i)
o << " from: " << reference_point[i]->str(false); return o.str();
}
probe_point*
derived_probe::sole_location () const
{
if (locations.size() == 0 || locations.size() > 1)
throw SEMANTIC_ERROR (_N("derived_probe with no locations",
"derived_probe with too many locations",
locations.size()), this->tok);
else
return locations[0];
}
probe_point*
derived_probe::script_location () const
{
vector<probe_point*> chain;
collect_derivation_pp_chain (chain);
for (int i=chain.size()-1; i>=0; i--)
if (chain[i]->well_formed)
return chain[i];
return sole_location();
}
void
derived_probe::emit_privilege_assertion (translator_output* o)
{
o->newline() << "#if ! STP_PRIVILEGE_CONTAINS (STP_PRIVILEGE, STP_PR_STAPDEV) && \\";
o->newline() << " ! STP_PRIVILEGE_CONTAINS (STP_PRIVILEGE, STP_PR_STAPSYS)";
o->newline() << "#error Internal Error: Probe ";
probe::printsig (o->line());
o->line() << " generated in --unprivileged mode";
o->newline() << "#endif";
}
void
derived_probe::emit_process_owner_assertion (translator_output* o)
{
o->newline() << "#if ! STP_PRIVILEGE_CONTAINS (STP_PRIVILEGE, STP_PR_STAPDEV) && \\";
o->newline() << " ! STP_PRIVILEGE_CONTAINS (STP_PRIVILEGE, STP_PR_STAPSYS)";
o->newline(1) << "if (! is_myproc ()) {";
o->newline(1) << "snprintf(c->error_buffer, sizeof(c->error_buffer),";
o->newline() << " \"Internal Error: Process %d does not belong to user %d in probe %s in --unprivileged mode\",";
o->newline() << " current->tgid, _stp_uid, c->probe_point);";
o->newline() << "c->last_error = c->error_buffer;";
o->newline() << "return;";
o->newline(-1) << "}";
o->newline(-1) << "#endif";
}
void
derived_probe::print_dupe_stamp_unprivileged(ostream& o)
{
o << _("unprivileged users: authorized") << endl;
}
void
derived_probe::print_dupe_stamp_unprivileged_process_owner(ostream& o)
{
o << _("unprivileged users: authorized for process owner") << endl;
}
void
derived_probe_builder::build_with_suffix(systemtap_session & sess,
probe * use,
probe_point * location,
std::map<std::string, literal *>
const & parameters,
std::vector<derived_probe *>
& finished_results,
std::vector<probe_point::component *>
const & suffix) {
XXX throw SEMANTIC_ERROR (_("invalid suffix for probe"));
}
bool
derived_probe_builder::get_param (std::map<std::string, literal*> const & params,
const std::string& key,
std::string& value)
{
map<string, literal *>::const_iterator i = params.find (key);
if (i == params.end())
return false;
literal_string * ls = dynamic_cast<literal_string *>(i->second);
if (!ls)
return false;
value = ls->value;
return true;
}
bool
derived_probe_builder::get_param (std::map<std::string, literal*> const & params,
const std::string& key,
int64_t& value)
{
map<string, literal *>::const_iterator i = params.find (key);
if (i == params.end())
return false;
if (i->second == NULL)
return false;
literal_number * ln = dynamic_cast<literal_number *>(i->second);
if (!ln)
return false;
value = ln->value;
return true;
}
bool
derived_probe_builder::has_null_param (std::map<std::string, literal*> const & params,
const std::string& key)
{
map<string, literal *>::const_iterator i = params.find(key);
return (i != params.end() && i->second == NULL);
}
bool
derived_probe_builder::has_param (std::map<std::string, literal*> const & params,
const std::string& key)
{
return (params.find(key) != params.end());
}
match_key::match_key(string const & n)
: name(n),
have_parameter(false),
parameter_type(pe_unknown)
{
}
match_key::match_key(probe_point::component const & c)
: name(c.functor),
have_parameter(c.arg != NULL),
parameter_type(c.arg ? c.arg->type : pe_unknown)
{
}
match_key &
match_key::with_number()
{
have_parameter = true;
parameter_type = pe_long;
return *this;
}
match_key &
match_key::with_string()
{
have_parameter = true;
parameter_type = pe_string;
return *this;
}
string
match_key::str() const
{
if (have_parameter)
switch (parameter_type)
{
case pe_string: return name + "(string)";
case pe_long: return name + "(number)";
default: return name + "(...)";
}
return name;
}
bool
match_key::operator<(match_key const & other) const
{
return ((name < other.name)
|| (name == other.name
&& have_parameter < other.have_parameter)
|| (name == other.name
&& have_parameter == other.have_parameter
&& parameter_type < other.parameter_type));
}
static bool
isglob(string const & str)
{
return(str.find('*') != str.npos);
}
static bool
isdoubleglob(string const & str)
{
return(str.find("**") != str.npos);
}
bool
match_key::globmatch(match_key const & other) const
{
const char *other_str = other.name.c_str();
const char *name_str = name.c_str();
return ((fnmatch(name_str, other_str, FNM_NOESCAPE) == 0)
&& have_parameter == other.have_parameter
&& parameter_type == other.parameter_type);
}
match_node::match_node() :
privilege(privilege_t (pr_stapdev | pr_stapsys))
{
}
match_node *
match_node::bind(match_key const & k)
{
if (k.name == "*")
throw SEMANTIC_ERROR(_("invalid use of wildcard probe point component"));
map<match_key, match_node *>::const_iterator i = sub.find(k);
if (i != sub.end())
return i->second;
match_node * n = new match_node();
sub.insert(make_pair(k, n));
return n;
}
void
match_node::bind(derived_probe_builder * e)
{
ends.push_back (e);
}
match_node *
match_node::bind(string const & k)
{
return bind(match_key(k));
}
match_node *
match_node::bind_str(string const & k)
{
return bind(match_key(k).with_string());
}
match_node *
match_node::bind_num(string const & k)
{
return bind(match_key(k).with_number());
}
match_node *
match_node::bind_privilege(privilege_t p)
{
privilege = p;
return this;
}
void
match_node::find_and_build (systemtap_session& s,
probe* p, probe_point *loc, unsigned pos,
vector<derived_probe *>& results)
{
assert (pos <= loc->components.size());
if (pos == loc->components.size()) {
if (ends.empty())
{
string alternatives;
for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
alternatives += string(" ") + i->first.str();
throw SEMANTIC_ERROR (_F("probe point truncated (follow: %s)",
alternatives.c_str()),
loc->components.back()->tok);
}
if (! pr_contains (privilege, s.privilege))
{
throw SEMANTIC_ERROR (_F("probe point is not allowed for --privilege=%s",
pr_name (s.privilege)),
loc->components.back()->tok);
}
map<string, literal *> param_map;
for (unsigned i=0; i<pos; i++)
param_map[loc->components[i]->functor] = loc->components[i]->arg;
for (unsigned k=0; k<ends.size(); k++)
{
derived_probe_builder *b = ends[k];
b->build (s, p, loc, param_map, results);
}
}
else if (isdoubleglob(loc->components[pos]->functor)) {
unsigned int num_results = results.size();
const probe_point::component *comp = loc->components[pos];
const string &functor = comp->functor;
size_t glob_start = functor.find("**");
size_t glob_end = functor.find_first_not_of('*', glob_start);
const string prefix = functor.substr(0, glob_start);
const string suffix = ((glob_end != string::npos) ?
functor.substr(glob_end) : "");
probe_point *simple_pp = new probe_point(*loc);
simple_pp->from_glob = true;
probe_point::component *simple_comp = new probe_point::component(*comp);
simple_comp->functor = prefix + "*" + suffix;
simple_pp->components[pos] = simple_comp;
try
{
find_and_build (s, p, simple_pp, pos, results);
}
catch (const semantic_error& e)
{
}
if (results.size() == num_results)
{
delete simple_pp;
delete simple_comp;
}
num_results = results.size();
probe_point *expanded_pp = new probe_point(*loc);
expanded_pp->from_glob = true;
probe_point::component *expanded_comp_pre = new probe_point::component(*comp);
expanded_comp_pre->functor = prefix + "*";
expanded_comp_pre->arg = NULL;
probe_point::component *expanded_comp_post = new probe_point::component(*comp);
expanded_comp_post->functor = "**" + suffix;
expanded_pp->components[pos] = expanded_comp_pre;
expanded_pp->components.insert(expanded_pp->components.begin() + pos + 1,
expanded_comp_post);
try
{
find_and_build (s, p, expanded_pp, pos, results);
}
catch (const semantic_error& e)
{
}
if (results.size() == num_results)
{
delete expanded_pp;
delete expanded_comp_pre;
delete expanded_comp_post;
}
if (num_results == results.size())
this->try_suffix_expansion (s, p, loc, pos, results);
if (! loc->optional && num_results == results.size())
{
string sugs = suggest_functors(functor);
throw SEMANTIC_ERROR (_F("probe point mismatch: didn't find any wildcard matches%s",
sugs.empty() ? "" : (" (similar: " + sugs + ")").c_str()),
comp->tok);
}
}
else if (isglob(loc->components[pos]->functor)) {
match_key match (* loc->components[pos]);
unsigned int num_results = results.size();
for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
{
const match_key& subkey = i->first;
match_node* subnode = i->second;
assert_no_interrupts();
if (match.globmatch(subkey))
{
if (s.verbose > 2)
clog << _F("wildcard '%s' matched '%s'",
loc->components[pos]->functor.c_str(),
subkey.name.c_str()) << endl;
probe_point *non_wildcard_pp = new probe_point(*loc);
non_wildcard_pp->from_glob = true;
probe_point::component *non_wildcard_component
= new probe_point::component(*loc->components[pos]);
non_wildcard_component->functor = subkey.name;
non_wildcard_pp->components[pos] = non_wildcard_component;
unsigned int inner_results = results.size();
try
{
subnode->find_and_build (s, p, non_wildcard_pp, pos+1,
results);
}
catch (const semantic_error& e)
{
}
if (results.size() == inner_results)
{
delete non_wildcard_pp;
delete non_wildcard_component;
}
}
}
if (num_results == results.size())
this->try_suffix_expansion (s, p, loc, pos, results);
if (! loc->optional && num_results == results.size())
{
string sugs = suggest_functors(loc->components[pos]->functor);
throw SEMANTIC_ERROR (_F("probe point mismatch: didn't find any wildcard matches%s",
sugs.empty() ? "" : (" (similar: " + sugs + ")").c_str()),
loc->components[pos]->tok);
}
}
else
{
match_key match (* loc->components[pos]);
sub_map_iterator_t i = sub.find (match);
if (i != sub.end()) {
match_node* subnode = i->second;
subnode->find_and_build (s, p, loc, pos+1, results);
return;
}
unsigned int num_results = results.size();
this->try_suffix_expansion (s, p, loc, pos, results);
XXX if (! loc->optional && num_results == results.size())
{
string sugs = suggest_functors(loc->components[pos]->functor);
throw SEMANTIC_ERROR (_F("probe point mismatch%s",
sugs.empty() ? "" : (" (similar: " + sugs + ")").c_str()),
loc->components[pos]->tok);
}
}
}
string
match_node::suggest_functors(string functor)
{
size_t glob = functor.find('*');
if (glob != string::npos && glob != 0)
functor.erase(glob);
if (functor.empty())
return "";
set<string> functors;
for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
{
string ftor = i->first.str();
if (ftor.find('(') != string::npos) ftor.erase(ftor.find('('));
functors.insert(ftor);
}
return levenshtein_suggest(functor, functors, 5); }
void
match_node::try_suffix_expansion (systemtap_session& s,
probe *p, probe_point *loc, unsigned pos,
vector<derived_probe *>& results)
{
if (strverscmp(s.compatible.c_str(), "2.0") >= 0)
{
XXX map<string, literal *> param_map;
vector<probe_point::component *> suffix (loc->components.begin()+pos,
loc->components.end());
for (unsigned k=0; k < ends.size(); k++)
{
derived_probe_builder *b = ends[k];
try
{
b->build_with_suffix (s, p, loc, param_map, results, suffix);
}
catch (const recursive_expansion_error &e)
{
throw semantic_error(e);
}
catch (const semantic_error &e)
{
if (! loc->optional)
throw semantic_error(e.errsrc, e.what(), loc->components[pos]->tok);
}
}
}
}
void
match_node::build_no_more (systemtap_session& s)
{
for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
i->second->build_no_more (s);
for (unsigned k=0; k<ends.size(); k++)
{
derived_probe_builder *b = ends[k];
b->build_no_more (s);
}
}
void
match_node::dump (systemtap_session &s, const string &name)
{
for (unsigned k=0; k<ends.size(); k++)
{
if (ends[k]->is_alias ())
continue;
if (pr_contains (privilege, s.privilege))
{
cout << name << endl;
break; }
}
string dot;
if (! name.empty ())
dot = ".";
for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
{
i->second->dump (s, name + dot + i->first.str());
}
}
struct alias_derived_probe: public derived_probe
{
alias_derived_probe (probe* base, probe_point *l, const probe_alias *a,
const vector<probe_point::component *> *suffix = 0);
~alias_derived_probe();
void upchuck () { throw SEMANTIC_ERROR (_("inappropriate"), this->tok); }
void join_group (systemtap_session&) { upchuck (); }
virtual const probe_alias *get_alias () const { return alias; }
virtual probe_point *get_alias_loc () const { return alias_loc; }
virtual probe_point *sole_location () const;
private:
const probe_alias *alias; probe_point *alias_loc; };
alias_derived_probe::alias_derived_probe(probe *base, probe_point *l,
const probe_alias *a,
const vector<probe_point::component *>
*suffix):
derived_probe (base, l), alias(a)
{
XXX assert (alias->alias_names.size() >= 1);
alias_loc = new probe_point(*alias->alias_names[0]); XXX alias_loc->well_formed = true;
vector<probe_point::component*>::const_iterator it;
for (it = suffix->begin(); it != suffix->end(); ++it)
{
alias_loc->components.push_back(*it);
if (isglob((*it)->functor))
alias_loc->well_formed = false; }
}
alias_derived_probe::~alias_derived_probe ()
{
delete alias_loc;
}
probe_point*
alias_derived_probe::sole_location () const
{
return const_cast<probe_point*>(alias_loc);
}
void
alias_expansion_builder::build(systemtap_session & sess,
probe * use,
probe_point * location,
std::map<std::string, literal *>
const & parameters,
vector<derived_probe *> & finished_results)
{
vector<probe_point::component *> empty_suffix;
build_with_suffix (sess, use, location, parameters,
finished_results, empty_suffix);
}
void
alias_expansion_builder::build_with_suffix(systemtap_session & sess,
probe * use,
probe_point * location,
std::map<std::string, literal *>
const &,
vector<derived_probe *>
& finished_results,
vector<probe_point::component *>
const & suffix)
{
if (checkForRecursiveExpansion (use)) {
stringstream msg;
msg << _F("recursive loop in alias expansion of %s at %s",
lex_cast(*location).c_str(), lex_cast(location->components.front()->tok->location).c_str());
throw recursive_expansion_error (msg.str());
XXX }
alias_derived_probe * n = new alias_derived_probe (use, location , this->alias, &suffix);
n->body = new block();
n->locations.clear();
for (unsigned i=0; i<alias->locations.size(); i++)
{
probe_point *pp = new probe_point(*alias->locations[i]);
pp->from_glob = location->from_glob;
pp->components.insert(pp->components.end(), suffix.begin(), suffix.end());
pp->condition = add_condition (pp->condition, location->condition);
n->locations.push_back(pp);
}
n->tok = location->components.front()->tok;
if (alias->epilogue_style)
n->body = new block (use->body, alias->body);
else
n->body = new block (alias->body, use->body);
unsigned old_num_results = finished_results.size();
derive_probes (sess, n, finished_results, location->optional, !suffix.empty());
if (finished_results.size() > old_num_results)
{
stapfile *f = alias->tok->location.file;
if (find (sess.files.begin(), sess.files.end(), f)
== sess.files.end())
sess.files.push_back (f);
}
}
bool
alias_expansion_builder::checkForRecursiveExpansion (probe *use)
{
vector<probe*>derivations;
use->collect_derivation_chain (derivations);
assert (derivations.size() > 0);
assert (derivations[0] == use);
for (unsigned d = 1; d < derivations.size(); ++d) {
if (use->get_alias() == derivations[d]->get_alias())
return true; }
return false;
}
static unsigned max_recursion = 100;
struct
recursion_guard
{
unsigned & i;
recursion_guard(unsigned & i) : i(i)
{
if (i > max_recursion)
throw SEMANTIC_ERROR(_("recursion limit reached"));
++i;
}
~recursion_guard()
{
--i;
}
};
void
derive_probes (systemtap_session& s,
probe *p, vector<derived_probe*>& dps,
bool optional,
bool rethrow_errors)
{
static bool parent_optional = false;
bool undo_parent_optional = false;
if (optional && !parent_optional)
{
parent_optional = true;
undo_parent_optional = true;
}
vector <semantic_error> optional_errs;
for (unsigned i = 0; i < p->locations.size(); ++i)
{
assert_no_interrupts();
probe_point *loc = p->locations[i];
if (s.verbose > 4)
clog << "derive-probes " << *loc << endl;
try
{
unsigned num_atbegin = dps.size();
try
{
s.pattern_root->find_and_build (s, p, loc, 0, dps); }
catch (const semantic_error& e)
{
if (!loc->optional && !parent_optional)
throw semantic_error(e);
else {
semantic_error err(ERR_SRC, _("while resolving probe point"),
loc->components[0]->tok, NULL, &e);
optional_errs.push_back(err);
continue;
}
}
unsigned num_atend = dps.size();
if (! (loc->optional||parent_optional) && num_atbegin == num_atend) throw SEMANTIC_ERROR (_("no match"));
if (loc->sufficient && (num_atend > num_atbegin))
{
if (s.verbose > 1)
{
clog << "Probe point ";
p->locations[i]->print(clog);
clog << " sufficient, skipped";
for (unsigned j = i+1; j < p->locations.size(); ++j)
{
clog << " ";
p->locations[j]->print(clog);
}
clog << endl;
}
break; }
}
catch (const semantic_error& e)
{
if (rethrow_errors)
{
throw semantic_error(e);
}
else if (!s.dump_mode || (s.verbose > 1))
{
semantic_error err(ERR_SRC, _("while resolving probe point"),
loc->components[0]->tok, NULL, &e);
s.print_error(err);
for (vector<semantic_error>::const_iterator it = optional_errs.begin();
it != optional_errs.end(); ++it)
{
s.print_error(*it);
}
}
}
}
if (undo_parent_optional)
parent_optional = false;
}
struct symbol_fetcher
: public throwing_visitor
{
symbol *&sym;
symbol_fetcher (symbol *&sym): sym(sym)
{}
void visit_symbol (symbol* e)
{
sym = e;
}
void visit_arrayindex (arrayindex* e)
{
e->base->visit (this);
}
void throwone (const token* t)
{
throw SEMANTIC_ERROR (_("Expecting symbol or array index expression"), t);
}
};
symbol *
get_symbol_within_expression (expression *e)
{
symbol *sym = NULL;
symbol_fetcher fetcher(sym);
e->visit (&fetcher);
return sym; }
static symbol *
get_symbol_within_indexable (indexable *ix)
{
symbol *array = NULL;
hist_op *hist = NULL;
classify_indexable(ix, array, hist);
if (array)
return array;
else
return get_symbol_within_expression (hist->stat);
}
struct mutated_var_collector
: public traversing_visitor
{
set<vardecl *> * mutated_vars;
mutated_var_collector (set<vardecl *> * mm)
: mutated_vars (mm)
{}
void visit_assignment(assignment* e)
{
if (e->type == pe_stats && e->op == "<<<")
{
vardecl *vd = get_symbol_within_expression (e->left)->referent;
if (vd)
mutated_vars->insert (vd);
}
traversing_visitor::visit_assignment(e);
}
void visit_arrayindex (arrayindex *e)
{
if (is_active_lvalue (e))
{
symbol *sym;
if (e->base->is_symbol (sym))
mutated_vars->insert (sym->referent);
else
throw SEMANTIC_ERROR(_("Assignment to read-only histogram bucket"), e->tok);
}
traversing_visitor::visit_arrayindex (e);
}
};
struct no_var_mutation_during_iteration_check
: public traversing_visitor
{
systemtap_session & session;
map<functiondecl *,set<vardecl *> *> & function_mutates_vars;
vector<vardecl *> vars_being_iterated;
no_var_mutation_during_iteration_check
(systemtap_session & sess,
map<functiondecl *,set<vardecl *> *> & fmv)
: session(sess), function_mutates_vars (fmv)
{}
void visit_arrayindex (arrayindex *e)
{
if (is_active_lvalue(e))
{
vardecl *vd = get_symbol_within_indexable (e->base)->referent;
if (vd)
{
for (unsigned i = 0; i < vars_being_iterated.size(); ++i)
{
vardecl *v = vars_being_iterated[i];
if (v == vd)
{
string err = _F("variable '%s' modified during 'foreach' iteration",
v->name.c_str());
session.print_error (SEMANTIC_ERROR (err, e->tok));
}
}
}
}
traversing_visitor::visit_arrayindex (e);
}
void visit_functioncall (functioncall* e)
{
map<functiondecl *,set<vardecl *> *>::const_iterator i
= function_mutates_vars.find (e->referent);
if (i != function_mutates_vars.end())
{
for (unsigned j = 0; j < vars_being_iterated.size(); ++j)
{
vardecl *m = vars_being_iterated[j];
if (i->second->find (m) != i->second->end())
{
string err = _F("function call modifies var '%s' during 'foreach' iteration",
m->name.c_str());
session.print_error (SEMANTIC_ERROR (err, e->tok));
}
}
}
traversing_visitor::visit_functioncall (e);
}
void visit_foreach_loop(foreach_loop* s)
{
vardecl *vd = get_symbol_within_indexable (s->base)->referent;
if (vd)
vars_being_iterated.push_back (vd);
traversing_visitor::visit_foreach_loop (s);
if (vd)
vars_being_iterated.pop_back();
}
};
struct stat_decl_collector
: public traversing_visitor
{
systemtap_session & session;
stat_decl_collector(systemtap_session & sess)
: session(sess)
{}
void visit_stat_op (stat_op* e)
{
symbol *sym = get_symbol_within_expression (e->stat);
if (session.stat_decls.find(sym->name) == session.stat_decls.end())
session.stat_decls[sym->name] = statistic_decl();
}
void visit_assignment (assignment* e)
{
if (e->op == "<<<")
{
symbol *sym = get_symbol_within_expression (e->left);
if (session.stat_decls.find(sym->name) == session.stat_decls.end())
session.stat_decls[sym->name] = statistic_decl();
}
else
traversing_visitor::visit_assignment(e);
}
void visit_hist_op (hist_op* e)
{
symbol *sym = get_symbol_within_expression (e->stat);
statistic_decl new_stat;
if (e->htype == hist_linear)
{
new_stat.type = statistic_decl::linear;
assert (e->params.size() == 3);
new_stat.linear_low = e->params[0];
new_stat.linear_high = e->params[1];
new_stat.linear_step = e->params[2];
}
else
{
assert (e->htype == hist_log);
new_stat.type = statistic_decl::logarithmic;
assert (e->params.size() == 0);
}
map<string, statistic_decl>::iterator i = session.stat_decls.find(sym->name);
if (i == session.stat_decls.end())
session.stat_decls[sym->name] = new_stat;
else
{
statistic_decl & old_stat = i->second;
if (!(old_stat == new_stat))
{
if (old_stat.type == statistic_decl::none)
i->second = new_stat;
else
{
FIXME semantic_error se(ERR_SRC, _F("multiple histogram types declared on '%s'", sym->name.c_str()), e->tok);
session.print_error (se);
}
}
}
}
};
static int
semantic_pass_stats (systemtap_session & sess)
{
stat_decl_collector sdc(sess);
for (map<string,functiondecl*>::iterator it = sess.functions.begin(); it != sess.functions.end(); it++)
it->second->body->visit (&sdc);
for (unsigned i = 0; i < sess.probes.size(); ++i)
sess.probes[i]->body->visit (&sdc);
for (unsigned i = 0; i < sess.globals.size(); ++i)
{
vardecl *v = sess.globals[i];
if (v->type == pe_stats)
{
if (sess.stat_decls.find(v->name) == sess.stat_decls.end())
{
semantic_error se(ERR_SRC, _F("unable to infer statistic parameters for global '%s'", v->name.c_str()));
sess.print_error (se);
}
}
}
return sess.num_errors();
}
static int
semantic_pass_vars (systemtap_session & sess)
{
map<functiondecl *, set<vardecl *> *> fmv;
no_var_mutation_during_iteration_check chk(sess, fmv);
for (map<string,functiondecl*>::iterator it = sess.functions.begin(); it != sess.functions.end(); it++)
{
functiondecl * fn = it->second;
if (fn->body)
{
set<vardecl *> * m = new set<vardecl *>();
mutated_var_collector mc (m);
fn->body->visit (&mc);
fmv[fn] = m;
}
}
for (map<string,functiondecl*>::iterator it = sess.functions.begin(); it != sess.functions.end(); it++)
{
functiondecl * fn = it->second;
if (fn->body) fn->body->visit (&chk);
}
for (unsigned i = 0; i < sess.probes.size(); ++i)
{
if (sess.probes[i]->body)
sess.probes[i]->body->visit (&chk);
}
return sess.num_errors();
}
static void
derived_probe_condition_inline (derived_probe *p)
{
expression* e = p->sole_location()->condition;
assert(e);
if_statement *ifs = new if_statement ();
ifs->tok = e->tok;
ifs->thenblock = new next_statement ();
ifs->thenblock->tok = e->tok;
ifs->elseblock = NULL;
unary_expression *notex = new unary_expression ();
notex->op = "!";
notex->tok = e->tok;
notex->operand = e;
ifs->condition = notex;
p->body = new block (ifs, p->body);
}
static int
semantic_pass_conditions (systemtap_session & sess)
{
map<derived_probe*, set<vardecl*> > vars_read_in_cond;
map<derived_probe*, set<vardecl*> > vars_written_in_body;
for (unsigned i = 0; i < sess.probes.size(); ++i)
{
derived_probe* p = sess.probes[i];
expression* e = p->sole_location()->condition;
if (e)
{
varuse_collecting_visitor vcv_cond(sess);
e->visit (& vcv_cond);
if (!vcv_cond.written.empty())
sess.print_error (SEMANTIC_ERROR (_("probe condition must not "
"modify any variables"),
e->tok));
else if (vcv_cond.embedded_seen)
sess.print_error (SEMANTIC_ERROR (_("probe condition must not "
"include impure embedded-C"),
e->tok));
derived_probe_condition_inline(p);
vars_read_in_cond[p].insert(vcv_cond.read.begin(),
vcv_cond.read.end());
}
varuse_collecting_visitor vcv_body(sess);
p->body->visit (& vcv_body);
vars_written_in_body[p].insert(vcv_body.written.begin(),
vcv_body.written.end());
}
for (unsigned i = 0; i < sess.probes.size(); ++i)
{
derived_probe *p = sess.probes[i];
set<vardecl*>::const_iterator var;
for (var = vars_written_in_body[p].begin();
var != vars_written_in_body[p].end(); ++var)
{
for (unsigned j = 0; j < sess.probes.size(); ++j)
{
if (vars_read_in_cond[sess.probes[j]].count(*var))
{
if (!p->probes_with_affected_conditions.count(sess.probes[j]))
{
p->probes_with_affected_conditions.insert(sess.probes[j]);
if (sess.verbose > 2)
clog << "probe " << i << " can affect condition of "
"probe " << j << endl;
}
}
}
}
}
return sess.num_errors();
}
struct embeddedcode_info: public functioncall_traversing_visitor
{
protected:
systemtap_session& session;
public:
embeddedcode_info (systemtap_session& s): session(s) { }
void visit_embeddedcode (embeddedcode* c)
{
if (! vma_tracker_enabled(session)
&& c->code.find("/* pragma:vma */") != string::npos)
{
if (session.verbose > 2)
clog << _F("Turning on task_finder vma_tracker, pragma:vma found in %s",
current_function->name.c_str()) << endl;
if (session.runtime_usermode_p())
throw SEMANTIC_ERROR(_("VMA-tracking is only supported by the kernel runtime (PR15052)"), c->tok);
enable_vma_tracker(session);
}
if (! session.need_unwind
&& c->code.find("/* pragma:unwind */") != string::npos)
{
if (session.verbose > 2)
clog << _F("Turning on unwind support, pragma:unwind found in %s",
current_function->name.c_str()) << endl;
session.need_unwind = true;
}
if (! session.need_symbols
&& c->code.find("/* pragma:symbols */") != string::npos)
{
if (session.verbose > 2)
clog << _F("Turning on symbol data collecting, pragma:symbols found in %s",
current_function->name.c_str()) << endl;
session.need_symbols = true;
}
}
};
void embeddedcode_info_pass (systemtap_session& s)
{
embeddedcode_info eci (s);
for (unsigned i=0; i<s.probes.size(); i++)
s.probes[i]->body->visit (& eci);
}
struct regex_collecting_visitor: public functioncall_traversing_visitor
{
protected:
systemtap_session& session;
public:
regex_collecting_visitor (systemtap_session& s): session(s) { }
void visit_regex_query (regex_query *q) {
functioncall_traversing_visitor::visit_regex_query (q);
string re = q->right->value;
regex_to_stapdfa (&session, re, q->right->tok);
}
};
int gen_dfa_table (systemtap_session& s)
{
regex_collecting_visitor rcv(s);
for (unsigned i=0; i<s.probes.size(); i++)
{
try
{
s.probes[i]->body->visit (& rcv);
if (s.probes[i]->sole_location()->condition)
s.probes[i]->sole_location()->condition->visit (& rcv);
}
catch (const semantic_error& e)
{
s.print_error (e);
}
}
return s.num_errors();
}
static int semantic_pass_symbols (systemtap_session&);
static int semantic_pass_optimize1 (systemtap_session&);
static int semantic_pass_optimize2 (systemtap_session&);
static int semantic_pass_types (systemtap_session&);
static int semantic_pass_vars (systemtap_session&);
static int semantic_pass_stats (systemtap_session&);
static int semantic_pass_conditions (systemtap_session&);
struct expression_build_no_more_visitor : public expression_visitor
{
void visit_expression(expression *e)
{
e->type_details.reset();
}
};
static void
build_no_more (systemtap_session& s)
{
expression_build_no_more_visitor v;
for (unsigned i=0; i<s.probes.size(); i++)
s.probes[i]->body->visit(&v);
for (map<string,functiondecl*>::iterator it = s.functions.begin();
it != s.functions.end(); it++)
it->second->body->visit(&v);
s.pattern_root->build_no_more (s);
}
static int
semantic_pass_symbols (systemtap_session& s)
{
symresolution_info sym (s);
if (s.dump_mode == systemtap_session::dump_functions)
{
s.files.insert(s.files.end(), s.library_files.begin(),
s.library_files.end());
}
else if (!s.user_files.empty())
{
s.files.insert (s.files.end(), s.user_files.begin(), s.user_files.end());
}
for (unsigned i = 0; i < s.files.size(); i++)
{
assert_no_interrupts();
stapfile* dome = s.files[i];
for (unsigned i=0; i<dome->globals.size(); i++)
{
vardecl* g = dome->globals[i];
for (unsigned j=0; j<s.globals.size(); j++)
{
vardecl* g2 = s.globals[j];
if (g->name == g2->name)
{
s.print_error (SEMANTIC_ERROR (_("conflicting global variables"),
g->tok, g2->tok));
}
}
s.globals.push_back (g);
}
for (unsigned i=0; i<dome->functions.size(); i++)
{
functiondecl* f = dome->functions[i];
functiondecl* f2 = s.functions[f->name];
if (f2 && f != f2)
{
s.print_error (SEMANTIC_ERROR (_("conflicting functions"),
f->tok, f2->tok));
}
s.functions[f->name] = f;
}
for (unsigned i=0; i<dome->embeds.size(); i++)
s.embeds.push_back (dome->embeds[i]);
for (unsigned i=0; i<dome->probes.size(); i++)
{
assert_no_interrupts();
probe* p = dome->probes [i];
vector<derived_probe*> dps;
derive_probes (s, p, dps);
for (unsigned j=0; j<dps.size(); j++)
{
assert_no_interrupts();
derived_probe* dp = dps[j];
s.probes.push_back (dp);
dp->join_group (s);
try
{
for (unsigned k=0; k<s.code_filters.size(); k++)
s.code_filters[k]->replace (dp->body);
sym.current_function = 0;
sym.current_probe = dp;
dp->body->visit (& sym);
sym.current_function = 0;
sym.current_probe = 0;
if (dp->sole_location()->condition)
dp->sole_location()->condition->visit (& sym);
}
catch (const semantic_error& e)
{
s.print_error (e);
}
}
}
for (unsigned i=0; i<dome->functions.size(); i++)
{
assert_no_interrupts();
functiondecl* fd = dome->functions[i];
try
{
for (unsigned j=0; j<s.code_filters.size(); j++)
s.code_filters[j]->replace (fd->body);
sym.current_function = fd;
sym.current_probe = 0;
fd->body->visit (& sym);
}
catch (const semantic_error& e)
{
s.print_error (e);
}
}
}
if(s.systemtap_v_check){
for(unsigned i=0;i<s.globals.size();i++){
if(s.globals[i]->systemtap_v_conditional)
s.print_warning(_("This global uses tapset constructs that are dependent on systemtap version"), s.globals[i]->tok);
}
for(map<string, functiondecl*>::const_iterator i=s.functions.begin();i != s.functions.end();++i){
if(i->second->systemtap_v_conditional)
s.print_warning(_("This function uses tapset constructs that are dependent on systemtap version"), i->second->tok);
}
for(unsigned i=0;i<s.probes.size();i++){
vector<probe*> sysvc;
s.probes[i]->collect_derivation_chain(sysvc);
for(unsigned j=0;j<sysvc.size();j++){
if(sysvc[j]->systemtap_v_conditional)
s.print_warning(_("This probe uses tapset constructs that are dependent on systemtap version"), sysvc[j]->tok);
if(sysvc[j]->get_alias() && sysvc[j]->get_alias()->systemtap_v_conditional)
s.print_warning(_("This alias uses tapset constructs that are dependent on systemtap version"), sysvc[j]->get_alias()->tok);
}
}
}
return s.num_errors(); }
void add_global_var_display (systemtap_session& s)
{
if (s.dump_mode) return;
varuse_collecting_visitor vut(s);
for (unsigned i=0; i<s.probes.size(); i++)
{
s.probes[i]->body->visit (& vut);
if (s.probes[i]->sole_location()->condition)
s.probes[i]->sole_location()->condition->visit (& vut);
}
for (unsigned g=0; g < s.globals.size(); g++)
{
vardecl* l = s.globals[g];
if ((vut.read.find (l) != vut.read.end()
&& vut.used.find (l) != vut.used.end())
|| vut.written.find (l) == vut.written.end())
continue;
bool tapset_global = false;
for (size_t m=0; m < s.library_files.size(); m++)
{
for (size_t n=0; n < s.library_files[m]->globals.size(); n++)
{
if (l->name == s.library_files[m]->globals[n]->name)
{tapset_global = true; break;}
}
}
if (tapset_global)
continue;
stringstream code;
code << "probe end {" << endl;
string format = l->name;
string indexes;
string foreach_value;
if (!l->index_types.empty())
{
format += "[";
for (size_t i = 0; i < l->index_types.size(); ++i)
{
if (i > 0)
{
indexes += ",";
format += ",";
}
indexes += "__idx" + lex_cast(i);
if (l->index_types[i] == pe_string)
format += "\\\"%#s\\\"";
else
format += "%#d";
}
format += "]";
code << "foreach (";
if (l->type != pe_stats)
{
foreach_value = "__val";
code << foreach_value << " = ";
}
code << "[" << indexes << "] in " << l->name << "-)" << endl;
}
else if (l->type == pe_stats)
{
code << "if (@count(" << l->name << ") == 0)" << endl;
code << "printf(\"" << l->name << " @count=0x0\\n\")" << endl;
code << "else" << endl;
}
static const string stats[] = { "@count", "@min", "@max", "@sum", "@avg" };
const string stats_format =
(strverscmp(s.compatible.c_str(), "1.4") >= 0) ? "%#d" : "%#x";
if (l->type == pe_stats)
for (size_t i = 0; i < sizeof(stats)/sizeof(stats[0]); ++i)
format += " " + stats[i] + "=" + stats_format;
else if (l->type == pe_string)
format += "=\\\"%#s\\\"";
else
format += "=%#x";
format += "\\n";
code << "printf (\"" << format << "\"";
string value = !foreach_value.empty() ? foreach_value : l->name;
if (!l->index_types.empty())
{
code << "," << indexes;
if (foreach_value.empty())
value += "[" + indexes + "]";
}
if (l->type == pe_stats)
for (size_t i = 0; i < sizeof(stats)/sizeof(stats[0]); ++i)
code << "," << stats[i] << "(" << value << ")";
else
code << "," << value;
code << ")" << endl;
code << "}" << endl;
probe *p = parse_synthetic_probe (s, code, l->tok);
if (!p)
throw SEMANTIC_ERROR (_("can't create global var display"), l->tok);
vector<derived_probe*> dps;
derive_probes (s, p, dps);
for (unsigned i = 0; i < dps.size(); i++)
{
derived_probe* dp = dps[i];
s.probes.push_back (dp);
dp->join_group (s);
symresolution_info sym (s);
sym.current_function = 0;
sym.current_probe = dp;
dp->body->visit (& sym);
}
semantic_pass_types(s);
vut.read.insert (l);
}
}
int
semantic_pass (systemtap_session& s)
{
int rc = 0;
try
{
s.register_library_aliases();
register_standard_tapsets(s);
if (rc == 0) rc = semantic_pass_symbols (s);
if (rc == 0) rc = semantic_pass_conditions (s);
if (rc == 0) rc = semantic_pass_optimize1 (s);
if (rc == 0) rc = semantic_pass_types (s);
if (rc == 0) rc = gen_dfa_table(s);
if (rc == 0) add_global_var_display (s);
if (rc == 0) rc = semantic_pass_optimize2 (s);
if (rc == 0) rc = semantic_pass_vars (s);
if (rc == 0) rc = semantic_pass_stats (s);
if (rc == 0) embeddedcode_info_pass (s);
}
catch (const semantic_error& e)
{
s.print_error (e);
rc ++;
}
bool no_primary_probes = true;
for (unsigned i = 0; i < s.probes.size(); i++)
if (s.is_primary_probe(s.probes[i]))
no_primary_probes = false;
if (s.num_errors() == 0 && no_primary_probes && !s.dump_mode)
{
s.print_error(SEMANTIC_ERROR(_("no probes found")));
rc ++;
}
build_no_more (s);
if (s.dump_mode == systemtap_session::dump_matched_probes ||
s.dump_mode == systemtap_session::dump_matched_probes_vars)
rc = no_primary_probes;
if (s.dump_mode == systemtap_session::dump_functions)
rc = s.functions.empty();
return rc;
}
symresolution_info::symresolution_info (systemtap_session& s):
session (s), current_function (0), current_probe (0)
{
}
void
symresolution_info::visit_block (block* e)
{
for (unsigned i=0; i<e->statements.size(); i++)
{
try
{
e->statements[i]->visit (this);
}
catch (const semantic_error& e)
{
session.print_error (e);
}
}
}
void
symresolution_info::visit_foreach_loop (foreach_loop* e)
{
for (unsigned i=0; i<e->indexes.size(); i++)
e->indexes[i]->visit (this);
for (unsigned i=0; i<e->array_slice.size(); i++)
if (e->array_slice[i])
e->array_slice[i]->visit(this);
symbol *array = NULL;
hist_op *hist = NULL;
classify_indexable (e->base, array, hist);
if (array)
{
if (!array->referent)
{
vardecl* d = find_var (array->name, e->indexes.size (), array->tok);
if (d)
array->referent = d;
else
{
stringstream msg;
msg << _F("unresolved arity-%zu global array %s, missing global declaration?",
e->indexes.size(), array->name.c_str());
throw SEMANTIC_ERROR (msg.str(), array->tok);
}
}
if (!e->array_slice.empty() && e->array_slice.size() != e->indexes.size())
{
stringstream msg;
msg << _F("unresolved arity-%zu global array %s, missing global declaration?",
e->array_slice.size(), array->name.c_str());
throw SEMANTIC_ERROR (msg.str(), array->tok);
}
}
else
{
assert (hist);
hist->visit (this);
}
if (e->value)
e->value->visit (this);
if (e->limit)
e->limit->visit (this);
e->block->visit (this);
}
struct
delete_statement_symresolution_info:
public traversing_visitor
{
symresolution_info *parent;
delete_statement_symresolution_info (symresolution_info *p):
parent(p)
{}
void visit_arrayindex (arrayindex* e)
{
parent->visit_arrayindex(e, true);
}
void visit_functioncall (functioncall* e)
{
parent->visit_functioncall (e);
}
void visit_symbol (symbol* e)
{
if (e->referent)
return;
vardecl* d = parent->find_var (e->name, -1, e->tok);
if (d)
e->referent = d;
else
throw SEMANTIC_ERROR (_("unresolved array in delete statement"), e->tok);
}
};
void
symresolution_info::visit_delete_statement (delete_statement* s)
{
delete_statement_symresolution_info di (this);
s->value->visit (&di);
}
void
symresolution_info::visit_symbol (symbol* e)
{
if (e->referent)
return;
vardecl* d = find_var (e->name, 0, e->tok);
if (d)
e->referent = d;
else
{
vardecl* v = new vardecl;
v->name = e->name;
v->tok = e->tok;
v->set_arity(0, e->tok);
if (current_function)
current_function->locals.push_back (v);
else if (current_probe)
current_probe->locals.push_back (v);
else
throw SEMANTIC_ERROR (_("probe condition must not reference undeclared global"), e->tok);
e->referent = v;
}
}
void
symresolution_info::visit_arrayindex (arrayindex* e)
{
visit_arrayindex(e, false);
}
void
symresolution_info::visit_arrayindex (arrayindex* e, bool wildcard_ok)
{
for (unsigned i=0; i<e->indexes.size(); i++)
{
if (e->indexes[i] == NULL)
{
if (!wildcard_ok)
throw SEMANTIC_ERROR(_("wildcard not allowed in array index"), e->tok);
}
else
e->indexes[i]->visit (this);
}
symbol *array = NULL;
hist_op *hist = NULL;
classify_indexable(e->base, array, hist);
if (array)
{
if (array->referent)
return;
vardecl* d = find_var (array->name, e->indexes.size (), array->tok);
if (d)
array->referent = d;
else
{
stringstream msg;
msg << _F("unresolved arity-%zu global array %s, missing global declaration?",
e->indexes.size(), array->name.c_str());
throw SEMANTIC_ERROR (msg.str(), e->tok);
}
}
else
{
assert (hist);
hist->visit (this);
}
}
void
symresolution_info::visit_array_in (array_in* e)
{
visit_arrayindex(e->operand, true);
}
void
symresolution_info::visit_functioncall (functioncall* e)
{
XXX if (! (current_function || current_probe))
{
throw SEMANTIC_ERROR (_("probe condition must not reference function"), e->tok);
}
for (unsigned i=0; i<e->args.size(); i++)
e->args[i]->visit (this);
if (e->referent)
return;
functiondecl* d = find_function (e->function, e->args.size (), e->tok);
if (d)
e->referent = d;
else
{
string sugs = levenshtein_suggest(e->function, collect_functions(), 5); throw SEMANTIC_ERROR(_F("unresolved function%s",
sugs.empty() ? "" : (_(" (similar: ") + sugs + ")").c_str()),
e->tok);
}
}
vardecl*
symresolution_info::find_var (const string& name, int arity, const token* tok)
{
if (current_function || current_probe)
{
vector<vardecl*>& locals = (current_function ?
current_function->locals :
current_probe->locals);
for (unsigned i=0; i<locals.size(); i++)
if (locals[i]->name == name)
{
locals[i]->set_arity (arity, tok);
return locals[i];
}
}
if (arity == 0 && current_function)
for (unsigned i=0; i<current_function->formal_args.size(); i++)
if (current_function->formal_args[i]->name == name)
{
current_function->formal_args[i]->set_arity (0, tok);
return current_function->formal_args[i];
}
for (unsigned i=0; i<session.globals.size(); i++)
if (session.globals[i]->name == name)
{
if (! session.suppress_warnings)
{
vardecl* v = session.globals[i];
stapfile* f = tok->location.file;
if (v->tok->location.file != f && !f->synthetic)
{
session.print_warning (_F("cross-file global variable reference to %s from",
lex_cast(*v->tok).c_str()), tok);
}
}
session.globals[i]->set_arity (arity, tok);
return session.globals[i];
}
for (unsigned i=0; i<session.library_files.size(); i++)
{
stapfile* f = session.library_files[i];
for (unsigned j=0; j<f->globals.size(); j++)
{
vardecl* g = f->globals[j];
if (g->name == name)
{
g->set_arity (arity, tok);
if (find (session.files.begin(), session.files.end(), f)
== session.files.end())
session.files.push_back (f);
return g;
}
}
}
return 0;
}
functiondecl*
symresolution_info::find_function (const string& name, unsigned arity, const token *tok)
{
if (session.functions.find(name) != session.functions.end())
{
functiondecl* fd = session.functions[name];
assert (fd->name == name);
if (fd->formal_args.size() == arity)
return fd;
throw SEMANTIC_ERROR(_F("arity mismatch found (function '%s' takes %zu args)",
name.c_str(), fd->formal_args.size()), tok, fd->tok);
}
for (unsigned i=0; i<session.library_files.size(); i++)
{
stapfile* f = session.library_files[i];
for (unsigned j=0; j<f->functions.size(); j++)
if (f->functions[j]->name == name)
{
if (f->functions[j]->formal_args.size() == arity)
{
if (0) cerr << _F(" function %s is defined from %s",
name.c_str(), f->name.c_str()) << endl;
if (find (session.files.begin(), session.files.end(), f)
== session.files.end())
session.files.push_back (f);
return f->functions[j];
}
throw SEMANTIC_ERROR(_F("arity mismatch found (function '%s' takes %zu args)",
name.c_str(), f->functions[j]->formal_args.size()),
tok, f->functions[j]->tok);
}
}
return 0;
}
set<string>
symresolution_info::collect_functions(void)
{
set<string> funcs;
for (map<string,functiondecl*>::const_iterator it = session.functions.begin();
it != session.functions.end(); ++it)
funcs.insert(it->first);
for (unsigned i=0; i<session.library_files.size(); i++)
{
stapfile* f = session.library_files[i];
for (unsigned j=0; j<f->functions.size(); j++)
funcs.insert(f->functions[j]->name);
}
return funcs;
}
void semantic_pass_opt1 (systemtap_session& s, bool& relaxed_p)
{
functioncall_traversing_visitor ftv;
for (unsigned i=0; i<s.probes.size(); i++)
{
s.probes[i]->body->visit (& ftv);
if (s.probes[i]->sole_location()->condition)
s.probes[i]->sole_location()->condition->visit (& ftv);
}
vector<functiondecl*> new_unused_functions;
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
functiondecl* fd = it->second;
if (ftv.seen.find(fd) == ftv.seen.end())
{
if (! fd->synthetic && s.is_user_file(fd->tok->location.file->name))
s.print_warning (_F("Eliding unused function '%s'", fd->name.c_str()), fd->tok);
new_unused_functions.push_back (fd);
relaxed_p = false;
}
}
for (unsigned i=0; i<new_unused_functions.size(); i++)
{
map<string,functiondecl*>::iterator where = s.functions.find (new_unused_functions[i]->name);
assert (where != s.functions.end());
s.functions.erase (where);
if (s.tapset_compile_coverage)
s.unused_functions.push_back (new_unused_functions[i]);
}
}
void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p, unsigned iterations)
{
varuse_collecting_visitor vut(s);
for (unsigned i=0; i<s.probes.size(); i++)
{
s.probes[i]->body->visit (& vut);
if (s.probes[i]->sole_location()->condition)
s.probes[i]->sole_location()->condition->visit (& vut);
}
for (unsigned i=0; i<s.probes.size(); i++)
for (unsigned j=0; j<s.probes[i]->locals.size(); )
{
vardecl* l = s.probes[i]->locals[j];
if (l->synthetic) { j++; continue; }
if (vut.read.find (l) == vut.read.end() &&
vut.written.find (l) == vut.written.end())
{
if (s.is_user_file(l->tok->location.file->name))
s.print_warning (_F("Eliding unused variable '%s'", l->name.c_str()), l->tok);
if (s.tapset_compile_coverage) {
s.probes[i]->unused_locals.push_back
(s.probes[i]->locals[j]);
}
s.probes[i]->locals.erase(s.probes[i]->locals.begin() + j);
relaxed_p = false;
}
else
{
if (vut.written.find (l) == vut.written.end())
if (iterations == 0 && ! s.suppress_warnings)
{
set<string> vars;
vector<vardecl*>::iterator it;
for (it = s.probes[i]->locals.begin(); it != s.probes[i]->locals.end(); it++)
vars.insert((*it)->name);
for (it = s.globals.begin(); it != s.globals.end(); it++)
vars.insert((*it)->name);
vars.erase(l->name);
string sugs = levenshtein_suggest(l->name, vars, 5); s.print_warning (_F("never-assigned local variable '%s'%s",
l->name.c_str(), (sugs.empty() ? "" :
(_(" (similar: ") + sugs + ")")).c_str()), l->tok);
}
j++;
}
}
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
functiondecl *fd = it->second;
for (unsigned j=0; j<fd->locals.size(); )
{
vardecl* l = fd->locals[j];
if (vut.read.find (l) == vut.read.end() &&
vut.written.find (l) == vut.written.end())
{
if (s.is_user_file(l->tok->location.file->name))
s.print_warning (_F("Eliding unused variable '%s'", l->name.c_str()), l->tok);
if (s.tapset_compile_coverage) {
fd->unused_locals.push_back (fd->locals[j]);
}
fd->locals.erase(fd->locals.begin() + j);
relaxed_p = false;
}
else
{
if (vut.written.find (l) == vut.written.end())
if (iterations == 0 && ! s.suppress_warnings)
{
set<string> vars;
vector<vardecl*>::iterator it;
for (it = fd->formal_args.begin() ;
it != fd->formal_args.end(); it++)
vars.insert((*it)->name);
for (it = fd->locals.begin(); it != fd->locals.end(); it++)
vars.insert((*it)->name);
for (it = s.globals.begin(); it != s.globals.end(); it++)
vars.insert((*it)->name);
vars.erase(l->name);
string sugs = levenshtein_suggest(l->name, vars, 5); s.print_warning (_F("never-assigned local variable '%s'%s",
l->name.c_str(), (sugs.empty() ? "" :
(_(" (similar: ") + sugs + ")")).c_str()), l->tok);
}
j++;
}
}
}
for (unsigned i=0; i<s.globals.size(); )
{
vardecl* l = s.globals[i];
if (vut.read.find (l) == vut.read.end() &&
vut.written.find (l) == vut.written.end())
{
if (s.is_user_file(l->tok->location.file->name))
s.print_warning (_F("Eliding unused variable '%s'", l->name.c_str()), l->tok);
if (s.tapset_compile_coverage) {
s.unused_globals.push_back(s.globals[i]);
}
s.globals.erase(s.globals.begin() + i);
relaxed_p = false;
}
else
{
if (vut.written.find (l) == vut.written.end() && ! l->init) if (iterations == 0 && ! s.suppress_warnings)
{
set<string> vars;
vector<vardecl*>::iterator it;
for (it = s.globals.begin(); it != s.globals.end(); it++)
if (l->name != (*it)->name)
vars.insert((*it)->name);
string sugs = levenshtein_suggest(l->name, vars, 5); s.print_warning (_F("never-assigned global variable '%s'%s",
l->name.c_str(), (sugs.empty() ? "" :
(_(" (similar: ") + sugs + ")")).c_str()), l->tok);
}
i++;
}
}
}
struct dead_assignment_remover: public update_visitor
{
systemtap_session& session;
bool& relaxed_p;
const varuse_collecting_visitor& vut;
dead_assignment_remover(systemtap_session& s, bool& r,
const varuse_collecting_visitor& v):
session(s), relaxed_p(r), vut(v) {}
void visit_assignment (assignment* e);
void visit_try_block (try_block *s);
};
struct assignment_symbol_fetcher
: public symbol_fetcher
{
assignment_symbol_fetcher (symbol *&sym): symbol_fetcher(sym)
{}
void visit_target_symbol (target_symbol* e)
{
sym = NULL;
}
void visit_atvar_op (atvar_op *e)
{
sym = NULL;
}
void visit_cast_op (cast_op* e)
{
sym = NULL;
}
void visit_autocast_op (autocast_op* e)
{
sym = NULL;
}
void throwone (const token* t)
{
if (t->type == tok_operator && t->content == ".")
XXX throw SEMANTIC_ERROR (_("Expecting lvalue expression, try -> instead"), t);
else
throw SEMANTIC_ERROR (_("Expecting lvalue expression"), t);
}
};
symbol *
get_assignment_symbol_within_expression (expression *e)
{
symbol *sym = NULL;
assignment_symbol_fetcher fetcher(sym);
e->visit (&fetcher);
return sym; }
void
dead_assignment_remover::visit_assignment (assignment* e)
{
replace (e->left);
replace (e->right);
symbol* left = get_assignment_symbol_within_expression (e->left);
if (left) {
vardecl* leftvar = left->referent;
if (vut.read.find(leftvar) == vut.read.end()) {
bool is_global = false;
vector<vardecl*>::iterator it;
for (it = session.globals.begin(); it != session.globals.end(); it++)
if (leftvar->name == (*it)->name)
{
is_global = true;
break;
}
varuse_collecting_visitor lvut(session);
e->left->visit (& lvut);
if (lvut.side_effect_free () && !is_global XXX && !leftvar->synthetic) {
if (session.is_user_file(e->left->tok->location.file->name))
session.print_warning(_F("Eliding assignment to '%s'", leftvar->name.c_str()), e->tok);
provide (e->right); relaxed_p = false;
return;
}
}
}
provide (e);
}
void
dead_assignment_remover::visit_try_block (try_block *s)
{
replace (s->try_block);
if (s->catch_error_var)
{
vardecl* errvar = s->catch_error_var->referent;
if (vut.read.find(errvar) == vut.read.end()) {
if (session.verbose>2)
clog << _F("Eliding unused error string catcher %s at %s",
errvar->name.c_str(), lex_cast(*s->tok).c_str()) << endl;
s->catch_error_var = 0;
}
}
replace (s->catch_block);
provide (s);
}
void semantic_pass_opt3 (systemtap_session& s, bool& relaxed_p)
{
varuse_collecting_visitor vut(s);
for (unsigned i=0; i<s.probes.size(); i++)
s.probes[i]->body->visit (& vut);
dead_assignment_remover dar (s, relaxed_p, vut);
for (unsigned i=0; i<s.probes.size(); i++)
dar.replace (s.probes[i]->body);
for (map<string,functiondecl*>::iterator it = s.functions.begin();
it != s.functions.end(); it++)
dar.replace (it->second->body);
XXX}
struct dead_stmtexpr_remover: public update_visitor
{
systemtap_session& session;
bool& relaxed_p;
set<vardecl*> focal_vars;
dead_stmtexpr_remover(systemtap_session& s, bool& r):
session(s), relaxed_p(r) {}
void visit_block (block *s);
void visit_try_block (try_block *s);
void visit_null_statement (null_statement *s);
void visit_if_statement (if_statement* s);
void visit_foreach_loop (foreach_loop *s);
void visit_for_loop (for_loop *s);
XXX
void visit_expr_statement (expr_statement *s);
};
void
dead_stmtexpr_remover::visit_null_statement (null_statement *s)
{
if (session.verbose>2)
clog << _("Eliding side-effect-free null statement ") << *s->tok << endl;
s = 0;
provide (s);
}
void
dead_stmtexpr_remover::visit_block (block *s)
{
vector<statement*> new_stmts;
for (unsigned i=0; i<s->statements.size(); i++ )
{
statement* new_stmt = require (s->statements[i], true);
if (new_stmt != 0)
{
block *b = dynamic_cast<block *>(new_stmt);
if (b)
{
if (session.verbose>2)
clog << _("Flattening nested block ") << *b->tok << endl;
new_stmts.insert(new_stmts.end(),
b->statements.begin(), b->statements.end());
relaxed_p = false;
}
else
new_stmts.push_back (new_stmt);
}
}
if (new_stmts.size() == 0)
{
if (session.verbose>2)
clog << _("Eliding side-effect-free empty block ") << *s->tok << endl;
s = 0;
}
else if (new_stmts.size() == 1)
{
if (session.verbose>2)
clog << _("Eliding side-effect-free singleton block ") << *s->tok << endl;
provide (new_stmts[0]);
return;
}
else
s->statements = new_stmts;
provide (s);
}
void
dead_stmtexpr_remover::visit_try_block (try_block *s)
{
replace (s->try_block, true);
replace (s->catch_block, true); if (s->try_block == 0)
{
if (session.verbose>2)
clog << _("Eliding empty try {} block ") << *s->tok << endl;
s = 0;
}
provide (s);
}
void
dead_stmtexpr_remover::visit_if_statement (if_statement *s)
{
replace (s->thenblock, true);
replace (s->elseblock, true);
if (s->thenblock == 0)
{
if (s->elseblock == 0)
{
varuse_collecting_visitor vct(session);
s->condition->visit(& vct);
if (vct.side_effect_free ())
{
if (session.verbose>2)
clog << _("Eliding side-effect-free if statement ")
<< *s->tok << endl;
s = 0; }
else
{
if (session.verbose>2)
clog << _("Creating simple evaluation from if statement ")
<< *s->tok << endl;
expr_statement *es = new expr_statement;
es->value = s->condition;
es->tok = es->value->tok;
provide (es);
return;
}
}
else
{
if (session.verbose>2)
clog << _("Inverting the condition of if statement ")
<< *s->tok << endl;
unary_expression *ue = new unary_expression;
ue->operand = s->condition;
ue->tok = ue->operand->tok;
ue->op = "!";
s->condition = ue;
s->thenblock = s->elseblock;
s->elseblock = 0;
}
}
provide (s);
}
void
dead_stmtexpr_remover::visit_foreach_loop (foreach_loop *s)
{
replace (s->block, true);
if (s->block == 0)
{
XXX XXX if(session.verbose > 2)
clog << _("Eliding side-effect-free foreach statement ") << *s->tok << endl;
s = 0; }
provide (s);
}
void
dead_stmtexpr_remover::visit_for_loop (for_loop *s)
{
replace (s->block, true);
if (s->block == 0)
{
varuse_collecting_visitor vct(session);
if (s->init) s->init->visit(& vct);
s->cond->visit(& vct);
if (s->incr) s->incr->visit(& vct);
if (vct.side_effect_free ())
{
if (session.verbose>2)
clog << _("Eliding side-effect-free for statement ") << *s->tok << endl;
s = 0; }
else
{
s->block = new null_statement(s->tok);
}
}
provide (s);
}
void
dead_stmtexpr_remover::visit_expr_statement (expr_statement *s)
{
varuse_collecting_visitor vut(session);
s->value->visit (& vut);
if (vut.side_effect_free_wrt (focal_vars))
{
if (session.is_user_file(s->value->tok->location.file->name))
session.print_warning("Eliding side-effect-free expression ", s->tok);
s = 0;
relaxed_p = false;
}
provide (s);
}
void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p)
{
dead_stmtexpr_remover duv (s, relaxed_p);
for (unsigned i=0; i<s.probes.size(); i++)
{
assert_no_interrupts();
derived_probe* p = s.probes[i];
duv.focal_vars.clear ();
duv.focal_vars.insert (s.globals.begin(),
s.globals.end());
duv.focal_vars.insert (p->locals.begin(),
p->locals.end());
duv.replace (p->body, true);
if (p->body == 0)
{
if (! s.timing) s.print_warning (_F("side-effect-free probe '%s'", p->name.c_str()), p->tok);
p->body = new null_statement(p->tok);
XXX }
}
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
assert_no_interrupts();
functiondecl* fn = it->second;
duv.focal_vars.clear ();
duv.focal_vars.insert (fn->locals.begin(),
fn->locals.end());
duv.focal_vars.insert (fn->formal_args.begin(),
fn->formal_args.end());
duv.focal_vars.insert (s.globals.begin(),
s.globals.end());
duv.replace (fn->body, true);
if (fn->body == 0)
{
s.print_warning (_F("side-effect-free function '%s'", fn->name.c_str()), fn->tok);
fn->body = new null_statement(fn->tok);
XXX XXX }
}
}
struct void_statement_reducer: public update_visitor
{
systemtap_session& session;
bool& relaxed_p;
set<vardecl*> focal_vars;
void_statement_reducer(systemtap_session& s, bool& r):
session(s), relaxed_p(r) {}
void visit_expr_statement (expr_statement* s);
void visit_if_statement (if_statement* s);
void visit_for_loop (for_loop* s);
void visit_foreach_loop (foreach_loop* s);
void visit_logical_or_expr (logical_or_expr* e);
void visit_logical_and_expr (logical_and_expr* e);
void visit_ternary_expression (ternary_expression* e);
void visit_binary_expression (binary_expression* e);
void visit_unary_expression (unary_expression* e);
void visit_regex_query (regex_query* e); XXX void visit_comparison (comparison* e);
void visit_concatenation (concatenation* e);
void visit_functioncall (functioncall* e);
void visit_print_format (print_format* e);
void visit_target_symbol (target_symbol* e);
void visit_atvar_op (atvar_op* e);
void visit_cast_op (cast_op* e);
void visit_autocast_op (autocast_op* e);
void visit_defined_op (defined_op* e);
void visit_array_in (array_in* e) { provide (e); }
void visit_arrayindex (arrayindex* e) { provide (e); }
void visit_stat_op (stat_op* e) { provide (e); }
void visit_hist_op (hist_op* e) { provide (e); }
void visit_return_statement (return_statement* s) { provide (s); }
void visit_delete_statement (delete_statement* s) { provide (s); }
void visit_pre_crement (pre_crement* e) { provide (e); }
void visit_post_crement (post_crement* e) { provide (e); }
void visit_assignment (assignment* e) { provide (e); }
private:
void reduce_target_symbol (target_symbol* e, expression* operand=NULL);
};
void
void_statement_reducer::visit_expr_statement (expr_statement* s)
{
replace (s->value, true);
if (s->value != 0)
provide(s);
}
void
void_statement_reducer::visit_if_statement (if_statement* s)
{
replace (s->thenblock);
replace (s->elseblock);
provide (s);
}
void
void_statement_reducer::visit_for_loop (for_loop* s)
{
replace (s->block);
provide (s);
}
void
void_statement_reducer::visit_foreach_loop (foreach_loop* s)
{
replace (s->block);
provide (s);
}
void
void_statement_reducer::visit_logical_or_expr (logical_or_expr* e)
{
if (session.verbose>2)
clog << _("Creating if statement from unused logical-or ")
<< *e->tok << endl;
if_statement *is = new if_statement;
is->tok = e->tok;
is->elseblock = 0;
unary_expression *ue = new unary_expression;
ue->operand = e->left;
ue->tok = e->tok;
ue->op = "!";
is->condition = ue;
expr_statement *es = new expr_statement;
es->value = e->right;
es->tok = es->value->tok;
is->thenblock = es;
is->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::visit_logical_and_expr (logical_and_expr* e)
{
if (session.verbose>2)
clog << _("Creating if statement from unused logical-and ")
<< *e->tok << endl;
if_statement *is = new if_statement;
is->tok = e->tok;
is->elseblock = 0;
is->condition = e->left;
expr_statement *es = new expr_statement;
es->value = e->right;
es->tok = es->value->tok;
is->thenblock = es;
is->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::visit_ternary_expression (ternary_expression* e)
{
if (session.verbose>2)
clog << _("Creating if statement from unused ternary expression ")
<< *e->tok << endl;
if_statement *is = new if_statement;
is->tok = e->tok;
is->condition = e->cond;
expr_statement *es = new expr_statement;
es->value = e->truevalue;
es->tok = es->value->tok;
is->thenblock = es;
es = new expr_statement;
es->value = e->falsevalue;
es->tok = es->value->tok;
is->elseblock = es;
is->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::visit_binary_expression (binary_expression* e)
{
if (session.verbose>2)
clog << _("Eliding unused binary ") << *e->tok << endl;
block *b = new block;
b->tok = e->tok;
expr_statement *es = new expr_statement;
es->value = e->left;
es->tok = es->value->tok;
b->statements.push_back(es);
es = new expr_statement;
es->value = e->right;
es->tok = es->value->tok;
b->statements.push_back(es);
b->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::visit_unary_expression (unary_expression* e)
{
if (session.verbose>2)
clog << _("Eliding unused unary ") << *e->tok << endl;
relaxed_p = false;
e->operand->visit(this);
}
void
void_statement_reducer::visit_regex_query (regex_query* e)
{
if (session.verbose>2)
clog << _("Eliding regex query ") << *e->tok << endl;
relaxed_p = false;
e->left->visit(this);
}
void
void_statement_reducer::visit_comparison (comparison* e)
{
visit_binary_expression(e);
}
void
void_statement_reducer::visit_concatenation (concatenation* e)
{
visit_binary_expression(e);
}
void
void_statement_reducer::visit_functioncall (functioncall* e)
{
if (!e->args.size())
{
provide (e);
return;
}
varuse_collecting_visitor vut(session);
vut.seen.insert (e->referent);
vut.current_function = e->referent;
e->referent->body->visit (& vut);
if (!vut.side_effect_free_wrt (focal_vars))
{
provide (e);
return;
}
if (session.verbose>2)
clog << _("Eliding side-effect-free function call ") << *e->tok << endl;
block *b = new block;
b->tok = e->tok;
for (unsigned i=0; i<e->args.size(); i++ )
{
expr_statement *es = new expr_statement;
es->value = e->args[i];
es->tok = es->value->tok;
b->statements.push_back(es);
}
b->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::visit_print_format (print_format* e)
{
if (e->print_to_stream || !e->args.size())
{
provide (e);
return;
}
if (session.verbose>2)
clog << _("Eliding unused print ") << *e->tok << endl;
block *b = new block;
b->tok = e->tok;
for (unsigned i=0; i<e->args.size(); i++ )
{
expr_statement *es = new expr_statement;
es->value = e->args[i];
es->tok = es->value->tok;
b->statements.push_back(es);
}
b->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::reduce_target_symbol (target_symbol* e,
expression* operand)
{
block *b = new block;
b->tok = e->tok;
if (operand)
{
expr_statement *es = new expr_statement;
es->value = operand;
es->tok = es->value->tok;
b->statements.push_back(es);
}
for (unsigned i=0; i<e->components.size(); i++ )
{
if (e->components[i].type != target_symbol::comp_expression_array_index)
continue;
expr_statement *es = new expr_statement;
es->value = e->components[i].expr_index;
es->tok = es->value->tok;
b->statements.push_back(es);
}
b->visit(this);
relaxed_p = false;
e = 0;
provide (e);
}
void
void_statement_reducer::visit_atvar_op (atvar_op* e)
{
if (session.verbose>2)
clog << _("Eliding unused target symbol ") << *e->tok << endl;
reduce_target_symbol (e);
}
void
void_statement_reducer::visit_target_symbol (target_symbol* e)
{
if (session.verbose>2)
clog << _("Eliding unused target symbol ") << *e->tok << endl;
reduce_target_symbol (e);
}
void
void_statement_reducer::visit_cast_op (cast_op* e)
{
if (session.verbose>2)
clog << _("Eliding unused typecast ") << *e->tok << endl;
reduce_target_symbol (e, e->operand);
}
void
void_statement_reducer::visit_autocast_op (autocast_op* e)
{
if (session.verbose>2)
clog << _("Eliding unused autocast ") << *e->tok << endl;
reduce_target_symbol (e, e->operand);
}
void
void_statement_reducer::visit_defined_op (defined_op* e)
{
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)
{
void_statement_reducer vuv (s, relaxed_p);
vuv.focal_vars.insert (s.globals.begin(), s.globals.end());
for (unsigned i=0; i<s.probes.size(); i++)
vuv.replace (s.probes[i]->body);
for (map<string,functiondecl*>::iterator it = s.functions.begin();
it != s.functions.end(); it++)
vuv.replace (it->second->body);
}
struct const_folder: public update_visitor
{
systemtap_session& session;
bool& relaxed_p;
const_folder(systemtap_session& s, bool& r):
session(s), relaxed_p(r), last_number(0), last_string(0) {}
literal_number* last_number;
literal_number* get_number(expression*& e);
void visit_literal_number (literal_number* e);
literal_string* last_string;
literal_string* get_string(expression*& e);
void visit_literal_string (literal_string* e);
void get_literal(expression*& e, literal_number*& n, literal_string*& s);
void visit_if_statement (if_statement* s);
void visit_for_loop (for_loop* s);
void visit_foreach_loop (foreach_loop* s);
void visit_binary_expression (binary_expression* e);
void visit_unary_expression (unary_expression* e);
void visit_logical_or_expr (logical_or_expr* e);
void visit_logical_and_expr (logical_and_expr* e);
XXX void visit_comparison (comparison* e);
void visit_concatenation (concatenation* e);
void visit_ternary_expression (ternary_expression* e);
void visit_defined_op (defined_op* e);
void visit_target_symbol (target_symbol* e);
};
void
const_folder::get_literal(expression*& e,
literal_number*& n,
literal_string*& s)
{
replace (e);
n = (e == last_number) ? last_number : NULL;
s = (e == last_string) ? last_string : NULL;
}
literal_number*
const_folder::get_number(expression*& e)
{
replace (e);
return (e == last_number) ? last_number : NULL;
}
void
const_folder::visit_literal_number (literal_number* e)
{
last_number = e;
provide (e);
}
literal_string*
const_folder::get_string(expression*& e)
{
replace (e);
return (e == last_string) ? last_string : NULL;
}
void
const_folder::visit_literal_string (literal_string* e)
{
last_string = e;
provide (e);
}
void
const_folder::visit_if_statement (if_statement* s)
{
literal_number* cond = get_number (s->condition);
if (!cond)
{
replace (s->thenblock);
replace (s->elseblock);
provide (s);
}
else
{
if (session.verbose>2)
clog << _F("Collapsing constant-%" PRIi64 " if-statement %s",
cond->value, lex_cast(*s->tok).c_str()) << endl;
relaxed_p = false;
statement* n = cond->value ? s->thenblock : s->elseblock;
if (n)
n->visit (this);
else
provide (new null_statement (s->tok));
}
}
void
const_folder::visit_for_loop (for_loop* s)
{
literal_number* cond = get_number (s->cond);
if (!cond || cond->value)
{
replace (s->init);
replace (s->incr);
replace (s->block);
provide (s);
}
else
{
if (session.verbose>2)
clog << _("Collapsing constantly-false for-loop ") << *s->tok << endl;
relaxed_p = false;
if (s->init)
s->init->visit (this);
else
provide (new null_statement (s->tok));
}
}
void
const_folder::visit_foreach_loop (foreach_loop* s)
{
literal_number* limit = get_number (s->limit);
if (!limit || limit->value > 0)
{
for (unsigned i = 0; i < s->indexes.size(); ++i)
replace (s->indexes[i]);
replace (s->base);
replace (s->value);
replace (s->block);
provide (s);
}
else
{
if (session.verbose>2)
clog << _("Collapsing constantly-limited foreach-loop ") << *s->tok << endl;
relaxed_p = false;
provide (new null_statement (s->tok));
}
}
void
const_folder::visit_binary_expression (binary_expression* e)
{
int64_t value;
literal_number* left = get_number (e->left);
literal_number* right = get_number (e->right);
if (right && !right->value && (e->op == "/" || e->op == "%"))
{
provide (e);
return;
}
if (left && right)
{
if (e->op == "+")
value = left->value + right->value;
else if (e->op == "-")
value = left->value - right->value;
else if (e->op == "*")
value = left->value * right->value;
else if (e->op == "&")
value = left->value & right->value;
else if (e->op == "|")
value = left->value | right->value;
else if (e->op == "^")
value = left->value ^ right->value;
else if (e->op == ">>")
value = left->value >> max(min(right->value, (int64_t)64), (int64_t)0);
else if (e->op == "<<")
value = left->value << max(min(right->value, (int64_t)64), (int64_t)0);
else if (e->op == "/")
value = (left->value == LLONG_MIN && right->value == -1) ? LLONG_MIN :
left->value / right->value;
else if (e->op == "%")
value = (left->value == LLONG_MIN && right->value == -1) ? 0 :
left->value % right->value;
else
throw SEMANTIC_ERROR (_("unsupported binary operator ") + e->op);
}
else if ((left && ((left->value == 0 && (e->op == "*" || e->op == "&" ||
e->op == ">>" || e->op == "<<" )) ||
(left->value ==-1 && (e->op == "|" || e->op == ">>"))))
||
(right && ((right->value == 0 && (e->op == "*" || e->op == "&")) ||
(right->value == 1 && (e->op == "%")) ||
(right->value ==-1 && (e->op == "%" || e->op == "|")))))
{
expression* other = left ? e->right : e->left;
varuse_collecting_visitor vu(session);
other->visit(&vu);
if (!vu.side_effect_free())
{
provide (e);
return;
}
if (left)
value = left->value;
else if (e->op == "%")
value = 0;
else
value = right->value;
}
else if ((left && ((left->value == 0 && (e->op == "+" || e->op == "|" ||
e->op == "^")) ||
(left->value == 1 && (e->op == "*")) ||
(left->value ==-1 && (e->op == "&"))))
||
(right && ((right->value == 0 && (e->op == "+" || e->op == "-" ||
e->op == "|" || e->op == "^")) ||
(right->value == 1 && (e->op == "*" || e->op == "/")) ||
(right->value ==-1 && (e->op == "&")) ||
(right->value <= 0 && (e->op == ">>" || e->op == "<<")))))
{
if (session.verbose>2)
clog << _("Collapsing constant-identity binary operator ") << *e->tok << endl;
relaxed_p = false;
provide (left ? e->right : e->left);
return;
}
else
{
provide (e);
return;
}
if (session.verbose>2)
clog << _F("Collapsing constant-%" PRIi64 " binary operator %s",
value, lex_cast(*e->tok).c_str()) << endl;
relaxed_p = false;
literal_number* n = new literal_number(value);
n->tok = e->tok;
n->visit (this);
}
void
const_folder::visit_unary_expression (unary_expression* e)
{
literal_number* operand = get_number (e->operand);
if (!operand)
provide (e);
else
{
if (session.verbose>2)
clog << _("Collapsing constant unary ") << *e->tok << endl;
relaxed_p = false;
literal_number* n = new literal_number (*operand);
n->tok = e->tok;
if (e->op == "+")
; else if (e->op == "-")
n->value = -n->value;
else if (e->op == "!")
n->value = !n->value;
else if (e->op == "~")
n->value = ~n->value;
else
throw SEMANTIC_ERROR (_("unsupported unary operator ") + e->op);
n->visit (this);
}
}
void
const_folder::visit_logical_or_expr (logical_or_expr* e)
{
int64_t value;
literal_number* left = get_number (e->left);
literal_number* right = get_number (e->right);
if (left && right)
value = left->value || right->value;
else if ((left && left->value) || (right && right->value))
{
if (right)
{
varuse_collecting_visitor vu(session);
e->left->visit(&vu);
if (!vu.side_effect_free())
{
provide (e);
return;
}
}
value = 1;
}
else
{
provide (e);
return;
}
if (session.verbose>2)
clog << _("Collapsing constant logical-OR ") << *e->tok << endl;
relaxed_p = false;
literal_number* n = new literal_number(value);
n->tok = e->tok;
n->visit (this);
}
void
const_folder::visit_logical_and_expr (logical_and_expr* e)
{
int64_t value;
literal_number* left = get_number (e->left);
literal_number* right = get_number (e->right);
if (left && right)
value = left->value && right->value;
else if ((left && !left->value) || (right && !right->value))
{
if (right)
{
varuse_collecting_visitor vu(session);
e->left->visit(&vu);
if (!vu.side_effect_free())
{
provide (e);
return;
}
}
value = 0;
}
else
{
provide (e);
return;
}
if (session.verbose>2)
clog << _("Collapsing constant logical-AND ") << *e->tok << endl;
relaxed_p = false;
literal_number* n = new literal_number(value);
n->tok = e->tok;
n->visit (this);
}
void
const_folder::visit_comparison (comparison* e)
{
int comp;
literal_number *left_num, *right_num;
literal_string *left_str, *right_str;
get_literal(e->left, left_num, left_str);
get_literal(e->right, right_num, right_str);
if (left_str && right_str)
comp = left_str->value.compare(right_str->value);
else if (left_num && right_num)
comp = left_num->value < right_num->value ? -1 :
left_num->value > right_num->value ? 1 : 0;
else if ((left_num && ((left_num->value == LLONG_MIN &&
(e->op == "<=" || e->op == ">")) ||
(left_num->value == LLONG_MAX &&
(e->op == ">=" || e->op == "<"))))
||
(right_num && ((right_num->value == LLONG_MIN &&
(e->op == ">=" || e->op == "<")) ||
(right_num->value == LLONG_MAX &&
(e->op == "<=" || e->op == ">")))))
{
expression* other = left_num ? e->right : e->left;
varuse_collecting_visitor vu(session);
other->visit(&vu);
if (!vu.side_effect_free())
provide (e);
else
{
if (session.verbose>2)
clog << _("Collapsing constant-boundary comparison ") << *e->tok << endl;
relaxed_p = false;
literal_number* n = new literal_number( e->op.length() == 2 );
n->tok = e->tok;
n->visit (this);
}
return;
}
else
{
provide (e);
return;
}
if (session.verbose>2)
clog << _("Collapsing constant comparison ") << *e->tok << endl;
relaxed_p = false;
int64_t value;
if (e->op == "==")
value = comp == 0;
else if (e->op == "!=")
value = comp != 0;
else if (e->op == "<")
value = comp < 0;
else if (e->op == ">")
value = comp > 0;
else if (e->op == "<=")
value = comp <= 0;
else if (e->op == ">=")
value = comp >= 0;
else
throw SEMANTIC_ERROR (_("unsupported comparison operator ") + e->op);
literal_number* n = new literal_number(value);
n->tok = e->tok;
n->visit (this);
}
void
const_folder::visit_concatenation (concatenation* e)
{
literal_string* left = get_string (e->left);
literal_string* right = get_string (e->right);
if (left && right)
{
if (session.verbose>2)
clog << _("Collapsing constant concatenation ") << *e->tok << endl;
relaxed_p = false;
literal_string* n = new literal_string (*left);
n->tok = e->tok;
n->value.append(right->value);
n->visit (this);
}
else if ((left && left->value.empty()) ||
(right && right->value.empty()))
{
if (session.verbose>2)
clog << _("Collapsing identity concatenation ") << *e->tok << endl;
relaxed_p = false;
provide(left ? e->right : e->left);
}
else
provide (e);
}
void
const_folder::visit_ternary_expression (ternary_expression* e)
{
literal_number* cond = get_number (e->cond);
if (!cond)
{
replace (e->truevalue);
replace (e->falsevalue);
provide (e);
}
else
{
if (session.verbose>2)
clog << _F("Collapsing constant-%" PRIi64 " ternary %s",
cond->value, lex_cast(*e->tok).c_str()) << endl;
relaxed_p = false;
expression* n = cond->value ? e->truevalue : e->falsevalue;
n->visit (this);
}
}
void
const_folder::visit_defined_op (defined_op* e)
{
if (session.verbose>2)
clog << _("Collapsing untouched @defined check ") << *e->tok << endl;
relaxed_p = false;
literal_number* n = new literal_number (0);
n->tok = e->tok;
n->visit (this);
}
void
const_folder::visit_target_symbol (target_symbol* e)
{
if (session.skip_badvars)
{
XXX literal_number* ln_zero = new literal_number (0);
ln_zero->tok = e->tok;
provide (ln_zero);
session.print_warning (_("Bad $context variable being substituted with literal 0"),
e->tok);
relaxed_p = false;
}
else
update_visitor::visit_target_symbol (e);
}
static void semantic_pass_const_fold (systemtap_session& s, bool& relaxed_p)
{
const_folder cf (s, relaxed_p);
for (unsigned i=0; i<s.probes.size(); i++)
cf.replace (s.probes[i]->body);
for (map<string,functiondecl*>::iterator it = s.functions.begin();
it != s.functions.end(); it++)
cf.replace (it->second->body);
}
struct dead_control_remover: public traversing_visitor
{
systemtap_session& session;
bool& relaxed_p;
statement* control;
dead_control_remover(systemtap_session& s, bool& r):
session(s), relaxed_p(r), control(NULL) {}
void visit_block (block *b);
void visit_return_statement (return_statement* s) { control = s; }
void visit_next_statement (next_statement* s) { control = s; }
void visit_break_statement (break_statement* s) { control = s; }
void visit_continue_statement (continue_statement* s) { control = s; }
};
void dead_control_remover::visit_block (block* b)
{
vector<statement*>& vs = b->statements;
if (vs.size() == 0) return;
for (size_t i = 0; i < vs.size() - 1; ++i)
{
vs[i]->visit (this);
if (vs[i] == control)
{
session.print_warning(_("statement will never be reached"),
vs[i + 1]->tok);
vs.erase(vs.begin() + i + 1, vs.end());
relaxed_p = false;
break;
}
}
}
static void semantic_pass_dead_control (systemtap_session& s, bool& relaxed_p)
{
dead_control_remover dc (s, relaxed_p);
for (unsigned i=0; i<s.probes.size(); i++)
s.probes[i]->body->visit(&dc);
for (map<string,functiondecl*>::iterator it = s.functions.begin();
it != s.functions.end(); it++)
it->second->body->visit(&dc);
}
struct duplicate_function_remover: public functioncall_traversing_visitor
{
systemtap_session& s;
map<functiondecl*, functiondecl*>& duplicate_function_map;
duplicate_function_remover(systemtap_session& sess,
map<functiondecl*, functiondecl*>&dfm):
s(sess), duplicate_function_map(dfm) {};
void visit_functioncall (functioncall* e);
};
void
duplicate_function_remover::visit_functioncall (functioncall *e)
{
functioncall_traversing_visitor::visit_functioncall (e);
if (duplicate_function_map.count(e->referent) != 0)
{
if (s.verbose>2)
clog << _F("Changing %s reference to %s reference\n",
e->referent->name.c_str(), duplicate_function_map[e->referent]->name.c_str());
e->tok = duplicate_function_map[e->referent]->tok;
e->function = duplicate_function_map[e->referent]->name;
e->referent = duplicate_function_map[e->referent];
}
}
static string
get_functionsig (functiondecl* f)
{
ostringstream s;
f->printsig(s);
f->body->print(s);
string str = s.str().erase(0, f->name.size() + 1);
return str;
}
void semantic_pass_opt6 (systemtap_session& s, bool& relaxed_p)
{
map<string, functiondecl*> functionsig_map;
map<functiondecl*, functiondecl*> duplicate_function_map;
vector<functiondecl*> newly_zapped_functions;
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
functiondecl *fd = it->second;
string functionsig = get_functionsig(fd);
if (functionsig_map.count(functionsig) == 0)
{
functionsig_map[functionsig] = fd;
}
else
{
duplicate_function_map[fd] = functionsig_map[functionsig];
newly_zapped_functions.push_back (fd);
relaxed_p = false;
}
}
for (unsigned i=0; i<newly_zapped_functions.size(); i++)
{
map<string,functiondecl*>::iterator where = s.functions.find (newly_zapped_functions[i]->name);
assert (where != s.functions.end());
s.functions.erase (where);
}
if (duplicate_function_map.size() != 0)
{
duplicate_function_remover dfr (s, duplicate_function_map);
for (unsigned i=0; i < s.probes.size(); i++)
s.probes[i]->body->visit(&dfr);
}
}
static int
semantic_pass_optimize1 (systemtap_session& s)
{
int rc = 0;
save_and_restore<bool> suppress_warnings(& s.suppress_warnings);
bool relaxed_p = false;
unsigned iterations = 0;
while (! relaxed_p)
{
assert_no_interrupts();
relaxed_p = true;
if(s.verbose > 2)
s.suppress_warnings = false;
else if (iterations > 0)
s.suppress_warnings = true;
if (!s.unoptimized)
{
semantic_pass_opt1 (s, relaxed_p);
semantic_pass_opt2 (s, relaxed_p, iterations); semantic_pass_opt3 (s, relaxed_p);
semantic_pass_opt4 (s, relaxed_p);
semantic_pass_opt5 (s, relaxed_p);
}
semantic_pass_const_fold (s, relaxed_p);
if (!s.unoptimized)
semantic_pass_dead_control (s, relaxed_p);
iterations ++;
}
return rc;
}
static int
semantic_pass_optimize2 (systemtap_session& s)
{
int rc = 0;
save_and_restore<bool> suppress_warnings(& s.suppress_warnings);
bool relaxed_p = false;
unsigned iterations = 0;
while (! relaxed_p)
{
assert_no_interrupts();
relaxed_p = true;
if(s.verbose > 2)
s.suppress_warnings = false;
else if (iterations > 0)
s.suppress_warnings = true;
if (!s.unoptimized)
semantic_pass_opt6 (s, relaxed_p);
iterations++;
}
return rc;
}
struct autocast_expanding_visitor: public var_expanding_visitor
{
typeresolution_info& ti;
autocast_expanding_visitor (typeresolution_info& ti): ti(ti) {}
void resolve_functioncall (functioncall* fc)
{
systemtap_session& s = ti.session;
size_t nfiles = s.files.size();
symresolution_info sym (s);
sym.current_function = ti.current_function;
sym.current_probe = ti.current_probe;
fc->visit (&sym);
if (fc->referent)
{
functiondecl* fd = fc->referent;
sym.current_function = fd;
sym.current_probe = 0;
fd->body->visit (&sym);
}
while (nfiles < s.files.size())
{
stapfile* dome = s.files[nfiles++];
for (size_t i = 0; i < dome->functions.size(); ++i)
{
functiondecl* fd = dome->functions[i];
sym.current_function = fd;
sym.current_probe = 0;
fd->body->visit (&sym);
}
}
functioncall_traversing_visitor ftv;
fc->visit (&ftv);
for (set<functiondecl*>::iterator it = ftv.seen.begin();
it != ftv.seen.end(); ++it)
{
functiondecl* fd = *it;
pair<map<string,functiondecl*>::iterator,bool> inserted =
s.functions.insert (make_pair (fd->name, fd));
if (!inserted.second && inserted.first->second != fd)
throw SEMANTIC_ERROR
(_F("resolved function '%s' conflicts with an existing function",
fd->name.c_str()), fc->tok);
}
}
void visit_autocast_op (autocast_op* e)
{
const bool lvalue = is_active_lvalue (e);
const exp_type_ptr& details = e->operand->type_details;
if (details && !e->saved_conversion_error)
{
functioncall* fc = details->expand (e, lvalue);
if (fc)
{
ti.num_newly_resolved++;
resolve_functioncall (fc);
if (lvalue)
provide_lvalue_call (fc);
fc->visit (this);
return;
}
}
var_expanding_visitor::visit_autocast_op (e);
}
};
static int
semantic_pass_types (systemtap_session& s)
{
int rc = 0;
unsigned iterations = 0;
typeresolution_info ti (s);
for (unsigned j=0; j<s.globals.size(); j++)
{
vardecl* gd = s.globals[j];
if (!gd->type_details)
gd->type_details = ti.null_type;
}
ti.assert_resolvability = false;
XXX while (1)
{
assert_no_interrupts();
iterations ++;
ti.num_newly_resolved = 0;
ti.num_still_unresolved = 0;
ti.num_available_autocasts = 0;
for (map<string,functiondecl*>::iterator it = s.functions.begin();
it != s.functions.end(); it++)
{
assert_no_interrupts();
functiondecl* fd = it->second;
ti.current_probe = 0;
ti.current_function = fd;
ti.t = pe_unknown;
fd->body->visit (& ti);
for (unsigned i=0; i < fd->locals.size(); ++i)
ti.check_local (fd->locals[i]);
if (ti.num_available_autocasts > 0)
{
autocast_expanding_visitor aev (ti);
aev.replace (fd->body);
ti.num_available_autocasts = 0;
}
}
for (unsigned j=0; j<s.probes.size(); j++)
{
assert_no_interrupts();
derived_probe* pn = s.probes[j];
ti.current_function = 0;
ti.current_probe = pn;
ti.t = pe_unknown;
pn->body->visit (& ti);
for (unsigned i=0; i < pn->locals.size(); ++i)
ti.check_local (pn->locals[i]);
if (ti.num_available_autocasts > 0)
{
autocast_expanding_visitor aev (ti);
aev.replace (pn->body);
ti.num_available_autocasts = 0;
}
probe_point* pp = pn->sole_location();
if (pp->condition)
{
ti.current_function = 0;
ti.current_probe = 0;
ti.t = pe_long; pp->condition->visit (& ti);
}
}
for (unsigned j=0; j<s.globals.size(); j++)
{
vardecl* gd = s.globals[j];
if (gd->type == pe_unknown)
ti.unresolved (gd->tok);
if(gd->arity == 0 && gd->wrap == true)
{
throw SEMANTIC_ERROR (_("wrapping not supported for scalars"), gd->tok);
}
}
if (ti.num_newly_resolved == 0) {
if (ti.num_still_unresolved == 0)
break; else if (! ti.assert_resolvability)
{
ti.assert_resolvability = true; if (s.verbose > 0)
ti.mismatch_complexity = 0; }
else
{ rc ++;
break;
}
}
else
ti.mismatch_complexity = 0; }
return rc + s.num_errors();
}
struct exp_type_null : public exp_type_details
{
uintptr_t id () const { return 0; }
bool expandable() const { return false; }
functioncall *expand(autocast_op*, bool) { return NULL; }
};
typeresolution_info::typeresolution_info (systemtap_session& s):
session(s), num_newly_resolved(0), num_still_unresolved(0),
assert_resolvability(false), mismatch_complexity(0),
current_function(0), current_probe(0), t(pe_unknown),
null_type(new exp_type_null())
{
}
void
typeresolution_info::visit_literal_number (literal_number* e)
{
assert (e->type == pe_long);
if ((t == e->type) || (t == pe_unknown))
return;
mismatch (e->tok, t, e->type);
}
void
typeresolution_info::visit_literal_string (literal_string* e)
{
assert (e->type == pe_string);
if ((t == e->type) || (t == pe_unknown))
return;
mismatch (e->tok, t, e->type);
}
void
typeresolution_info::visit_logical_or_expr (logical_or_expr *e)
{
visit_binary_expression (e);
}
void
typeresolution_info::visit_logical_and_expr (logical_and_expr *e)
{
visit_binary_expression (e);
}
void
typeresolution_info::visit_regex_query (regex_query *e)
{
if (t == pe_stats || t == pe_string)
invalid (e->tok, t);
t = pe_string;
e->left->visit (this);
t = pe_string;
e->right->visit (this);
if (e->type == pe_unknown)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_comparison (comparison *e)
{
if (t == pe_stats || t == pe_string)
invalid (e->tok, t);
t = (e->right->type != pe_unknown) ? e->right->type : pe_unknown;
e->left->visit (this);
t = (e->left->type != pe_unknown) ? e->left->type : pe_unknown;
e->right->visit (this);
if (e->left->type != pe_unknown &&
e->right->type != pe_unknown &&
e->left->type != e->right->type)
mismatch (e);
if (e->type == pe_unknown)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_concatenation (concatenation *e)
{
if (t != pe_unknown && t != pe_string)
invalid (e->tok, t);
t = pe_string;
e->left->visit (this);
t = pe_string;
e->right->visit (this);
if (e->type == pe_unknown)
{
e->type = pe_string;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_assignment (assignment *e)
{
if (t == pe_stats)
invalid (e->tok, t);
if (e->op == "<<<") {
if (t == pe_string)
invalid (e->tok, t);
t = pe_stats;
e->left->visit (this);
t = pe_long;
e->right->visit (this);
if (e->type == pe_unknown ||
e->type == pe_stats)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
}
else if (e->left->type == pe_stats)
invalid (e->left->tok, e->left->type);
else if (e->right->type == pe_stats)
invalid (e->right->tok, e->right->type);
else if (e->op == "+=" || e->op == "-=" ||
e->op == "*=" ||
e->op == "/=" ||
e->op == "%=" ||
e->op == "&=" ||
e->op == "^=" ||
e->op == "|=" ||
e->op == "<<=" ||
e->op == ">>=" ||
false)
{
visit_binary_expression (e);
}
else if (e->op == ".=" || false)
{
if (t == pe_long || t == pe_stats)
invalid (e->tok, t);
t = pe_string;
e->left->visit (this);
t = pe_string;
e->right->visit (this);
if (e->type == pe_unknown)
{
e->type = pe_string;
resolved (e->tok, e->type);
}
}
else if (e->op == "=") {
exp_type sub_type = t;
if (sub_type == pe_unknown && e->type != pe_unknown)
sub_type = e->type;
t = (sub_type != pe_unknown) ? sub_type :
(e->right->type != pe_unknown) ? e->right->type :
pe_unknown;
e->left->visit (this);
t = (sub_type != pe_unknown) ? sub_type :
(e->left->type != pe_unknown) ? e->left->type :
pe_unknown;
e->right->visit (this);
if ((sub_type != pe_unknown) && (e->type == pe_unknown))
{
e->type = sub_type;
resolved (e->tok, e->type);
}
if ((sub_type == pe_unknown) && (e->left->type != pe_unknown))
{
e->type = e->left->type;
resolved (e->tok, e->type);
}
if (e->left->type != pe_unknown &&
e->right->type != pe_unknown &&
e->left->type != e->right->type)
mismatch (e);
if (e->type == e->right->type &&
e->right->type_details && !e->type_details)
resolved_details(e->right->type_details, e->type_details);
if (e->type == e->left->type && e->type_details)
{
if (e->left->type_details &&
*e->left->type_details != *e->type_details &&
*e->left->type_details != *null_type)
resolved_details(null_type, e->left->type_details);
else if (!e->left->type_details)
resolved_details(e->type_details, e->left->type_details);
}
}
else
throw SEMANTIC_ERROR (_("unsupported assignment operator ") + e->op);
}
void
typeresolution_info::visit_embedded_expr (embedded_expr *e)
{
if (e->type == pe_unknown)
{
if (e->code.find ("/* string */") != string::npos)
e->type = pe_string;
else e->type = pe_long;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_binary_expression (binary_expression* e)
{
if (t == pe_stats || t == pe_string)
invalid (e->tok, t);
t = pe_long;
e->left->visit (this);
t = pe_long;
e->right->visit (this);
if (e->left->type != pe_unknown &&
e->right->type != pe_unknown &&
e->left->type != e->right->type)
mismatch (e);
if (e->type == pe_unknown)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_pre_crement (pre_crement *e)
{
visit_unary_expression (e);
}
void
typeresolution_info::visit_post_crement (post_crement *e)
{
visit_unary_expression (e);
}
void
typeresolution_info::visit_unary_expression (unary_expression* e)
{
if (t == pe_stats || t == pe_string)
invalid (e->tok, t);
t = pe_long;
e->operand->visit (this);
if (e->type == pe_unknown)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_ternary_expression (ternary_expression* e)
{
exp_type sub_type = t;
t = pe_long;
e->cond->visit (this);
if (sub_type == pe_unknown && e->type != pe_unknown)
sub_type = e->type;
t = sub_type;
e->truevalue->visit (this);
t = sub_type;
e->falsevalue->visit (this);
if ((sub_type == pe_unknown) && (e->type != pe_unknown))
; else if ((sub_type != pe_unknown) && (e->type == pe_unknown))
{
e->type = sub_type;
resolved (e->tok, e->type);
}
else if ((sub_type == pe_unknown) && (e->truevalue->type != pe_unknown))
{
e->type = e->truevalue->type;
resolved (e->tok, e->type);
}
else if ((sub_type == pe_unknown) && (e->falsevalue->type != pe_unknown))
{
e->type = e->falsevalue->type;
resolved (e->tok, e->type);
}
else if (e->type != sub_type)
mismatch (e->tok, sub_type, e->type);
if (!e->type_details &&
e->type == e->truevalue->type && e->type == e->falsevalue->type &&
e->truevalue->type_details && e->falsevalue->type_details &&
*e->truevalue->type_details == *e->falsevalue->type_details)
resolved_details(e->truevalue->type_details, e->type_details);
}
template <class Referrer, class Referent>
void resolve_2types (Referrer* referrer, Referent* referent,
typeresolution_info* r, exp_type t, bool accept_unknown = false)
{
exp_type& re_type = referrer->type;
const token* re_tok = referrer->tok;
exp_type& te_type = referent->type;
if (t != pe_unknown && re_type == t && re_type == te_type)
; else if (t == pe_unknown && re_type != pe_unknown && re_type == te_type)
; else if (re_type != pe_unknown && te_type != pe_unknown && re_type != te_type)
r->mismatch (re_tok, re_type, referent); else if (re_type != pe_unknown && t != pe_unknown && re_type != t)
r->mismatch (re_tok, t, referent); else if (te_type != pe_unknown && t != pe_unknown && te_type != t)
r->mismatch (re_tok, t, referent); else if (re_type == pe_unknown && t != pe_unknown)
{
re_type = t;
r->resolved (re_tok, re_type);
}
else if (re_type == pe_unknown && te_type != pe_unknown)
{
re_type = te_type;
r->resolved (re_tok, re_type);
}
else if (re_type != pe_unknown && te_type == pe_unknown)
{
te_type = re_type;
r->resolved (re_tok, re_type, referent);
}
else if (! accept_unknown)
r->unresolved (re_tok);
}
void
typeresolution_info::visit_symbol (symbol* e)
{
if (e->referent == 0)
throw SEMANTIC_ERROR (_F("internal error: unresolved symbol '%s'",
e->name.c_str()), e->tok);
resolve_2types (e, e->referent, this, t);
if (e->type == e->referent->type)
{
if (e->type_details && e->referent->type_details &&
*e->type_details != *e->referent->type_details)
{
resolved_details(null_type, e->type_details);
resolved_details(null_type, e->referent->type_details);
}
else if (e->type_details && !e->referent->type_details)
resolved_details(e->type_details, e->referent->type_details);
else if (!e->type_details && e->referent->type_details)
resolved_details(e->referent->type_details, e->type_details);
}
}
void
typeresolution_info::visit_target_symbol (target_symbol* e)
{
if (session.verbose > 2)
{
clog << _("Resolution problem with ");
if (current_function)
{
clog << "function " << current_function->name << endl;
current_function->body->print (clog);
clog << endl;
}
else if (current_probe)
{
clog << "probe " << *current_probe->sole_location() << endl;
current_probe->body->print (clog);
clog << endl;
}
else
clog << _("other") << endl;
}
if (e->saved_conversion_error)
throw (* (e->saved_conversion_error));
else
throw SEMANTIC_ERROR(_("unresolved target-symbol expression"), e->tok);
}
void
typeresolution_info::visit_atvar_op (atvar_op* e)
{
if (session.verbose > 2)
{
clog << _("Resolution problem with ");
if (current_function)
{
clog << "function " << current_function->name << endl;
current_function->body->print (clog);
clog << endl;
}
else if (current_probe)
{
clog << "probe " << *current_probe->sole_location() << endl;
current_probe->body->print (clog);
clog << endl;
}
else
clog << _("other") << endl;
}
if (e->saved_conversion_error)
throw (* (e->saved_conversion_error));
else
throw SEMANTIC_ERROR(_("unresolved @var() expression"), e->tok);
}
void
typeresolution_info::visit_defined_op (defined_op* e)
{
throw SEMANTIC_ERROR(_("unexpected @defined"), e->tok);
}
void
typeresolution_info::visit_entry_op (entry_op* e)
{
throw SEMANTIC_ERROR(_("@entry is only valid in .return probes"), e->tok);
}
void
typeresolution_info::visit_cast_op (cast_op* e)
{
if (e->saved_conversion_error)
throw (* (e->saved_conversion_error));
else
throw SEMANTIC_ERROR(_F("type definition '%s' not found in '%s'",
e->type_name.c_str(), e->module.c_str()), e->tok);
}
void
typeresolution_info::visit_autocast_op (autocast_op* e)
{
if (assert_resolvability && e->saved_conversion_error)
throw (* (e->saved_conversion_error));
else if (assert_resolvability)
throw SEMANTIC_ERROR(_("unknown type in dereference"), e->tok);
t = pe_long;
e->operand->visit (this);
num_still_unresolved++;
if (e->operand->type_details &&
e->operand->type_details->expandable())
num_available_autocasts++;
}
void
typeresolution_info::visit_perf_op (perf_op* e)
{
if (t == pe_stats || t == pe_string)
invalid (e->tok, t);
e->type = pe_long;
t = pe_string;
e->operand->visit (this);
}
void
typeresolution_info::visit_arrayindex (arrayindex* e)
{
symbol *array = NULL;
hist_op *hist = NULL;
classify_indexable(e->base, array, hist);
if (hist)
{
if (e->indexes.size() != 1)
unresolved (e->tok);
t = pe_long;
e->indexes[0]->visit (this);
if (e->indexes[0]->type != pe_long)
unresolved (e->tok);
hist->visit (this);
if (e->type != pe_long)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
return;
}
assert (array);
assert (array->referent != 0);
resolve_2types (e, array->referent, this, t);
if (e->indexes.size() != array->referent->index_types.size())
unresolved (e->tok); else for (unsigned i=0; i<e->indexes.size(); i++)
{
if (e->indexes[i])
{
expression* ee = e->indexes[i];
exp_type& ft = array->referent->index_types [i];
t = ft;
ee->visit (this);
exp_type at = ee->type;
if ((at == pe_string || at == pe_long) && ft == pe_unknown)
{
ft = at;
resolved (ee->tok, ft, array->referent, i);
}
if (at == pe_stats)
invalid (ee->tok, at);
if (ft == pe_stats)
invalid (ee->tok, ft);
if (at != pe_unknown && ft != pe_unknown && ft != at)
mismatch (ee->tok, ee->type, array->referent, i);
if (at == pe_unknown)
unresolved (ee->tok);
}
}
}
void
typeresolution_info::visit_functioncall (functioncall* e)
{
if (e->referent == 0)
throw SEMANTIC_ERROR (_F("internal error: unresolved function call to '%s'",
e->function.c_str()), e->tok);
resolve_2types (e, e->referent, this, t, true);
if (e->type == pe_stats)
invalid (e->tok, e->type);
const exp_type_ptr& func_type = e->referent->type_details;
if (func_type && e->referent->type == e->type
&& (!e->type_details || *func_type != *e->type_details))
resolved_details(e->referent->type_details, e->type_details);
if (e->args.size() != e->referent->formal_args.size())
unresolved (e->tok); else for (unsigned i=0; i<e->args.size(); i++)
{
expression* ee = e->args[i];
exp_type& ft = e->referent->formal_args[i]->type;
const token* fe_tok = e->referent->formal_args[i]->tok;
t = ft;
ee->visit (this);
exp_type at = ee->type;
if (((at == pe_string) || (at == pe_long)) && ft == pe_unknown)
{
ft = at;
resolved (ee->tok, ft, e->referent->formal_args[i], i);
}
if (at == pe_stats)
invalid (ee->tok, at);
if (ft == pe_stats)
invalid (fe_tok, ft);
if (at != pe_unknown && ft != pe_unknown && ft != at)
mismatch (ee->tok, ee->type, e->referent->formal_args[i], i);
if (at == pe_unknown)
unresolved (ee->tok);
}
}
void
typeresolution_info::visit_block (block* e)
{
for (unsigned i=0; i<e->statements.size(); i++)
{
t = pe_unknown;
e->statements[i]->visit (this);
}
}
void
typeresolution_info::visit_try_block (try_block* e)
{
if (e->try_block)
e->try_block->visit (this);
if (e->catch_error_var)
{
t = pe_string;
e->catch_error_var->visit (this);
}
if (e->catch_block)
e->catch_block->visit (this);
}
void
typeresolution_info::visit_embeddedcode (embeddedcode* s)
{
if (! session.need_uprobes
&& s->code.find("/* pragma:uprobes */") != string::npos)
{
if (session.verbose > 2)
clog << _("Activating uprobes support because /* pragma:uprobes */ seen.") << endl;
session.need_uprobes = true;
}
if (! session.need_tagged_dfa
&& s->code.find("/* pragma:tagged_dfa */") != string::npos)
{
throw SEMANTIC_ERROR (_("Tagged DFA support is not yet available"), s->tok);
}
}
void
typeresolution_info::visit_if_statement (if_statement* e)
{
t = pe_long;
e->condition->visit (this);
t = pe_unknown;
e->thenblock->visit (this);
if (e->elseblock)
{
t = pe_unknown;
e->elseblock->visit (this);
}
}
void
typeresolution_info::visit_for_loop (for_loop* e)
{
t = pe_unknown;
if (e->init) e->init->visit (this);
t = pe_long;
e->cond->visit (this);
t = pe_unknown;
if (e->incr) e->incr->visit (this);
t = pe_unknown;
e->block->visit (this);
}
void
typeresolution_info::visit_foreach_loop (foreach_loop* e)
{
exp_type wanted_value = pe_unknown;
symbol *array = NULL;
hist_op *hist = NULL;
classify_indexable(e->base, array, hist);
if (hist)
{
if (e->indexes.size() != 1)
unresolved (e->tok);
t = pe_long;
e->indexes[0]->visit (this);
if (e->indexes[0]->type != pe_long)
unresolved (e->tok);
hist->visit (this);
wanted_value = pe_long;
}
else
{
assert (array);
if (e->indexes.size() != array->referent->index_types.size())
unresolved (e->tok); else
{
for (unsigned i=0; i<e->indexes.size(); i++)
{
expression* ee = e->indexes[i];
exp_type& ft = array->referent->index_types [i];
t = ft;
ee->visit (this);
exp_type at = ee->type;
if ((at == pe_string || at == pe_long) && ft == pe_unknown)
{
ft = at;
resolved (ee->tok, ee->type, array->referent, i);
}
if (at == pe_stats)
invalid (ee->tok, at);
if (ft == pe_stats)
invalid (ee->tok, ft);
if (at != pe_unknown && ft != pe_unknown && ft != at)
mismatch (ee->tok, ee->type, array->referent, i);
if (at == pe_unknown)
unresolved (ee->tok);
}
for (unsigned i=0; i<e->array_slice.size(); i++)
if (e->array_slice[i])
{
expression* ee = e->array_slice[i];
exp_type& ft = array->referent->index_types [i];
t = ft;
ee->visit (this);
exp_type at = ee->type;
if ((at == pe_string || at == pe_long) && ft == pe_unknown)
{
ft = at;
resolved (ee->tok, ee->type, array->referent, i);
}
if (at == pe_stats)
invalid (ee->tok, at);
if (ft == pe_stats)
invalid (ee->tok, ft);
if (at != pe_unknown && ft != pe_unknown && ft != at)
mismatch (ee->tok, ee->type, array->referent, i);
if (at == pe_unknown)
unresolved (ee->tok);
}
}
t = pe_unknown;
array->visit (this);
wanted_value = array->type;
}
if (e->value)
{
if (wanted_value == pe_stats)
invalid(e->value->tok, wanted_value);
else if (wanted_value != pe_unknown)
check_arg_type(wanted_value, e->value);
else
{
t = pe_unknown;
e->value->visit (this);
}
}
if (wanted_value != pe_unknown)
if (e->sort_aggr != sc_none && wanted_value != pe_stats)
invalid (array->tok, wanted_value);
if (e->limit)
{
t = pe_long;
e->limit->visit (this);
}
t = pe_unknown;
e->block->visit (this);
}
void
typeresolution_info::visit_null_statement (null_statement*)
{
}
void
typeresolution_info::visit_expr_statement (expr_statement* e)
{
t = pe_unknown;
e->value->visit (this);
}
struct delete_statement_typeresolution_info:
public throwing_visitor
{
typeresolution_info *parent;
delete_statement_typeresolution_info (typeresolution_info *p):
throwing_visitor (_("invalid operand of delete expression")),
parent (p)
{}
void visit_arrayindex (arrayindex* e)
{
parent->visit_arrayindex (e);
}
void visit_symbol (symbol* e)
{
exp_type ignored = pe_unknown;
assert (e->referent != 0);
resolve_2types (e, e->referent, parent, ignored);
}
};
void
typeresolution_info::visit_delete_statement (delete_statement* e)
{
delete_statement_typeresolution_info di (this);
t = pe_unknown;
e->value->visit (&di);
}
void
typeresolution_info::visit_next_statement (next_statement*)
{
}
void
typeresolution_info::visit_break_statement (break_statement*)
{
}
void
typeresolution_info::visit_continue_statement (continue_statement*)
{
}
void
typeresolution_info::visit_array_in (array_in* e)
{
exp_type t1 = t;
t = pe_unknown; e->operand->visit (this);
if (t1 == pe_unknown && e->type != pe_unknown)
; else if (t1 == pe_string || t1 == pe_stats)
mismatch (e->tok, t1, pe_long);
else if (e->type == pe_unknown)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
}
void
typeresolution_info::visit_return_statement (return_statement* e)
{
if (current_function == 0)
return;
exp_type& e_type = current_function->type;
t = current_function->type;
e->value->visit (this);
if (e_type != pe_unknown && e->value->type != pe_unknown
&& e_type != e->value->type)
mismatch (e->value->tok, e->value->type, current_function);
if (e_type == pe_unknown &&
(e->value->type == pe_long || e->value->type == pe_string))
{
e_type = e->value->type;
resolved (e->value->tok, e_type, current_function);
}
if (e->value->type == pe_stats)
invalid (e->value->tok, e->value->type);
const exp_type_ptr& value_type = e->value->type_details;
if (value_type && current_function->type == e->value->type)
{
exp_type_ptr& func_type = current_function->type_details;
if (!func_type)
resolved_details(value_type, func_type);
else if (*func_type != *value_type && *func_type != *null_type)
resolved_details(null_type, func_type);
}
}
void
typeresolution_info::visit_print_format (print_format* e)
{
size_t unresolved_args = 0;
if (e->hist)
{
e->hist->visit(this);
}
else if (e->print_with_format)
{
unsigned expected_num_args = 0;
std::vector<print_format::format_component> components;
for (size_t i = 0; i < e->components.size(); ++i)
{
if (e->components[i].type == print_format::conv_unspecified)
throw SEMANTIC_ERROR (_("Unspecified conversion in print operator format string"),
e->tok);
else if (e->components[i].type == print_format::conv_literal)
continue;
components.push_back(e->components[i]);
++expected_num_args;
if (e->components[i].widthtype == print_format::width_dynamic)
++expected_num_args;
if (e->components[i].prectype == print_format::prec_dynamic)
++expected_num_args;
}
if (expected_num_args != e->args.size())
throw SEMANTIC_ERROR (_("Wrong number of args to formatted print operator"),
e->tok);
unsigned argno = 0;
for (size_t i = 0; i < components.size(); ++i)
{
if (components[i].widthtype == print_format::width_dynamic)
{
check_arg_type (pe_long, e->args[argno]);
++argno;
}
if (components[i].prectype == print_format::prec_dynamic)
{
check_arg_type (pe_long, e->args[argno]);
++argno;
}
exp_type wanted = pe_unknown;
switch (components[i].type)
{
case print_format::conv_unspecified:
case print_format::conv_literal:
assert (false);
break;
case print_format::conv_pointer:
case print_format::conv_number:
case print_format::conv_binary:
case print_format::conv_char:
case print_format::conv_memory:
case print_format::conv_memory_hex:
wanted = pe_long;
break;
case print_format::conv_string:
wanted = pe_string;
break;
}
assert (wanted != pe_unknown);
check_arg_type (wanted, e->args[argno]);
++argno;
}
}
else
{
for (size_t i = 0; i < e->args.size(); ++i)
{
t = pe_unknown;
e->args[i]->visit (this);
if (e->args[i]->type == pe_unknown)
{
unresolved (e->args[i]->tok);
++unresolved_args;
}
}
}
if (unresolved_args == 0)
{
if (e->type == pe_unknown)
{
if (e->print_to_stream)
e->type = pe_long;
else
e->type = pe_string;
resolved (e->tok, e->type);
}
}
else
{
e->type = pe_unknown;
unresolved (e->tok);
}
}
void
typeresolution_info::visit_stat_op (stat_op* e)
{
t = pe_stats;
e->stat->visit (this);
if (e->type == pe_unknown)
{
e->type = pe_long;
resolved (e->tok, e->type);
}
else if (e->type != pe_long)
mismatch (e->tok, pe_long, e->type);
}
void
typeresolution_info::visit_hist_op (hist_op* e)
{
t = pe_stats;
e->stat->visit (this);
}
void
typeresolution_info::check_arg_type (exp_type wanted, expression* arg)
{
t = wanted;
arg->visit (this);
if (arg->type == pe_unknown)
{
arg->type = wanted;
resolved (arg->tok, arg->type);
}
else if (arg->type != wanted)
{
mismatch (arg->tok, wanted, arg->type);
}
}
void
typeresolution_info::check_local (vardecl* v)
{
if (v->arity != 0)
{
num_still_unresolved ++;
if (assert_resolvability)
session.print_error
(SEMANTIC_ERROR (_("array locals not supported, missing global declaration? "), v->tok));
}
if (v->type == pe_unknown)
unresolved (v->tok);
else if (v->type == pe_stats)
{
num_still_unresolved ++;
if (assert_resolvability)
session.print_error
(SEMANTIC_ERROR (_("stat locals not supported, missing global declaration? "), v->tok));
}
else if (!(v->type == pe_long || v->type == pe_string))
invalid (v->tok, v->type);
}
void
typeresolution_info::unresolved (const token* tok)
{
num_still_unresolved ++;
if (assert_resolvability && mismatch_complexity <= 0)
{
stringstream msg;
msg << _("unresolved type ");
session.print_error (SEMANTIC_ERROR (msg.str(), tok));
}
}
void
typeresolution_info::invalid (const token* tok, exp_type pe)
{
num_still_unresolved ++;
if (assert_resolvability)
{
stringstream msg;
if (tok && tok->type == tok_operator)
msg << _("invalid operator");
else
msg << _("invalid type ") << pe;
session.print_error (SEMANTIC_ERROR (msg.str(), tok));
}
}
void
typeresolution_info::mismatch (const binary_expression* e)
{
num_still_unresolved ++;
if (assert_resolvability && mismatch_complexity <= 1)
{
stringstream msg;
msg << _F("type mismatch: left and right sides don't agree (%s vs %s)",
lex_cast(e->left->type).c_str(), lex_cast(e->right->type).c_str());
session.print_error (SEMANTIC_ERROR (msg.str(), e->tok));
}
else if (!assert_resolvability)
mismatch_complexity = max(1, mismatch_complexity);
}
void
typeresolution_info::mismatch (const token* tok, exp_type t1, exp_type t2)
{
num_still_unresolved ++;
if (assert_resolvability && mismatch_complexity <= 2)
{
stringstream msg;
msg << _F("type mismatch: expected %s", lex_cast(t1).c_str());
if (t2 != pe_unknown)
msg << _F(" but found %s", lex_cast(t2).c_str());
session.print_error (SEMANTIC_ERROR (msg.str(), tok));
}
else if (!assert_resolvability)
mismatch_complexity = max(2, mismatch_complexity);
}
void
typeresolution_info::mismatch (const token *tok, exp_type type,
const symboldecl* decl, int index)
{
num_still_unresolved ++;
if (assert_resolvability && mismatch_complexity <= 3)
{
assert(decl != NULL);
if (current_function != NULL && index == -1)
{
vector<vardecl*>& args = current_function->formal_args;
for (unsigned i = 0; i < args.size() && index < 0; i++)
if (args[i] == decl)
index = i;
}
const resolved_type *original = NULL;
for (vector<resolved_type>::const_iterator it = resolved_types.begin();
it != resolved_types.end() && original == NULL; ++it)
{
if (it->decl == decl && it->index == index)
original = &(*it);
}
if (original == NULL)
{
session.print_error (SEMANTIC_ERROR (
_F("type mismatch: expected %s but found %s",
lex_cast(type).c_str(),
lex_cast(decl->type).c_str()),
tok));
return;
}
stringstream msg;
if (index >= 0)
msg << _F("index %d ", index);
msg << _F("type mismatch (%s)", lex_cast(type).c_str());
semantic_error err(ERR_SRC, msg.str(), tok);
stringstream chain_msg;
chain_msg << _("type");
if (index >= 0)
chain_msg << _F(" of index %d", index);
chain_msg << _F(" was first inferred here (%s)",
lex_cast(decl->type).c_str());
semantic_error chain(ERR_SRC, chain_msg.str(), original->tok);
err.set_chain(chain);
session.print_error (err);
}
else if (!assert_resolvability)
mismatch_complexity = max(3, mismatch_complexity);
}
void
typeresolution_info::resolved (const token *tok, exp_type type,
const symboldecl* decl, int index)
{
num_newly_resolved ++;
if (decl != NULL)
{
for (unsigned i = 0; i < resolved_types.size(); i++)
{
if (resolved_types[i].decl == decl
&& resolved_types[i].index == index)
{
resolved_types[i].tok = tok;
return;
}
}
resolved_type res(tok, decl, index);
resolved_types.push_back(res);
}
}
void
typeresolution_info::resolved_details (const exp_type_ptr& src,
exp_type_ptr& dest)
{
num_newly_resolved ++;
dest = src;
}