summaryrefslogtreecommitdiffstats
path: root/tapset-mark.cxx
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2009-05-07 15:15:29 -0700
committerJosh Stone <jistone@redhat.com>2009-05-07 15:16:27 -0700
commitdd0e4fa70fd7589ff80618305bd3d24e98a5d73b (patch)
treead838bedde6211cfc00bc3e080d6e31af0e57ce8 /tapset-mark.cxx
parent01c2eefe76e5a0eb8dd5a9504b522cdcb11f2665 (diff)
downloadsystemtap-steved-dd0e4fa70fd7589ff80618305bd3d24e98a5d73b.tar.gz
systemtap-steved-dd0e4fa70fd7589ff80618305bd3d24e98a5d73b.tar.xz
systemtap-steved-dd0e4fa70fd7589ff80618305bd3d24e98a5d73b.zip
Separate the kernel.mark tapset
Diffstat (limited to 'tapset-mark.cxx')
-rw-r--r--tapset-mark.cxx712
1 files changed, 712 insertions, 0 deletions
diff --git a/tapset-mark.cxx b/tapset-mark.cxx
new file mode 100644
index 00000000..1f0ef2ce
--- /dev/null
+++ b/tapset-mark.cxx
@@ -0,0 +1,712 @@
+// tapset for kernel static markers
+// Copyright (C) 2005-2009 Red Hat Inc.
+// Copyright (C) 2005-2007 Intel Corporation.
+// Copyright (C) 2008 James.Bottomley@HansenPartnership.com
+//
+// This file is part of systemtap, and is free software. You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+#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 string TOK_KERNEL("kernel");
+static string TOK_MARK("mark");
+static string TOK_FORMAT("format");
+
+
+// ------------------------------------------------------------------------
+// statically inserted macro-based derived probes
+// ------------------------------------------------------------------------
+
+struct mark_arg
+{
+ bool str;
+ 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 emit_probe_context_vars (translator_output* o);
+ void initialize_probe_context_vars (translator_output* o);
+ void printargs (std::ostream &o) 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->base_name.substr(4,e->base_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);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("marker argument may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("marker argument may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid marker argument use", e->tok);
+ break;
+ }
+ }
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+
+ e->probe_context_var = "__mark_arg" + lex_cast<string>(argnum);
+ e->type = mark_args[argnum-1]->stp_type;
+ provide (e);
+}
+
+
+void
+mark_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
+{
+ string sname = e->base_name;
+
+ if (is_active_lvalue (e))
+ throw semantic_error("write to marker '" + sname + "' not permitted", e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("marker '" + sname + "' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("marker '" + sname + "' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid marker '" + sname + "' use", e->tok);
+ break;
+ }
+ }
+
+ string fname;
+ if (e->base_name == "$format") {
+ fname = string("_mark_format_get");
+ } else {
+ fname = string("_mark_name_get");
+ }
+
+ // Synthesize a functioncall.
+ functioncall* n = new functioncall;
+ n->tok = e->tok;
+ n->function = fname;
+ n->referent = 0; // NB: must not resolve yet, to ensure inclusion in session
+ provide (n);
+}
+
+void
+mark_var_expanding_visitor::visit_target_symbol (target_symbol* e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ if (e->base_name.substr(0,4) == "$arg")
+ visit_target_symbol_arg (e);
+ else if (e->base_name == "$format" || e->base_name == "$name")
+ visit_target_symbol_context (e);
+ else
+ throw semantic_error ("invalid target symbol for marker, $argN, $name or $format expected",
+ e->tok);
+}
+
+
+
+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, new probe_point(*loc) /* .components soon rewritten */),
+ sess (s), probe_name (p_n), probe_format (p_f),
+ target_symbol_seen (false)
+{
+ // create synthetic probe point name; preserve condition
+ 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;
+
+ // expand the marker format
+ parse_probe_format();
+
+ // Now expand the local variables in the probe body
+ mark_var_expanding_visitor v (sess, name, mark_args);
+ this->body = v.require (this->body);
+ target_symbol_seen = v.target_symbol_seen;
+
+ 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; // 'h', 'l', or 'L' for integer fields
+ mark_arg *arg;
+
+ for (; *fmt ; ++fmt)
+ {
+ if (*fmt != '%')
+ {
+ /* Skip text */
+ continue;
+ }
+
+repeat:
+ ++fmt;
+
+ // skip conversion flags (if present)
+ switch (*fmt)
+ {
+ case '-':
+ case '+':
+ case ' ':
+ case '#':
+ case '0':
+ goto repeat;
+ }
+
+ // skip minimum field witdh (if present)
+ if (isdigit(*fmt))
+ skip_atoi(&fmt);
+
+ // skip precision (if present)
+ if (*fmt == '.')
+ {
+ ++fmt;
+ if (isdigit(*fmt))
+ skip_atoi(&fmt);
+ }
+
+ // get the conversion qualifier (if present)
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
+ {
+ qualifier = *fmt;
+ ++fmt;
+ if (qualifier == 'l' && *fmt == 'l')
+ {
+ qualifier = 'L';
+ ++fmt;
+ }
+ }
+
+ // get the conversion type
+ switch (*fmt)
+ {
+ case 'c':
+ arg = new mark_arg;
+ arg->str = 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->c_type = "char *";
+ arg->stp_type = pe_string;
+ mark_args.push_back(arg);
+ continue;
+
+ case 'p':
+ arg = new mark_arg;
+ arg->str = false;
+ // This should really be 'void *'. But, then we'll get a
+ // compile error when we assign the void pointer to an
+ // integer without a cast. So, we use 'long' instead, since
+ // it should have the same size as 'void *'.
+ 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':
+ // fall through...
+ break;
+
+ default:
+ if (!*fmt)
+ --fmt;
+ continue;
+ }
+
+ arg = new mark_arg;
+ arg->str = 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 ();
+
+ // Make sure <linux/marker.h> is included early.
+ 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);
+}
+
+
+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::emit_probe_context_vars (translator_output* o)
+{
+ // If we haven't seen a target symbol for this probe, quit.
+ if (! target_symbol_seen)
+ return;
+
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ {
+ string localname = "__mark_arg" + lex_cast<string>(i+1);
+ switch (mark_args[i]->stp_type)
+ {
+ case pe_long:
+ o->newline() << "int64_t " << localname << ";";
+ break;
+ case pe_string:
+ o->newline() << "string_t " << localname << ";";
+ break;
+ default:
+ throw semantic_error ("cannot expand unknown type");
+ break;
+ }
+ }
+}
+
+
+void
+mark_derived_probe::initialize_probe_context_vars (translator_output* o)
+{
+ // If we haven't seen a target symbol for this probe, quit.
+ if (! target_symbol_seen)
+ return;
+
+ bool deref_fault_needed = false;
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ {
+ string localname = "l->__mark_arg" + lex_cast<string>(i+1);
+ switch (mark_args[i]->stp_type)
+ {
+ case pe_long:
+ o->newline() << localname << " = va_arg(*c->mark_va_list, "
+ << mark_args[i]->c_type << ");";
+ break;
+
+ case pe_string:
+ // We're assuming that this is a kernel string (this code is
+ // basically the guts of kernel_string), not a user string.
+ o->newline() << "{ " << mark_args[i]->c_type
+ << " tmp_str = va_arg(*c->mark_va_list, "
+ << mark_args[i]->c_type << ");";
+ o->newline() << "deref_string (" << localname
+ << ", tmp_str, MAXSTRINGLEN); }";
+ deref_fault_needed = true;
+ break;
+
+ default:
+ throw semantic_error ("cannot expand unknown type");
+ break;
+ }
+ }
+ if (deref_fault_needed)
+ // Need to report errors?
+ o->newline() << "deref_fault: ;";
+}
+
+void
+mark_derived_probe::printargs(std::ostream &o) const
+{
+ for (unsigned i = 0; i < mark_args.size(); i++)
+ {
+ string localname = "$arg" + lex_cast<string>(i+1);
+ switch (mark_args[i]->stp_type)
+ {
+ case pe_long:
+ o << " " << localname << ":long";
+ break;
+ case pe_string:
+ o << " " << localname << ":string";
+ break;
+ default:
+ o << " " << 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 char * const pp;";
+ s.op->newline() << "void (* const ph) (struct context *);";
+
+ 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() << " .pp=" << lex_cast_qstring (*probes[i]->sole_location())
+ << ",";
+ s.op->line() << " .ph=&" << probes[i]->name;
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+ s.op->newline();
+
+
+ // Emit the marker callback function
+ 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.op, "STAP_SESSION_RUNNING", "smp->pp");
+ s.op->newline() << "c->marker_name = smp->name;";
+ s.op->newline() << "c->marker_format = smp->format;";
+ s.op->newline() << "c->mark_va_list = args;";
+ s.op->newline() << "(*smp->ph) (c);";
+ s.op->newline() << "c->mark_va_list = NULL;";
+ s.op->newline() << "c->data = NULL;";
+
+ common_probe_entryfn_epilogue (s.op);
+ 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->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--) {"; // partial rollback
+ 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;"; // don't attempt to register any more probes
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+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) << "}"; // for loop
+}
+
+
+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;
+
+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);
+};
+
+
+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);
+
+ // trim leading whitespace
+ string::size_type notwhite = format.find_first_not_of(" \t");
+ format.erase(0, notwhite);
+
+ // If the format is empty, make sure we add back a space
+ // character, which is what MARK_NOARGS expands to.
+ if (format.length() == 0)
+ format = " ";
+
+ if (sess.verbose>3)
+ clog << "'" << name << "' '" << module << "' '" << format
+ << "'" << endl;
+
+ if (mark_cache.count(name) > 0)
+ {
+ // If we have 2 markers with the same we've got 2 cases:
+ // different format strings or duplicate format strings.
+ // If an existing marker in the cache doesn't have the
+ // same format string, add this marker.
+ 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();
+ }
+
+ // Search marker list for matching markers
+ for (mark_cache_const_iterator_t it = mark_cache.begin();
+ it != mark_cache.end(); it++)
+ {
+ // Below, "rc" has negative polarity: zero iff matching.
+ int rc = fnmatch(mark_str_val.c_str(), it->first.c_str(), 0);
+ if (! rc)
+ {
+ bool add_result = true;
+
+ // Match format strings (if the user specified one)
+ 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);
+ }
+ }
+ }
+}
+
+
+
+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);
+}
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */