tapset-mark.cxx - systemtap
Data types defined
Functions defined
Source code
#include "session.h"
#include "tapsets.h"
#include "translate.h"
#include "util.h"
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <string>
extern "C" {
#include <fnmatch.h>
}
using namespace std;
using namespace __gnu_cxx;
static const string TOK_KERNEL("kernel");
static const string TOK_MARK("mark");
static const string TOK_FORMAT("format");
struct mark_arg
{
bool str;
bool isptr;
string c_type;
exp_type stp_type;
};
struct mark_derived_probe: public derived_probe
{
mark_derived_probe (systemtap_session &s,
const string& probe_name, const string& probe_format,
probe* base_probe, probe_point* location);
systemtap_session& sess;
string probe_name, probe_format;
vector <struct mark_arg *> mark_args;
bool target_symbol_seen;
void join_group (systemtap_session& s);
void print_dupe_stamp (ostream& o);
void initialize_probe_context_vars (translator_output* o);
void getargs (std::list<std::string> &arg_set) const;
void parse_probe_format ();
};
struct mark_derived_probe_group: public generic_dpg<mark_derived_probe>
{
public:
void emit_module_decls (systemtap_session& s);
void emit_module_init (systemtap_session& s);
void emit_module_exit (systemtap_session& s);
};
struct mark_var_expanding_visitor: public var_expanding_visitor
{
mark_var_expanding_visitor(systemtap_session& s, const string& pn,
vector <struct mark_arg *> &mark_args):
sess (s), probe_name (pn), mark_args (mark_args),
target_symbol_seen (false) {}
systemtap_session& sess;
string probe_name;
vector <struct mark_arg *> &mark_args;
bool target_symbol_seen;
void visit_target_symbol (target_symbol* e);
void visit_target_symbol_arg (target_symbol* e);
void visit_target_symbol_context (target_symbol* e);
};
void
mark_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
{
string argnum_s = e->name.substr(4,e->name.length()-4);
int argnum = atoi (argnum_s.c_str());
if (argnum < 1 || argnum > (int)mark_args.size())
throw SEMANTIC_ERROR (_("invalid marker argument number"), e->tok);
if (is_active_lvalue (e))
throw SEMANTIC_ERROR(_("write to marker parameter not permitted"), e->tok);
e->assert_no_components("marker");
target_symbol_seen = true;
symbol* sym = new symbol;
sym->tok = e->tok;
sym->name = "__mark_arg" + lex_cast(argnum);
provide (sym);
}
void
mark_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
{
string sname = e->name;
if (is_active_lvalue (e))
throw SEMANTIC_ERROR(_F("write to marker '%s' not permitted", sname.c_str()), e->tok);
e->assert_no_components("marker");
if (e->name == "$format" || e->name == "$name") {
embedded_expr *expr = new embedded_expr;
expr->tok = e->tok;
if (e->name == "$format")
expr->code = string("/* string */ /* pure */ ")
+ string("c->ips.kmark.marker_format ? c->ips.kmark.marker_format : \"\"");
else
expr->code = string("/* string */ /* pure */ ")
+ string("c->ips.kmark.marker_name ? c->ips.kmark.marker_name : \"\"");
provide (expr);
}
else if (e->name == "$$vars" || e->name == "$$parms")
{
print_format* pf = print_format::create(e->tok, "sprintf");
for (unsigned i = 0; i < mark_args.size(); ++i)
{
if (i > 0)
pf->raw_components += " ";
pf->raw_components += "$arg" + lex_cast(i+1);
target_symbol *tsym = new target_symbol;
tsym->tok = e->tok;
tsym->name = "$arg" + lex_cast(i+1);
tsym->saved_conversion_error = 0;
expression *texp = require (tsym); assert (!tsym->saved_conversion_error);
switch (mark_args[i]->stp_type)
{
case pe_long:
pf->raw_components += mark_args[i]->isptr ? "=%p" : "=%#x";
break;
case pe_string:
pf->raw_components += "=%s";
break;
default:
pf->raw_components += "=%#x";
break;
}
pf->args.push_back(texp);
}
pf->components = print_format::string_to_components(pf->raw_components);
provide (pf);
}
}
void
mark_var_expanding_visitor::visit_target_symbol (target_symbol* e)
{
assert(e->name.size() > 0 && e->name[0] == '$');
try
{
if (e->addressof)
throw SEMANTIC_ERROR(_("cannot take address of marker variable"), e->tok);
if (startswith(e->name, "$arg"))
visit_target_symbol_arg (e);
else if (e->name == "$format" || e->name == "$name"
|| e->name == "$$parms" || e->name == "$$vars")
visit_target_symbol_context (e);
else
throw SEMANTIC_ERROR (_("invalid target symbol for marker, $argN, $name, $format, $$parms or $$vars expected"),
e->tok);
}
catch (const semantic_error &er)
{
e->chain (er);
provide (e);
}
}
mark_derived_probe::mark_derived_probe (systemtap_session &s,
const string& p_n,
const string& p_f,
probe* base, probe_point* loc):
derived_probe (base, loc, true ),
sess (s), probe_name (p_n), probe_format (p_f),
target_symbol_seen (false)
{
vector<probe_point::component*> comps;
comps.push_back (new probe_point::component (TOK_KERNEL));
comps.push_back (new probe_point::component (TOK_MARK, new literal_string (probe_name)));
comps.push_back (new probe_point::component (TOK_FORMAT, new literal_string (probe_format)));
this->sole_location()->components = comps;
parse_probe_format();
mark_var_expanding_visitor v (sess, name, mark_args);
v.replace (this->body);
target_symbol_seen = v.target_symbol_seen;
if (target_symbol_seen)
for (unsigned i = 0; i < mark_args.size(); ++i)
{
vardecl* v = new vardecl;
v->name = "__mark_arg" + lex_cast(i+1);
v->tok = this->tok;
v->set_arity(0, this->tok);
v->type = mark_args[i]->stp_type;
v->synthetic = true;
this->locals.push_back (v);
}
if (sess.verbose > 2)
clog << "marker-based " << name << " mark=" << probe_name << " fmt='" << probe_format
<< "'" << endl;
}
static int
skip_atoi(const char **s)
{
int i = 0;
while (isdigit(**s))
i = i * 10 + *((*s)++) - '0';
return i;
}
void
mark_derived_probe::parse_probe_format()
{
const char *fmt = probe_format.c_str();
int qualifier; mark_arg *arg;
for (; *fmt ; ++fmt)
{
if (*fmt != '%')
{
continue;
}
repeat:
++fmt;
switch (*fmt)
{
case '-':
case '+':
case ' ':
case '#':
case '0':
goto repeat;
}
if (isdigit(*fmt))
skip_atoi(&fmt);
if (*fmt == '.')
{
++fmt;
if (isdigit(*fmt))
skip_atoi(&fmt);
}
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
{
qualifier = *fmt;
++fmt;
if (qualifier == 'l' && *fmt == 'l')
{
qualifier = 'L';
++fmt;
}
}
switch (*fmt)
{
case 'c':
arg = new mark_arg;
arg->str = false;
arg->isptr = false;
arg->c_type = "int";
arg->stp_type = pe_long;
mark_args.push_back(arg);
continue;
case 's':
arg = new mark_arg;
arg->str = true;
arg->isptr = false;
arg->c_type = "char *";
arg->stp_type = pe_string;
mark_args.push_back(arg);
continue;
case 'p':
arg = new mark_arg;
arg->str = false;
arg->isptr = true;
arg->c_type = "long";
arg->stp_type = pe_long;
mark_args.push_back(arg);
continue;
case '%':
continue;
case 'o':
case 'X':
case 'x':
case 'd':
case 'i':
case 'u':
break;
default:
if (!*fmt)
--fmt;
continue;
}
arg = new mark_arg;
arg->str = false;
arg->isptr = false;
arg->stp_type = pe_long;
switch (qualifier)
{
case 'L':
arg->c_type = "long long";
break;
case 'l':
arg->c_type = "long";
break;
case 'h':
arg->c_type = "short";
break;
default:
arg->c_type = "int";
break;
}
mark_args.push_back(arg);
}
}
void
mark_derived_probe::join_group (systemtap_session& s)
{
if (! s.mark_derived_probes)
{
s.mark_derived_probes = new mark_derived_probe_group ();
embeddedcode *ec = new embeddedcode;
ec->tok = NULL;
ec->code = string("#if ! defined(CONFIG_MARKERS)\n")
+ string("#error \"Need CONFIG_MARKERS!\"\n")
+ string("#endif\n")
+ string("#include <linux/marker.h>\n");
s.embeds.push_back(ec);
}
s.mark_derived_probes->enroll (this);
this->group = s.mark_derived_probes;
}
void
mark_derived_probe::print_dupe_stamp (ostream& o)
{
if (target_symbol_seen)
for (unsigned i = 0; i < mark_args.size(); i++)
o << mark_args[i]->c_type << " __mark_arg" << (i+1) << endl;
}
void
mark_derived_probe::initialize_probe_context_vars (translator_output* o)
{
if (! target_symbol_seen)
return;
bool deref_fault_needed = false;
for (unsigned i = 0; i < mark_args.size(); i++)
{
string localname = "l->" +
sess.up->c_localname("__mark_arg" + lex_cast(i+1));
switch (mark_args[i]->stp_type)
{
case pe_long:
o->newline() << localname << " = va_arg(*c->ips.kmark.mark_va_list, "
<< mark_args[i]->c_type << ");";
break;
case pe_string:
o->newline() << "{ " << mark_args[i]->c_type
<< " tmp_str = va_arg(*c->ips.kmark.mark_va_list, "
<< mark_args[i]->c_type << ");";
o->newline() << "kderef_string (" << localname
<< ", tmp_str, MAXSTRINGLEN); }";
deref_fault_needed = true;
break;
default:
throw SEMANTIC_ERROR (_("cannot expand unknown type"));
break;
}
}
if (deref_fault_needed)
o->newline() << "deref_fault: ;";
}
void
mark_derived_probe::getargs(std::list<std::string> &arg_set) const
{
arg_set.push_back("$name:string");
arg_set.push_back("$format:string");
for (unsigned i = 0; i < mark_args.size(); i++)
{
string localname = "$arg" + lex_cast(i+1);
switch (mark_args[i]->stp_type)
{
case pe_long:
arg_set.push_back(localname+":long");
break;
case pe_string:
arg_set.push_back(localname+":string");
break;
default:
arg_set.push_back(localname+":unknown");
break;
}
}
}
void
mark_derived_probe_group::emit_module_decls (systemtap_session& s)
{
if (probes.empty())
return;
s.op->newline() << "/* ---- marker probes ---- */";
s.op->newline() << "static struct stap_marker_probe {";
s.op->newline(1) << "const char * const name;";
s.op->newline() << "const char * const format;";
s.op->newline() << "const struct stap_probe * const probe;";
s.op->newline(-1) << "} stap_marker_probes [" << probes.size() << "] = {";
s.op->indent(1);
for (unsigned i=0; i < probes.size(); i++)
{
s.op->newline () << "{";
s.op->line() << " .name=" << lex_cast_qstring(probes[i]->probe_name) << ",";
s.op->line() << " .format=" << lex_cast_qstring(probes[i]->probe_format) << ",";
s.op->line() << " .probe=" << common_probe_init (probes[i]) << ",";
s.op->line() << " },";
}
s.op->newline(-1) << "};";
s.op->newline();
s.op->newline();
s.op->newline() << "static void enter_marker_probe (void *probe_data, void *call_data, const char *fmt, va_list *args) {";
s.op->newline(1) << "struct stap_marker_probe *smp = (struct stap_marker_probe *)probe_data;";
common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "smp->probe",
"stp_probe_type_marker");
s.op->newline() << "c->ips.kmark.marker_name = smp->name;";
s.op->newline() << "c->ips.kmark.marker_format = smp->format;";
s.op->newline() << "c->ips.kmark.mark_va_list = args;";
s.op->newline() << "(*smp->probe->ph) (c);";
s.op->newline() << "c->ips.kmark.mark_va_list = NULL;";
common_probe_entryfn_epilogue (s, true, otf_safe_context(s));
s.op->newline(-1) << "}";
return;
}
void
mark_derived_probe_group::emit_module_init (systemtap_session &s)
{
if (probes.size () == 0)
return;
s.op->newline() << "/* init marker probes */";
s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];";
s.op->newline() << "probe_point = smp->probe->pp;";
s.op->newline() << "rc = marker_probe_register(smp->name, smp->format, enter_marker_probe, smp);";
s.op->newline() << "if (rc) {";
s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; s.op->newline(1) << "struct stap_marker_probe *smp2 = &stap_marker_probes[j];";
s.op->newline() << "marker_probe_unregister(smp2->name, enter_marker_probe, smp2);";
s.op->newline(-1) << "}";
s.op->newline() << "break;"; s.op->newline(-1) << "}";
s.op->newline(-1) << "}"; }
void
mark_derived_probe_group::emit_module_exit (systemtap_session& s)
{
if (probes.empty())
return;
s.op->newline() << "/* deregister marker probes */";
s.op->newline() << "for (i=0; i<" << probes.size() << "; i++) {";
s.op->newline(1) << "struct stap_marker_probe *smp = &stap_marker_probes[i];";
s.op->newline() << "marker_probe_unregister(smp->name, enter_marker_probe, smp);";
s.op->newline(-1) << "}"; }
struct mark_builder: public derived_probe_builder
{
private:
bool cache_initialized;
typedef multimap<string, string> mark_cache_t;
typedef multimap<string, string>::const_iterator mark_cache_const_iterator_t;
typedef pair<mark_cache_const_iterator_t, mark_cache_const_iterator_t>
mark_cache_const_iterator_pair_t;
mark_cache_t mark_cache;
string suggest_marks(systemtap_session& sess,
const string& mark);
public:
mark_builder(): cache_initialized(false) {}
void build_no_more (systemtap_session &s)
{
if (! mark_cache.empty())
{
if (s.verbose > 3)
clog << _("mark_builder releasing cache") << endl;
mark_cache.clear();
}
}
void build(systemtap_session & sess,
probe * base,
probe_point * location,
literal_map_t const & parameters,
vector<derived_probe *> & finished_results);
};
string
mark_builder::suggest_marks(systemtap_session& sess,
const string& mark)
{
if (mark.empty() || mark_cache.empty())
return "";
set<string> marks;
for (mark_cache_const_iterator_t it = mark_cache.begin();
it != mark_cache.end(); it++)
marks.insert(it->first);
if (sess.verbose > 2)
clog << "suggesting from " << marks.size()
<< " kernel marks" << endl;
if (marks.empty())
return "";
return levenshtein_suggest(mark, marks, 5); }
void
mark_builder::build(systemtap_session & sess,
probe * base,
probe_point *loc,
literal_map_t const & parameters,
vector<derived_probe *> & finished_results)
{
string mark_str_val;
bool has_mark_str = get_param (parameters, TOK_MARK, mark_str_val);
string mark_format_val;
bool has_mark_format = get_param (parameters, TOK_FORMAT, mark_format_val);
assert (has_mark_str);
(void) has_mark_str;
if (! cache_initialized)
{
cache_initialized = true;
string module_markers_path = sess.kernel_build_tree + "/Module.markers";
ifstream module_markers;
module_markers.open(module_markers_path.c_str(), ifstream::in);
if (! module_markers)
{
if (sess.verbose>3)
clog << module_markers_path << _(" cannot be opened: ")
<< strerror(errno) << endl;
return;
}
string name, module, format;
do
{
module_markers >> name >> module;
getline(module_markers, format);
string::size_type notwhite = format.find_first_not_of(" \t");
format.erase(0, notwhite);
if (format.length() == 0)
format = " ";
if (sess.verbose>3)
clog << "'" << name << "' '" << module << "' '" << format
<< "'" << endl;
if (mark_cache.count(name) > 0)
{
mark_cache_const_iterator_pair_t ret;
mark_cache_const_iterator_t it;
bool matching_format_string = false;
ret = mark_cache.equal_range(name);
for (it = ret.first; it != ret.second; ++it)
{
if (format == it->second)
{
matching_format_string = true;
break;
}
}
if (! matching_format_string)
mark_cache.insert(pair<string,string>(name, format));
}
else
mark_cache.insert(pair<string,string>(name, format));
}
while (! module_markers.eof());
module_markers.close();
}
unsigned results_pre = finished_results.size();
for (mark_cache_const_iterator_t it = mark_cache.begin();
it != mark_cache.end(); it++)
{
int rc = fnmatch(mark_str_val.c_str(), it->first.c_str(), 0);
if (! rc)
{
bool add_result = true;
if (has_mark_format && fnmatch(mark_format_val.c_str(),
it->second.c_str(), 0))
add_result = false;
if (add_result)
{
derived_probe *dp
= new mark_derived_probe (sess,
it->first, it->second,
base, loc);
finished_results.push_back (dp);
}
}
}
if (results_pre == finished_results.size() && !loc->from_glob)
{
string sugs = suggest_marks(sess, mark_str_val);
if (!sugs.empty())
throw SEMANTIC_ERROR (_NF("no match (similar mark: %s)",
"no match (similar marks: %s)",
sugs.find(',') == string::npos,
sugs.c_str()));
}
}
void
register_tapset_mark(systemtap_session& s)
{
match_node* root = s.pattern_root;
derived_probe_builder *builder = new mark_builder();
root = root->bind(TOK_KERNEL);
root = root->bind_str(TOK_MARK);
root->bind(builder);
root->bind_str(TOK_FORMAT)->bind(builder);
}