translate.cxx - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include "config.h"
#include "staptree.h"
#include "elaborate.h"
#include "translate.h"
#include "session.h"
#include "tapsets.h"
#include "util.h"
#include "dwarf_wrappers.h"
#include "setupdwfl.h"
#include "task_finder.h"
#include "runtime/k_syms.h"
#include "dwflpp.h"
#include "stapregex.h"
#include <cstdlib>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include <cassert>
#include <cstring>
#include <cerrno>
extern "C" {
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include <elfutils/libdw.h>
#include <ftw.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
}
#define MAX_UNWIND_TABLE_SIZE (16 * 1024 * 1024)
#define STAP_T_01 _("\"Array overflow, check ")
#define STAP_T_02 _("\"MAXNESTING exceeded\";")
#define STAP_T_03 _("\"division by 0\";")
#define STAP_T_04 _("\"MAXACTION exceeded\";")
#define STAP_T_05 _("\"aggregation overflow in ")
#define STAP_T_06 _("\"empty aggregate\";")
#define STAP_T_07 _("\"histogram index out of range\";")
using namespace std;
class var;
struct tmpvar;
struct aggvar;
struct mapvar;
class itervar;
struct c_unparser: public unparser, public visitor
{
systemtap_session* session;
translator_output* o;
derived_probe* current_probe;
functiondecl* current_function;
unsigned tmpvar_counter;
unsigned label_counter;
unsigned action_counter;
bool already_checked_action_count;
varuse_collecting_visitor vcv_needs_global_locks;
map<string, string> probe_contents;
map<pair<bool, string>, string> compiled_printfs;
c_unparser (systemtap_session* ss):
session (ss), o (ss->op), current_probe(0), current_function (0),
tmpvar_counter (0), label_counter (0), action_counter(0),
already_checked_action_count(false), vcv_needs_global_locks (*ss) {}
~c_unparser () {}
void emit_map_type_instantiations ();
void emit_common_header ();
void emit_global (vardecl* v);
void emit_global_init (vardecl* v);
void emit_global_init_type (vardecl *v);
void emit_global_param (vardecl* v);
void emit_global_init_setters ();
void emit_functionsig (functiondecl* v);
void emit_kernel_module_init ();
void emit_kernel_module_exit ();
void emit_module_init ();
void emit_module_refresh ();
void emit_module_exit ();
void emit_function (functiondecl* v);
void emit_lock_decls (const varuse_collecting_visitor& v);
void emit_locks ();
void emit_probe (derived_probe* v);
void emit_probe_condition_initialize(derived_probe* v);
void emit_probe_condition_update(derived_probe* v);
void emit_unlocks ();
void emit_compiled_printfs ();
void emit_compiled_printf_locals ();
void declare_compiled_printf (bool print_to_stream, const string& format);
const string& get_compiled_printf (bool print_to_stream, const string& format);
set<string> aggregations_active;
map<string, string> foreach_loop_values;
void visit_foreach_loop_value (visitor* vis, foreach_loop* s,
const string& value="");
bool get_foreach_loop_value (arrayindex* ai, string& value);
vector<string> loop_break_labels;
vector<string> loop_continue_labels;
string c_typename (exp_type e);
virtual string c_localname (const string& e, bool mangle_oldstyle = false);
virtual string c_globalname (const string &e);
virtual string c_funcname (const string &e);
string c_expression (expression* e);
string c_arg_define (const string& e);
string c_arg_undef (const string& e);
void c_assign (var& lvalue, const string& rvalue, const token* tok);
void c_assign (const string& lvalue, expression* rvalue, const string& msg);
void c_assign (const string& lvalue, const string& rvalue, exp_type type,
const string& msg, const token* tok);
void c_declare(exp_type ty, const string &ident);
void c_declare_static(exp_type ty, const string &ident);
void c_strcat (const string& lvalue, const string& rvalue);
void c_strcat (const string& lvalue, expression* rvalue);
void c_strcpy (const string& lvalue, const string& rvalue);
void c_strcpy (const string& lvalue, expression* rvalue);
bool is_local (vardecl const* r, token const* tok);
tmpvar gensym(exp_type ty);
aggvar gensym_aggregate();
var getvar(vardecl* v, token const* tok = NULL);
itervar getiter(symbol* s);
mapvar getmap(vardecl* v, token const* tok = NULL);
void load_map_indices(arrayindex* e,
vector<tmpvar> & idx);
var* load_aggregate (expression *e, aggvar & agg);
string histogram_index_check(var & vase, tmpvar & idx) const;
void collect_map_index_types(vector<vardecl* > const & vars,
set< pair<vector<exp_type>, exp_type> > & types);
void record_actions (unsigned actions, const token* tok, bool update=false);
void visit_block (block* s);
void visit_try_block (try_block* s);
void visit_embeddedcode (embeddedcode* s);
void visit_null_statement (null_statement* s);
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_return_statement (return_statement* s);
void visit_delete_statement (delete_statement* s);
void visit_next_statement (next_statement* s);
void visit_break_statement (break_statement* s);
void visit_continue_statement (continue_statement* s);
void visit_literal_string (literal_string* e);
void visit_literal_number (literal_number* e);
void visit_embedded_expr (embedded_expr* e);
void visit_binary_expression (binary_expression* e);
void visit_unary_expression (unary_expression* e);
void visit_pre_crement (pre_crement* e);
void visit_post_crement (post_crement* e);
void visit_logical_or_expr (logical_or_expr* e);
void visit_logical_and_expr (logical_and_expr* e);
void visit_array_in (array_in* e);
void visit_regex_query (regex_query* e);
void visit_comparison (comparison* e);
void visit_concatenation (concatenation* e);
void visit_ternary_expression (ternary_expression* e);
void visit_assignment (assignment* e);
void visit_symbol (symbol* e);
void visit_target_symbol (target_symbol* e);
void visit_arrayindex (arrayindex* e);
void visit_functioncall (functioncall* e);
void visit_print_format (print_format* e);
void visit_stat_op (stat_op* e);
void visit_hist_op (hist_op* e);
void visit_cast_op (cast_op* e);
void visit_autocast_op (autocast_op* e);
void visit_atvar_op (atvar_op* e);
void visit_defined_op (defined_op* e);
void visit_entry_op (entry_op* e);
void visit_perf_op (perf_op* e);
};
struct c_tmpcounter:
public traversing_visitor
{
c_unparser* parent;
c_tmpcounter (c_unparser* p):
parent (p)
{
parent->tmpvar_counter = 0;
}
void load_map_indices(arrayindex* e);
void load_aggregate (expression *e);
void visit_block (block *s);
void visit_for_loop (for_loop* s);
void visit_foreach_loop (foreach_loop* s);
void visit_delete_statement (delete_statement* s);
void visit_binary_expression (binary_expression* e);
void visit_pre_crement (pre_crement* e);
void visit_post_crement (post_crement* e);
void visit_array_in (array_in* e);
void visit_regex_query (regex_query* e);
void visit_comparison (comparison* e);
void visit_concatenation (concatenation* e);
void visit_assignment (assignment* e);
void visit_arrayindex (arrayindex* e);
void visit_functioncall (functioncall* e);
void visit_print_format (print_format* e);
void visit_stat_op (stat_op* e);
};
struct c_unparser_assignment:
public throwing_visitor
{
c_unparser* parent;
string op;
expression* rvalue;
bool post; c_unparser_assignment (c_unparser* p, const string& o, expression* e):
throwing_visitor ("invalid lvalue type"),
parent (p), op (o), rvalue (e), post (false) {}
c_unparser_assignment (c_unparser* p, const string& o, bool pp):
throwing_visitor ("invalid lvalue type"),
parent (p), op (o), rvalue (0), post (pp) {}
void prepare_rvalue (string const & op,
tmpvar & rval,
token const* tok);
void c_assignop(tmpvar & res,
var const & lvar,
tmpvar const & tmp,
token const* tok);
void visit_symbol (symbol* e);
void visit_arrayindex (arrayindex* e);
};
struct c_tmpcounter_assignment:
public traversing_visitor
{
c_tmpcounter* parent;
const string& op;
expression* rvalue;
bool post; c_tmpcounter_assignment (c_tmpcounter* p, const string& o, expression* e, bool pp = false):
parent (p), op (o), rvalue (e), post (pp) {}
void prepare_rvalue (tmpvar & rval);
void c_assignop(tmpvar & res);
void visit_symbol (symbol* e);
void visit_arrayindex (arrayindex* e);
};
ostream & operator<<(ostream & o, var const & v);
class var
{
protected:
c_unparser *u;
bool local;
exp_type ty;
statistic_decl sd;
string name;
bool do_mangle;
public:
var(c_unparser *u, bool local, exp_type ty,
statistic_decl const & sd, string const & name)
: u(u), local(local), ty(ty), sd(sd), name(name), do_mangle(true)
{}
var(c_unparser *u, bool local, exp_type ty, string const & name)
: u(u), local(local), ty(ty), name(name), do_mangle(true)
{}
var(c_unparser *u, bool local, exp_type ty,
string const & name, bool do_mangle)
: u(u), local(local), ty(ty), name(name), do_mangle(do_mangle)
{}
virtual ~var() {}
bool is_local() const
{
return local;
}
statistic_decl const & sdecl() const
{
return sd;
}
void assert_hist_compatible(hist_op const & hop)
{
switch (sd.type)
{
case statistic_decl::linear:
assert(hop.htype == hist_linear);
assert(hop.params.size() == 3);
assert(hop.params[0] == sd.linear_low);
assert(hop.params[1] == sd.linear_high);
assert(hop.params[2] == sd.linear_step);
break;
case statistic_decl::logarithmic:
assert(hop.htype == hist_log);
assert(hop.params.size() == 0);
break;
case statistic_decl::none:
assert(false);
}
}
exp_type type() const
{
return ty;
}
string c_name() const
{
if (!do_mangle)
return name;
else if (local)
return u->c_localname(name);
else
return u->c_globalname(name);
}
string value() const
{
if (local)
return "l->" + c_name();
else
return "global(" + c_name() + ")";
}
virtual string hist() const
{
assert (ty == pe_stats);
assert (sd.type != statistic_decl::none);
return "(&(" + value() + "->hist))";
}
virtual string buckets() const
{
assert (ty == pe_stats);
assert (sd.type != statistic_decl::none);
return "(" + value() + "->hist.buckets)";
}
string init() const
{
switch (type())
{
case pe_string:
if (! local)
return ""; else
return value() + "[0] = '\\0';";
case pe_long:
if (! local)
return ""; else
return value() + " = 0;";
case pe_stats:
{
if (local)
throw SEMANTIC_ERROR(_F("unsupported local stats init for %s", value().c_str()));
string prefix = "global_set(" + c_name() + ", _stp_stat_init (";
string suffix = "if (" + value () + " == NULL) rc = -ENOMEM;";
switch (sd.type)
{
case statistic_decl::none:
prefix += "HIST_NONE";
break;
case statistic_decl::linear:
prefix += string("HIST_LINEAR")
+ ", " + lex_cast(sd.linear_low)
+ ", " + lex_cast(sd.linear_high)
+ ", " + lex_cast(sd.linear_step);
break;
case statistic_decl::logarithmic:
prefix += string("HIST_LOG");
break;
default:
throw SEMANTIC_ERROR(_F("unsupported stats type for %s", value().c_str()));
}
prefix = prefix + ")); ";
return string (prefix + suffix);
}
default:
throw SEMANTIC_ERROR(_F("unsupported initializer for %s", value().c_str()));
}
}
string fini () const
{
switch (type())
{
case pe_string:
case pe_long:
return ""; case pe_stats:
return "_stp_stat_del (" + value () + ");";
default:
throw SEMANTIC_ERROR(_F("unsupported deallocator for %s", value().c_str()));
}
}
void declare(c_unparser &c) const
{
c.c_declare(ty, c_name());
}
};
ostream & operator<<(ostream & o, var const & v)
{
return o << v.value();
}
struct stmt_expr
{
c_unparser & c;
stmt_expr(c_unparser & c) : c(c)
{
c.o->newline() << "({";
c.o->indent(1);
}
~stmt_expr()
{
c.o->newline(-1) << "})";
}
};
struct tmpvar
: public var
{
protected:
bool overridden;
string override_value;
public:
tmpvar(c_unparser *u, exp_type ty, unsigned & counter)
: var(u, true, ty, ("__tmp" + lex_cast(counter++)), false),
overridden(false)
{}
tmpvar(const var& source)
: var(source), overridden(false)
{}
void override(const string &value)
{
overridden = true;
override_value = value;
}
string value() const
{
if (overridden)
return override_value;
else
return var::value();
}
};
ostream & operator<<(ostream & o, tmpvar const & v)
{
return o << v.value();
}
struct aggvar
: public var
{
aggvar(c_unparser *u, unsigned & counter)
: var(u, true, pe_stats, ("__tmp" + lex_cast(counter++)), false)
{}
string init() const
{
assert (type() == pe_stats);
return value() + " = NULL;";
}
void declare(c_unparser &c) const
{
assert (type() == pe_stats);
c.o->newline() << "struct stat_data *" << name << ";";
}
string get_hist (var& index) const
{
return "(" + value() + "->histogram[" + index.value() + "])";
}
};
struct mapvar
: public var
{
vector<exp_type> index_types;
int maxsize;
bool wrap;
mapvar (c_unparser *u,
bool local, exp_type ty,
statistic_decl const & sd,
string const & name,
vector<exp_type> const & index_types,
int maxsize, bool wrap)
: var (u, local, ty, sd, name),
index_types (index_types),
maxsize (maxsize), wrap(wrap)
{}
static string shortname(exp_type e);
static string key_typename(exp_type e);
static string value_typename(exp_type e);
string keysym () const
{
string result;
vector<exp_type> tmp = index_types;
tmp.push_back (type ());
for (unsigned i = 0; i < tmp.size(); ++i)
{
switch (tmp[i])
{
case pe_long:
result += 'i';
break;
case pe_string:
result += 's';
break;
case pe_stats:
result += 'x';
break;
default:
throw SEMANTIC_ERROR(_("unknown type of map"));
break;
}
}
return result;
}
string function_keysym(string const & fname, bool pre_agg=false) const
{
string mtype = (is_parallel() && !pre_agg) ? "pmap" : "map";
string result = "_stp_" + mtype + "_" + fname + "_" + keysym();
return result;
}
string call_prefix (string const & fname, vector<tmpvar> const & indices, bool pre_agg=false) const
{
string result = function_keysym(fname, pre_agg) + " (";
result += pre_agg? fetch_existing_aggregate() : value();
for (unsigned i = 0; i < indices.size(); ++i)
{
if (indices[i].type() != index_types[i])
throw SEMANTIC_ERROR(_("index type mismatch"));
result += ", ";
result += indices[i].value();
}
return result;
}
bool is_parallel() const
{
return type() == pe_stats;
}
string calculate_aggregate() const
{
if (!is_parallel())
throw SEMANTIC_ERROR(_("aggregating non-parallel map type"));
return function_keysym("agg") + " (" + value() + ")";
}
string fetch_existing_aggregate() const
{
if (!is_parallel())
throw SEMANTIC_ERROR(_("fetching aggregate of non-parallel map type"));
return "_stp_pmap_get_agg(" + value() + ")";
}
string del (vector<tmpvar> const & indices) const
{
return (call_prefix("del", indices) + ")");
}
string exists (vector<tmpvar> const & indices) const
{
if (type() == pe_long || type() == pe_string)
return (call_prefix("exists", indices) + ")");
else if (type() == pe_stats)
return ("((uintptr_t)" + call_prefix("get", indices)
+ ") != (uintptr_t) 0)");
else
throw SEMANTIC_ERROR(_("checking existence of an unsupported map type"));
}
string get (vector<tmpvar> const & indices, bool pre_agg=false) const
{
if (type() == pe_string)
return ("({ char *v = " + call_prefix("get", indices, pre_agg) + ");"
+ "if (!v) v = \"\"; v; })");
else if (type() == pe_long || type() == pe_stats)
return call_prefix("get", indices, pre_agg) + ")";
else
throw SEMANTIC_ERROR(_("getting a value from an unsupported map type"));
}
string add (vector<tmpvar> const & indices, tmpvar const & val) const
{
string res = "{ int rc = ";
if (type() == pe_stats)
res += (call_prefix("add", indices) + ", " + val.value() + ")");
else
throw SEMANTIC_ERROR(_("adding a value of an unsupported map type"));
res += "; if (unlikely(rc)) { c->last_error = ";
res += STAP_T_01 +
lex_cast(maxsize > 0 ?
"size limit (" + lex_cast(maxsize) + ")" : "MAXMAPENTRIES")
+ "\"; goto out; }}";
return res;
}
string set (vector<tmpvar> const & indices, tmpvar const & val) const
{
string res = "{ int rc = ";
if (type() == pe_string)
res += (call_prefix("set", indices)
+ ", (" + val.value() + "[0] ? " + val.value() + " : NULL))");
else if (type() == pe_long)
res += (call_prefix("set", indices) + ", " + val.value() + ")");
else
throw SEMANTIC_ERROR(_("setting a value of an unsupported map type"));
res += "; if (unlikely(rc)) { c->last_error = ";
res += STAP_T_01 +
lex_cast(maxsize > 0 ?
"size limit (" + lex_cast(maxsize) + ")" : "MAXMAPENTRIES")
+ "\"; goto out; }}";
return res;
}
string hist() const
{
assert (ty == pe_stats);
assert (sd.type != statistic_decl::none);
return "(&(" + fetch_existing_aggregate() + "->hist))";
}
string buckets() const
{
assert (ty == pe_stats);
assert (sd.type != statistic_decl::none);
return "(" + fetch_existing_aggregate() + "->hist.buckets)";
}
string init () const
{
if (local)
throw SEMANTIC_ERROR(_F("unsupported local map init for %s", value().c_str()));
string prefix = "global_set(" + c_name() + ", ";
prefix += function_keysym("new") + " ("
+ (maxsize > 0 ? lex_cast(maxsize) : "MAXMAPENTRIES")
+ ((wrap == true) ? ", 1" : ", 0");
string suffix = "if (" + value () + " == NULL) rc = -ENOMEM;";
if (type() == pe_stats)
{
switch (sdecl().type)
{
case statistic_decl::none:
prefix = prefix + ", HIST_NONE";
break;
case statistic_decl::linear:
FIXME prefix = prefix + ", HIST_LINEAR"
+ ", " + lex_cast(sdecl().linear_low)
+ ", " + lex_cast(sdecl().linear_high)
+ ", " + lex_cast(sdecl().linear_step);
break;
case statistic_decl::logarithmic:
prefix = prefix + ", HIST_LOG";
break;
}
}
prefix = prefix + ")); ";
return (prefix + suffix);
}
string fini () const
{
if (is_parallel())
return "_stp_pmap_del (" + value() + ");";
else
return "_stp_map_del (" + value() + ");";
}
};
class itervar
{
exp_type referent_ty;
string name;
public:
itervar (symbol* e, unsigned & counter)
: referent_ty(e->referent->type),
name("__tmp" + lex_cast(counter++))
{
if (referent_ty == pe_unknown)
throw SEMANTIC_ERROR(_("iterating over unknown reference type"), e->tok);
}
string declare () const
{
return "struct map_node *" + name + ";";
}
string start (mapvar const & mv) const
{
string res;
if (mv.type() != referent_ty)
throw SEMANTIC_ERROR(_("inconsistent iterator type in itervar::start()"));
if (mv.is_parallel())
return "_stp_map_start (" + mv.fetch_existing_aggregate() + ")";
else
return "_stp_map_start (" + mv.value() + ")";
}
string next (mapvar const & mv) const
{
if (mv.type() != referent_ty)
throw SEMANTIC_ERROR(_("inconsistent iterator type in itervar::next()"));
if (mv.is_parallel())
return "_stp_map_iter (" + mv.fetch_existing_aggregate() + ", " + value() + ")";
else
return "_stp_map_iter (" + mv.value() + ", " + value() + ")";
}
string del_next (mapvar const & mv) const
{
if (mv.type() != referent_ty)
throw SEMANTIC_ERROR(_("inconsistent iterator type in itervar::next()"));
if (mv.is_parallel())
throw SEMANTIC_ERROR(_("deleting a value of an unsupported map type"));
else
return "_stp_map_iterdel (" + mv.value() + ", " + value() + ")";
}
string value () const
{
return "l->" + name;
}
string get_key (mapvar const& mv, exp_type ty, unsigned i) const
{
switch (ty)
{
case pe_long:
return mv.function_keysym("key_get_int64", true)
+ " (" + value() + ", " + lex_cast(i+1) + ")";
case pe_string:
return "(" + mv.function_keysym("key_get_str", true)
+ " (" + value() + ", " + lex_cast(i+1) + ") ?: \"\")";
default:
throw SEMANTIC_ERROR(_("illegal key type"));
}
}
string get_value (mapvar const& mv, exp_type ty) const
{
if (ty != referent_ty)
throw SEMANTIC_ERROR(_("inconsistent iterator value in itervar::get_value()"));
switch (ty)
{
case pe_long:
return mv.function_keysym("get_int64", true) + " ("+ value() + ")";
case pe_string:
return "(" + mv.function_keysym("get_str", true) + " ("+ value() + ") ?: \"\")";
case pe_stats:
return mv.function_keysym("get_stat_data", true) + " ("+ value() + ")";
default:
throw SEMANTIC_ERROR(_("illegal value type"));
}
}
};
ostream & operator<<(ostream & o, itervar const & v)
{
return o << v.value();
}
struct unmodified_fnargs_checker : public embedded_tags_visitor
{
bool embedded_seen; unmodified_fnargs_checker (): embedded_tags_visitor(true), embedded_seen(false) {}
void visit_embeddedcode (embeddedcode *e)
{
embedded_tags_visitor::visit_embeddedcode(e);
embedded_seen = (embedded_seen || !tagged_p("/* unmodified-fnargs */"));
}
};
bool
is_unmodified_string_fnarg (systemtap_session* sess, functiondecl* fd, vardecl* v)
{
if (sess->unoptimized || v->type != pe_string)
return false;
unmodified_fnargs_checker ecv;
fd->body->visit(& ecv);
if (ecv.embedded_seen)
return false;
varuse_collecting_visitor vut (*sess);
vut.current_function = fd;
fd->body->visit(& vut);
return (find(vut.written.begin(), vut.written.end(), v) == vut.written.end());
}
void
c_unparser::emit_common_header ()
{
o->newline();
o->newline() << "struct context {";
o->newline(1) << "#include \"common_probe_context.h\"";
o->newline() << "union {";
o->indent(1);
XXX
map<string, string> tmp_probe_contents;
for (unsigned i=0; i<session->probes.size(); i++)
{
derived_probe* dp = session->probes[i];
ostringstream oss;
oss << "# needs_global_locks: " << dp->needs_global_locks () << endl;
dp->print_dupe_stamp (oss);
dp->body->print(oss);
if (session->unoptimized || tmp_probe_contents.count(oss.str()) == 0) {
tmp_probe_contents[oss.str()] = dp->name;
o->newline() << "struct " << dp->name << "_locals {";
o->indent(1);
for (unsigned j=0; j<dp->locals.size(); j++)
{
vardecl* v = dp->locals[j];
try
{
o->newline() << c_typename (v->type) << " "
<< c_localname (v->name) << ";";
} catch (const semantic_error& e) {
semantic_error e2 (e);
if (e2.tok1 == 0) e2.tok1 = v->tok;
throw e2;
}
}
c_tmpcounter ct (this);
dp->body->visit (& ct);
o->newline(-1) << "} " << dp->name << ";";
}
}
o->newline(-1) << "} probe_locals;";
o->newline() << "union {";
o->indent(1);
for (map<string,functiondecl*>::iterator it = session->functions.begin(); it != session->functions.end(); it++)
{
functiondecl* fd = it->second;
o->newline()
<< "struct " << c_funcname (fd->name) << "_locals {";
o->indent(1);
for (unsigned j=0; j<fd->locals.size(); j++)
{
vardecl* v = fd->locals[j];
try
{
if (fd->mangle_oldstyle)
{
o->newline() << "union { "
<< c_typename (v->type) << " "
<< c_localname (v->name) << "; "
<< c_typename (v->type) << " "
<< c_localname (v->name, true) << "; };";
}
else
{
o->newline() << c_typename (v->type) << " "
<< c_localname (v->name) << ";";
}
} catch (const semantic_error& e) {
semantic_error e2 (e);
if (e2.tok1 == 0) e2.tok1 = v->tok;
throw e2;
}
}
for (unsigned j=0; j<fd->formal_args.size(); j++)
{
vardecl* v = fd->formal_args[j];
try
{
v->char_ptr_arg = (is_unmodified_string_fnarg (session, fd, v));
if (v->char_ptr_arg && session->verbose > 2)
clog << _F("variable %s for function %s will be passed by reference (char *)",
v->name.c_str(), fd->name.c_str()) << endl;
if (fd->mangle_oldstyle)
{
o->newline() << "union { "
<< (v->char_ptr_arg ? "const char *" : c_typename (v->type))
<< " " << c_localname (v->name) << "; "
<< (v->char_ptr_arg ? "const char *" : c_typename (v->type))
<< " " << c_localname (v->name, true) << "; };";
}
else
{
o->newline() << (v->char_ptr_arg ? "const char *" : c_typename (v->type))
<< " " << c_localname (v->name) << ";";
}
} catch (const semantic_error& e) {
semantic_error e2 (e);
if (e2.tok1 == 0) e2.tok1 = v->tok;
throw e2;
}
}
c_tmpcounter ct (this);
fd->body->visit (& ct);
if (fd->type == pe_unknown)
o->newline() << "/* no return value */";
else
{
if (!session->unoptimized && fd->type == pe_string&& session->verbose > 2)
clog << _F("return value for function %s will be passed by reference (char *)",
fd->name.c_str()) << endl;
o->newline() << (!session->unoptimized && fd->type == pe_string ? "char *" : c_typename (fd->type))
<< " __retvalue;";
}
o->newline(-1) << "} " << c_funcname (fd->name) << ";";
}
o->newline(-1) << "} locals [MAXNESTING+1];";
o->newline() << "#if MAXNESTING < 0";
o->newline() << "#error \"MAXNESTING must be positive\"";
o->newline() << "#endif";
emit_compiled_printf_locals ();
o->newline(-1) << "};\n";
o->newline() << "#include \"runtime_context.h\"";
emit_map_type_instantiations ();
emit_compiled_printfs();
if (!session->runtime_usermode_p())
{
o->newline( 0) << "static atomic_t need_module_refresh = ATOMIC_INIT(0);";
o->newline( 0) << "#include <linux/workqueue.h>";
o->newline( 0) << "static struct work_struct module_refresher_work;";
o->newline( 0) << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)";
o->newline( 0) << "static void module_refresher(void *data) {";
o->newline( 0) << "#else";
o->newline( 0) << "static void module_refresher(struct work_struct *work) {";
o->newline( 0) << "#endif";
o->newline( 1) << "systemtap_module_refresh(NULL);";
o->newline(-1) << "}";
o->newline( 0) << "#ifdef STP_ON_THE_FLY_TIMER_ENABLE";
o->newline( 0) << "#include <linux/hrtimer.h>";
o->newline( 0) << "#include \"timer.h\"";
o->newline( 0) << "static struct hrtimer module_refresh_timer;";
o->newline( 0) << "#ifndef STP_ON_THE_FLY_INTERVAL";
o->newline( 0) << "#define STP_ON_THE_FLY_INTERVAL (100*1000*1000)"; o->newline( 0) << "#endif";
o->newline( 0) << "hrtimer_return_t module_refresh_timer_cb(struct hrtimer *timer) {";
o->newline(+1) << "if (atomic_cmpxchg(&need_module_refresh, 1, 0) == 1)";
o->newline(+1) << "schedule_work(&module_refresher_work);";
o->newline(-1) << "hrtimer_set_expires(timer,";
o->newline( 0) << " ktime_add(hrtimer_get_expires(timer),";
o->newline( 0) << " ktime_set(0, STP_ON_THE_FLY_INTERVAL))); ";
o->newline( 0) << "return HRTIMER_RESTART;";
o->newline(-1) << "}";
o->newline( 0) << "#endif /* STP_ON_THE_FLY_ENABLE */";
}
o->newline();
}
void
c_unparser::declare_compiled_printf (bool print_to_stream, const string& format)
{
pair<bool, string> index (print_to_stream, format);
map<pair<bool, string>, string>::iterator it = compiled_printfs.find(index);
if (it == compiled_printfs.end())
compiled_printfs[index] = (print_to_stream ? "stp_printf_" : "stp_sprintf_")
+ lex_cast(compiled_printfs.size() + 1);
}
const string&
c_unparser::get_compiled_printf (bool print_to_stream, const string& format)
{
map<pair<bool, string>, string>::iterator it =
compiled_printfs.find(make_pair(print_to_stream, format));
if (it == compiled_printfs.end())
throw SEMANTIC_ERROR (_("internal error translating printf"));
return it->second;
}
void
c_unparser::emit_compiled_printf_locals ()
{
o->newline() << "#ifndef STP_LEGACY_PRINT";
o->newline() << "union {";
o->indent(1);
map<pair<bool, string>, string>::iterator it;
for (it = compiled_printfs.begin(); it != compiled_printfs.end(); ++it)
{
bool print_to_stream = it->first.first;
const string& format_string = it->first.second;
const string& name = it->second;
vector<print_format::format_component> components =
print_format::string_to_components(format_string);
o->newline() << "struct " << name << "_locals {";
o->indent(1);
size_t arg_ix = 0;
vector<print_format::format_component>::const_iterator c;
for (c = components.begin(); c != components.end(); ++c)
{
if (c->type == print_format::conv_literal)
continue;
if (c->widthtype == print_format::width_dynamic)
o->newline() << "int64_t arg" << arg_ix++ << ";";
if (c->prectype == print_format::prec_dynamic)
o->newline() << "int64_t arg" << arg_ix++ << ";";
switch (c->type)
{
case print_format::conv_pointer:
case print_format::conv_number:
case print_format::conv_char:
case print_format::conv_memory:
case print_format::conv_memory_hex:
case print_format::conv_binary:
o->newline() << "int64_t arg" << arg_ix++ << ";";
break;
case print_format::conv_string:
o->newline() << "const char* arg" << arg_ix++ << ";";
break;
default:
assert(false); XXX
break;
}
}
if (!print_to_stream)
o->newline() << "char * __retvalue;";
o->newline(-1) << "} " << name << ";";
}
o->newline(-1) << "} printf_locals;";
o->newline() << "#endif // STP_LEGACY_PRINT";
}
void
c_unparser::emit_compiled_printfs ()
{
o->newline() << "#ifndef STP_LEGACY_PRINT";
map<pair<bool, string>, string>::iterator it;
for (it = compiled_printfs.begin(); it != compiled_printfs.end(); ++it)
{
bool print_to_stream = it->first.first;
const string& format_string = it->first.second;
const string& name = it->second;
vector<print_format::format_component> components =
print_format::string_to_components(format_string);
o->newline();
o->newline() << "static void " << name
<< " (struct context* __restrict__ c) {";
o->newline(1) << "struct " << name << "_locals * __restrict__ l = "
<< "& c->printf_locals." << name << ";";
o->newline() << "char *str = NULL, *end = NULL;";
o->newline() << "const char *src;";
o->newline() << "int width;";
o->newline() << "int precision;";
o->newline() << "unsigned long ptr_value;";
o->newline() << "int num_bytes;";
o->newline() << "(void) width;";
o->newline() << "(void) precision;";
o->newline() << "(void) ptr_value;";
o->newline() << "(void) num_bytes;";
if (print_to_stream)
{
size_t arg_ix = 0;
o->newline() << "num_bytes = 0;";
vector<print_format::format_component>::const_iterator c;
for (c = components.begin(); c != components.end(); ++c)
{
if (c->type == print_format::conv_literal)
{
literal_string ls(c->literal_string);
o->newline() << "num_bytes += sizeof(";
visit_literal_string(&ls);
o->line() << ") - 1;"; continue;
}
o->newline() << "width = ";
if (c->widthtype == print_format::width_dynamic)
o->line() << "clamp_t(int, l->arg" << arg_ix++
<< ", 0, STP_BUFFER_SIZE);";
else if (c->widthtype == print_format::width_static)
o->line() << "clamp_t(int, " << c->width
<< ", 0, STP_BUFFER_SIZE);";
else
o->line() << "-1;";
o->newline() << "precision = ";
if (c->prectype == print_format::prec_dynamic)
o->line() << "clamp_t(int, l->arg" << arg_ix++
<< ", 0, STP_BUFFER_SIZE);";
else if (c->prectype == print_format::prec_static)
o->line() << "clamp_t(int, " << c->precision
<< ", 0, STP_BUFFER_SIZE);";
else
o->line() << "-1;";
string value = "l->arg" + lex_cast(arg_ix++);
switch (c->type)
{
case print_format::conv_pointer:
if (strverscmp(session->compatible.c_str(), "1.3") < 0)
{
o->newline() << "ptr_value = " << value << ";";
o->newline() << "if (width == -1)";
o->newline(1) << "width = 2 + 2 * sizeof(void*);";
o->newline(-1) << "precision = width - 2;";
if (!c->test_flag(print_format::fmt_flag_left))
o->newline() << "precision = min_t(int, precision, 2 * sizeof(void*));";
o->newline() << "num_bytes += number_size(ptr_value, "
<< c->base << ", width, precision, " << c->flags << ");";
break;
}
case print_format::conv_number:
o->newline() << "num_bytes += number_size(" << value << ", "
<< c->base << ", width, precision, " << c->flags << ");";
break;
case print_format::conv_char:
o->newline() << "num_bytes += _stp_vsprint_char_size("
<< value << ", width, " << c->flags << ");";
break;
case print_format::conv_string:
o->newline() << "num_bytes += _stp_vsprint_memory_size("
<< value << ", width, precision, 's', "
<< c->flags << ");";
break;
case print_format::conv_memory:
case print_format::conv_memory_hex:
o->newline() << "num_bytes += _stp_vsprint_memory_size("
<< "(const char*)(intptr_t)" << value
<< ", width, precision, '"
<< ((c->type == print_format::conv_memory) ? "m" : "M")
<< "', " << c->flags << ");";
break;
case print_format::conv_binary:
o->newline() << "num_bytes += _stp_vsprint_binary_size("
<< value << ", width, precision);";
break;
default:
assert(false); XXX
break;
}
}
o->newline() << "num_bytes = clamp(num_bytes, 0, STP_BUFFER_SIZE);";
o->newline() << "str = (char*)_stp_reserve_bytes(num_bytes);";
o->newline() << "end = str ? str + num_bytes - 1 : 0;";
}
else {
o->newline() << "str = l->__retvalue;";
o->newline() << "end = str + MAXSTRINGLEN - 1;";
}
o->newline() << "if (str && str <= end) {";
o->indent(1);
size_t arg_ix = 0;
vector<print_format::format_component>::const_iterator c;
for (c = components.begin(); c != components.end(); ++c)
{
if (c->type == print_format::conv_literal)
{
literal_string ls(c->literal_string);
o->newline() << "src = ";
visit_literal_string(&ls);
o->line() << ";";
o->newline() << "while (*src && str <= end)";
o->newline(1) << "*str++ = *src++;";
o->indent(-1);
continue;
}
o->newline() << "width = ";
if (c->widthtype == print_format::width_dynamic)
o->line() << "clamp_t(int, l->arg" << arg_ix++
<< ", 0, end - str + 1);";
else if (c->widthtype == print_format::width_static)
o->line() << "clamp_t(int, " << c->width
<< ", 0, end - str + 1);";
else
o->line() << "-1;";
o->newline() << "precision = ";
if (c->prectype == print_format::prec_dynamic)
o->line() << "clamp_t(int, l->arg" << arg_ix++
<< ", 0, end - str + 1);";
else if (c->prectype == print_format::prec_static)
o->line() << "clamp_t(int, " << c->precision
<< ", 0, end - str + 1);";
else
o->line() << "-1;";
string value = "l->arg" + lex_cast(arg_ix++);
switch (c->type)
{
case print_format::conv_pointer:
if (strverscmp(session->compatible.c_str(), "1.3") < 0)
{
o->newline() << "ptr_value = " << value << ";";
o->newline() << "if (width == -1)";
o->newline(1) << "width = 2 + 2 * sizeof(void*);";
o->newline(-1) << "precision = width - 2;";
if (!c->test_flag(print_format::fmt_flag_left))
o->newline() << "precision = min_t(int, precision, 2 * sizeof(void*));";
o->newline() << "str = number(str, end, ptr_value, "
<< c->base << ", width, precision, " << c->flags << ");";
break;
}
case print_format::conv_number:
o->newline() << "str = number(str, end, " << value << ", "
<< c->base << ", width, precision, " << c->flags << ");";
break;
case print_format::conv_char:
o->newline() << "str = _stp_vsprint_char(str, end, "
<< value << ", width, " << c->flags << ");";
break;
case print_format::conv_string:
o->newline() << "str = _stp_vsprint_memory(str, end, "
<< value << ", width, precision, 's', "
<< c->flags << ");";
break;
case print_format::conv_memory:
case print_format::conv_memory_hex:
o->newline() << "str = _stp_vsprint_memory(str, end, "
<< "(const char*)(intptr_t)" << value
<< ", width, precision, '"
<< ((c->type == print_format::conv_memory) ? "m" : "M")
<< "', " << c->flags << ");";
o->newline() << "if (unlikely(str == NULL)) {";
o->indent(1);
if (print_to_stream)
o->newline() << "_stp_unreserve_bytes(num_bytes);";
o->newline() << "return;";
o->newline(-1) << "}";
break;
case print_format::conv_binary:
o->newline() << "str = _stp_vsprint_binary(str, end, "
<< value << ", width, precision, "
<< c->flags << ");";
break;
default:
assert(false); XXX
break;
}
}
if (!print_to_stream)
{
o->newline() << "if (str <= end)";
o->newline(1) << "*str = '\\0';";
o->newline(-1) << "else";
o->newline(1) << "*end = '\\0';";
o->indent(-1);
}
o->newline(-1) << "}";
o->newline(-1) << "}";
}
o->newline() << "#endif // STP_LEGACY_PRINT";
}
void
c_unparser::emit_global_param (vardecl *v)
{
string vn = c_globalname (v->name);
assert (!session->runtime_usermode_p());
XXX o->newline() << "#undef " << v->name;
if (v->arity == 0 && v->type == pe_long)
{
o->newline() << "module_param_named (" << v->name << ", "
<< "global(" << vn << "), int64_t, 0);";
}
else if (v->arity == 0 && v->type == pe_string)
{
o->newline() << "module_param_string (" << v->name << ", "
<< "global(" << vn << "), MAXSTRINGLEN, 0);";
}
}
void
c_unparser::emit_global_init_setters ()
{
o->newline() << "int stp_global_setter (const char *name, const char *value) {";
o->newline(1);
for (unsigned i=0; i<session->globals.size(); i++)
{
vardecl* v = session->globals[i];
if (v->arity > 0) continue;
if (v->type != pe_string && v->type != pe_long) continue;
o->line() << "if (0 == strcmp(name,\"" << v->name << "\"))" << " {";
o->indent(1);
if (v->type == pe_string)
{
c_assign("stp_global_init." + c_globalname(v->name), "value", pe_string, "BUG: global module param", v->tok);
o->newline() << "return 0;";
}
else
{
o->newline() << "return set_int64_t(value, &stp_global_init." << c_globalname(v->name) << ");";
}
o->newline(-1) << "} else ";
}
o->line() << "return stp_session_attribute_setter(name, value);";
o->newline(-1) << "}";
o->newline();
}
void
c_unparser::emit_global (vardecl *v)
{
string vn = c_globalname (v->name);
string type;
if (v->arity > 0)
type = (v->type == pe_stats) ? "PMAP" : "MAP";
else
type = c_typename (v->type);
if (session->runtime_usermode_p())
{
bool offptr_p = (v->type == pe_stats) || (v->arity > 0);
string stored_type = offptr_p ? "offptr_t" : type;
string casted_type = offptr_p ? type : "void*";
o->newline() << "union {";
o->newline(1) << casted_type << " " << vn << "_typed;";
o->newline() << stored_type << " " << vn << ";";
o->newline(-1) << "};";
}
else
o->newline() << type << " " << vn << ";";
o->newline() << "rwlock_t " << vn << "_lock;";
o->newline() << "#ifdef STP_TIMING";
o->newline() << "atomic_t " << vn << "_lock_skip_count;";
o->newline() << "#endif\n";
}
void
c_unparser::emit_global_init (vardecl *v)
{
if (v->arity == 0 && v->init)
{
o->newline() << "." << c_globalname (v->name) << " = ";
v->init->visit(this);
o->line() << ",";
}
else if (v->arity == 0 && session->runtime_usermode_p())
{
if (v->type == pe_long)
o->newline() << "." << c_globalname (v->name) << " = 0,";
else if (v->type == pe_string)
o->newline() << "." << c_globalname (v->name) << " = { '\\0' },"; XXX }
}
void
c_unparser::emit_global_init_type (vardecl *v)
{
if (v->arity == 0) {
o->newline() << c_typename(v->type) << " " << c_globalname(v->name) << ";";
}
}
void
c_unparser::emit_functionsig (functiondecl* v)
{
o->newline() << "static void " << c_funcname(v->name)
<< " (struct context * __restrict__ c);";
}
void
c_unparser::emit_kernel_module_init ()
{
if (session->runtime_usermode_p())
return;
o->newline();
o->newline() << "static int systemtap_kernel_module_init (void) {";
o->newline(1) << "int rc = 0;";
o->newline() << "int i=0, j=0;";
vector<derived_probe_group*> g = all_session_groups (*session);
for (unsigned i=0; i<g.size(); i++)
{
g[i]->emit_kernel_module_init (*session);
o->newline() << "if (rc) {";
o->indent(1);
if (i>0)
{
for (int j=i-1; j>=0; j--)
g[j]->emit_kernel_module_exit (*session);
}
o->newline() << "goto out;";
o->newline(-1) << "}";
}
o->newline(-1) << "out:";
o->indent(1);
o->newline() << "return rc;";
o->newline(-1) << "}\n";
o->assert_0_indent();
}
void
c_unparser::emit_kernel_module_exit ()
{
if (session->runtime_usermode_p())
return;
o->newline();
o->newline() << "static void systemtap_kernel_module_exit (void) {";
o->newline(1) << "int i=0, j=0;";
vector<derived_probe_group*> g = all_session_groups (*session);
for (vector<derived_probe_group*>::reverse_iterator i = g.rbegin();
i != g.rend(); i++)
{
(*i)->emit_kernel_module_exit (*session);
}
o->newline(-1) << "}\n";
o->assert_0_indent();
}
void
c_unparser::emit_module_init ()
{
vector<derived_probe_group*> g = all_session_groups (*session);
for (unsigned i=0; i<g.size(); i++)
{
g[i]->emit_module_decls (*session);
o->assert_0_indent();
}
o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
o->newline() << "#include \"linux/stp_tracepoint.c\"";
o->newline() << "#endif";
o->newline();
o->newline() << "static int systemtap_module_init (void) {";
o->newline(1) << "int rc = 0;";
o->newline() << "int cpu;";
o->newline() << "int i=0, j=0;"; o->newline() << "const char *probe_point = \"\";";
if (! session->runtime_usermode_p())
{
XXX
o->newline() << "{";
o->newline(1) << "const char* release = UTS_RELEASE;";
o->newline() << "#ifdef STAPCONF_GENERATED_COMPILE";
o->newline() << "const char* version = UTS_VERSION;";
o->newline() << "#endif";
o->newline() << "if (strcmp (release, "
<< lex_cast_qstring (session->kernel_release) << ")) {";
o->newline(1) << "_stp_error (\"module release mismatch (%s vs %s)\", "
<< "release, "
<< lex_cast_qstring (session->kernel_release)
<< ");";
o->newline() << "rc = -EINVAL;";
o->newline(-1) << "}";
o->newline() << "#ifdef STAPCONF_GENERATED_COMPILE";
o->newline() << "if (strcmp (utsname()->version, version)) {";
o->newline(1) << "_stp_error (\"module version mismatch (%s vs %s), release %s\", "
<< "version, "
<< "utsname()->version, "
<< "release"
<< ");";
o->newline() << "rc = -EINVAL;";
o->newline(-1) << "}";
o->newline() << "#endif";
o->newline() << "if (_stp_module_check()) rc = -EINVAL;";
o->newline() << "if (_stp_privilege_credentials == 0) {";
o->newline(1) << "if (STP_PRIVILEGE_CONTAINS(STP_PRIVILEGE, STP_PR_STAPDEV) ||";
o->newline() << " STP_PRIVILEGE_CONTAINS(STP_PRIVILEGE, STP_PR_STAPUSR)) {";
o->newline(1) << "_stp_privilege_credentials = STP_PRIVILEGE;";
o->newline() << "#ifdef DEBUG_PRIVILEGE";
o->newline(1) << "_dbug(\"User's privilege credentials default to %s\\n\",";
o->newline() << " privilege_to_text(_stp_privilege_credentials));";
o->newline(-1) << "#endif";
o->newline(-1) << "}";
o->newline() << "else {";
o->newline(1) << "_stp_error (\"Unable to verify that you have the required privilege credentials to run this module (%s required). You must use staprun version 1.7 or higher.\",";
o->newline() << " privilege_to_text(STP_PRIVILEGE));";
o->newline() << "rc = -EINVAL;";
o->newline(-1) << "}";
o->newline(-1) << "}";
o->newline() << "else {";
o->newline(1) << "#ifdef DEBUG_PRIVILEGE";
o->newline(1) << "_dbug(\"User's privilege credentials provided as %s\\n\",";
o->newline() << " privilege_to_text(_stp_privilege_credentials));";
o->newline(-1) << "#endif";
o->newline() << "if (! STP_PRIVILEGE_CONTAINS(_stp_privilege_credentials, STP_PRIVILEGE)) {";
o->newline(1) << "_stp_error (\"Your privilege credentials (%s) are insufficient to run this module (%s required).\",";
o->newline () << " privilege_to_text(_stp_privilege_credentials), privilege_to_text(STP_PRIVILEGE));";
o->newline() << "rc = -EINVAL;";
o->newline(-1) << "}";
o->newline(-1) << "}";
o->newline(-1) << "}";
o->newline() << "if (rc) goto out;";
}
o->newline() << "rc = stp_session_init();";
o->newline() << "if (rc) {";
o->newline(1) << "_stp_error (\"couldn't initialize the main session (rc %d)\", rc);";
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
o->newline() << "rc = _stp_init_time();"; o->newline() << "if (rc) {";
o->newline(1) << "_stp_error (\"couldn't initialize gettimeofday\");";
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline() << "#endif";
o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
o->newline() << "rc = stp_tracepoint_init();";
o->newline() << "if (rc) {";
o->newline(1) << "_stp_error (\"couldn't initialize tracepoints\");";
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline() << "#endif";
XXX o->newline() << "(void) probe_point;";
o->newline() << "(void) i;";
o->newline() << "(void) j;";
o->newline() << "atomic_set (session_state(), STAP_SESSION_STARTING);";
o->newline() << "rc = _stp_runtime_contexts_alloc();";
o->newline() << "if (rc != 0)";
o->newline(1) << "goto out;";
o->indent(-1);
for (unsigned i=0; i<session->globals.size(); i++)
{
vardecl* v = session->globals[i];
if (v->index_types.size() > 0)
o->newline() << getmap (v).init();
else if (session->runtime_usermode_p() && v->arity == 0
&& (v->type == pe_long || v->type == pe_string))
c_assign(getvar (v).value(), "stp_global_init." + c_globalname(v->name), v->type, "BUG: global initialization", v->tok);
else
o->newline() << getvar (v).init();
o->newline() << "if (rc) {";
o->newline(1) << "_stp_error (\"global variable '" << v->name << "' allocation failed\");";
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline() << "global_lock_init(" << c_globalname (v->name) << ");";
o->newline() << "#ifdef STP_TIMING";
o->newline() << "atomic_set(global_skipped(" << c_globalname (v->name) << "), 0);";
o->newline() << "#endif";
}
if (! session->runtime_usermode_p())
o->newline() << "_stp_print_kernel_info("
<< "\"" << VERSION
<< "/" << dwfl_version (NULL) << "\""
<< ", (num_online_cpus() * sizeof(struct context))"
<< ", " << session->probes.size()
<< ");";
else
{
o->newline() << "rc = stp_session_init_finished();";
o->newline() << "if (rc) goto out;";
}
if (!session->runtime_usermode_p())
{
o->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)";
o->newline() << "INIT_WORK(&module_refresher_work, module_refresher, NULL);";
o->newline() << "#else";
o->newline() << "INIT_WORK(&module_refresher_work, module_refresher);";
o->newline() << "#endif";
}
for (unsigned i=0; i<session->probes.size(); i++)
emit_probe_condition_initialize(session->probes[i]);
for (unsigned i=0; i<g.size(); i++)
{
g[i]->emit_module_init (*session);
o->newline() << "if (rc) {";
o->newline(1) << "if (probe_point)";
o->newline(1) << "_stp_error (\"probe %s registration error (rc %d)\", probe_point, rc);";
o->indent(-1);
o->newline() << "atomic_set (session_state(), STAP_SESSION_ERROR);";
if (i>0)
for (int j=i-1; j>=0; j--)
g[j]->emit_module_exit (*session);
o->newline() << "goto out;";
o->newline(-1) << "}";
}
o->newline() << "if (atomic_read (session_state()) == STAP_SESSION_STARTING)";
o->newline(1) << "atomic_set (session_state(), STAP_SESSION_RUNNING);";
o->newline(-1);
for (unsigned i=0; i<g.size(); i++)
{
g[i]->emit_module_post_init (*session);
}
if (!session->runtime_usermode_p())
{
o->newline() << "#ifdef STP_ON_THE_FLY_TIMER_ENABLE";
o->newline() << "hrtimer_init(&module_refresh_timer, CLOCK_MONOTONIC,";
o->newline() << " HRTIMER_MODE_REL);";
o->newline() << "module_refresh_timer.function = &module_refresh_timer_cb;";
{
bool start_timer = false;
for (unsigned i=0; i<session->probes.size() && !start_timer; i++)
{
if (session->probes[i]->group
&& !session->probes[i]->group->otf_safe_context(*session)
&& !session->probes[i]->probes_with_affected_conditions.empty())
{
for (set<derived_probe*>::const_iterator
it = session->probes[i]->probes_with_affected_conditions.begin();
it != session->probes[i]->probes_with_affected_conditions.end()
&& !start_timer; ++it)
{
if ((*it)->group->otf_supported(*session))
start_timer = true;
}
}
}
if (start_timer)
{
o->newline() << "hrtimer_start(&module_refresh_timer,";
o->newline() << " ktime_set(0, STP_ON_THE_FLY_INTERVAL),";
o->newline() << " HRTIMER_MODE_REL);";
}
}
o->newline() << "#endif /* STP_ON_THE_FLY_TIMER_ENABLE */";
}
o->newline() << "return 0;";
o->newline(-1) << "out:";
o->indent(1);
for (unsigned i=0; i<session->globals.size(); i++)
{
vardecl* v = session->globals[i];
if (v->index_types.size() > 0)
o->newline() << getmap (v).fini();
else
o->newline() << getvar (v).fini();
}
o->newline() << "atomic_set (session_state(), STAP_SESSION_STOPPED);";
o->newline() << "stp_synchronize_sched();";
o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
o->newline() << " stp_tracepoint_exit();";
o->newline() << "#endif";
o->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
o->newline() << " _stp_kill_time();"; o->newline() << "#endif";
o->newline() << "_stp_runtime_contexts_free();";
o->newline() << "return rc;";
o->newline(-1) << "}\n";
}
void
c_unparser::emit_module_refresh ()
{
o->newline() << "static void systemtap_module_refresh (const char *modname) {";
o->newline(1) << "int state;";
o->newline() << "int i=0, j=0;";
if (!session->runtime_usermode_p())
{
o->newline() << "#if defined(STP_TIMING)";
o->newline() << "cycles_t cycles_atstart = get_cycles();";
o->newline() << "#endif";
}
if (!session->runtime_usermode_p())
o->newline() << "mutex_lock(&module_refresh_mutex);";
o->newline() << "state = atomic_read (session_state());";
o->newline() << "if (state != STAP_SESSION_RUNNING && state != STAP_SESSION_STARTING && state != STAP_SESSION_ERROR) {";
o->newline(1) << "#if defined(__KERNEL__)";
o->newline() << "if (state != STAP_SESSION_STOPPING)";
o->newline(1) << "printk (KERN_ERR \"stap module notifier triggered in unexpected state %d\\n\", state);";
o->indent(-1);
o->newline() << "#endif";
if (!session->runtime_usermode_p())
o->newline() << "mutex_unlock(&module_refresh_mutex);";
o->newline() << "return;";
o->newline(-1) << "}";
o->newline() << "(void) i;";
o->newline() << "(void) j;";
vector<derived_probe_group*> g = all_session_groups (*session);
for (unsigned i=0; i<g.size(); i++)
{
g[i]->emit_module_refresh (*session);
}
if (!session->runtime_usermode_p())
{
o->newline() << "#if defined(STP_TIMING)";
o->newline() << "if (likely(g_refresh_timing)) {";
o->newline(1) << "cycles_t cycles_atend = get_cycles ();";
o->newline() << "int32_t cycles_elapsed = ((int32_t)cycles_atend > (int32_t)cycles_atstart)";
o->newline(1) << "? ((int32_t)cycles_atend - (int32_t)cycles_atstart)";
o->newline() << ": (~(int32_t)0) - (int32_t)cycles_atstart + (int32_t)cycles_atend + 1;";
o->indent(-1);
o->newline() << "_stp_stat_add(g_refresh_timing, cycles_elapsed);";
o->newline(-1) << "}";
o->newline() << "#endif";
}
if (!session->runtime_usermode_p())
o->newline() << "mutex_unlock(&module_refresh_mutex);";
o->newline(-1) << "}\n";
}
void
c_unparser::emit_module_exit ()
{
o->newline() << "static void systemtap_module_exit (void) {";
o->newline(1) << "int i=0, j=0;"; o->newline() << "(void) i;";
o->newline() << "(void) j;";
o->newline() << "if (atomic_read (session_state()) == STAP_SESSION_STARTING)";
o->newline(1) << "return;";
o->indent(-1);
o->newline() << "if (atomic_read (session_state()) == STAP_SESSION_RUNNING)";
o->newline(1) << "atomic_set (session_state(), STAP_SESSION_STOPPING);";
o->indent(-1);
if (!session->runtime_usermode_p())
{
o->newline() << "#ifdef STP_ON_THE_FLY_TIMER_ENABLE";
o->newline() << "hrtimer_cancel(&module_refresh_timer);";
o->newline() << "#endif";
}
o->newline() << "stp_synchronize_sched();";
if (!session->runtime_usermode_p())
o->newline() << "mutex_lock(&module_refresh_mutex);";
vector<derived_probe_group*> g = all_session_groups (*session);
for (vector<derived_probe_group*>::reverse_iterator i = g.rbegin();
i != g.rend(); i++)
(*i)->emit_module_exit (*session);
if (!session->runtime_usermode_p())
o->newline() << "mutex_unlock(&module_refresh_mutex);";
o->newline() << "stp_synchronize_sched();";
o->newline() << "_stp_runtime_context_wait();";
o->newline() << "atomic_set (session_state(), STAP_SESSION_STOPPED);";
o->newline() << "stp_synchronize_sched();";
XXX
for (unsigned i=0; i<session->globals.size(); i++)
{
vardecl* v = session->globals[i];
if (v->index_types.size() > 0)
o->newline() << getmap (v).fini();
else
o->newline() << getvar (v).fini();
}
if (!session->runtime_usermode_p())
{
o->newline() << "_stp_runtime_contexts_free();";
}
else
{
o->newline() << "struct context* __restrict__ c;";
o->newline() << "c = _stp_runtime_entryfn_get_context();";
}
o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
o->newline() << " stp_tracepoint_exit();";
o->newline() << "#endif";
o->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
o->newline() << " _stp_kill_time();"; o->newline() << "#endif";
XXX o->newline() << "preempt_disable();";
o->newline() << "#if defined(STP_TIMING) || defined(STP_ALIBI)";
o->newline() << "_stp_printf(\"----- probe hit report: \\n\");";
o->newline() << "for (i = 0; i < ARRAY_SIZE(stap_probes); ++i) {";
o->newline(1) << "const struct stap_probe *const p = &stap_probes[i];";
o->newline() << "#ifdef STP_ALIBI";
o->newline() << "int alibi = atomic_read(probe_alibi(i));";
o->newline() << "if (alibi)";
o->newline(1) << "_stp_printf (\"%s, (%s), hits: %d,%s, index: %d\\n\",";
o->newline(2) << "p->pp, p->location, alibi, p->derivation, i);";
o->newline(-3) << "#endif"; o->newline() << "#ifdef STP_TIMING";
o->newline() << "if (likely (probe_timing(i))) {"; o->newline(1) << "struct stat_data *stats = _stp_stat_get (probe_timing(i), 0);";
o->newline() << "if (stats->count) {";
o->newline(1) << "int64_t avg = _stp_div64 (NULL, stats->sum, stats->count);";
o->newline() << "_stp_printf (\"%s, (%s), hits: %lld, "
<< (!session->runtime_usermode_p() ? "cycles" : "nsecs")
<< ": %lldmin/%lldavg/%lldmax,%s, index: %d\\n\",";
o->newline(2) << "p->pp, p->location, (long long) stats->count,";
o->newline() << "(long long) stats->min, (long long) avg, (long long) stats->max,";
o->newline() << "p->derivation, i);";
o->newline(-3) << "}";
o->newline() << "_stp_stat_del (probe_timing(i));";
o->newline(-1) << "}";
o->newline() << "#endif"; o->newline(-1) << "}";
if (!session->runtime_usermode_p())
{
o->newline() << "#if defined(STP_TIMING)";
o->newline() << "_stp_printf(\"----- refresh report:\\n\");";
o->newline() << "if (likely (g_refresh_timing)) {";
o->newline(1) << "struct stat_data *stats = _stp_stat_get (g_refresh_timing, 0);";
o->newline() << "if (stats->count) {";
o->newline(1) << "int64_t avg = _stp_div64 (NULL, stats->sum, stats->count);";
o->newline() << "_stp_printf (\"hits: %lld, cycles: %lldmin/%lldavg/%lldmax\\n\",";
o->newline(2) << "(long long) stats->count, (long long) stats->min, ";
o->newline() << "(long long) avg, (long long) stats->max);";
o->newline(-3) << "}";
o->newline() << "_stp_stat_del (g_refresh_timing);";
o->newline(-1) << "}";
o->newline() << "#endif"; }
o->newline() << "_stp_print_flush();";
o->newline() << "#endif";
o->newline() << "if (atomic_read (skipped_count()) || "
<< "atomic_read (error_count()) || "
<< "atomic_read (skipped_count_reentrant())) {"; o->newline(1) << "_stp_warn (\"Number of errors: %d, "
<< "skipped probes: %d\\n\", "
<< "(int) atomic_read (error_count()), "
<< "(int) atomic_read (skipped_count()));";
o->newline() << "#ifdef STP_TIMING";
o->newline() << "{";
o->newline(1) << "int ctr;";
for (unsigned i=0; i<session->globals.size(); i++)
{
string orig_vn = session->globals[i]->name;
string vn = c_globalname (orig_vn);
o->newline() << "ctr = atomic_read (global_skipped(" << vn << "));";
o->newline() << "if (ctr) _stp_warn (\"Skipped due to global '%s' lock timeout: %d\\n\", "
<< lex_cast_qstring(orig_vn) << ", ctr);";
}
o->newline() << "ctr = atomic_read (skipped_count_lowstack());";
o->newline() << "if (ctr) _stp_warn (\"Skipped due to low stack: %d\\n\", ctr);";
o->newline() << "ctr = atomic_read (skipped_count_reentrant());";
o->newline() << "if (ctr) _stp_warn (\"Skipped due to reentrancy: %d\\n\", ctr);";
o->newline() << "ctr = atomic_read (skipped_count_uprobe_reg());";
o->newline() << "if (ctr) _stp_warn (\"Skipped due to uprobe register failure: %d\\n\", ctr);";
o->newline() << "ctr = atomic_read (skipped_count_uprobe_unreg());";
o->newline() << "if (ctr) _stp_warn (\"Skipped due to uprobe unregister failure: %d\\n\", ctr);";
o->newline(-1) << "}";
o->newline () << "#endif";
o->newline() << "_stp_print_flush();";
o->newline(-1) << "}";
o->newline() << "preempt_enable_no_resched();";
if (session->runtime_usermode_p())
{
o->newline() << "_stp_runtime_entryfn_put_context(c);";
o->newline() << "_stp_dyninst_transport_shutdown();";
o->newline() << "_stp_runtime_contexts_free();";
}
o->newline(-1) << "}\n";
}
struct max_action_info: public functioncall_traversing_visitor
{
max_action_info(systemtap_session& s): sess(s), statement_count(0) {}
systemtap_session& sess;
unsigned statement_count;
static const unsigned max_statement_count = ~0;
void add_stmt_count (unsigned val)
{
statement_count = (statement_count > max_statement_count - val) ? max_statement_count : statement_count + val;
}
void add_max_stmt_count () { statement_count = max_statement_count; }
bool statement_count_finite() { return statement_count < max_statement_count; }
void visit_for_loop (for_loop* stmt) { add_max_stmt_count(); }
void visit_foreach_loop (foreach_loop* stmt) { add_max_stmt_count(); }
void visit_expr_statement (expr_statement *stmt)
{
add_stmt_count(1);
traversing_visitor::visit_expr_statement(stmt); }
void visit_if_statement (if_statement *stmt)
{
add_stmt_count(1);
stmt->condition->visit(this);
max_action_info tmp_visitor_then (*this);
max_action_info tmp_visitor_else (*this);
stmt->thenblock->visit(& tmp_visitor_then);
if (stmt->elseblock)
{
stmt->elseblock->visit(& tmp_visitor_else);
}
statement_count = max(tmp_visitor_then.statement_count, tmp_visitor_else.statement_count);
}
void note_recursive_functioncall (functioncall *e) { add_max_stmt_count(); }
void visit_null_statement (null_statement *stmt) { add_stmt_count(1); }
void visit_return_statement (return_statement *stmt) { add_stmt_count(1); }
void visit_delete_statement (delete_statement *stmt) { add_stmt_count(1); }
void visit_next_statement (next_statement *stmt) { add_stmt_count(1); }
void visit_break_statement (break_statement *stmt) { add_stmt_count(1); }
void visit_continue_statement (continue_statement *stmt) { add_stmt_count(1); }
};
void
c_unparser::emit_function (functiondecl* v)
{
o->newline() << "static void " << c_funcname (v->name)
<< " (struct context* __restrict__ c) {";
o->indent(1);
this->current_probe = 0;
this->current_function = v;
this->tmpvar_counter = 0;
this->action_counter = 0;
this->already_checked_action_count = false;
o->newline() << "__label__ out;";
o->newline()
<< "struct " << c_funcname (v->name) << "_locals * "
<< " __restrict__ l = "
<< "& c->locals[c->nesting+1]." << c_funcname (v->name) << ";";
o->newline() << "(void) l;"; o->newline() << "#define CONTEXT c";
o->newline() << "#define THIS l";
for (unsigned i = 0; i < v->formal_args.size(); i++) {
o->newline() << c_arg_define(v->formal_args[i]->name); }
for (unsigned i = 0; i < v->locals.size(); i++) {
o->newline() << c_arg_define(v->locals[i]->name); }
if (v->type != pe_unknown)
o->newline() << "#define STAP_RETVALUE THIS->__retvalue";
o->newline() << "c->last_stmt = " << lex_cast_qstring(*v->tok) << ";";
o->newline() << "if (unlikely (c->nesting+1 >= MAXNESTING)) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_02;
o->newline() << "return;";
o->newline(-1) << "} else {";
o->newline(1) << "c->nesting ++;";
o->newline(-1) << "}";
XXX for (unsigned i=0; i<v->locals.size(); i++)
{
if (v->locals[i]->index_types.size() > 0) throw SEMANTIC_ERROR (_("array locals not supported, missing global declaration?"),
v->locals[i]->tok);
o->newline() << getvar (v->locals[i]).init();
}
if (v->type != pe_unknown)
{
var retvalue = var(this, true, v->type, "__retvalue", false); o->newline() << retvalue.init();
}
switch (v->type)
{
case pe_long:
o->newline() << "#define STAP_RETURN(v) do { STAP_RETVALUE = (int64_t) (v); "
"goto out; } while(0)";
break;
case pe_string:
o->newline() <<
"#define STAP_RETURN(v) do { strlcpy(STAP_RETVALUE, (v), MAXSTRINGLEN); "
"goto out; } while(0)";
break;
default:
o->newline() << "#define STAP_RETURN() do { goto out; } while(0)";
break;
}
o->newline() << "#define STAP_ERROR(...) do { snprintf(CONTEXT->error_buffer, MAXSTRINGLEN, __VA_ARGS__); CONTEXT->last_error = CONTEXT->error_buffer; goto out; } while (0)";
o->newline() << "#define return goto out";
max_action_info mai (*session);
v->body->visit (&mai);
if (mai.statement_count_finite() && !session->suppress_time_limits
&& !session->unoptimized) {
o->newline() << "if (c->actionremaining < " << mai.statement_count
<< ") { c->last_error = " << STAP_T_04 << "goto out; }";
this->already_checked_action_count = true;
}
v->body->visit (this);
o->newline() << "#undef return";
o->newline() << "#undef STAP_ERROR";
o->newline() << "#undef STAP_RETURN";
this->current_function = 0;
record_actions(0, v->body->tok, true);
o->newline(-1) << "out:";
o->newline(1) << "if (0) goto out;";
o->newline() << "c->nesting --;";
o->newline() << "#undef CONTEXT";
o->newline() << "#undef THIS";
for (unsigned i = 0; i < v->formal_args.size(); i++) {
o->newline() << c_arg_undef(v->formal_args[i]->name); }
for (unsigned i = 0; i < v->locals.size(); i++) {
o->newline() << c_arg_undef(v->locals[i]->name); }
o->newline() << "#undef STAP_RETVALUE";
o->newline(-1) << "}\n";
this->already_checked_action_count = false;
}
#define DUPMETHOD_CALL 0
#define DUPMETHOD_ALIAS 0
#define DUPMETHOD_RENAME 1
void
c_unparser::emit_probe (derived_probe* v)
{
this->current_function = 0;
this->current_probe = v;
this->tmpvar_counter = 0;
this->action_counter = 0;
this->already_checked_action_count = false;
ostringstream oss;
v->print_dupe_stamp (oss);
v->body->print(oss);
oss << "# needs_global_locks: " << v->needs_global_locks () << endl;
if (!session->unoptimized && probe_contents.count(oss.str()) != 0)
{
string dupe = probe_contents[oss.str()];
if (session->verbose > 1)
clog << _F("%s elided, duplicates %s\n", v->name.c_str(), dupe.c_str());
#if DUPMETHOD_CALL
o->newline();
o->newline() << "static void " << v->name << " (struct context * __restrict__ c) ";
o->newline() << "{ " << dupe << " (c); }";
#elif DUPMETHOD_ALIAS
o->newline();
o->newline() << "static void " << v->name << " (struct context * __restrict__ c) ";
o->line() << "__attribute__ ((alias (\"" << dupe << "\")));";
#elif DUPMETHOD_RENAME
v->name = dupe;
#else
#error "Unknown duplicate elimination method"
#endif
}
else {
o->newline();
o->newline() << "static void " << v->name << " (struct context * __restrict__ c) ";
o->line () << "{";
o->indent (1);
probe_contents[oss.str()] = v->name;
o->newline() << "__label__ out;";
if (v->needs_global_locks ())
{
varuse_collecting_visitor vut(*session);
v->body->visit (& vut);
for (set<derived_probe*>::const_iterator
it = v->probes_with_affected_conditions.begin();
it != v->probes_with_affected_conditions.end(); ++it)
{
assert((*it)->sole_location()->condition != NULL);
(*it)->sole_location()->condition->visit (& vut);
}
emit_lock_decls (vut);
}
o->newline() << "struct " << v->name << "_locals * __restrict__ l = "
<< "& c->probe_locals." << v->name << ";";
o->newline() << "(void) l;";
if (!session->runtime_usermode_p())
v->emit_privilege_assertion (o);
v->emit_probe_local_init(*this->session, o);
if (v->needs_global_locks ())
emit_locks ();
for (unsigned j=0; j<v->locals.size(); j++)
{
if (v->locals[j]->synthetic)
continue;
if (v->locals[j]->index_types.size() > 0) throw SEMANTIC_ERROR (_("array locals not supported, missing global declaration?"),
v->locals[j]->tok);
else if (v->locals[j]->type == pe_long)
o->newline() << "l->" << c_localname (v->locals[j]->name)
<< " = 0;";
else if (v->locals[j]->type == pe_string)
o->newline() << "l->" << c_localname (v->locals[j]->name)
<< "[0] = '\\0';";
else
throw SEMANTIC_ERROR (_("unsupported local variable type"),
v->locals[j]->tok);
}
v->initialize_probe_context_vars (o);
max_action_info mai (*session);
v->body->visit (&mai);
if (session->verbose > 1)
clog << _F("%d statements for probe %s", mai.statement_count, v->name.c_str()) << endl;
if (mai.statement_count_finite() && !session->suppress_time_limits
&& !session->unoptimized) {
o->newline() << "if (c->actionremaining < " << mai.statement_count
<< ") { c->last_error = " << STAP_T_04 << " goto out; }";
this->already_checked_action_count = true;
}
v->body->visit (this);
record_actions(0, v->body->tok, true);
o->newline(-1) << "out:";
o->indent(1);
if (!v->probes_with_affected_conditions.empty())
{
for (set<derived_probe*>::const_iterator
it = v->probes_with_affected_conditions.begin();
it != v->probes_with_affected_conditions.end(); ++it)
emit_probe_condition_update(*it);
}
if (v->needs_global_locks ())
emit_unlocks ();
XXX o->newline() << "_stp_print_flush();";
o->newline(-1) << "}\n";
}
this->current_probe = 0;
this->already_checked_action_count = false;
}
void
c_unparser::emit_probe_condition_initialize(derived_probe* v)
{
unsigned i = v->session_index;
assert(i < session->probes.size());
expression *cond = v->sole_location()->condition;
string cond_enabled = "stap_probes[" + lex_cast(i) + "].cond_enabled";
if (!cond)
return;
o->newline() << cond_enabled << " = !!";
cond->visit(this);
o->line() << ";";
}
void
c_unparser::emit_probe_condition_update(derived_probe* v)
{
unsigned i = v->session_index;
assert(i < session->probes.size());
expression *cond = v->sole_location()->condition;
assert(cond);
string cond_enabled = "stap_probes[" + lex_cast(i) + "].cond_enabled";
o->newline() << "if (" << cond_enabled << " != ";
o->line() << "!!"; v->sole_location()->condition->visit(this);
o->line() << ") {";
o->newline(1) << cond_enabled << " ^= 1;";
if (!session->runtime_usermode_p()
&& v->group && v->group->otf_supported(*session))
o->newline() << "atomic_set(&need_module_refresh, 1);";
o->newline(-1) << "}";
}
void
c_unparser::emit_lock_decls(const varuse_collecting_visitor& vut)
{
unsigned numvars = 0;
if (session->verbose > 1)
clog << "probe " << current_probe->session_index << " "
"('" << *current_probe->sole_location() << "') locks";
o->newline();
if (!session->runtime_usermode_p())
o->line() << "static ";
o->line() << "const struct stp_probe_lock locks[] = {";
o->indent(1);
for (unsigned i = 0; i < session->globals.size(); i++)
{
vardecl* v = session->globals[i];
bool read_p = vut.read.count(v) > 0;
bool write_p = vut.written.count(v) > 0;
if (!read_p && !write_p) continue;
bool written_p;
if (v->type == pe_stats) {
if (write_p && !read_p) { read_p = true; write_p = false; }
else if (read_p && !write_p) { read_p = false; write_p = true; }
written_p = vcv_needs_global_locks.read.count(v) > 0;
}
else
written_p = vcv_needs_global_locks.written.count(v) > 0;
if (!written_p && read_p && !write_p)
continue;
o->newline() << "{";
o->newline(1) << ".lock = global_lock(" + c_globalname(v->name) + "),";
o->newline() << ".write_p = " << (write_p ? 1 : 0) << ",";
o->newline() << "#ifdef STP_TIMING";
o->newline() << ".skipped = global_skipped(" << c_globalname (v->name) << "),";
o->newline() << "#endif";
o->newline(-1) << "},";
numvars ++;
if (session->verbose > 1)
clog << " " << v->name << "[" << (read_p ? "r" : "")
<< (write_p ? "w" : "") << "]";
}
o->newline(-1) << "};";
if (session->verbose > 1)
{
if (!numvars)
clog << _(" nothing");
clog << endl;
}
}
void
c_unparser::emit_locks()
{
o->newline() << "if (!stp_lock_probe(locks, ARRAY_SIZE(locks)))";
o->newline(1) << "return;";
o->indent(-1);
}
void
c_unparser::emit_unlocks()
{
o->newline() << "stp_unlock_probe(locks, ARRAY_SIZE(locks));";
}
void
c_unparser::collect_map_index_types(vector<vardecl *> const & vars,
set< pair<vector<exp_type>, exp_type> > & types)
{
for (unsigned i = 0; i < vars.size(); ++i)
{
vardecl *v = vars[i];
if (v->arity > 0)
{
types.insert(make_pair(v->index_types, v->type));
}
}
}
string
mapvar::value_typename(exp_type e)
{
switch (e)
{
case pe_long:
return "INT64";
case pe_string:
return "STRING";
case pe_stats:
return "STAT";
default:
throw SEMANTIC_ERROR(_("array type is neither string nor long"));
}
}
string
mapvar::key_typename(exp_type e)
{
switch (e)
{
case pe_long:
return "INT64";
case pe_string:
return "STRING";
default:
throw SEMANTIC_ERROR(_("array key is neither string nor long"));
}
}
string
mapvar::shortname(exp_type e)
{
switch (e)
{
case pe_long:
return "i";
case pe_string:
return "s";
default:
throw SEMANTIC_ERROR(_("array type is neither string nor long"));
}
}
void
c_unparser::emit_map_type_instantiations ()
{
set< pair<vector<exp_type>, exp_type> > types;
collect_map_index_types(session->globals, types);
for (unsigned i = 0; i < session->probes.size(); ++i)
collect_map_index_types(session->probes[i]->locals, types);
for (map<string,functiondecl*>::iterator it = session->functions.begin(); it != session->functions.end(); it++)
collect_map_index_types(it->second->locals, types);
if (!types.empty())
o->newline() << "#include \"alloc.c\"";
for (set< pair<vector<exp_type>, exp_type> >::const_iterator i = types.begin();
i != types.end(); ++i)
{
o->newline() << "#define VALUE_TYPE " << mapvar::value_typename(i->second);
for (unsigned j = 0; j < i->first.size(); ++j)
{
string ktype = mapvar::key_typename(i->first.at(j));
o->newline() << "#define KEY" << (j+1) << "_TYPE " << ktype;
}
if (i->second == pe_stats)
o->newline() << "#define MAP_DO_PMAP 1";
o->newline() << "#include \"map-gen.c\"";
o->newline() << "#undef MAP_DO_PMAP";
o->newline() << "#undef VALUE_TYPE";
for (unsigned j = 0; j < i->first.size(); ++j)
{
o->newline() << "#undef KEY" << (j+1) << "_TYPE";
}
}
if (!types.empty())
o->newline() << "#include \"map.c\"";
};
string
c_unparser::c_typename (exp_type e)
{
switch (e)
{
case pe_long: return string("int64_t");
case pe_string: return string("string_t");
case pe_stats: return string("Stat");
case pe_unknown:
default:
throw SEMANTIC_ERROR (_("cannot expand unknown type"));
}
}
XXX
string
c_unparser::c_localname (const string& e, bool mangle_oldstyle)
{
if (strverscmp(session->compatible.c_str(), "1.8") < 0 || mangle_oldstyle)
return e; else
XXX return "l_" + e;
}
string
c_unparser::c_globalname (const string& e)
{
XXX return "s_" + e;
}
string
c_unparser::c_funcname (const string& e)
{
XXX return "function_" + e;
}
string
c_unparser::c_arg_define (const string& e)
{
return "#define STAP_ARG_" + e + " THIS->" + c_localname(e);
}
string
c_unparser::c_arg_undef (const string& e)
{
return "#undef STAP_ARG_" + e;
}
string
c_unparser::c_expression (expression *e)
{
if (e->tok->type != tok_number && e->tok->type != tok_string)
throw SEMANTIC_ERROR(_("unsupported c_expression token type"));
ostringstream oss;
translator_output tmp_o(oss);
translator_output *saved_o = o;
o = &tmp_o;
e->visit (this);
o = saved_o;
return (oss.str());
}
void
c_unparser::c_assign (var& lvalue, const string& rvalue, const token *tok)
{
switch (lvalue.type())
{
case pe_string:
c_strcpy(lvalue.value(), rvalue);
break;
case pe_long:
o->newline() << lvalue << " = " << rvalue << ";";
break;
default:
throw SEMANTIC_ERROR (_("unknown lvalue type in assignment"), tok);
}
}
struct expression_is_functioncall : public expression_visitor
{
c_unparser* parent;
functioncall* fncall;
expression_is_functioncall (c_unparser* p)
: parent(p), fncall(NULL) {}
void visit_expression (expression* e)
{
fncall = NULL;
}
void visit_functioncall (functioncall* e)
{
fncall = e;
}
};
void
c_unparser::c_assign (const string& lvalue, expression* rvalue,
const string& msg)
{
if (rvalue->type == pe_long)
{
o->newline() << lvalue << " = ";
rvalue->visit (this);
o->line() << ";";
}
else if (rvalue->type == pe_string)
{
expression_is_functioncall eif (this);
rvalue->visit(& eif);
if (!session->unoptimized && eif.fncall)
{
eif.fncall->var_assigned_to_retval = lvalue;
eif.fncall->visit (this);
o->line() << ";";
}
else
{
c_strcpy (lvalue, rvalue);
}
}
else
{
string fullmsg = msg + _(" type unsupported");
throw SEMANTIC_ERROR (fullmsg, rvalue->tok);
}
}
void
c_unparser::c_assign (const string& lvalue, const string& rvalue,
exp_type type, const string& msg, const token* tok)
{
if (type == pe_long)
{
o->newline() << lvalue << " = " << rvalue << ";";
}
else if (type == pe_string)
{
c_strcpy (lvalue, rvalue);
}
else
{
string fullmsg = msg + _(" type unsupported");
throw SEMANTIC_ERROR (fullmsg, tok);
}
}
void
c_unparser_assignment::c_assignop(tmpvar & res,
var const & lval,
tmpvar const & rval,
token const * tok)
{
translator_output* o = parent->o;
if (res.type() == pe_string)
{
if (post)
throw SEMANTIC_ERROR (_("post assignment on strings not supported"),
tok);
if (op == "=")
{
parent->c_strcpy (lval.value(), rval.value());
res = rval;
}
else if (op == ".=")
{
parent->c_strcat (lval.value(), rval.value());
res = lval;
}
else
throw SEMANTIC_ERROR (_F("string assignment operator %s unsupported", op.c_str()), tok);
}
else if (op == "<<<")
{
assert(lval.type() == pe_stats);
assert(rval.type() == pe_long);
assert(res.type() == pe_long);
o->newline() << "_stp_stat_add (" << lval << ", " << rval << ");";
res = rval;
}
else if (res.type() == pe_long)
{
string macop;
unsigned oplen = op.size();
if (op == "=")
macop = "*error*"; else if (op == "++" || op == "+=")
macop = "+=";
else if (op == "--" || op == "-=")
macop = "-=";
else if (oplen > 1 && op[oplen-1] == '=') macop = op;
else
throw SEMANTIC_ERROR (_("unknown macop for assignment"), tok);
if (post)
{
if (macop == "/" || macop == "%" || op == "=")
throw SEMANTIC_ERROR (_("invalid post-mode operator"), tok);
o->newline() << res << " = " << lval << ";";
if (macop == "+=" || macop == "-=")
o->newline() << lval << " " << macop << " " << rval << ";";
else
o->newline() << lval << " = " << res << " " << macop << " " << rval << ";";
}
else
{
if (op == "=") {
o->newline() << lval << " = " << rval << ";";
res = rval;
}
else
{
if (macop == "/=" || macop == "%=")
{
o->newline() << "if (unlikely(!" << rval << ")) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_03;
o->newline() << "c->last_stmt = " << lex_cast_qstring(*rvalue->tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline() << lval << " = "
<< ((macop == "/=") ? "_stp_div64" : "_stp_mod64")
<< " (NULL, " << lval << ", " << rval << ");";
}
else
o->newline() << lval << " " << macop << " " << rval << ";";
res = lval;
}
}
}
else
throw SEMANTIC_ERROR (_("assignment type not yet implemented"), tok);
}
void
c_unparser::c_declare(exp_type ty, const string &ident)
{
o->newline() << c_typename (ty) << " " << ident << ";";
}
void
c_unparser::c_declare_static(exp_type ty, const string &ident)
{
o->newline() << "static " << c_typename (ty) << " " << ident << ";";
}
void
c_unparser::c_strcpy (const string& lvalue, const string& rvalue)
{
o->newline() << "strlcpy ("
<< lvalue << ", "
<< rvalue << ", MAXSTRINGLEN);";
}
void
c_unparser::c_strcpy (const string& lvalue, expression* rvalue)
{
o->newline() << "strlcpy (" << lvalue << ", ";
rvalue->visit (this);
o->line() << ", MAXSTRINGLEN);";
}
void
c_unparser::c_strcat (const string& lvalue, const string& rvalue)
{
o->newline() << "strlcat ("
<< lvalue << ", "
<< rvalue << ", MAXSTRINGLEN);";
}
void
c_unparser::c_strcat (const string& lvalue, expression* rvalue)
{
o->newline() << "strlcat (" << lvalue << ", ";
rvalue->visit (this);
o->line() << ", MAXSTRINGLEN);";
}
bool
c_unparser::is_local(vardecl const *r, token const *tok)
{
if (current_probe)
{
for (unsigned i=0; i<current_probe->locals.size(); i++)
{
if (current_probe->locals[i] == r)
return true;
}
}
else if (current_function)
{
for (unsigned i=0; i<current_function->locals.size(); i++)
{
if (current_function->locals[i] == r)
return true;
}
for (unsigned i=0; i<current_function->formal_args.size(); i++)
{
if (current_function->formal_args[i] == r)
return true;
}
}
for (unsigned i=0; i<session->globals.size(); i++)
{
if (session->globals[i] == r)
return false;
}
if (tok)
throw SEMANTIC_ERROR (_("unresolved symbol"), tok);
else
throw SEMANTIC_ERROR (_("unresolved symbol: ") + r->name);
}
tmpvar
c_unparser::gensym(exp_type ty)
{
return tmpvar (this, ty, tmpvar_counter);
}
aggvar
c_unparser::gensym_aggregate()
{
return aggvar (this, tmpvar_counter);
}
var
c_unparser::getvar(vardecl *v, token const *tok)
{
bool loc = is_local (v, tok);
if (loc)
return var (this, loc, v->type, v->name);
else
{
statistic_decl sd;
std::map<std::string, statistic_decl>::const_iterator i;
i = session->stat_decls.find(v->name);
if (i != session->stat_decls.end())
sd = i->second;
return var (this, loc, v->type, sd, v->name);
}
}
mapvar
c_unparser::getmap(vardecl *v, token const *tok)
{
if (v->arity < 1)
throw SEMANTIC_ERROR(_("attempt to use scalar where map expected"), tok);
statistic_decl sd;
std::map<std::string, statistic_decl>::const_iterator i;
i = session->stat_decls.find(v->name);
if (i != session->stat_decls.end())
sd = i->second;
return mapvar (this, is_local (v, tok), v->type, sd,
v->name, v->index_types, v->maxsize, v->wrap);
}
itervar
c_unparser::getiter(symbol *s)
{
return itervar (s, tmpvar_counter);
}
void
c_unparser::record_actions (unsigned actions, const token* tok, bool update)
{
action_counter += actions;
if (((update && action_counter > 0) || action_counter >= 10)
&& !session->suppress_time_limits && !already_checked_action_count)
{
o->newline() << "c->actionremaining -= " << action_counter << ";";
o->newline() << "if (unlikely (c->actionremaining <= 0)) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_04;
XXX if (tok)
o->newline() << "c->last_stmt = " << lex_cast_qstring(*tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "}";
action_counter = 0;
}
}
void
c_unparser::visit_block (block *s)
{
o->newline() << "{";
o->indent (1);
for (unsigned i=0; i<s->statements.size(); i++)
{
try
{
s->statements[i]->visit (this);
o->newline();
}
catch (const semantic_error& e)
{
session->print_error (e);
}
}
o->newline(-1) << "}";
}
void c_unparser::visit_try_block (try_block *s)
{
record_actions(0, s->tok, true);
o->newline() << "{";
o->newline(1) << "__label__ normal_fallthrough;";
o->newline(1) << "{";
o->newline() << "__label__ out;";
assert (!session->unoptimized || s->try_block); if (s->try_block)
{
s->try_block->visit (this);
record_actions(0, s->try_block->tok, true); }
o->newline() << "goto normal_fallthrough;";
o->newline() << "if (0) goto out;"; o->newline() << "out:";
o->newline() << ";";
o->newline(-1) << "}";
o->newline() << "if (likely(c->last_error == NULL)) goto out;";
if (s->catch_error_var)
{
var cev(getvar(s->catch_error_var->referent, s->catch_error_var->tok));
c_strcpy (cev.value(), "c->last_error");
}
o->newline() << "c->last_error = NULL;";
record_actions(1, s->tok, true);
if (s->catch_block)
{
s->catch_block->visit (this);
record_actions(0, s->catch_block->tok, true); }
o->newline() << "normal_fallthrough:";
o->newline() << ";"; o->newline(-1) << "}";
}
void
c_unparser::visit_embeddedcode (embeddedcode *s)
{
if (s->code.find ("/* myproc-unprivileged */") != string::npos)
o->newline() << "assert_is_myproc();";
o->newline() << "{";
o->newline(1) << s->code;
o->newline(-1) << "}";
}
void
c_unparser::visit_null_statement (null_statement *)
{
o->newline() << "/* null */;";
}
void
c_unparser::visit_expr_statement (expr_statement *s)
{
o->newline() << "(void) ";
s->value->visit (this);
o->line() << ";";
record_actions(1, s->tok);
}
void
c_unparser::visit_if_statement (if_statement *s)
{
record_actions(1, s->tok, true);
o->newline() << "if (";
o->indent (1);
s->condition->visit (this);
o->indent (-1);
o->line() << ") {";
o->indent (1);
s->thenblock->visit (this);
record_actions(0, s->thenblock->tok, true);
o->newline(-1) << "}";
if (s->elseblock)
{
o->newline() << "else {";
o->indent (1);
s->elseblock->visit (this);
record_actions(0, s->elseblock->tok, true);
o->newline(-1) << "}";
}
}
void
c_tmpcounter::visit_block (block *s)
{
parent->o->newline() << "union {";
parent->o->indent(1);
for (unsigned i=0; i<s->statements.size(); i++)
{
std::ostream::pos_type before_struct_pos = parent->o->tellp();
parent->o->newline() << "struct {";
parent->o->indent(1);
std::ostream::pos_type after_struct_pos = parent->o->tellp();
s->statements[i]->visit (this);
parent->o->indent(-1);
if (after_struct_pos == parent->o->tellp())
parent->o->seekp(before_struct_pos);
else
parent->o->newline() << "};";
}
parent->o->newline(-1) << "};";
}
void
c_tmpcounter::visit_for_loop (for_loop *s)
{
if (s->init) s->init->visit (this);
s->cond->visit (this);
s->block->visit (this);
if (s->incr) s->incr->visit (this);
}
void
c_unparser::visit_for_loop (for_loop *s)
{
string ctr = lex_cast (label_counter++);
string toplabel = "top_" + ctr;
string contlabel = "continue_" + ctr;
string breaklabel = "break_" + ctr;
if (s->init) s->init->visit (this);
record_actions(1, s->tok, true);
o->newline(-1) << toplabel << ":";
o->indent(1);
record_actions(1, s->tok);
o->newline() << "if (! (";
if (s->cond->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric type"), s->cond->tok);
s->cond->visit (this);
o->line() << ")) goto " << breaklabel << ";";
loop_break_labels.push_back (breaklabel);
loop_continue_labels.push_back (contlabel);
s->block->visit (this);
record_actions(0, s->block->tok, true);
loop_break_labels.pop_back ();
loop_continue_labels.pop_back ();
o->newline(-1) << contlabel << ":";
o->indent(1);
if (s->incr) s->incr->visit (this);
o->newline() << "goto " << toplabel << ";";
o->newline(-1) << breaklabel << ":";
o->newline(1) << "; /* dummy statement */";
}
struct arrayindex_downcaster
: public traversing_visitor
{
arrayindex *& arr;
arrayindex_downcaster (arrayindex *& arr)
: arr(arr)
{}
void visit_arrayindex (arrayindex* e)
{
arr = e;
}
};
static bool
expression_is_arrayindex (expression *e,
arrayindex *& hist)
{
arrayindex *h = NULL;
arrayindex_downcaster d(h);
e->visit (&d);
if (static_cast<void*>(h) == static_cast<void*>(e))
{
hist = h;
return true;
}
return false;
}
void
c_unparser::visit_foreach_loop_value (visitor* vis, foreach_loop* s,
const string& value)
{
bool stable_value = false;
symbol *array;
hist_op *hist;
classify_indexable (s->base, array, hist);
if (!(hist && get_symbol_within_expression(hist->stat)->referent->arity > 0))
{
set<vardecl*> indexes;
for (unsigned i=0; i < s->indexes.size(); ++i)
indexes.insert(s->indexes[i]->referent);
varuse_collecting_visitor v(*session);
s->block->visit (&v);
v.embedded_seen = false; if (v.side_effect_free_wrt(indexes))
stable_value = true;
}
if (stable_value)
{
arrayindex ai;
ai.base = s->base;
for (unsigned i=0; i < s->indexes.size(); ++i)
ai.indexes.push_back(s->indexes[i]);
string loopai = lex_cast(ai);
foreach_loop_values[loopai] = value;
s->block->visit (vis);
foreach_loop_values.erase(loopai);
}
else
s->block->visit (vis);
}
bool
c_unparser::get_foreach_loop_value (arrayindex* ai, string& value)
{
if (!ai)
return false;
map<string,string>::iterator it = foreach_loop_values.find(lex_cast(*ai));
if (it == foreach_loop_values.end())
return false;
value = it->second;
return true;
}
void
c_tmpcounter::visit_foreach_loop (foreach_loop *s)
{
symbol *array;
hist_op *hist;
classify_indexable (s->base, array, hist);
if (array)
{
itervar iv = parent->getiter (array);
parent->o->newline() << iv.declare();
for (unsigned i=0; i<s->array_slice.size(); i++)
{
if (s->array_slice[i])
{
tmpvar slice_index = parent->gensym (s->array_slice[i]->type);
slice_index.declare(*parent);
s->array_slice[i]->visit (this);
}
}
}
else
{
if (s->indexes.size() != 1 || s->indexes[0]->referent->type != pe_long)
throw SEMANTIC_ERROR(_("Invalid indexing of histogram"), s->tok);
aggvar agg = parent->gensym_aggregate ();
agg.declare(*(this->parent));
load_aggregate (hist->stat);
}
if (s->limit)
{
tmpvar res_limit = parent->gensym (pe_long);
res_limit.declare(*parent);
s->limit->visit (this);
tmpvar limitv = parent->gensym (pe_long);
limitv.declare(*parent);
}
parent->visit_foreach_loop_value(this, s);
}
void
c_unparser::visit_foreach_loop (foreach_loop *s)
{
symbol *array;
hist_op *hist;
classify_indexable (s->base, array, hist);
string ctr = lex_cast (label_counter++);
string toplabel = "top_" + ctr;
string contlabel = "continue_" + ctr;
string breaklabel = "break_" + ctr;
if (array)
{
mapvar mv = getmap (array->referent, s->tok);
itervar iv = getiter (array);
vector<var> keys;
tmpvar *res_limit = NULL;
if (s->limit)
{
res_limit = new tmpvar(gensym(pe_long));
c_assign (res_limit->value(), s->limit, "foreach limit");
}
if (mv.is_parallel())
{
o->newline() << "if (unlikely(NULL == " << mv.calculate_aggregate() << ")) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_05 << mv << "\";";
o->newline() << "c->last_stmt = " << lex_cast_qstring(*s->tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "}";
if (s->sort_direction)
{
string sort_column;
if (s->sort_column == 0)
switch (s->sort_aggr) {
default: case sc_none: case sc_count: sort_column = "SORT_COUNT"; break;
case sc_sum: sort_column = "SORT_SUM"; break;
case sc_min: sort_column = "SORT_MIN"; break;
case sc_max: sort_column = "SORT_MAX"; break;
case sc_average: sort_column = "SORT_AVG"; break;
}
else
sort_column = lex_cast(s->sort_column);
o->newline() << "else"; if (s->limit)
{
o->newline(1) << mv.function_keysym("sortn", true) <<" ("
<< mv.fetch_existing_aggregate() << ", "
<< *res_limit << ", " << sort_column << ", "
<< - s->sort_direction << ");";
}
else
{
o->newline(1) << mv.function_keysym("sort", true) <<" ("
<< mv.fetch_existing_aggregate() << ", "
<< sort_column << ", "
<< - s->sort_direction << ");";
}
o->indent(-1);
}
}
else
{
if (s->sort_direction)
{
if (s->limit)
{
o->newline() << mv.function_keysym("sortn") <<" ("
<< mv.value() << ", "
<< *res_limit << ", " << s->sort_column << ", "
<< - s->sort_direction << ");";
}
else
{
o->newline() << mv.function_keysym("sort") <<" ("
<< mv.value() << ", "
<< s->sort_column << ", "
<< - s->sort_direction << ");";
}
}
}
if (mv.is_parallel())
aggregations_active.insert(mv.value());
o->newline() << iv << " = " << iv.start (mv) << ";";
tmpvar *limitv = NULL;
if (s->limit)
{
limitv = new tmpvar(gensym (pe_long));
o->newline() << *limitv << " = 0LL;";
}
vector<tmpvar *> array_slice_vars;
if (!s->array_slice.empty())
for (unsigned i = 0; i < s->array_slice.size(); ++i)
{
if (s->array_slice[i])
{
tmpvar *asvar = new tmpvar(gensym(s->array_slice[i]->type));
c_assign(asvar->value(), s->array_slice[i], "array slice index");
array_slice_vars.push_back(asvar);
}
else
array_slice_vars.push_back(NULL);
}
record_actions(1, s->tok, true);
o->newline(-1) << toplabel << ":";
o->indent(1);
record_actions(1, s->tok);
o->newline() << "if (! (" << iv << ")) goto " << breaklabel << ";";
loop_break_labels.push_back (breaklabel);
loop_continue_labels.push_back (contlabel);
o->newline() << "{";
o->indent (1);
if (s->limit)
{
o->newline() << "if (" << *limitv << "++ >= " << *res_limit
<< ") goto " << breaklabel << ";";
delete limitv;
delete res_limit;
}
for (unsigned i = 0; i < s->indexes.size(); ++i)
{
var v = getvar (s->indexes[i]->referent);
c_assign (v, iv.get_key (mv, v.type(), i), s->tok);
}
if (!s->array_slice.empty())
{
o->newline() << "if (0"; for (unsigned i = 0; i < s->array_slice.size(); ++i)
if (s->array_slice[i])
{
o->line() << " || ";
if (s->indexes[i]->type == pe_string)
{
if (s->array_slice[i]->type != pe_string)
throw SEMANTIC_ERROR (_("expected string types"), s->tok);
o->line() << "strncmp(" << getvar (s->indexes[i]->referent)
<< ", " << *array_slice_vars[i];
o->line() << ", MAXSTRINGLEN) !=0";
}
else if (s->indexes[i]->type == pe_long)
{
if (s->array_slice[i]->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric types"), s->tok);
o->line() << getvar (s->indexes[i]->referent) << " != "
<< *array_slice_vars[i];
}
else
{
throw SEMANTIC_ERROR (_("unexpected type"), s->tok);
}
}
o->line() << ") goto " << contlabel << ";"; }
if (s->value)
{
var v = getvar (s->value->referent);
c_assign (v, iv.get_value (mv, v.type()), s->tok);
}
visit_foreach_loop_value(this, s, iv.get_value(mv, array->type));
record_actions(0, s->block->tok, true);
o->newline(-1) << "}";
loop_break_labels.pop_back ();
loop_continue_labels.pop_back ();
o->newline(-1) << contlabel << ":";
o->newline(1) << iv << " = " << iv.next (mv) << ";";
o->newline() << "goto " << toplabel << ";";
o->newline(-1) << breaklabel << ":";
o->newline(1) << "; /* dummy statement */";
if (mv.is_parallel())
aggregations_active.erase(mv.value());
}
else
{
assert(s->indexes.size() == 1);
assert(s->indexes[0]->referent->type == pe_long);
var bucketvar = getvar (s->indexes[0]->referent);
aggvar agg = gensym_aggregate ();
var *v = load_aggregate(hist->stat, agg);
v->assert_hist_compatible(*hist);
tmpvar *res_limit = NULL;
tmpvar *limitv = NULL;
if (s->limit)
{
res_limit = new tmpvar(gensym(pe_long));
c_assign (res_limit->value(), s->limit, "foreach limit");
limitv = new tmpvar(gensym (pe_long));
o->newline() << *limitv << " = 0LL;";
}
record_actions(1, s->tok, true);
o->newline() << "for (" << bucketvar << " = 0; "
<< bucketvar << " < " << v->buckets() << "; "
<< bucketvar << "++) { ";
o->newline(1);
loop_break_labels.push_back (breaklabel);
loop_continue_labels.push_back (contlabel);
if (s->limit)
{
o->newline() << "if (" << *limitv << "++ >= " << *res_limit
<< ") break;";
delete limitv;
delete res_limit;
}
if (s->value)
{
var v = getvar (s->value->referent);
c_assign (v, agg.get_hist (bucketvar), s->tok);
}
visit_foreach_loop_value(this, s, agg.get_hist(bucketvar));
record_actions(1, s->block->tok, true);
o->newline(-1) << contlabel << ":";
o->newline(1) << "continue;";
o->newline(-1) << breaklabel << ":";
o->newline(1) << "break;";
o->newline(-1) << "}";
loop_break_labels.pop_back ();
loop_continue_labels.pop_back ();
delete v;
}
}
void
c_unparser::visit_return_statement (return_statement* s)
{
if (current_function == 0)
throw SEMANTIC_ERROR (_("cannot 'return' from probe"), s->tok);
if (s->value->type != current_function->type)
throw SEMANTIC_ERROR (_("return type mismatch"), current_function->tok,
s->tok);
c_assign ("l->__retvalue", s->value, "return value");
record_actions(1, s->tok, true);
o->newline() << "goto out;";
}
void
c_unparser::visit_next_statement (next_statement* s)
{
if (current_probe == 0)
throw SEMANTIC_ERROR (_("cannot 'next' from function"), s->tok);
record_actions(1, s->tok, true);
o->newline() << "goto out;";
}
struct delete_statement_operand_tmp_visitor:
public traversing_visitor
{
c_tmpcounter *parent;
delete_statement_operand_tmp_visitor (c_tmpcounter *p):
parent (p)
{}
void visit_arrayindex (arrayindex* e);
};
struct delete_statement_operand_visitor:
public throwing_visitor
{
c_unparser *parent;
delete_statement_operand_visitor (c_unparser *p):
throwing_visitor (_("invalid operand of delete expression")),
parent (p)
{}
void visit_symbol (symbol* e);
void visit_arrayindex (arrayindex* e);
};
void
delete_statement_operand_visitor::visit_symbol (symbol* e)
{
assert (e->referent != 0);
if (e->referent->arity > 0)
{
mapvar mvar = parent->getmap(e->referent, e->tok);
if (mvar.is_parallel())
parent->o->newline() << "_stp_pmap_clear (" << mvar.value() << ");";
else
parent->o->newline() << "_stp_map_clear (" << mvar.value() << ");";
}
else
{
var v = parent->getvar(e->referent, e->tok);
switch (e->type)
{
case pe_stats:
parent->o->newline() << "_stp_stat_clear (" << v.value() << ");";
break;
case pe_long:
parent->o->newline() << v.value() << " = 0;";
break;
case pe_string:
parent->o->newline() << v.value() << "[0] = '\\0';";
break;
case pe_unknown:
default:
throw SEMANTIC_ERROR(_("Cannot delete unknown expression type"), e->tok);
}
}
}
void
delete_statement_operand_tmp_visitor::visit_arrayindex (arrayindex* e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
assert (array->referent != 0);
vardecl* r = array->referent;
bool array_slice = false;
for (unsigned i = 0; i < e->indexes.size(); i ++)
if (e->indexes[i] == NULL)
{
array_slice = true;
break;
}
if (array_slice)
{
itervar iv = parent->parent->getiter(array);
parent->parent->o->newline() << iv.declare();
}
for (unsigned i=0; i<r->index_types.size(); i++)
{
if (array->referent->type == pe_stats || !array_slice || e->indexes[i])
{
tmpvar ix = parent->parent->gensym (r->index_types[i]);
ix.declare (*(parent->parent));
if (e->indexes[i])
e->indexes[i]->visit(parent);
}
}
}
else
{
throw SEMANTIC_ERROR(_("cannot delete histogram bucket entries\n"), e->tok);
}
}
void
delete_statement_operand_visitor::visit_arrayindex (arrayindex* e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
bool array_slice = false;
for (unsigned i = 0; i < e->indexes.size(); i ++)
if (e->indexes[i] == NULL)
{
array_slice = true;
break;
}
if (!array_slice) {
vector<tmpvar> idx;
parent->load_map_indices (e, idx);
mapvar mvar = parent->getmap (array->referent, e->tok);
parent->o->newline() << mvar.del (idx) << ";";
}
else {
vardecl* r = array->referent;
mapvar mvar = parent->getmap (r, e->tok);
itervar iv = parent->getiter(array);
vector<tmpvar *> array_slice_vars;
vector<tmpvar> idx; for (unsigned i=0; i<e->indexes.size(); i++)
{
if (e->indexes[i])
{
tmpvar *asvar = new tmpvar(parent->gensym(e->indexes[i]->type));
parent->c_assign (asvar->value(), e->indexes[i], "tmp var");
array_slice_vars.push_back(asvar);
if (mvar.is_parallel())
idx.push_back(*asvar);
}
else
{
array_slice_vars.push_back(NULL);
if (mvar.is_parallel())
{
tmpvar *asvar = new tmpvar(parent->gensym(r->index_types[i]));
idx.push_back(*asvar);
}
}
}
if (mvar.is_parallel())
{
parent->o->newline() << "if (unlikely(NULL == "
<< mvar.calculate_aggregate() << ")) {";
parent->o->newline(1) << "c->last_error = ";
parent->o->line() << STAP_T_05 << mvar << "\";";
parent->o->newline() << "c->last_stmt = "
<< lex_cast_qstring(*e->tok) << ";";
parent->o->newline() << "goto out;";
parent->o->newline(-1) << "}";
}
string ctr = lex_cast (parent->label_counter++);
string toplabel = "top_" + ctr;
string breaklabel = "break_" + ctr;
parent->o->newline() << iv << " = " << iv.start(mvar) << ";";
parent->o->newline() << toplabel << ":";
parent->o->newline(1) << "if (!(" << iv << ")){";
parent->o->newline(1) << "goto " << breaklabel << ";}";
parent->o->newline(-1) << "if (1"; for (unsigned i=0; i<array_slice_vars.size(); i++)
if (array_slice_vars[i] != NULL)
{
if (array_slice_vars[i]->type() == pe_long)
parent->o->line() << " && " << *array_slice_vars[i] << " == "
<< iv.get_key(mvar, array_slice_vars[i]->type(), i);
else if (array_slice_vars[i]->type() == pe_string)
parent->o->line() << " && strncmp(" << *array_slice_vars[i] << ", "
<< iv.get_key(mvar, array_slice_vars[i]->type(), i)
<< ", MAXSTRINGLEN) == 0";
else
throw SEMANTIC_ERROR (_("unexpected type"), e->tok);
}
parent->o->line() << ") {";
if (mvar.is_parallel())
{
parent->o->indent(1);
for (unsigned i = 0; i<array_slice_vars.size(); i++)
if (array_slice_vars[i] == NULL)
parent->c_assign (idx[i].value(),
iv.get_key(mvar, r->index_types[i], i),
r->index_types[i], "tmpvar", e->tok);
parent->o->newline() << iv << " = " << iv.next(mvar) << ";";
parent->o->newline() << mvar.del(idx) << ";";
}
else
parent->o->newline(1) << iv << " = " << iv.del_next(mvar) << ";";
parent->o->newline(-1) << "} else";
parent->o->newline(1) << iv << " = " << iv.next(mvar) << ";";
parent->o->newline(-1) << "goto " << toplabel << ";";
parent->o->newline(-1) << breaklabel<< ":";
parent->o->newline(1) << "; /* dummy statement */";
parent->o->indent(-1);
}
}
else
{
throw SEMANTIC_ERROR(_("cannot delete histogram bucket entries\n"), e->tok);
}
}
void
c_tmpcounter::visit_delete_statement (delete_statement* s)
{
delete_statement_operand_tmp_visitor dv (this);
s->value->visit (&dv);
}
void
c_unparser::visit_delete_statement (delete_statement* s)
{
delete_statement_operand_visitor dv (this);
s->value->visit (&dv);
record_actions(1, s->tok);
}
void
c_unparser::visit_break_statement (break_statement* s)
{
if (loop_break_labels.empty())
throw SEMANTIC_ERROR (_("cannot 'break' outside loop"), s->tok);
record_actions(1, s->tok, true);
o->newline() << "goto " << loop_break_labels.back() << ";";
}
void
c_unparser::visit_continue_statement (continue_statement* s)
{
if (loop_continue_labels.empty())
throw SEMANTIC_ERROR (_("cannot 'continue' outside loop"), s->tok);
record_actions(1, s->tok, true);
o->newline() << "goto " << loop_continue_labels.back() << ";";
}
void
c_unparser::visit_literal_string (literal_string* e)
{
const string& v = e->value;
o->line() << '"';
for (unsigned i=0; i<v.size(); i++)
if (v[i] == '"') o->line() << '\\' << '"';
else
o->line() << v[i];
o->line() << '"';
}
void
c_unparser::visit_literal_number (literal_number* e)
{
if (e->value == -9223372036854775807LL-1) o->line() << "((int64_t)" << (unsigned long long) e->value << "ULL)";
else
o->line() << "((int64_t)" << e->value << "LL)";
}
void
c_tmpcounter::visit_binary_expression (binary_expression* e)
{
if (e->op == "/" || e->op == "%")
{
tmpvar left = parent->gensym (pe_long);
tmpvar right = parent->gensym (pe_long);
if (e->left->tok->type != tok_number)
left.declare (*parent);
if (e->right->tok->type != tok_number)
right.declare (*parent);
}
e->left->visit (this);
e->right->visit (this);
}
void
c_unparser::visit_embedded_expr (embedded_expr* e)
{
o->line() << "(";
if (e->code.find ("/* myproc-unprivileged */") != string::npos)
o->line() << "({ assert_is_myproc(); }), ";
if (e->type == pe_long)
o->line() << "((int64_t) (" << e->code << "))";
else if (e->type == pe_string)
o->line() << "((const char *) (" << e->code << "))";
else
throw SEMANTIC_ERROR (_("expected numeric or string type"), e->tok);
o->line() << ")";
}
void
c_unparser::visit_binary_expression (binary_expression* e)
{
if (e->type != pe_long ||
e->left->type != pe_long ||
e->right->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);
if (e->op == "+" ||
e->op == "-" ||
e->op == "*" ||
e->op == "&" ||
e->op == "|" ||
e->op == "^")
{
o->line() << "((";
e->left->visit (this);
o->line() << ") " << e->op << " (";
e->right->visit (this);
o->line() << "))";
}
else if (e->op == ">>" ||
e->op == "<<")
{
o->line() << "((";
e->left->visit (this);
o->line() << ") " << e->op << "max(min(";
e->right->visit (this);
o->line() << ", (int64_t)64LL), (int64_t)0LL))"; }
else if (e->op == "/" ||
e->op == "%")
{
tmpvar left = gensym (pe_long);
tmpvar right = gensym (pe_long);
o->line() << "({";
o->indent(1);
if (e->left->tok->type == tok_number)
left.override(c_expression(e->left));
else
{
o->newline() << left << " = ";
e->left->visit (this);
o->line() << ";";
}
if (e->right->tok->type == tok_number)
right.override(c_expression(e->right));
else
{
o->newline() << right << " = ";
e->right->visit (this);
o->line() << ";";
}
o->newline() << "if (unlikely(!" << right << ")) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_03;
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline() << ((e->op == "/") ? "_stp_div64" : "_stp_mod64")
<< " (NULL, " << left << ", " << right << ");";
o->newline(-1) << "})";
}
else
throw SEMANTIC_ERROR (_("operator not yet implemented"), e->tok);
}
void
c_unparser::visit_unary_expression (unary_expression* e)
{
if (e->type != pe_long ||
e->operand->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);
if (e->op == "-")
{
o->line() << "(int64_t)(0 " << e->op << " (uint64_t)(";
e->operand->visit (this);
o->line() << "))";
}
else
{
o->line() << "(" << e->op << " (";
e->operand->visit (this);
o->line() << "))";
}
}
void
c_unparser::visit_logical_or_expr (logical_or_expr* e)
{
if (e->type != pe_long ||
e->left->type != pe_long ||
e->right->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);
o->line() << "((";
e->left->visit (this);
o->line() << ") " << e->op << " (";
e->right->visit (this);
o->line() << "))";
}
void
c_unparser::visit_logical_and_expr (logical_and_expr* e)
{
if (e->type != pe_long ||
e->left->type != pe_long ||
e->right->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);
o->line() << "((";
e->left->visit (this);
o->line() << ") " << e->op << " (";
e->right->visit (this);
o->line() << "))";
}
void
c_tmpcounter::visit_array_in (array_in* e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->operand->base, array, hist);
if (array)
{
assert (array->referent != 0);
vardecl* r = array->referent;
bool array_slice = false;
tmpvar res = parent->gensym (e->type);
res.declare (*parent);
for (unsigned i = 0; i < e->operand->indexes.size(); i ++)
if (e->operand->indexes[i] == NULL)
{
array_slice = true;
break;
}
for (unsigned i=0; i<r->index_types.size(); i++)
{
if (!array_slice || e->operand->indexes[i])
{
tmpvar ix = parent->gensym (r->index_types[i]);
ix.declare (*parent);
e->operand->indexes[i]->visit(this);
}
}
if (array_slice)
{
itervar iv = parent->getiter(array);
parent->o->newline() << iv.declare();
}
}
else
{
e->operand->visit(this);
}
}
void
c_unparser::visit_array_in (array_in* e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->operand->base, array, hist);
if (array)
{
stmt_expr block(*this);
tmpvar res = gensym (pe_long);
vector<tmpvar> idx;
bool array_slice = false;
for (unsigned i = 0; i < e->operand->indexes.size(); i ++)
if (e->operand->indexes[i] == NULL)
{
array_slice = true;
break;
}
if (!array_slice) {
load_map_indices (e->operand, idx);
mapvar mvar = getmap (array->referent, e->tok);
c_assign (res, mvar.exists(idx), e->tok);
o->newline() << res << ";";
}
else
{
vector<tmpvar *> array_slice_vars;
for (unsigned i=0; i<e->operand->indexes.size(); i++)
{
if (e->operand->indexes[i])
{
tmpvar *asvar = new tmpvar(gensym(e->operand->indexes[i]->type));
c_assign (asvar->value(), e->operand->indexes[i], "tmp var");
array_slice_vars.push_back(asvar);
}
else
array_slice_vars.push_back(NULL);
}
mapvar mvar = getmap (array->referent, e->operand->tok);
itervar iv = getiter(array);
vector<tmpvar> idx;
bool pre_agg = (aggregations_active.count(mvar.value()) > 0);
if (mvar.is_parallel() && !pre_agg)
{
o->newline() << "if (unlikely(NULL == "
<< mvar.calculate_aggregate() << ")) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_05 << mvar << "\";";
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "}";
}
string ctr = lex_cast (label_counter++);
string toplabel = "top_" + ctr;
string contlabel = "continue_" + ctr;
string breaklabel = "break_" + ctr;
o->newline() << iv << " = " << iv.start(mvar) << ";";
c_assign (res, "0", e->tok);
o->newline() << toplabel << ":";
o->newline(1) << "if (!(" << iv << "))";
o->newline(1) << "goto " << breaklabel << ";";
o->newline(-1) << "if (1"; for (unsigned i=0; i<array_slice_vars.size(); i++)
{
if (array_slice_vars[i] != NULL)
{
if (array_slice_vars[i]->type() == pe_long)
o->line() << " && " << *array_slice_vars[i] << " == "
<< iv.get_key(mvar, array_slice_vars[i]->type(), i);
else if (array_slice_vars[i]->type() == pe_string)
o->line() << " && strncmp(" << *array_slice_vars[i] << ", "
<< iv.get_key(mvar, array_slice_vars[i]->type(), i)
<< ", MAXSTRINGLEN) == 0";
else
throw SEMANTIC_ERROR (_("unexpected type"), e->tok);
}
}
o->line() << "){";
o->indent(1);
c_assign (res, "1", e->tok);
o->newline() << "goto " << breaklabel << ";";
o->newline(-1) << "}";
o->newline() << iv << " = " << iv.next(mvar) << ";";
o->newline() << "goto " << toplabel << ";";
o->newline(-1) << breaklabel<< ":";
o->newline(1) << "; /* dummy statement */";
o->newline(-1) << res << ";";
}
}
else
{
e->operand->visit(this);
}
}
void
c_tmpcounter::visit_regex_query (regex_query* e)
{
e->left->visit(this);
e->right->visit(this);
}
void
c_unparser::visit_regex_query (regex_query* e)
{
o->line() << "(";
o->indent(1);
o->newline();
if (e->op == "!~") o->line() << "!";
stapdfa *dfa = session->dfas[e->right->value];
dfa->emit_matchop_start (o);
e->left->visit(this);
dfa->emit_matchop_end (o);
o->newline(-1) << ")";
}
void
c_tmpcounter::visit_comparison (comparison* e)
{
if (e->left->type == pe_string)
{
tmpvar left = parent->gensym (pe_string);
if (e->left->tok->type != tok_string)
left.declare (*parent);
}
e->left->visit (this);
e->right->visit (this);
}
void
c_unparser::visit_comparison (comparison* e)
{
o->line() << "(";
if (e->left->type == pe_string)
{
if (e->right->type != pe_string)
throw SEMANTIC_ERROR (_("expected string types"), e->tok);
o->line() << "({";
o->indent(1);
tmpvar left = gensym (pe_string);
if (e->left->tok->type == tok_string)
left.override(c_expression(e->left));
else
c_assign (left.value(), e->left, "assignment");
o->newline() << "strncmp (" << left << ", ";
e->right->visit (this);
o->line() << ", MAXSTRINGLEN) " << e->op << " 0;";
o->newline(-1) << "})";
}
else if (e->left->type == pe_long)
{
if (e->right->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);
o->line() << "((";
e->left->visit (this);
o->line() << ") " << e->op << " (";
e->right->visit (this);
o->line() << "))";
}
else
throw SEMANTIC_ERROR (_("unexpected type"), e->left->tok);
o->line() << ")";
}
void
c_tmpcounter::visit_concatenation (concatenation* e)
{
tmpvar t = parent->gensym (e->type);
t.declare (*parent);
e->left->visit (this);
e->right->visit (this);
}
void
c_unparser::visit_concatenation (concatenation* e)
{
if (e->op != ".")
throw SEMANTIC_ERROR (_("unexpected concatenation operator"), e->tok);
if (e->type != pe_string ||
e->left->type != pe_string ||
e->right->type != pe_string)
throw SEMANTIC_ERROR (_("expected string types"), e->tok);
tmpvar t = gensym (e->type);
o->line() << "({ ";
o->indent(1);
c_assign (t.value(), e->left, "assignment");
c_strcat (t.value(), e->right);
o->newline() << t << ";";
o->newline(-1) << "})";
}
void
c_unparser::visit_ternary_expression (ternary_expression* e)
{
if (e->cond->type != pe_long)
throw SEMANTIC_ERROR (_("expected numeric condition"), e->cond->tok);
if (e->truevalue->type != e->falsevalue->type ||
e->type != e->truevalue->type ||
(e->truevalue->type != pe_long && e->truevalue->type != pe_string))
throw SEMANTIC_ERROR (_("expected matching types"), e->tok);
o->line() << "((";
e->cond->visit (this);
o->line() << ") ? (";
e->truevalue->visit (this);
o->line() << ") : (";
e->falsevalue->visit (this);
o->line() << "))";
}
void
c_tmpcounter::visit_assignment (assignment *e)
{
c_tmpcounter_assignment tav (this, e->op, e->right);
e->left->visit (& tav);
}
void
c_unparser::visit_assignment (assignment* e)
{
if (e->op == "<<<")
{
if (e->type != pe_long)
throw SEMANTIC_ERROR (_("non-number <<< expression"), e->tok);
if (e->left->type != pe_stats)
throw SEMANTIC_ERROR (_("non-stats left operand to <<< expression"), e->left->tok);
if (e->right->type != pe_long)
throw SEMANTIC_ERROR (_("non-number right operand to <<< expression"), e->right->tok);
}
else
{
if (e->type != e->left->type)
throw SEMANTIC_ERROR (_("type mismatch"), e->tok, e->left->tok);
if (e->right->type != e->left->type)
throw SEMANTIC_ERROR (_("type mismatch"), e->right->tok, e->left->tok);
}
c_unparser_assignment tav (this, e->op, e->right);
e->left->visit (& tav);
}
void
c_tmpcounter::visit_pre_crement (pre_crement* e)
{
c_tmpcounter_assignment tav (this, e->op, 0);
e->operand->visit (& tav);
}
void
c_unparser::visit_pre_crement (pre_crement* e)
{
if (e->type != pe_long ||
e->type != e->operand->type)
throw SEMANTIC_ERROR (_("expected numeric type"), e->tok);
c_unparser_assignment tav (this, e->op, false);
e->operand->visit (& tav);
}
void
c_tmpcounter::visit_post_crement (post_crement* e)
{
c_tmpcounter_assignment tav (this, e->op, 0, true);
e->operand->visit (& tav);
}
void
c_unparser::visit_post_crement (post_crement* e)
{
if (e->type != pe_long ||
e->type != e->operand->type)
throw SEMANTIC_ERROR (_("expected numeric type"), e->tok);
c_unparser_assignment tav (this, e->op, true);
e->operand->visit (& tav);
}
void
c_unparser::visit_symbol (symbol* e)
{
assert (e->referent != 0);
vardecl* r = e->referent;
if (r->index_types.size() != 0)
throw SEMANTIC_ERROR (_("invalid reference to array"), e->tok);
var v = getvar(r, e->tok);
o->line() << v;
}
void
c_tmpcounter_assignment::prepare_rvalue (tmpvar & rval)
{
if (rvalue)
{
if (rvalue->tok->type != tok_number && rvalue->tok->type != tok_string)
rval.declare (*(parent->parent));
rvalue->visit (parent);
}
}
void
c_tmpcounter_assignment::c_assignop(tmpvar & res)
{
if (res.type() == pe_string)
{
}
else if (op == "<<<")
res.declare (*(parent->parent));
else if (res.type() == pe_long)
{
if (post)
res.declare (*(parent->parent));
}
}
void
c_tmpcounter_assignment::visit_symbol (symbol *e)
{
exp_type ty = rvalue ? rvalue->type : e->type;
tmpvar rval = parent->parent->gensym (ty);
tmpvar res = parent->parent->gensym (ty);
prepare_rvalue(rval);
c_assignop (res);
}
void
c_unparser_assignment::prepare_rvalue (string const & op,
tmpvar & rval,
token const * tok)
{
if (rvalue)
{
if (rvalue->tok->type == tok_number || rvalue->tok->type == tok_string)
rval.override(parent->c_expression(rvalue));
else
parent->c_assign (rval.value(), rvalue, "assignment");
}
else
{
if (op == "++" || op == "--")
rval.override("1");
else
throw SEMANTIC_ERROR (_("need rvalue for assignment"), tok);
}
}
void
c_unparser_assignment::visit_symbol (symbol *e)
{
stmt_expr block(*parent);
assert (e->referent != 0);
if (e->referent->index_types.size() != 0)
throw SEMANTIC_ERROR (_("unexpected reference to array"), e->tok);
exp_type ty = rvalue ? rvalue->type : e->type;
tmpvar rval = parent->gensym (ty);
tmpvar res = parent->gensym (ty);
prepare_rvalue (op, rval, e->tok);
var lvar = parent->getvar (e->referent, e->tok);
c_assignop (res, lvar, rval, e->tok);
parent->o->newline() << res << ";";
}
void
c_unparser::visit_target_symbol (target_symbol* e)
{
throw SEMANTIC_ERROR(_("cannot translate general target-symbol expression"), e->tok);
}
void
c_unparser::visit_atvar_op (atvar_op* e)
{
throw SEMANTIC_ERROR(_("cannot translate general @var expression"), e->tok);
}
void
c_unparser::visit_cast_op (cast_op* e)
{
throw SEMANTIC_ERROR(_("cannot translate general @cast expression"), e->tok);
}
void
c_unparser::visit_autocast_op (autocast_op* e)
{
throw SEMANTIC_ERROR(_("cannot translate general dereference expression"), e->tok);
}
void
c_unparser::visit_defined_op (defined_op* e)
{
throw SEMANTIC_ERROR(_("cannot translate general @defined expression"), e->tok);
}
void
c_unparser::visit_entry_op (entry_op* e)
{
throw SEMANTIC_ERROR(_("cannot translate general @entry expression"), e->tok);
}
void
c_unparser::visit_perf_op (perf_op* e)
{
throw SEMANTIC_ERROR(_("cannot translate general @perf expression"), e->tok);
}
void
c_tmpcounter::load_map_indices(arrayindex *e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
assert (array->referent != 0);
vardecl* r = array->referent;
for (unsigned i=0; i<r->index_types.size(); i++)
{
tmpvar ix = parent->gensym (r->index_types[i]);
if (e->indexes[i]->tok->type == tok_number
|| e->indexes[i]->tok->type == tok_string)
{
}
else
ix.declare (*parent);
e->indexes[i]->visit(this);
}
}
}
void
c_unparser::load_map_indices(arrayindex *e,
vector<tmpvar> & idx)
{
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
idx.clear();
assert (array->referent != 0);
vardecl* r = array->referent;
if (r->index_types.size() == 0 ||
r->index_types.size() != e->indexes.size())
throw SEMANTIC_ERROR (_("invalid array reference"), e->tok);
for (unsigned i=0; i<r->index_types.size(); i++)
{
if (r->index_types[i] != e->indexes[i]->type)
throw SEMANTIC_ERROR (_("array index type mismatch"), e->indexes[i]->tok);
tmpvar ix = gensym (r->index_types[i]);
if (e->indexes[i]->tok->type == tok_number
|| e->indexes[i]->tok->type == tok_string)
ix.override(c_expression(e->indexes[i]));
else
{
c_assign (ix.value(), e->indexes[i], "array index copy");
}
idx.push_back (ix);
}
}
else
{
assert (e->indexes.size() == 1);
assert (e->indexes[0]->type == pe_long);
tmpvar ix = gensym (pe_long);
c_assign (ix.value(), e->indexes[0], "array index copy");
idx.push_back(ix);
}
}
void
c_tmpcounter::load_aggregate (expression *e)
{
symbol *sym = get_symbol_within_expression (e);
string agg_value;
arrayindex* arr = NULL;
expression_is_arrayindex (e, arr);
if (sym->referent->arity != 0 &&
!parent->get_foreach_loop_value(arr, agg_value))
{
if (!arr)
throw SEMANTIC_ERROR(_("expected arrayindex expression"), e->tok);
load_map_indices (arr);
}
}
var*
c_unparser::load_aggregate (expression *e, aggvar & agg)
{
symbol *sym = get_symbol_within_expression (e);
if (sym->referent->type != pe_stats)
throw SEMANTIC_ERROR (_("unexpected aggregate of non-statistic"), sym->tok);
var *v;
if (sym->referent->arity == 0)
{
v = new var(getvar(sym->referent, sym->tok));
o->newline() << agg << " = _stp_stat_get (" << *v << ", 0);";
}
else
{
mapvar *mv = new mapvar(getmap(sym->referent, sym->tok));
v = mv;
arrayindex *arr = NULL;
if (!expression_is_arrayindex (e, arr))
throw SEMANTIC_ERROR(_("unexpected aggregate of non-arrayindex"), e->tok);
string agg_value;
if (get_foreach_loop_value(arr, agg_value))
o->newline() << agg << " = " << agg_value << ";";
else
{
vector<tmpvar> idx;
load_map_indices (arr, idx);
bool pre_agg = (aggregations_active.count(mv->value()) > 0);
o->newline() << agg << " = " << mv->get(idx, pre_agg) << ";";
}
}
return v;
}
string
c_unparser::histogram_index_check(var & base, tmpvar & idx) const
{
return "((" + idx.value() + " >= 0)"
+ " && (" + idx.value() + " < " + base.buckets() + "))";
}
void
c_tmpcounter::visit_arrayindex (arrayindex *e)
{
string ai_value;
if (parent->get_foreach_loop_value(e, ai_value))
return;
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
load_map_indices(e);
tmpvar res = parent->gensym (e->type);
res.declare (*parent);
}
else
{
assert(hist);
if (e->indexes.size() != 1)
throw SEMANTIC_ERROR(_("Invalid indexing of histogram"), e->tok);
tmpvar ix = parent->gensym (pe_long);
ix.declare (*parent);
e->indexes[0]->visit(this);
tmpvar res = parent->gensym (pe_long);
res.declare (*parent);
aggvar agg = parent->gensym_aggregate ();
agg.declare(*(this->parent));
load_aggregate (hist->stat);
}
}
void
c_unparser::visit_arrayindex (arrayindex* e)
{
string ai_value;
if (get_foreach_loop_value(e, ai_value))
{
o->line() << ai_value;
return;
}
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
if (array->referent->type == pe_stats)
throw SEMANTIC_ERROR (_("statistic-valued array in rvalue context"), e->tok);
stmt_expr block(*this);
vector<tmpvar> idx;
load_map_indices (e, idx);
tmpvar res = gensym (e->type);
mapvar mvar = getmap (array->referent, e->tok);
c_assign (res, mvar.get(idx), e->tok);
o->newline() << res << ";";
}
else
{
assert(hist);
stmt_expr block(*this);
vector<tmpvar> idx;
load_map_indices (e, idx);
tmpvar res = gensym (e->type);
aggvar agg = gensym_aggregate ();
assert(idx.size() == 1);
assert(idx[0].type() == pe_long);
var *v = load_aggregate(hist->stat, agg);
v->assert_hist_compatible(*hist);
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
o->newline() << "if (unlikely (" << agg.value() << " == NULL)"
<< " || " << agg.value() << "->count == 0) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_06;
o->newline() << "goto out;";
o->newline(-1) << "} else {";
o->newline(1) << "if (" << histogram_index_check(*v, idx[0]) << ")";
o->newline(1) << res << " = " << agg << "->histogram[" << idx[0] << "];";
o->newline(-1) << "else {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_07;
o->newline() << "goto out;";
o->newline(-1) << "}";
o->newline(-1) << "}";
o->newline() << res << ";";
delete v;
}
}
void
c_tmpcounter_assignment::visit_arrayindex (arrayindex *e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
parent->load_map_indices(e);
exp_type ty = rvalue ? rvalue->type : e->type;
tmpvar rval = parent->parent->gensym (ty);
tmpvar lval = parent->parent->gensym (ty);
tmpvar res = parent->parent->gensym (ty);
prepare_rvalue(rval);
lval.declare (*(parent->parent));
if (op == "<<<")
res.declare (*(parent->parent));
else
c_assignop(res);
}
else
{
throw SEMANTIC_ERROR(_("cannot assign to histogram buckets"), e->tok);
}
}
void
c_unparser_assignment::visit_arrayindex (arrayindex *e)
{
symbol *array;
hist_op *hist;
classify_indexable (e->base, array, hist);
if (array)
{
stmt_expr block(*parent);
translator_output *o = parent->o;
if (array->referent->index_types.size() == 0)
throw SEMANTIC_ERROR (_("unexpected reference to scalar"), e->tok);
vector<tmpvar> idx;
parent->load_map_indices (e, idx);
exp_type ty = rvalue ? rvalue->type : e->type;
tmpvar rvar = parent->gensym (ty);
tmpvar lvar = parent->gensym (ty);
tmpvar res = parent->gensym (ty);
prepare_rvalue (op, rvar, e->tok);
if (op == "<<<")
{
assert (e->type == pe_stats);
assert (rvalue->type == pe_long);
mapvar mvar = parent->getmap (array->referent, e->tok);
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
o->newline() << mvar.add (idx, rvar) << ";";
res = rvar;
}
else
{
mapvar mvar = parent->getmap (array->referent, e->tok);
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
if (op != "=") parent->c_assign (lvar, mvar.get(idx), e->tok);
c_assignop (res, lvar, rvar, e->tok);
o->newline() << mvar.set (idx, lvar) << ";";
}
o->newline() << res << ";";
}
else
{
throw SEMANTIC_ERROR(_("cannot assign to histogram buckets"), e->tok);
}
}
void
c_tmpcounter::visit_functioncall (functioncall *e)
{
assert (e->referent != 0);
functiondecl* r = e->referent;
for (unsigned i=0; i<r->formal_args.size(); i++)
{
tmpvar t = parent->gensym (r->formal_args[i]->type);
if (e->args[i]->tok->type != tok_number
&& e->args[i]->tok->type != tok_string)
t.declare (*parent);
e->args[i]->visit (this);
}
tmpvar t = parent->gensym (e->type);
if (!parent->session->unoptimized && e->type == pe_string && e->var_assigned_to_retval.empty())
t.declare(*parent);
}
void
c_unparser::visit_functioncall (functioncall* e)
{
assert (e->referent != 0);
functiondecl* r = e->referent;
if (r->formal_args.size() != e->args.size())
throw SEMANTIC_ERROR (_("invalid length argument list"), e->tok);
stmt_expr block(*this);
vector<tmpvar> tmp;
for (unsigned i=0; i<e->args.size(); i++)
{
tmpvar t = gensym(e->args[i]->type);
if (r->formal_args[i]->type != e->args[i]->type)
throw SEMANTIC_ERROR (_("function argument type mismatch"),
e->args[i]->tok, r->formal_args[i]->tok);
symbol *sym_out;
if (e->args[i]->tok->type == tok_number
|| e->args[i]->tok->type == tok_string)
t.override(c_expression(e->args[i]));
else if (r->formal_args[i]->char_ptr_arg && e->args[i]->is_symbol(sym_out)
&& is_local(sym_out->referent, sym_out->tok))
t.override(getvar(sym_out->referent, e->args[i]->tok).value());
else
{
c_assign (t.value(), e->args[i],
_("function actual argument evaluation"));
}
tmp.push_back(t);
}
for (unsigned i=0; i<e->args.size(); i++)
{
if (r->formal_args[i]->type != e->args[i]->type)
throw SEMANTIC_ERROR (_("function argument type mismatch"),
e->args[i]->tok, r->formal_args[i]->tok);
if (r->formal_args[i]->char_ptr_arg)
o->newline() << "c->locals[c->nesting+1]." + c_funcname (r->name) + "."
+ c_localname (r->formal_args[i]->name) << " = "
<< tmp[i].value() << ";";
else
c_assign ("c->locals[c->nesting+1]." +
c_funcname (r->name) + "." +
c_localname (r->formal_args[i]->name),
tmp[i].value(),
e->args[i]->type,
"function actual argument copy",
e->args[i]->tok);
}
tmpvar tmp_ret = gensym (e->type);
if (!e->var_assigned_to_retval.empty())
o->newline() << "c->locals[c->nesting+1]." << c_funcname(r->name)
<< ".__retvalue = &" << e->var_assigned_to_retval << "[0];";
else if (!session->unoptimized && e->type == pe_string)
o->newline() << "c->locals[c->nesting+1]." << c_funcname(r->name)
<< ".__retvalue = &" << tmp_ret.value() << "[0];";
o->newline() << c_funcname (r->name) << " (c);";
o->newline() << "if (unlikely(c->last_error)) goto out;";
if (!already_checked_action_count && !session->suppress_time_limits
&& !session->unoptimized)
{
max_action_info mai (*session);
e->referent->body->visit(&mai);
if(mai.statement_count_finite())
record_actions (mai.statement_count, e->tok, true);
}
if (r->type == pe_unknown)
o->newline() << "(void) 0;";
else if (session->unoptimized || r->type != pe_string)
o->newline() << "c->locals[c->nesting+1]"
<< "." << c_funcname (r->name)
<< ".__retvalue;";
else if (e->var_assigned_to_retval.empty())
o->newline() << tmp_ret.value() << ";";
}
static int
preprocess_print_format(print_format* e, vector<tmpvar>& tmp,
vector<print_format::format_component>& components,
string& format_string)
{
int use_print = 0;
if (e->print_with_format)
{
format_string = e->raw_components;
components = e->components;
}
else
{
string delim;
if (e->print_with_delim)
{
stringstream escaped_delim;
const string& dstr = e->delimiter.literal_string;
for (string::const_iterator i = dstr.begin();
i != dstr.end(); ++i)
{
if (*i == '%')
escaped_delim << '%';
escaped_delim << *i;
}
delim = escaped_delim.str();
}
stringstream format;
for (unsigned i = 0; i < e->args.size(); ++i)
{
if (i > 0 && e->print_with_delim)
format << delim;
switch (e->args[i]->type)
{
default:
case pe_unknown:
throw SEMANTIC_ERROR(_("cannot print unknown expression type"), e->args[i]->tok);
case pe_stats:
throw SEMANTIC_ERROR(_("cannot print a raw stats object"), e->args[i]->tok);
case pe_long:
format << "%d";
break;
case pe_string:
format << "%s";
break;
}
}
if (e->print_with_newline)
format << "\\n";
format_string = format.str();
components = print_format::string_to_components(format_string);
}
if ((tmp.size() == 0 && format_string.find("%%") == string::npos)
|| (tmp.size() == 1 && format_string == "%s"))
use_print = 1;
else if (tmp.size() == 1
&& e->args[0]->tok->type == tok_string
&& format_string == "%s\\n")
{
use_print = 1;
tmp[0].override(tmp[0].value() + "\"\\n\"");
}
return use_print;
}
void
c_tmpcounter::visit_print_format (print_format* e)
{
if (e->hist)
{
aggvar agg = parent->gensym_aggregate ();
agg.declare(*(this->parent));
load_aggregate (e->hist->stat);
if (!e->print_to_stream)
{
exp_type ty = pe_string;
tmpvar res = parent->gensym(ty);
res.declare(*parent);
}
}
else
{
vector<tmpvar> tmp;
for (unsigned i=0; i < e->args.size(); i++)
{
tmpvar t = parent->gensym (e->args[i]->type);
tmp.push_back(t);
if (e->args[i]->type == pe_unknown)
{
throw SEMANTIC_ERROR(_("unknown type of arg to print operator"),
e->args[i]->tok);
}
if (e->args[i]->tok->type != tok_number
&& e->args[i]->tok->type != tok_string)
t.declare (*parent);
e->args[i]->visit (this);
}
exp_type ty = e->print_to_stream ? pe_long : pe_string;
tmpvar res = parent->gensym (ty);
if (ty == pe_string)
res.declare (*parent);
vector<print_format::format_component> components;
string format_string;
int use_print = preprocess_print_format(e, tmp, components, format_string);
if (!(e->print_to_stream && (e->print_char || use_print)))
parent->declare_compiled_printf(e->print_to_stream, format_string);
}
}
void
c_unparser::visit_print_format (print_format* e)
{
if (e->hist)
{
stmt_expr block(*this);
aggvar agg = gensym_aggregate ();
var *v = load_aggregate(e->hist->stat, agg);
v->assert_hist_compatible(*e->hist);
{
o->newline() << "if (unlikely (" << agg.value() << " == NULL)"
<< " || " << agg.value() << "->count == 0) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_06;
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "} else";
if (e->print_to_stream)
{
o->newline(1) << "_stp_stat_print_histogram (" << v->hist() << ", " << agg.value() << ");";
o->indent(-1);
}
else
{
exp_type ty = pe_string;
tmpvar res = gensym (ty);
o->newline(1) << "_stp_stat_print_histogram_buf (" << res.value() << ", MAXSTRINGLEN, " << v->hist() << ", " << agg.value() << ");";
o->newline(-1) << res.value() << ";";
}
}
delete v;
}
else
{
stmt_expr block(*this);
if (e->args.size() > 32)
throw SEMANTIC_ERROR(_NF("additional argument to print", "too many arguments to print (%zu)",
e->args.size(), e->args.size()), e->tok);
vector<tmpvar> tmp;
for (unsigned i=0; i<e->args.size(); i++)
{
tmpvar t = gensym(e->args[i]->type);
tmp.push_back(t);
if (e->args[i]->tok->type == tok_number
|| e->args[i]->tok->type == tok_string)
tmp[i].override(c_expression(e->args[i]));
else
c_assign (t.value(), e->args[i],
"print format actual argument evaluation");
}
exp_type ty = e->print_to_stream ? pe_long : pe_string;
tmpvar res = gensym (ty);
vector<print_format::format_component> components;
string format_string, format_string_out;
int use_print = preprocess_print_format(e, tmp, components, format_string);
format_string_out = print_format::components_to_string(components);
size_t arg_ix = 0;
for (unsigned i = 0; i < components.size(); ++i) {
if (components[i].type == print_format::conv_literal)
continue;
int width_ix = -1, prec_ix= -1;
if (components[i].widthtype == print_format::width_dynamic)
width_ix = arg_ix++;
if (components[i].prectype == print_format::prec_dynamic)
prec_ix = arg_ix++;
(void) width_ix; XXX
if (components[i].type == print_format::conv_memory
|| components[i].type == print_format::conv_memory_hex)
{
string mem_size;
const token* prec_tok = e->tok;
if (prec_ix != -1)
{
mem_size = tmp[prec_ix].value();
prec_tok = e->args[prec_ix]->tok;
}
else if (components[i].prectype == print_format::prec_static &&
components[i].precision > 0)
mem_size = lex_cast(components[i].precision) + "LL";
else
mem_size = "1LL";
o->newline() << "c->last_stmt = " << lex_cast_qstring(*prec_tok) << ";";
o->newline() << "if (" << mem_size << " > 1024) {";
o->newline(1) << "snprintf(c->error_buffer, sizeof(c->error_buffer), "
<< "\"%lld is too many bytes for a memory dump\", (long long)"
<< mem_size << ");";
o->newline() << "c->last_error = c->error_buffer;";
o->newline() << "goto out;";
o->newline(-1) << "}";
}
++arg_ix;
}
if (e->print_to_stream)
{
if (e->print_char)
{
o->newline() << "_stp_print_char (";
if (tmp.size())
o->line() << tmp[0].value() << ");";
else
o->line() << '"' << format_string_out << "\");";
return;
}
if (use_print)
{
o->newline() << "_stp_print (";
if (tmp.size())
o->line() << tmp[0].value() << ");";
else
o->line() << '"' << format_string_out << "\");";
return;
}
}
o->newline() << "#ifndef STP_LEGACY_PRINT";
o->indent(1);
const string& compiled_printf =
get_compiled_printf (e->print_to_stream, format_string);
for (unsigned i = 0; i < tmp.size(); ++i)
o->newline() << "c->printf_locals." << compiled_printf
<< ".arg" << i << " = " << tmp[i].value() << ";";
if (e->print_to_stream)
res.override("((int64_t)0LL)");
else
o->newline() << "c->printf_locals." << compiled_printf
<< ".__retvalue = " << res.value() << ";";
o->newline() << compiled_printf << " (c);";
o->newline(-1) << "#else // STP_LEGACY_PRINT";
o->indent(1);
if (e->print_to_stream)
o->newline() << "_stp_printf (";
else
o->newline() << "_stp_snprintf (" << res.value() << ", MAXSTRINGLEN, ";
o->line() << '"' << format_string_out << '"';
arg_ix = 0;
for (unsigned i = 0; i < components.size(); ++i)
{
if (components[i].type == print_format::conv_literal)
continue;
if (components[i].widthtype == print_format::width_dynamic)
o->line() << ", (int)" << tmp[arg_ix++].value();
if (components[i].prectype == print_format::prec_dynamic)
o->line() << ", (int)" << tmp[arg_ix++].value();
if (components[i].type == print_format::conv_memory
|| components[i].type == print_format::conv_memory_hex)
o->line() << ", (char*)(uintptr_t)" << tmp[arg_ix++].value();
else if (components[i].type == print_format::conv_char)
o->line() << ", (int)" << tmp[arg_ix++].value();
else if (arg_ix < tmp.size())
o->line() << ", " << tmp[arg_ix++].value();
}
o->line() << ");";
o->newline(-1) << "#endif // STP_LEGACY_PRINT";
o->newline() << "if (unlikely(c->last_error)) goto out;";
o->newline() << res.value() << ";";
}
}
void
c_tmpcounter::visit_stat_op (stat_op* e)
{
aggvar agg = parent->gensym_aggregate ();
tmpvar res = parent->gensym (pe_long);
agg.declare(*(this->parent));
res.declare(*(this->parent));
load_aggregate (e->stat);
}
void
c_unparser::visit_stat_op (stat_op* e)
{
FIXME
FIXME
{
stmt_expr block(*this);
aggvar agg = gensym_aggregate ();
tmpvar res = gensym (pe_long);
var *v = load_aggregate(e->stat, agg);
{
if ((e->ctype == sc_count) ||
(e->ctype == sc_sum &&
strverscmp(session->compatible.c_str(), "1.5") >= 0))
{
o->newline() << "if (unlikely (" << agg.value() << " == NULL))";
o->indent(1);
c_assign(res, "0", e->tok);
o->indent(-1);
}
else
{
o->newline() << "if (unlikely (" << agg.value() << " == NULL)"
<< " || " << agg.value() << "->count == 0) {";
o->newline(1) << "c->last_error = ";
o->line() << STAP_T_06;
o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
o->newline() << "goto out;";
o->newline(-1) << "}";
}
o->newline() << "else";
o->indent(1);
switch (e->ctype)
{
case sc_average:
c_assign(res, ("_stp_div64(NULL, " + agg.value() + "->sum, "
+ agg.value() + "->count)"),
e->tok);
break;
case sc_count:
c_assign(res, agg.value() + "->count", e->tok);
break;
case sc_sum:
c_assign(res, agg.value() + "->sum", e->tok);
break;
case sc_min:
c_assign(res, agg.value() + "->min", e->tok);
break;
case sc_max:
c_assign(res, agg.value() + "->max", e->tok);
break;
case sc_none:
assert (0); }
o->indent(-1);
}
o->newline() << res << ";";
delete v;
}
}
void
c_unparser::visit_hist_op (hist_op*)
{
assert(false);
}
typedef map<Dwarf_Addr,const char*> addrmap_t;
struct unwindsym_dump_context
{
systemtap_session& session;
ostream& output;
unsigned stp_module_index;
int build_id_len;
unsigned char *build_id_bits;
GElf_Addr build_id_vaddr;
unsigned long stp_kretprobe_trampoline_addr;
Dwarf_Addr stext_offset;
vector<pair<string,unsigned> > seclist; map<unsigned, addrmap_t> addrmap;
void *debug_frame;
size_t debug_len;
void *debug_frame_hdr;
size_t debug_frame_hdr_len;
Dwarf_Addr debug_frame_off;
void *eh_frame;
void *eh_frame_hdr;
size_t eh_len;
size_t eh_frame_hdr_len;
Dwarf_Addr eh_addr;
Dwarf_Addr eh_frame_hdr_addr;
set<string> undone_unwindsym_modules;
};
static void create_debug_frame_hdr (const unsigned char e_ident[],
Elf_Data *debug_frame,
void **debug_frame_hdr,
size_t *debug_frame_hdr_len,
Dwarf_Addr *debug_frame_off,
systemtap_session& session,
Dwfl_Module *mod)
{
*debug_frame_hdr = NULL;
*debug_frame_hdr_len = 0;
int cies = 0;
set< pair<Dwarf_Addr, Dwarf_Off> > fdes;
set< pair<Dwarf_Addr, Dwarf_Off> >::iterator it;
int size = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8;
int res = 0;
Dwarf_Off off = 0;
Dwarf_CFI_Entry entry;
while (res != 1)
{
Dwarf_Off next_off;
res = dwarf_next_cfi (e_ident, debug_frame, false, off, &next_off,
&entry);
if (res == 0)
{
if (entry.CIE_id == DW_CIE_ID_64)
cies++; else
{
Dwarf_Addr addr;
if (size == 4)
addr = (*((uint32_t *) entry.fde.start));
else
addr = (*((uint64_t *) entry.fde.start));
fdes.insert(pair<Dwarf_Addr, Dwarf_Off>(addr, off));
}
}
else if (res > 0)
; else
{
if (session.verbose > 2 && ! session.suppress_warnings)
{
const char *modname = dwfl_module_info (mod, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL);
session.print_warning("Problem creating debug frame hdr for "
+ lex_cast_qstring(modname)
+ ", " + dwarf_errmsg (-1));
}
return;
}
off = next_off;
}
if (fdes.size() > 0)
{
it = fdes.begin();
Dwarf_Addr first_addr = (*it).first;
int res = dwfl_module_relocate_address (mod, &first_addr);
DWFL_ASSERT ("create_debug_frame_hdr, dwfl_module_relocate_address",
res >= 0);
*debug_frame_off = (*it).first - first_addr;
}
size_t total_size = 4 + (2 * size) + (2 * size * fdes.size());
uint8_t *hdr = (uint8_t *) malloc(total_size);
*debug_frame_hdr = hdr;
*debug_frame_hdr_len = total_size;
hdr[0] = 1; hdr[1] = DW_EH_PE_absptr; hdr[2] = (size == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8; hdr[3] = DW_EH_PE_absptr; if (size == 4)
{
uint32_t *table = (uint32_t *)(hdr + 4);
*table++ = (uint32_t) 0; *table++ = (uint32_t) fdes.size();
for (it = fdes.begin(); it != fdes.end(); it++)
{
*table++ = (*it).first;
*table++ = (*it).second;
}
}
else
{
uint64_t *table = (uint64_t *)(hdr + 4);
*table++ = (uint64_t) 0; *table++ = (uint64_t) fdes.size();
for (it = fdes.begin(); it != fdes.end(); it++)
{
*table++ = (*it).first;
*table++ = (*it).second;
}
}
}
static set<string> vdso_paths;
static void get_unwind_data (Dwfl_Module *m,
void **debug_frame, void **eh_frame,
size_t *debug_len, size_t *eh_len,
Dwarf_Addr *eh_addr,
void **eh_frame_hdr, size_t *eh_frame_hdr_len,
void **debug_frame_hdr,
size_t *debug_frame_hdr_len,
Dwarf_Addr *debug_frame_off,
Dwarf_Addr *eh_frame_hdr_addr,
systemtap_session& session)
{
Dwarf_Addr start, bias = 0;
GElf_Ehdr *ehdr, ehdr_mem;
GElf_Shdr *shdr, shdr_mem;
Elf_Scn *scn;
Elf_Data *data = NULL;
Elf *elf;
dwfl_module_info (m, NULL, &start, NULL, NULL, NULL, NULL, NULL);
elf = dwfl_module_getelf(m, &bias);
ehdr = gelf_getehdr(elf, &ehdr_mem);
scn = NULL;
bool eh_frame_seen = false;
bool eh_frame_hdr_seen = false;
while ((scn = elf_nextscn(elf, scn)))
{
shdr = gelf_getshdr(scn, &shdr_mem);
const char* scn_name = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name);
if (!eh_frame_seen
&& strcmp(scn_name, ".eh_frame") == 0
&& shdr->sh_type == SHT_PROGBITS)
{
data = elf_rawdata(scn, NULL);
*eh_frame = data->d_buf;
*eh_len = data->d_size;
if (ehdr->e_type != ET_EXEC && dwfl_module_relocations (m) > 0)
*eh_addr = shdr->sh_addr - start + bias;
else
*eh_addr = shdr->sh_addr;
eh_frame_seen = true;
}
else if (!eh_frame_hdr_seen
&& strcmp(scn_name, ".eh_frame_hdr") == 0
&& shdr->sh_type == SHT_PROGBITS)
{
data = elf_rawdata(scn, NULL);
*eh_frame_hdr = data->d_buf;
*eh_frame_hdr_len = data->d_size;
if (ehdr->e_type != ET_EXEC && dwfl_module_relocations (m) > 0)
*eh_frame_hdr_addr = shdr->sh_addr - start + bias;
else
*eh_frame_hdr_addr = shdr->sh_addr;
eh_frame_hdr_seen = true;
}
if (eh_frame_seen && eh_frame_hdr_seen)
break;
}
elf = (dwarf_getelf (dwfl_module_getdwarf (m, &bias))
?: dwfl_module_getelf (m, &bias));
ehdr = gelf_getehdr(elf, &ehdr_mem);
scn = NULL;
while ((scn = elf_nextscn(elf, scn)))
{
shdr = gelf_getshdr(scn, &shdr_mem);
if (strcmp(elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name),
".debug_frame") == 0)
{
data = elf_rawdata(scn, NULL);
*debug_frame = data->d_buf;
*debug_len = data->d_size;
break;
}
}
if (*debug_frame != NULL && *debug_len > 0)
create_debug_frame_hdr (ehdr->e_ident, data,
debug_frame_hdr, debug_frame_hdr_len,
debug_frame_off, session, m);
}
static int
dump_build_id (Dwfl_Module *m,
unwindsym_dump_context *c,
const char *name, Dwarf_Addr base)
{
string modname = name;
int build_id_len = 0;
unsigned char *build_id_bits;
GElf_Addr build_id_vaddr;
if ((build_id_len=dwfl_module_build_id(m,
(const unsigned char **)&build_id_bits,
&build_id_vaddr)) > 0)
{
if (modname != "kernel")
{
Dwarf_Addr reloc_vaddr = build_id_vaddr;
const char *secname;
int i;
i = dwfl_module_relocate_address (m, &reloc_vaddr);
DWFL_ASSERT ("dwfl_module_relocate_address reloc_vaddr", i >= 0);
secname = dwfl_module_relocation_info (m, i, NULL);
if (modname[0] != '/')
if (!secname || strcmp(secname, ".note.gnu.build-id"))
throw SEMANTIC_ERROR (_("unexpected build-id reloc section ") +
string(secname ?: "null"));
build_id_vaddr = reloc_vaddr;
}
if (c->session.verbose > 1)
{
clog << _F("Found build-id in %s, length %d, start at %#" PRIx64,
name, build_id_len, build_id_vaddr) << endl;
}
c->build_id_len = build_id_len;
c->build_id_vaddr = build_id_vaddr;
c->build_id_bits = build_id_bits;
}
return DWARF_CB_OK;
}
static int
dump_section_list (Dwfl_Module *m,
unwindsym_dump_context *c,
const char *name, Dwarf_Addr base)
{
string modname = name;
Dwarf_Addr start, end;
dwfl_module_info (m, NULL, &start, &end, NULL, NULL, NULL, NULL);
int n = dwfl_module_relocations (m);
DWFL_ASSERT ("dwfl_module_relocations", n >= 0);
if (n == 0)
{
string secname = ".absolute";
unsigned size = end - start;
c->seclist.push_back (make_pair (secname, size));
return DWARF_CB_OK;
}
else if (n == 1)
{
string secname;
secname = (modname == "kernel") ? "_stext" : ".dynamic";
unsigned size = end - start;
c->seclist.push_back (make_pair (secname, size));
return DWARF_CB_OK;
}
else if (n > 1)
{
string secname;
unsigned size;
Dwarf_Addr bias;
GElf_Ehdr *ehdr, ehdr_mem;
GElf_Shdr *shdr, shdr_mem;
Elf *elf = dwfl_module_getelf(m, &bias);
ehdr = gelf_getehdr(elf, &ehdr_mem);
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn(elf, scn)))
{
shdr = gelf_getshdr(scn, &shdr_mem);
if ((shdr->sh_type == SHT_PROGBITS || shdr->sh_type == SHT_NOBITS)
&& (shdr->sh_flags & SHF_ALLOC))
{
size = shdr->sh_size;
const char* scn_name = elf_strptr(elf, ehdr->e_shstrndx,
shdr->sh_name);
secname = scn_name;
c->seclist.push_back (make_pair (secname, size));
}
}
return DWARF_CB_OK;
}
return DWARF_CB_ABORT;
}
static int
skippable_arch_symbol (GElf_Half e_machine, const char *name, GElf_Sym *sym)
{
if (e_machine == EM_ARM
&& GELF_ST_TYPE (sym->st_info) == STT_NOTYPE
&& (! strcmp(name, "$a") || ! strcmp(name, "$t")
|| ! strcmp(name, "$t.x") || ! strcmp(name, "$d")
|| ! strcmp(name, "$v") || ! strcmp(name, "$d.realdata")))
return 1;
return 0;
}
static int
dump_symbol_tables (Dwfl_Module *m,
unwindsym_dump_context *c,
const char *modname, Dwarf_Addr base)
{
Dwarf_Addr end;
dwfl_module_info (m, NULL, NULL, &end, NULL, NULL, NULL, NULL);
int syments = dwfl_module_getsymtab(m);
DWFL_ASSERT (_F("Getting symbol table for %s", modname), syments >= 0);
int n = dwfl_module_relocations (m);
DWFL_ASSERT ("dwfl_module_relocations", n >= 0);
Dwarf_Addr elf_bias;
GElf_Ehdr *ehdr, ehdr_mem;
Elf *elf;
elf = dwfl_module_getelf(m, &elf_bias);
ehdr = gelf_getehdr(elf, &ehdr_mem);
XXX
Dwarf_Addr extra_offset = 0;
Dwarf_Addr kretprobe_trampoline_addr = (unsigned long) -1;
int is_kernel = !strcmp(modname, "kernel");
int done = 0;
for (int i = 0; i < syments && !done; ++i)
{
if (pending_interrupts)
return DWARF_CB_ABORT;
GElf_Sym sym;
GElf_Word shndxp;
const char *name = dwfl_module_getsym(m, i, &sym, &shndxp);
if (name)
{
Dwarf_Addr sym_addr = sym.st_value;
if (is_kernel)
{
if (! strcmp(name, KERNEL_RELOC_SYMBOL))
{
int ki;
extra_offset = sym_addr;
ki = dwfl_module_relocate_address (m, &extra_offset);
DWFL_ASSERT ("dwfl_module_relocate_address extra_offset",
ki >= 0);
if (c->session.verbose > 2)
clog << _F("Found kernel _stext extra offset %#" PRIx64,
extra_offset) << endl;
if (! c->session.need_symbols
&& (kretprobe_trampoline_addr != (unsigned long) -1
|| ! c->session.need_unwind))
done = 1;
}
else if (kretprobe_trampoline_addr == (unsigned long) -1
&& c->session.need_unwind
&& ! strcmp(name, "kretprobe_trampoline_holder"))
{
int ki;
kretprobe_trampoline_addr = sym_addr;
ki = dwfl_module_relocate_address(m,
&kretprobe_trampoline_addr);
DWFL_ASSERT ("dwfl_module_relocate_address, kretprobe_trampoline_addr", ki >= 0);
if (! c->session.need_symbols
&& extra_offset != 0)
done = 1;
}
}
if (!done && c->session.need_symbols
&& ! skippable_arch_symbol(ehdr->e_machine, name, &sym)
&& (GELF_ST_TYPE (sym.st_info) == STT_FUNC
|| (GELF_ST_TYPE (sym.st_info) == STT_NOTYPE
&& (ehdr->e_type == ET_REL || is_kernel)) || GELF_ST_TYPE (sym.st_info) == STT_OBJECT) && !(sym.st_shndx == SHN_UNDEF || shndxp == (GElf_Word) -1 || sym_addr >= end || sym_addr < base)) {
const char *secname = NULL;
unsigned secidx = 0; Dwarf_Addr func_desc_addr = 0;
if (ehdr->e_machine == EM_PPC64
&& GELF_ST_TYPE (sym.st_info) == STT_FUNC
&& ehdr->e_type != ET_REL)
{
Elf64_Addr opd_addr;
Dwarf_Addr opd_bias;
Elf_Scn *opd;
func_desc_addr = sym_addr;
opd = dwfl_module_address_section (m, &sym_addr, &opd_bias);
DWFL_ASSERT ("dwfl_module_address_section opd", opd != NULL);
Elf_Data *opd_data = elf_rawdata (opd, NULL);
assert(opd_data != NULL);
Elf_Data opd_in, opd_out;
opd_out.d_buf = &opd_addr;
opd_in.d_buf = (char *) opd_data->d_buf + sym_addr;
opd_out.d_size = opd_in.d_size = sizeof (Elf64_Addr);
opd_out.d_type = opd_in.d_type = ELF_T_ADDR;
if (elf64_xlatetom (&opd_out, &opd_in,
ehdr->e_ident[EI_DATA]) == NULL)
throw runtime_error ("elf64_xlatetom failed");
sym_addr = opd_addr + opd_bias;
}
if (n > 0) {
int ki = dwfl_module_relocate_address (m, &sym_addr);
DWFL_ASSERT ("dwfl_module_relocate_address sym_addr", ki >= 0);
secname = dwfl_module_relocation_info (m, ki, NULL);
if (func_desc_addr != 0)
dwfl_module_relocate_address (m, &func_desc_addr);
}
if (n == 1 && is_kernel)
{
if (GELF_ST_TYPE (sym.st_info) == STT_FUNC
&& sym.st_shndx == SHN_ABS)
continue;
secname = "_stext";
}
else if (n > 0)
{
assert (secname != NULL);
if (secname[0] == '\0')
secname = ".dynamic";
else
{
for (secidx = 0; secidx < c->seclist.size(); secidx++)
if (c->seclist[secidx].first==secname)
break;
if (secidx == c->seclist.size()) {
string m = _F("%s has unknown section %s for sym %s",
modname, secname, name);
throw runtime_error(m);
}
}
}
else
{
assert (n == 0);
secname = ".absolute"; }
(c->addrmap[secidx])[sym_addr] = name;
if (func_desc_addr != 0)
(c->addrmap[secidx])[func_desc_addr] = name;
}
}
}
if (is_kernel)
{
c->stext_offset = extra_offset;
if (kretprobe_trampoline_addr != (unsigned long) -1)
c->stp_kretprobe_trampoline_addr = (kretprobe_trampoline_addr
- extra_offset);
}
return DWARF_CB_OK;
}
static int
dump_unwind_tables (Dwfl_Module *m,
unwindsym_dump_context *c,
const char *name, Dwarf_Addr base)
{
get_unwind_data (m, &c->debug_frame, &c->eh_frame,
&c->debug_len, &c->eh_len,
&c->eh_addr, &c->eh_frame_hdr, &c->eh_frame_hdr_len,
&c->debug_frame_hdr, &c->debug_frame_hdr_len,
&c->debug_frame_off, &c->eh_frame_hdr_addr,
c->session);
return DWARF_CB_OK;
}
static void
dump_unwindsym_cxt_table(systemtap_session& session, ostream& output,
const string& modname, unsigned modindex,
const string& secname, unsigned secindex,
const string& table, void*& data, size_t& len)
{
if (data == NULL || len == 0)
return;
if (len > MAX_UNWIND_TABLE_SIZE)
{
if (secname.empty())
session.print_warning (_F("skipping module %s %s table (too big: %zi > %zi)",
modname.c_str(), table.c_str(),
len, (size_t)MAX_UNWIND_TABLE_SIZE));
else
session.print_warning (_F("skipping module %s, section %s %s table (too big: %zi > %zi)",
modname.c_str(), secname.c_str(), table.c_str(),
len, (size_t)MAX_UNWIND_TABLE_SIZE));
data = NULL;
len = 0;
return;
}
output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n";
output << "static uint8_t _stp_module_" << modindex << "_" << table;
if (!secname.empty())
output << "_" << secindex;
output << "[] = \n";
output << " {";
for (size_t i = 0; i < len; i++)
{
int h = ((uint8_t *)data)[i];
output << h << ","; if ((i + 1) % 16 == 0)
output << "\n" << " ";
}
output << "};\n";
output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA */\n";
}
static int
dump_unwindsym_cxt (Dwfl_Module *m,
unwindsym_dump_context *c,
const char *name, Dwarf_Addr base)
{
string modname = name;
unsigned stpmod_idx = c->stp_module_index;
void *debug_frame = c->debug_frame;
size_t debug_len = c->debug_len;
void *debug_frame_hdr = c->debug_frame_hdr;
size_t debug_frame_hdr_len = c->debug_frame_hdr_len;
Dwarf_Addr debug_frame_off = c->debug_frame_off;
void *eh_frame = c->eh_frame;
void *eh_frame_hdr = c->eh_frame_hdr;
size_t eh_len = c->eh_len;
size_t eh_frame_hdr_len = c->eh_frame_hdr_len;
Dwarf_Addr eh_addr = c->eh_addr;
Dwarf_Addr eh_frame_hdr_addr = c->eh_frame_hdr_addr;
dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, "", 0,
"debug_frame", debug_frame, debug_len);
dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, "", 0,
"eh_frame", eh_frame, eh_len);
dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, "", 0,
"eh_frame_hdr", eh_frame_hdr, eh_frame_hdr_len);
if (c->session.need_unwind && debug_frame == NULL && eh_frame == NULL)
{
if (c->session.verbose > 2)
c->session.print_warning ("No unwind data for " + modname
+ ", " + dwfl_errmsg (-1));
}
for (unsigned secidx = 0; secidx < c->seclist.size(); secidx++)
{
c->output << "static struct _stp_symbol "
<< "_stp_module_" << stpmod_idx<< "_symbols_" << secidx << "[] = {\n";
string secname = c->seclist[secidx].first;
Dwarf_Addr extra_offset;
extra_offset = (secname == "_stext") ? c->stext_offset : 0;
if (c->session.need_symbols)
{
for (addrmap_t::iterator it = c->addrmap[secidx].begin();
it != c->addrmap[secidx].end(); it++)
{
if (it->first < extra_offset)
continue;
c->output << " { 0x" << hex << it->first-extra_offset << dec
<< ", " << lex_cast_qstring (it->second) << " },\n";
XXX }
}
c->output << "};\n";
if (secname == ".dynamic" || secname == ".absolute"
|| secname == ".text" || secname == "_stext")
{
dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, secname, secidx,
"debug_frame_hdr", debug_frame_hdr, debug_frame_hdr_len);
}
}
c->output << "static struct _stp_section _stp_module_" << stpmod_idx<< "_sections[] = {\n";
for (unsigned secidx = 0; secidx < c->seclist.size(); secidx++)
{
c->output << "{\n"
<< ".name = " << lex_cast_qstring(c->seclist[secidx].first) << ",\n"
<< ".size = 0x" << hex << c->seclist[secidx].second << dec << ",\n"
<< ".symbols = _stp_module_" << stpmod_idx << "_symbols_" << secidx << ",\n"
<< ".num_symbols = " << c->addrmap[secidx].size() << ",\n";
string secname = c->seclist[secidx].first;
if (debug_frame_hdr && (secname == ".dynamic" || secname == ".absolute"
|| secname == ".text" || secname == "_stext"))
{
c->output << "#if defined(STP_USE_DWARF_UNWINDER)"
<< " && defined(STP_NEED_UNWIND_DATA)\n";
c->output << ".debug_hdr = "
<< "_stp_module_" << stpmod_idx
<< "_debug_frame_hdr_" << secidx << ",\n";
c->output << ".debug_hdr_len = " << debug_frame_hdr_len << ", \n";
Dwarf_Addr dwbias = 0;
dwfl_module_getdwarf (m, &dwbias);
c->output << ".sec_load_offset = 0x"
<< hex << debug_frame_off - dwbias << dec << "\n";
c->output << "#else\n";
c->output << ".debug_hdr = NULL,\n";
c->output << ".debug_hdr_len = 0,\n";
c->output << ".sec_load_offset = 0\n";
c->output << "#endif /* STP_USE_DWARF_UNWINDER"
<< " && STP_NEED_UNWIND_DATA */\n";
}
else
{
c->output << ".debug_hdr = NULL,\n";
c->output << ".debug_hdr_len = 0,\n";
c->output << ".sec_load_offset = 0\n";
}
c->output << "},\n";
}
c->output << "};\n";
const char *mainfile;
dwfl_module_info (m, NULL, NULL, NULL, NULL, NULL, &mainfile, NULL);
string mainpath = resolve_path(mainfile);
string mainname;
if (is_user_module(modname)) mainname = lex_cast_qstring (path_remove_sysroot(c->session,mainpath));
else
{
if (is_fully_resolved(modname, c->session.sysroot, c->session.sysenv))
mainname = lex_cast_qstring (modname_from_path(modname));
else
mainname = lex_cast_qstring (modname);
}
c->output << "static struct _stp_module _stp_module_" << stpmod_idx << " = {\n";
c->output << ".name = " << mainname.c_str() << ",\n";
c->output << ".path = " << lex_cast_qstring (path_remove_sysroot(c->session,mainpath)) << ",\n";
c->output << ".eh_frame_addr = 0x" << hex << eh_addr << dec << ", \n";
c->output << ".unwind_hdr_addr = 0x" << hex << eh_frame_hdr_addr
<< dec << ", \n";
if (debug_frame != NULL)
{
c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n";
c->output << ".debug_frame = "
<< "_stp_module_" << stpmod_idx << "_debug_frame, \n";
c->output << ".debug_frame_len = " << debug_len << ", \n";
c->output << "#else\n";
}
c->output << ".debug_frame = NULL,\n";
c->output << ".debug_frame_len = 0,\n";
if (debug_frame != NULL)
c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA*/\n";
if (eh_frame != NULL)
{
c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n";
c->output << ".eh_frame = "
<< "_stp_module_" << stpmod_idx << "_eh_frame, \n";
c->output << ".eh_frame_len = " << eh_len << ", \n";
if (eh_frame_hdr)
{
c->output << ".unwind_hdr = "
<< "_stp_module_" << stpmod_idx << "_eh_frame_hdr, \n";
c->output << ".unwind_hdr_len = " << eh_frame_hdr_len << ", \n";
}
else
{
c->output << ".unwind_hdr = NULL,\n";
c->output << ".unwind_hdr_len = 0,\n";
}
c->output << "#else\n";
}
c->output << ".eh_frame = NULL,\n";
c->output << ".eh_frame_len = 0,\n";
c->output << ".unwind_hdr = NULL,\n";
c->output << ".unwind_hdr_len = 0,\n";
if (eh_frame != NULL)
c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA*/\n";
c->output << ".sections = _stp_module_" << stpmod_idx << "_sections" << ",\n";
c->output << ".num_sections = sizeof(_stp_module_" << stpmod_idx << "_sections)/"
<< "sizeof(struct _stp_section),\n";
if (c->build_id_len > 0
&& (modname != "kernel" || (c->build_id_vaddr > base + c->stext_offset))) {
c->output << ".build_id_bits = (unsigned char *)\"" ;
for (int j=0; j<c->build_id_len;j++)
c->output << "\\x" << hex
<< (unsigned short) *(c->build_id_bits+j) << dec;
c->output << "\",\n";
c->output << ".build_id_len = " << c->build_id_len << ",\n";
XXX if (modname == "kernel")
c->output << ".build_id_offset = 0x" << hex << c->build_id_vaddr - (base + c->stext_offset)
<< dec << ",\n";
else
c->output << ".build_id_offset = 0x" << hex
<< c->build_id_vaddr << dec << ",\n";
} else
c->output << ".build_id_len = 0,\n";
c->output << ".notes_sect = 0,\n";
c->output << "};\n\n";
c->undone_unwindsym_modules.erase (modname);
if (debug_frame_hdr) free (debug_frame_hdr);
return DWARF_CB_OK;
}
static int
dump_unwindsyms (Dwfl_Module *m,
void **userdata __attribute__ ((unused)),
const char *name,
Dwarf_Addr base,
void *arg)
{
if (pending_interrupts)
return DWARF_CB_ABORT;
unwindsym_dump_context *c = (unwindsym_dump_context*) arg;
assert (c);
string modname = name;
if (c->session.unwindsym_modules.find(modname)
== c->session.unwindsym_modules.end())
return DWARF_CB_OK;
if (c->session.verbose > 1)
clog << "dump_unwindsyms " << name
<< " index=" << c->stp_module_index
<< " base=0x" << hex << base << dec << endl;
int res = DWARF_CB_OK;
c->build_id_len = 0;
c->build_id_vaddr = 0;
c->build_id_bits = NULL;
res = dump_build_id (m, c, name, base);
c->seclist.clear();
if (res == DWARF_CB_OK)
res = dump_section_list(m, c, name, base);
c->addrmap.clear();
if (res == DWARF_CB_OK
&& (c->session.need_symbols || ! strcmp(name, "kernel")))
res = dump_symbol_tables (m, c, name, base);
c->debug_frame = NULL;
c->debug_len = 0;
c->debug_frame_hdr = NULL;
c->debug_frame_hdr_len = 0;
c->debug_frame_off = 0;
c->eh_frame = NULL;
c->eh_frame_hdr = NULL;
c->eh_len = 0;
c->eh_frame_hdr_len = 0;
c->eh_addr = 0;
c->eh_frame_hdr_addr = 0;
if (res == DWARF_CB_OK && c->session.need_unwind)
res = dump_unwind_tables (m, c, name, base);
if (res == DWARF_CB_OK)
res = dump_unwindsym_cxt (m, c, name, base);
if (res == DWARF_CB_OK)
c->stp_module_index++;
return res;
}
void emit_symbol_data_done (unwindsym_dump_context*, systemtap_session&);
void
add_unwindsym_iol_callback (set<string> *added, const char *data)
{
added->insert (string (data));
}
static int
query_module (Dwfl_Module *mod,
void **,
const char *,
Dwarf_Addr,
struct dwflpp *dwflpp)
{
dwflpp->focus_on_module(mod, NULL);
return DWARF_CB_OK;
}
void
add_unwindsym_ldd (systemtap_session &s)
{
std::set<std::string> added;
for (std::set<std::string>::iterator it = s.unwindsym_modules.begin();
it != s.unwindsym_modules.end();
it++)
{
string modname = *it;
assert (modname.length() != 0);
if (! is_user_module (modname)) continue;
dwflpp mod_dwflpp (s, modname, false);
mod_dwflpp.iterate_over_modules(&query_module, &mod_dwflpp);
if (mod_dwflpp.module) {
assert (mod_dwflpp.module_name != "");
mod_dwflpp.iterate_over_libraries (&add_unwindsym_iol_callback, &added);
}
}
s.unwindsym_modules.insert (added.begin(), added.end());
}
static int find_vdso(const char *path, const struct stat *, int type)
{
if (type == FTW_F)
{
const char *name = strrchr(path, '/');
if (name)
{
const char *ext;
name++;
ext = strrchr(name, '.');
if (ext
&& strncmp("vdso", name, 4) == 0
&& strcmp(".so", ext) == 0)
vdso_paths.insert(path);
}
}
return 0;
}
void
add_unwindsym_vdso (systemtap_session &s)
{
string vdso_dir;
if (s.kernel_build_tree == string(s.sysroot + "/lib/modules/"
+ s.kernel_release
+ "/build"))
vdso_dir = s.sysroot + "/lib/modules/" + s.kernel_release + "/vdso";
else
vdso_dir = s.kernel_build_tree + "/arch/";
if (s.verbose > 1)
clog << _("Searching for vdso candidates: ") << vdso_dir << endl;
ftw(vdso_dir.c_str(), find_vdso, 1);
for (set<string>::iterator it = vdso_paths.begin();
it != vdso_paths.end();
it++)
{
s.unwindsym_modules.insert(*it);
if (s.verbose > 1)
clog << _("vdso candidate: ") << *it << endl;
}
}
static void
prepare_symbol_data (systemtap_session& s)
{
if (s.unwindsym_ldd)
add_unwindsym_ldd (s);
if (vma_tracker_enabled (s))
add_unwindsym_vdso (s);
}
void
emit_symbol_data (systemtap_session& s)
{
string symfile = "stap-symbols.h";
s.op->newline() << "#include " << lex_cast_qstring (symfile);
ofstream kallsyms_out ((s.tmpdir + "/" + symfile).c_str());
vector<pair<string,unsigned> > seclist;
map<unsigned, addrmap_t> addrmap;
unwindsym_dump_context ctx = { s, kallsyms_out,
0, 0, NULL, 0, ~0UL, 0, seclist, addrmap,
NULL, 0, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, s.unwindsym_modules };
if (s.unwindsym_modules.size () == 0)
{
emit_symbol_data_done(&ctx, s);
return;
}
set<string> offline_search_modules;
unsigned count;
for (set<string>::iterator it = s.unwindsym_modules.begin();
it != s.unwindsym_modules.end();
it++)
{
string foo = *it;
if (! is_user_module (foo)) offline_search_modules.insert (foo);
}
Dwfl *dwfl = setup_dwfl_kernel (offline_search_modules, &count, s);
ptrdiff_t off = 0;
do
{
assert_no_interrupts();
if (ctx.undone_unwindsym_modules.empty()) break;
off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, off);
}
while (off > 0);
DWFL_ASSERT("dwfl_getmodules", off == 0);
dwfl_end(dwfl);
for (std::set<std::string>::iterator it = s.unwindsym_modules.begin();
it != s.unwindsym_modules.end();
it++)
{
string modname = *it;
assert (modname.length() != 0);
if (! is_user_module (modname)) continue;
Dwfl *dwfl = setup_dwfl_user (modname);
if (dwfl != NULL) {
ptrdiff_t off = 0;
do
{
assert_no_interrupts();
if (ctx.undone_unwindsym_modules.empty()) break;
off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, off);
}
while (off > 0);
DWFL_ASSERT("dwfl_getmodules", off == 0);
}
dwfl_end(dwfl);
}
emit_symbol_data_done (&ctx, s);
}
void
self_unwind_declarations(unwindsym_dump_context *ctx)
{
ctx->output << "static uint8_t _stp_module_self_eh_frame [] = {0,};\n";
ctx->output << "static struct _stp_symbol _stp_module_self_symbols_0[] = {{0},};\n";
ctx->output << "static struct _stp_symbol _stp_module_self_symbols_1[] = {{0},};\n";
ctx->output << "static struct _stp_section _stp_module_self_sections[] = {\n";
ctx->output << "{.name = \".symtab\", .symbols = _stp_module_self_symbols_0, .num_symbols = 0},\n";
ctx->output << "{.name = \".text\", .symbols = _stp_module_self_symbols_1, .num_symbols = 0},\n";
ctx->output << "};\n";
ctx->output << "static struct _stp_module _stp_module_self = {\n";
ctx->output << ".name = \"stap_self_tmp_value\",\n";
ctx->output << ".path = \"stap_self_tmp_value\",\n";
ctx->output << ".num_sections = 2,\n";
ctx->output << ".sections = _stp_module_self_sections,\n";
ctx->output << ".eh_frame = _stp_module_self_eh_frame,\n";
ctx->output << ".eh_frame_len = 0,\n";
ctx->output << ".unwind_hdr_addr = 0x0,\n";
ctx->output << ".unwind_hdr = NULL,\n";
ctx->output << ".unwind_hdr_len = 0,\n";
ctx->output << ".debug_frame = NULL,\n";
ctx->output << ".debug_frame_len = 0,\n";
ctx->output << "};\n";
}
void
emit_symbol_data_done (unwindsym_dump_context *ctx, systemtap_session& s)
{
translator_output *T_800 = s.op_create_auxiliary(true);
T_800->newline() << "__extension__ unsigned int T_800 []"; T_800->newline(1) << "__attribute__((used, section(\".eh_frame\"), aligned(4)))";
T_800->newline() << "= { 0 };";
T_800->newline(-1);
T_800->assert_0_indent ();
ctx->output << "\n";
self_unwind_declarations(ctx);
ctx->output << "static struct _stp_module *_stp_modules [] = {\n";
for (unsigned i=0; i<ctx->stp_module_index; i++)
{
ctx->output << "& _stp_module_" << i << ",\n";
}
ctx->output << "& _stp_module_self,\n";
ctx->output << "};\n";
ctx->output << "static const unsigned _stp_num_modules = ARRAY_SIZE(_stp_modules);\n";
ctx->output << "static unsigned long _stp_kretprobe_trampoline = ";
if (ctx->stp_kretprobe_trampoline_addr == (unsigned long) -1)
ctx->output << "-1;\n";
else
ctx->output << "0x" << hex << ctx->stp_kretprobe_trampoline_addr << dec
<< ";\n";
if (! s.suppress_warnings)
for (set<string>::iterator it = ctx->undone_unwindsym_modules.begin();
it != ctx->undone_unwindsym_modules.end();
it ++)
s.print_warning (_("missing unwind/symbol data for module '")
+ (*it) + "'");
}
struct recursion_info: public traversing_visitor
{
recursion_info (systemtap_session& s): sess(s), nesting_max(0), recursive(false) {}
systemtap_session& sess;
unsigned nesting_max;
bool recursive;
std::vector <functiondecl *> current_nesting;
void visit_functioncall (functioncall* e) {
traversing_visitor::visit_functioncall (e);
unsigned nesting_depth = current_nesting.size() + 1;
if (nesting_max < nesting_depth)
{
if (sess.verbose > 3)
clog << _F("identified max-nested function: %s (%d)",
e->referent->name.c_str(), nesting_depth) << endl;
nesting_max = nesting_depth;
}
for (unsigned j=0; j<current_nesting.size(); j++)
if (current_nesting[j] == e->referent)
{
recursive = true;
if (sess.verbose > 3)
clog << _F("identified recursive function: %s", e->referent->name.c_str()) << endl;
return;
}
current_nesting.push_back (e->referent);
e->referent->body->visit (this);
current_nesting.pop_back ();
}
};
void translate_runtime(systemtap_session& s)
{
s.op->newline() << "#define STAP_MSG_RUNTIME_H_01 "
<< lex_cast_qstring(_("myproc-unprivileged tapset function called "
"without is_myproc checking for pid %d (euid %d)"));
s.op->newline() << "#define STAP_MSG_LOC2C_01 "
<< lex_cast_qstring(_("read fault [man error::fault] at 0x%p (%s)"));
s.op->newline() << "#define STAP_MSG_LOC2C_02 "
<< lex_cast_qstring(_("write fault [man error::fault] at 0x%p (%s)"));
s.op->newline() << "#define STAP_MSG_LOC2C_03 "
<< lex_cast_qstring(_("divide by zero in DWARF operand (%s)"));
}
int
prepare_translate_pass (systemtap_session& s)
{
int rc = 0;
try
{
prepare_symbol_data (s);
}
catch (const semantic_error& e)
{
s.print_error (e);
rc = 1;
}
return rc;
}
int
translate_pass (systemtap_session& s)
{
int rc = 0;
s.op = new translator_output (s.translated_source);
c_unparser cup (& s);
s.up = & cup;
translate_runtime(s);
try
{
int64_t major=0, minor=0;
try
{
vector<string> versions;
tokenize (s.compatible, versions, ".");
if (versions.size() >= 1)
major = lex_cast<int64_t> (versions[0]);
if (versions.size() >= 2)
minor = lex_cast<int64_t> (versions[1]);
if (versions.size() >= 3 && s.verbose > 1)
clog << _F("ignoring extra parts of compat version: %s", s.compatible.c_str()) << endl;
}
catch (const runtime_error)
{
throw SEMANTIC_ERROR(_F("parse error in compatibility version: %s", s.compatible.c_str()));
}
if (major < 0 || major > 255 || minor < 0 || minor > 255)
throw SEMANTIC_ERROR(_F("compatibility version out of range: %s", s.compatible.c_str()));
s.op->newline() << "#define STAP_VERSION(a, b) ( ((a) << 8) + (b) )";
s.op->newline() << "#ifndef STAP_COMPAT_VERSION";
s.op->newline() << "#define STAP_COMPAT_VERSION STAP_VERSION("
<< major << ", " << minor << ")";
s.op->newline() << "#endif";
recursion_info ri (s);
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
functiondecl *fd = it->second;
fd->body->visit (& ri);
}
if (s.verbose > 1)
clog << _F("function recursion-analysis: max-nesting %d %s", ri.nesting_max,
(ri.recursive ? _(" recursive") : _(" non-recursive"))) << endl;
unsigned nesting = ri.nesting_max + 1; if (ri.recursive) nesting += 10;
s.op->newline() << "#include \"runtime_defines.h\"";
if (s.perf_derived_probes)
s.op->newline() << "#define _HAVE_PERF_ 1";
s.op->newline() << "#include \"linux/perf_read.h\"";
s.op->newline() << "#define STP_PR_STAPUSR 0x" << hex << pr_stapusr << dec;
s.op->newline() << "#define STP_PR_STAPSYS 0x" << hex << pr_stapsys << dec;
s.op->newline() << "#define STP_PR_STAPDEV 0x" << hex << pr_stapdev << dec;
s.op->newline() << "#define STP_PRIVILEGE 0x" << hex << s.privilege << dec;
s.op->newline() << "int stp_required_privilege "
<< "__attribute__ ((section (\"" << STAP_PRIVILEGE_SECTION <<"\")))"
<< " = STP_PRIVILEGE;";
s.op->newline() << "#ifndef MAXNESTING";
s.op->newline() << "#define MAXNESTING " << nesting;
s.op->newline() << "#endif";
s.op->newline() << "#define STAPREGEX_MAX_STATE" << s.dfa_maxstate;
s.op->newline() << "#define STAPREGEX_MAX_TAG" << s.dfa_maxtag;
s.op->newline() << "#define STP_SKIP_BADVARS " << (s.skip_badvars ? 1 : 0);
if (s.bulk_mode)
s.op->newline() << "#define STP_BULKMODE";
if (s.timing)
s.op->newline() << "#define STP_TIMING";
if (s.need_unwind)
s.op->newline() << "#define STP_NEED_UNWIND_DATA 1";
s.op->newline() << "#define STP_PROBE_COUNT " << s.probes.size();
s.op->newline() << "static void systemtap_module_refresh (const char* modname);";
if (!s.runtime_usermode_p())
{
s.op->newline() << "#include <linux/mutex.h>";
s.op->newline() << "static DEFINE_MUTEX(module_refresh_mutex);";
s.op->newline() << "#include <linux/version.h>";
s.op->newline() << "#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)";
s.op->newline() << "#define STP_ON_THE_FLY_TIMER_ENABLE";
s.op->newline() << "#endif";
}
s.op->newline() << "#include \"runtime.h\"";
for (unsigned i=0; i<s.embeds.size(); i++)
{
s.op->newline() << s.embeds[i]->code << "\n";
}
s.up->emit_common_header ();
if (s.need_unwind)
s.op->newline() << "#include \"stack.c\"";
if (s.globals.size()>0)
{
s.op->newline() << "struct stp_globals {";
s.op->indent(1);
for (unsigned i=0; i<s.globals.size(); i++)
{
s.up->emit_global (s.globals[i]);
}
s.op->newline(-1) << "};";
s.op->newline();
if (!s.runtime_usermode_p ())
s.op->newline() << "static struct stp_globals stp_global = {";
else
{
s.op->newline() << "static struct {";
s.op->indent(1);
for (unsigned i=0; i<s.globals.size(); i++)
{
assert_no_interrupts();
s.up->emit_global_init_type (s.globals[i]);
}
s.op->newline(-1) << "} stp_global_init = {";
}
s.op->newline(1);
for (unsigned i=0; i<s.globals.size(); i++)
{
assert_no_interrupts();
s.up->emit_global_init (s.globals[i]);
}
s.op->newline(-1) << "};";
s.op->assert_0_indent();
}
else
s.op->newline() << "struct stp_globals {};";
s.op->newline();
s.op->newline() << "#include \"common_session_state.h\"";
s.op->newline() << "#include \"probe_lock.h\" ";
s.op->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
s.op->newline() << "#include \"time.c\""; s.op->newline() << "#endif";
for (map<string,stapdfa*>::iterator it = s.dfas.begin(); it != s.dfas.end(); it++)
{
assert_no_interrupts();
s.op->newline();
try
{
it->second->emit_declaration (s.op);
}
catch (const semantic_error &e)
{
s.print_error(e);
}
}
s.op->assert_0_indent();
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
assert_no_interrupts();
s.op->newline();
s.up->emit_functionsig (it->second);
}
s.op->assert_0_indent();
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
assert_no_interrupts();
s.op->newline();
s.up->emit_function (it->second);
}
s.op->assert_0_indent();
size_t pp_max = 0, pn_max = 0, location_max = 0, derivation_max = 0;
size_t pp_tot = 0, pn_tot = 0, location_tot = 0, derivation_tot = 0;
for (unsigned i=0; i<s.probes.size(); i++)
{
derived_probe* p = s.probes[i];
#define DOIT(var,expr) do { \
size_t var##_size = (expr) + 1; \
var##_max = max (var##_max, var##_size); \
var##_tot += var##_size; } while (0)
DOIT(pp, lex_cast_qstring(*p->sole_location()).size());
DOIT(pn, lex_cast_qstring(*p->script_location()).size());
DOIT(location, lex_cast_qstring(p->tok->location).size());
DOIT(derivation, lex_cast_qstring(p->derived_locations()).size());
#undef DOIT
}
#define CALCIT(var) \
if (s.verbose > 2) \
clog << "adapt " << #var << ":" << var##_max << "max - " << var##_tot << "/" << s.probes.size() << "tot =>"; \
if ((var##_max-(var##_tot/s.probes.size())) < (3 * sizeof(void*))) \
{ \
s.op->newline() << "const char " << #var << "[" << var##_max << "];"; \
if (s.verbose > 2) \
clog << "[]" << endl; \
} \
else \
{ \
s.op->newline() << "const char * const " << #var << ";"; \
if (s.verbose > 2) \
clog << "*" << endl; \
}
s.op->newline();
s.op->newline() << "struct stap_probe {";
s.op->newline(1) << "const size_t index;";
s.op->newline() << "void (* const ph) (struct context*);";
s.op->newline() << "unsigned cond_enabled:1;"; s.op->newline() << "#if defined(STP_TIMING) || defined(STP_ALIBI)";
CALCIT(location);
CALCIT(derivation);
s.op->newline() << "#define STAP_PROBE_INIT_TIMING(L, D) "
<< ".location=(L), .derivation=(D),";
s.op->newline() << "#else";
s.op->newline() << "#define STAP_PROBE_INIT_TIMING(L, D)";
s.op->newline() << "#endif";
CALCIT(pp);
s.op->newline() << "#ifdef STP_NEED_PROBE_NAME";
CALCIT(pn);
s.op->newline() << "#define STAP_PROBE_INIT_NAME(PN) .pn=(PN),";
s.op->newline() << "#else";
s.op->newline() << "#define STAP_PROBE_INIT_NAME(PN)";
s.op->newline() << "#endif";
s.op->newline() << "#define STAP_PROBE_INIT(I, PH, PP, PN, L, D) "
<< "{ .index=(I), .ph=(PH), .cond_enabled=1, .pp=(PP), "
<< "STAP_PROBE_INIT_NAME(PN) "
<< "STAP_PROBE_INIT_TIMING(L, D) "
<< "}";
s.op->newline(-1) << "} static stap_probes[];";
s.op->assert_0_indent();
#undef CALCIT
for (unsigned i=0; i<s.probes.size(); i++)
{
assert_no_interrupts();
s.probes[i]->session_index = i;
if (s.probes[i]->needs_global_locks())
s.probes[i]->body->visit (&cup.vcv_needs_global_locks);
}
s.op->assert_0_indent();
for (unsigned i=0; i<s.probes.size(); i++)
{
assert_no_interrupts();
s.up->emit_probe (s.probes[i]);
}
s.op->assert_0_indent();
s.op->newline() << "static struct stap_probe stap_probes[] = {";
s.op->indent(1);
for (unsigned i=0; i<s.probes.size(); ++i)
{
derived_probe* p = s.probes[i];
s.op->newline() << "STAP_PROBE_INIT(" << i << ", &" << p->name << ", "
<< lex_cast_qstring (*p->sole_location()) << ", "
<< lex_cast_qstring (*p->script_location()) << ", "
<< lex_cast_qstring (p->tok->location) << ", "
<< lex_cast_qstring (p->derived_locations()) << "),";
}
s.op->newline(-1) << "};";
if (s.runtime_usermode_p())
{
s.op->newline() << "static const char* stp_probe_point(size_t index) {";
s.op->newline(1) << "if (index < ARRAY_SIZE(stap_probes))";
s.op->newline(1) << "return stap_probes[index].pp;";
s.op->newline(-1) << "return NULL;";
s.op->newline(-1) << "}";
s.op->assert_0_indent();
}
s.op->assert_0_indent();
s.op->newline();
s.up->emit_module_init ();
s.op->assert_0_indent();
s.op->newline();
s.up->emit_module_refresh ();
s.op->assert_0_indent();
s.op->newline();
s.up->emit_module_exit ();
s.op->assert_0_indent();
s.up->emit_kernel_module_init ();
s.op->assert_0_indent();
s.up->emit_kernel_module_exit ();
s.op->assert_0_indent();
s.op->newline();
emit_symbol_data (s);
s.op->newline() << "MODULE_DESCRIPTION(\"systemtap-generated probe\");";
s.op->newline() << "MODULE_LICENSE(\"GPL\");";
for (unsigned i = 0; i < s.modinfos.size(); i++)
{
const string& mi = s.modinfos[i];
size_t loc = mi.find('=');
string tag = mi.substr (0, loc);
string value = mi.substr (loc+1);
s.op->newline() << "MODULE_INFO(" << tag << "," << lex_cast_qstring(value) << ");";
}
s.op->assert_0_indent();
if (s.runtime_usermode_p())
s.up->emit_global_init_setters();
else
for (unsigned i=0; i<s.globals.size(); i++)
{
s.op->newline();
s.up->emit_global_param (s.globals[i]);
}
s.op->assert_0_indent();
}
catch (const semantic_error& e)
{
s.print_error (e);
}
s.op->line() << "\n";
delete s.op;
s.op = 0;
s.up = 0;
return rc + s.num_errors();
}