summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--Makefile.in21
-rw-r--r--tapset-procfs.cxx521
-rw-r--r--tapsets.cxx500
-rw-r--r--tapsets.h15
5 files changed, 557 insertions, 502 deletions
diff --git a/Makefile.am b/Makefile.am
index a5920b00..08775a74 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,7 +38,7 @@ stap_SOURCES = main.cxx \
parse.cxx staptree.cxx elaborate.cxx translate.cxx \
tapsets.cxx buildrun.cxx loc2c.c hash.cxx mdfour.c \
cache.cxx util.cxx coveragedb.cxx dwarf_wrappers.cxx \
- tapset-been.cxx tapset-timers.cxx
+ tapset-been.cxx tapset-procfs.cxx tapset-timers.cxx
stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@
BUILT_SOURCES =
diff --git a/Makefile.in b/Makefile.in
index 55515d02..7d12e709 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -122,7 +122,8 @@ am_stap_OBJECTS = stap-main.$(OBJEXT) stap-parse.$(OBJEXT) \
stap-hash.$(OBJEXT) stap-mdfour.$(OBJEXT) stap-cache.$(OBJEXT) \
stap-util.$(OBJEXT) stap-coveragedb.$(OBJEXT) \
stap-dwarf_wrappers.$(OBJEXT) stap-tapset-been.$(OBJEXT) \
- stap-tapset-timers.$(OBJEXT) $(am__objects_1)
+ stap-tapset-procfs.$(OBJEXT) stap-tapset-timers.$(OBJEXT) \
+ $(am__objects_1)
stap_OBJECTS = $(am_stap_OBJECTS)
stap_LINK = $(CXXLD) $(stap_CXXFLAGS) $(CXXFLAGS) $(stap_LDFLAGS) \
$(LDFLAGS) -o $@
@@ -328,7 +329,8 @@ oldinclude_HEADERS = includes/sys/sdt.h
stap_SOURCES = main.cxx parse.cxx staptree.cxx elaborate.cxx \
translate.cxx tapsets.cxx buildrun.cxx loc2c.c hash.cxx \
mdfour.c cache.cxx util.cxx coveragedb.cxx dwarf_wrappers.cxx \
- tapset-been.cxx tapset-timers.cxx $(am__append_4)
+ tapset-been.cxx tapset-procfs.cxx tapset-timers.cxx \
+ $(am__append_4)
stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@ $(am__append_6)
# Arrange for git_version.h to be regenerated at every "make".
@@ -603,6 +605,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-parse.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-staptree.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-been.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-procfs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapset-timers.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-tapsets.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stap-translate.Po@am__quote@
@@ -1098,6 +1101,20 @@ stap-tapset-been.obj: tapset-been.cxx
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-been.obj `if test -f 'tapset-been.cxx'; then $(CYGPATH_W) 'tapset-been.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-been.cxx'; fi`
+stap-tapset-procfs.o: tapset-procfs.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-procfs.o -MD -MP -MF $(DEPDIR)/stap-tapset-procfs.Tpo -c -o stap-tapset-procfs.o `test -f 'tapset-procfs.cxx' || echo '$(srcdir)/'`tapset-procfs.cxx
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-procfs.Tpo $(DEPDIR)/stap-tapset-procfs.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-procfs.cxx' object='stap-tapset-procfs.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-procfs.o `test -f 'tapset-procfs.cxx' || echo '$(srcdir)/'`tapset-procfs.cxx
+
+stap-tapset-procfs.obj: tapset-procfs.cxx
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-procfs.obj -MD -MP -MF $(DEPDIR)/stap-tapset-procfs.Tpo -c -o stap-tapset-procfs.obj `if test -f 'tapset-procfs.cxx'; then $(CYGPATH_W) 'tapset-procfs.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-procfs.cxx'; fi`
+@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-procfs.Tpo $(DEPDIR)/stap-tapset-procfs.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tapset-procfs.cxx' object='stap-tapset-procfs.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -c -o stap-tapset-procfs.obj `if test -f 'tapset-procfs.cxx'; then $(CYGPATH_W) 'tapset-procfs.cxx'; else $(CYGPATH_W) '$(srcdir)/tapset-procfs.cxx'; fi`
+
stap-tapset-timers.o: tapset-timers.cxx
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(stap_CPPFLAGS) $(CPPFLAGS) $(stap_CXXFLAGS) $(CXXFLAGS) -MT stap-tapset-timers.o -MD -MP -MF $(DEPDIR)/stap-tapset-timers.Tpo -c -o stap-tapset-timers.o `test -f 'tapset-timers.cxx' || echo '$(srcdir)/'`tapset-timers.cxx
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/stap-tapset-timers.Tpo $(DEPDIR)/stap-tapset-timers.Po
diff --git a/tapset-procfs.cxx b/tapset-procfs.cxx
new file mode 100644
index 00000000..3568c3d3
--- /dev/null
+++ b/tapset-procfs.cxx
@@ -0,0 +1,521 @@
+// tapset for procfs
+// 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 <cstring>
+#include <string>
+
+
+using namespace std;
+using namespace __gnu_cxx;
+
+
+static string TOK_PROCFS("procfs");
+static string TOK_READ("read");
+static string TOK_WRITE("write");
+
+
+// ------------------------------------------------------------------------
+// procfs file derived probes
+// ------------------------------------------------------------------------
+
+
+struct procfs_derived_probe: public derived_probe
+{
+ string path;
+ bool write;
+ bool target_symbol_seen;
+
+ procfs_derived_probe (systemtap_session &, probe* p, probe_point* l, string ps, bool w);
+ void join_group (systemtap_session& s);
+};
+
+
+struct procfs_probe_set
+{
+ procfs_derived_probe* read_probe;
+ procfs_derived_probe* write_probe;
+
+ procfs_probe_set () : read_probe (NULL), write_probe (NULL) {}
+};
+
+
+struct procfs_derived_probe_group: public generic_dpg<procfs_derived_probe>
+{
+private:
+ map<string, procfs_probe_set*> probes_by_path;
+ typedef map<string, procfs_probe_set*>::iterator p_b_p_iterator;
+ bool has_read_probes;
+ bool has_write_probes;
+
+public:
+ procfs_derived_probe_group () :
+ has_read_probes(false), has_write_probes(false) {}
+
+ void enroll (procfs_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+
+struct procfs_var_expanding_visitor: public var_expanding_visitor
+{
+ procfs_var_expanding_visitor(systemtap_session& s, const string& pn,
+ string path, bool write_probe):
+ sess (s), probe_name (pn), path (path), write_probe (write_probe),
+ target_symbol_seen (false) {}
+
+ systemtap_session& sess;
+ string probe_name;
+ string path;
+ bool write_probe;
+ bool target_symbol_seen;
+
+ void visit_target_symbol (target_symbol* e);
+};
+
+
+procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p,
+ probe_point* l, string ps, bool w):
+ derived_probe(p, l), path(ps), write(w), target_symbol_seen(false)
+{
+ // Expand local variables in the probe body
+ procfs_var_expanding_visitor v (s, name, path, write);
+ this->body = v.require (this->body);
+ target_symbol_seen = v.target_symbol_seen;
+}
+
+
+void
+procfs_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.procfs_derived_probes)
+ s.procfs_derived_probes = new procfs_derived_probe_group ();
+ s.procfs_derived_probes->enroll (this);
+}
+
+
+void
+procfs_derived_probe_group::enroll (procfs_derived_probe* p)
+{
+ procfs_probe_set *pset;
+
+ if (probes_by_path.count(p->path) == 0)
+ {
+ pset = new procfs_probe_set;
+ probes_by_path[p->path] = pset;
+ }
+ else
+ {
+ pset = probes_by_path[p->path];
+
+ // You can only specify 1 read and 1 write probe.
+ if (p->write && pset->write_probe != NULL)
+ throw semantic_error("only one write procfs probe can exist for procfs path \"" + p->path + "\"");
+ else if (! p->write && pset->read_probe != NULL)
+ throw semantic_error("only one read procfs probe can exist for procfs path \"" + p->path + "\"");
+
+ // XXX: multiple writes should be acceptable
+ }
+
+ if (p->write)
+ {
+ pset->write_probe = p;
+ has_write_probes = true;
+ }
+ else
+ {
+ pset->read_probe = p;
+ has_read_probes = true;
+ }
+}
+
+
+void
+procfs_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_path.empty())
+ return;
+
+ s.op->newline() << "/* ---- procfs probes ---- */";
+ s.op->newline() << "#include \"procfs.c\"";
+
+ // Emit the procfs probe data list
+ s.op->newline() << "static struct stap_procfs_probe {";
+ s.op->newline(1)<< "const char *path;";
+ s.op->newline() << "const char *read_pp;";
+ s.op->newline() << "void (*read_ph) (struct context*);";
+ s.op->newline() << "const char *write_pp;";
+ s.op->newline() << "void (*write_ph) (struct context*);";
+ s.op->newline(-1) << "} stap_procfs_probes[] = {";
+ s.op->indent(1);
+
+ for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end();
+ it++)
+ {
+ procfs_probe_set *pset = it->second;
+
+ s.op->newline() << "{";
+ s.op->line() << " .path=" << lex_cast_qstring (it->first) << ",";
+
+ if (pset->read_probe != NULL)
+ {
+ s.op->line() << " .read_pp="
+ << lex_cast_qstring (*pset->read_probe->sole_location())
+ << ",";
+ s.op->line() << " .read_ph=&" << pset->read_probe->name << ",";
+ }
+ else
+ {
+ s.op->line() << " .read_pp=NULL,";
+ s.op->line() << " .read_ph=NULL,";
+ }
+
+ if (pset->write_probe != NULL)
+ {
+ s.op->line() << " .write_pp="
+ << lex_cast_qstring (*pset->write_probe->sole_location())
+ << ",";
+ s.op->line() << " .write_ph=&" << pset->write_probe->name;
+ }
+ else
+ {
+ s.op->line() << " .write_pp=NULL,";
+ s.op->line() << " .write_ph=NULL";
+ }
+ s.op->line() << " },";
+ }
+ s.op->newline(-1) << "};";
+
+ if (has_read_probes)
+ {
+ // Output routine to fill in 'page' with our data.
+ s.op->newline();
+
+ s.op->newline() << "static int _stp_procfs_read(char *page, char **start, off_t off, int count, int *eof, void *data) {";
+
+ s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
+ s.op->newline() << "int bytes = 0;";
+ s.op->newline() << "string_t strdata = {'\\0'};";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->read_pp");
+
+ s.op->newline() << "if (c->data == NULL)";
+ s.op->newline(1) << "c->data = &strdata;";
+ s.op->newline(-1) << "else {";
+
+ s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
+ s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << "_stp_exit ();";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "atomic_dec (& c->busy);";
+ s.op->newline() << "goto probe_epilogue;";
+ s.op->newline(-1) << "}";
+
+ // call probe function (which copies data into strdata)
+ s.op->newline() << "(*spp->read_ph) (c);";
+
+ // copy string data into 'page'
+ s.op->newline() << "c->data = NULL;";
+ s.op->newline() << "bytes = strnlen(strdata, MAXSTRINGLEN - 1);";
+ s.op->newline() << "if (off >= bytes)";
+ s.op->newline(1) << "*eof = 1;";
+ s.op->newline(-1) << "else {";
+ s.op->newline(1) << "bytes -= off;";
+ s.op->newline() << "if (bytes > count)";
+ s.op->newline(1) << "bytes = count;";
+ s.op->newline(-1) << "memcpy(page, strdata + off, bytes);";
+ s.op->newline() << "*start = page;";
+ s.op->newline(-1) << "}";
+
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return bytes;";
+
+ s.op->newline(-1) << "}";
+ }
+ if (has_write_probes)
+ {
+ s.op->newline() << "static int _stp_procfs_write(struct file *file, const char *buffer, unsigned long count, void *data) {";
+
+ s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
+ s.op->newline() << "string_t strdata = {'\\0'};";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->write_pp");
+
+ s.op->newline() << "if (count > (MAXSTRINGLEN - 1))";
+ s.op->newline(1) << "count = MAXSTRINGLEN - 1;";
+ s.op->newline(-1) << "_stp_copy_from_user(strdata, buffer, count);";
+
+ s.op->newline() << "if (c->data == NULL)";
+ s.op->newline(1) << "c->data = &strdata;";
+ s.op->newline(-1) << "else {";
+
+ s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
+ s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << "_stp_exit ();";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "atomic_dec (& c->busy);";
+ s.op->newline() << "goto probe_epilogue;";
+ s.op->newline(-1) << "}";
+
+ // call probe function (which copies data out of strdata)
+ s.op->newline() << "(*spp->write_ph) (c);";
+
+ s.op->newline() << "c->data = NULL;";
+ common_probe_entryfn_epilogue (s.op);
+
+ s.op->newline() << "return count;";
+ s.op->newline(-1) << "}";
+ }
+}
+
+
+void
+procfs_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ if (probes_by_path.empty())
+ return;
+
+ s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];";
+
+ s.op->newline() << "if (spp->read_pp)";
+ s.op->newline(1) << "probe_point = spp->read_pp;";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "probe_point = spp->write_pp;";
+
+ s.op->newline(-1) << "rc = _stp_create_procfs(spp->path, i);";
+
+ s.op->newline() << "if (rc) {";
+ s.op->newline(1) << "_stp_close_procfs();";
+ s.op->newline() << "break;";
+ s.op->newline(-1) << "}";
+
+ if (has_read_probes)
+ {
+ s.op->newline() << "if (spp->read_pp)";
+ s.op->newline(1) << "_stp_procfs_files[i]->read_proc = &_stp_procfs_read;";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "_stp_procfs_files[i]->read_proc = NULL;";
+ s.op->indent(-1);
+ }
+ else
+ s.op->newline() << "_stp_procfs_files[i]->read_proc = NULL;";
+
+ if (has_write_probes)
+ {
+ s.op->newline() << "if (spp->write_pp)";
+ s.op->newline(1) << "_stp_procfs_files[i]->write_proc = &_stp_procfs_write;";
+ s.op->newline(-1) << "else";
+ s.op->newline(1) << "_stp_procfs_files[i]->write_proc = NULL;";
+ s.op->indent(-1);
+ }
+ else
+ s.op->newline() << "_stp_procfs_files[i]->write_proc = NULL;";
+
+ s.op->newline() << "_stp_procfs_files[i]->data = spp;";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+
+void
+procfs_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ if (probes_by_path.empty())
+ return;
+
+ s.op->newline() << "_stp_close_procfs();";
+}
+
+
+void
+procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e)
+{
+ assert(e->base_name.size() > 0 && e->base_name[0] == '$');
+
+ if (e->base_name != "$value")
+ throw semantic_error ("invalid target symbol for procfs probe, $value expected",
+ e->tok);
+
+ if (e->components.size() > 0)
+ {
+ switch (e->components[0].first)
+ {
+ case target_symbol::comp_literal_array_index:
+ throw semantic_error("procfs target variable '$value' may not be used as array",
+ e->tok);
+ break;
+ case target_symbol::comp_struct_member:
+ throw semantic_error("procfs target variable '$value' may not be used as a structure",
+ e->tok);
+ break;
+ default:
+ throw semantic_error ("invalid use of procfs target variable '$value'",
+ e->tok);
+ break;
+ }
+ }
+
+ bool lvalue = is_active_lvalue(e);
+ if (write_probe && lvalue)
+ throw semantic_error("procfs $value variable is read-only in a procfs write probe", e->tok);
+ else if (! write_probe && ! lvalue)
+ throw semantic_error("procfs $value variable cannot be read in a procfs read probe", e->tok);
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+
+ // Synthesize a function.
+ functiondecl *fdecl = new functiondecl;
+ fdecl->tok = e->tok;
+ embeddedcode *ec = new embeddedcode;
+ ec->tok = e->tok;
+
+ string fname = (string(lvalue ? "_procfs_value_set" : "_procfs_value_get")
+ + "_" + lex_cast<string>(tick++));
+ string locvalue = "CONTEXT->data";
+
+ if (! lvalue)
+ ec->code = string("strlcpy (THIS->__retvalue, ") + locvalue
+ + string(", MAXSTRINGLEN); /* pure */");
+ else
+ ec->code = string("strlcpy (") + locvalue
+ + string(", THIS->value, MAXSTRINGLEN);");
+
+ fdecl->name = fname;
+ fdecl->body = ec;
+ fdecl->type = pe_string;
+
+ if (lvalue)
+ {
+ // Modify the fdecl so it carries a single pe_string formal
+ // argument called "value".
+
+ vardecl *v = new vardecl;
+ v->type = pe_string;
+ v->name = "value";
+ v->tok = e->tok;
+ fdecl->formal_args.push_back(v);
+ }
+ sess.functions[fdecl->name]=fdecl;
+
+ // 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
+
+ if (lvalue)
+ {
+ // Provide the functioncall to our parent, so that it can be
+ // used to substitute for the assignment node immediately above
+ // us.
+ assert(!target_symbol_setter_functioncalls.empty());
+ *(target_symbol_setter_functioncalls.top()) = n;
+ }
+
+ provide (n);
+}
+
+
+struct procfs_builder: public derived_probe_builder
+{
+ procfs_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+
+void
+procfs_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string path;
+ bool has_procfs = get_param(parameters, TOK_PROCFS, path);
+ bool has_read = (parameters.find(TOK_READ) != parameters.end());
+ bool has_write = (parameters.find(TOK_WRITE) != parameters.end());
+
+ // If no procfs path, default to "command". The runtime will do
+ // this for us, but if we don't do it here, we'll think the
+ // following 2 probes are attached to different paths:
+ //
+ // probe procfs("command").read {}"
+ // probe procfs.write {}
+
+ if (! has_procfs)
+ path = "command";
+ // If we have a path, we need to validate it.
+ else
+ {
+ string::size_type start_pos, end_pos;
+ string component;
+ start_pos = 0;
+ while ((end_pos = path.find('/', start_pos)) != string::npos)
+ {
+ // Make sure it doesn't start with '/'.
+ if (end_pos == 0)
+ throw semantic_error ("procfs path cannot start with a '/'",
+ location->tok);
+
+ component = path.substr(start_pos, end_pos - start_pos);
+ // Make sure it isn't empty.
+ if (component.size() == 0)
+ throw semantic_error ("procfs path component cannot be empty",
+ location->tok);
+ // Make sure it isn't relative.
+ else if (component == "." || component == "..")
+ throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
+
+ start_pos = end_pos + 1;
+ }
+ component = path.substr(start_pos);
+ // Make sure it doesn't end with '/'.
+ if (component.size() == 0)
+ throw semantic_error ("procfs path cannot end with a '/'", location->tok);
+ // Make sure it isn't relative.
+ else if (component == "." || component == "..")
+ throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
+ }
+
+ if (!(has_read ^ has_write))
+ throw semantic_error ("need read/write component", location->tok);
+
+ finished_results.push_back(new procfs_derived_probe(sess, base, location,
+ path, has_write));
+}
+
+
+void
+register_tapset_procfs(systemtap_session& s)
+{
+ match_node* root = s.pattern_root;
+ derived_probe_builder *builder = new procfs_builder();
+
+ root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(builder);
+ root->bind_str(TOK_PROCFS)->bind(TOK_READ)->bind(builder);
+ root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder);
+ root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)->bind(builder);
+}
+
+
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/tapsets.cxx b/tapsets.cxx
index e6d641bd..86b2aa53 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -4270,15 +4270,6 @@ dwflpp::query_modules(base_query *q)
iterate_over_modules(&query_module, q);
}
-struct var_expanding_visitor: public update_visitor
-{
- static unsigned tick;
- stack<functioncall**> target_symbol_setter_functioncalls;
-
- var_expanding_visitor() {}
- void visit_assignment (assignment* e);
-};
-
struct dwarf_var_expanding_visitor: public var_expanding_visitor
{
@@ -8550,488 +8541,6 @@ kprobe_builder::build(systemtap_session & sess,
// ------------------------------------------------------------------------
-// procfs file derived probes
-// ------------------------------------------------------------------------
-
-
-static string TOK_PROCFS("procfs");
-static string TOK_READ("read");
-static string TOK_WRITE("write");
-
-struct procfs_derived_probe: public derived_probe
-{
- string path;
- bool write;
- bool target_symbol_seen;
-
- procfs_derived_probe (systemtap_session &, probe* p, probe_point* l, string ps, bool w);
- void join_group (systemtap_session& s);
-};
-
-
-struct procfs_probe_set
-{
- procfs_derived_probe* read_probe;
- procfs_derived_probe* write_probe;
-
- procfs_probe_set () : read_probe (NULL), write_probe (NULL) {}
-};
-
-
-struct procfs_derived_probe_group: public generic_dpg<procfs_derived_probe>
-{
-private:
- map<string, procfs_probe_set*> probes_by_path;
- typedef map<string, procfs_probe_set*>::iterator p_b_p_iterator;
- bool has_read_probes;
- bool has_write_probes;
-
-public:
- procfs_derived_probe_group () :
- has_read_probes(false), has_write_probes(false) {}
-
- void enroll (procfs_derived_probe* probe);
- void emit_module_decls (systemtap_session& s);
- void emit_module_init (systemtap_session& s);
- void emit_module_exit (systemtap_session& s);
-};
-
-
-struct procfs_var_expanding_visitor: public var_expanding_visitor
-{
- procfs_var_expanding_visitor(systemtap_session& s, const string& pn,
- string path, bool write_probe):
- sess (s), probe_name (pn), path (path), write_probe (write_probe),
- target_symbol_seen (false) {}
-
- systemtap_session& sess;
- string probe_name;
- string path;
- bool write_probe;
- bool target_symbol_seen;
-
- void visit_target_symbol (target_symbol* e);
-};
-
-
-procfs_derived_probe::procfs_derived_probe (systemtap_session &s, probe* p,
- probe_point* l, string ps, bool w):
- derived_probe(p, l), path(ps), write(w), target_symbol_seen(false)
-{
- // Expand local variables in the probe body
- procfs_var_expanding_visitor v (s, name, path, write);
- this->body = v.require (this->body);
- target_symbol_seen = v.target_symbol_seen;
-}
-
-
-void
-procfs_derived_probe::join_group (systemtap_session& s)
-{
- if (! s.procfs_derived_probes)
- s.procfs_derived_probes = new procfs_derived_probe_group ();
- s.procfs_derived_probes->enroll (this);
-}
-
-
-void
-procfs_derived_probe_group::enroll (procfs_derived_probe* p)
-{
- procfs_probe_set *pset;
-
- if (probes_by_path.count(p->path) == 0)
- {
- pset = new procfs_probe_set;
- probes_by_path[p->path] = pset;
- }
- else
- {
- pset = probes_by_path[p->path];
-
- // You can only specify 1 read and 1 write probe.
- if (p->write && pset->write_probe != NULL)
- throw semantic_error("only one write procfs probe can exist for procfs path \"" + p->path + "\"");
- else if (! p->write && pset->read_probe != NULL)
- throw semantic_error("only one read procfs probe can exist for procfs path \"" + p->path + "\"");
-
- // XXX: multiple writes should be acceptable
- }
-
- if (p->write)
- {
- pset->write_probe = p;
- has_write_probes = true;
- }
- else
- {
- pset->read_probe = p;
- has_read_probes = true;
- }
-}
-
-
-void
-procfs_derived_probe_group::emit_module_decls (systemtap_session& s)
-{
- if (probes_by_path.empty())
- return;
-
- s.op->newline() << "/* ---- procfs probes ---- */";
- s.op->newline() << "#include \"procfs.c\"";
-
- // Emit the procfs probe data list
- s.op->newline() << "static struct stap_procfs_probe {";
- s.op->newline(1)<< "const char *path;";
- s.op->newline() << "const char *read_pp;";
- s.op->newline() << "void (*read_ph) (struct context*);";
- s.op->newline() << "const char *write_pp;";
- s.op->newline() << "void (*write_ph) (struct context*);";
- s.op->newline(-1) << "} stap_procfs_probes[] = {";
- s.op->indent(1);
-
- for (p_b_p_iterator it = probes_by_path.begin(); it != probes_by_path.end();
- it++)
- {
- procfs_probe_set *pset = it->second;
-
- s.op->newline() << "{";
- s.op->line() << " .path=" << lex_cast_qstring (it->first) << ",";
-
- if (pset->read_probe != NULL)
- {
- s.op->line() << " .read_pp="
- << lex_cast_qstring (*pset->read_probe->sole_location())
- << ",";
- s.op->line() << " .read_ph=&" << pset->read_probe->name << ",";
- }
- else
- {
- s.op->line() << " .read_pp=NULL,";
- s.op->line() << " .read_ph=NULL,";
- }
-
- if (pset->write_probe != NULL)
- {
- s.op->line() << " .write_pp="
- << lex_cast_qstring (*pset->write_probe->sole_location())
- << ",";
- s.op->line() << " .write_ph=&" << pset->write_probe->name;
- }
- else
- {
- s.op->line() << " .write_pp=NULL,";
- s.op->line() << " .write_ph=NULL";
- }
- s.op->line() << " },";
- }
- s.op->newline(-1) << "};";
-
- if (has_read_probes)
- {
- // Output routine to fill in 'page' with our data.
- s.op->newline();
-
- s.op->newline() << "static int _stp_procfs_read(char *page, char **start, off_t off, int count, int *eof, void *data) {";
-
- s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
- s.op->newline() << "int bytes = 0;";
- s.op->newline() << "string_t strdata = {'\\0'};";
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->read_pp");
-
- s.op->newline() << "if (c->data == NULL)";
- s.op->newline(1) << "c->data = &strdata;";
- s.op->newline(-1) << "else {";
-
- s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
- s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
- s.op->newline() << "_stp_exit ();";
- s.op->newline(-1) << "}";
- s.op->newline() << "atomic_dec (& c->busy);";
- s.op->newline() << "goto probe_epilogue;";
- s.op->newline(-1) << "}";
-
- // call probe function (which copies data into strdata)
- s.op->newline() << "(*spp->read_ph) (c);";
-
- // copy string data into 'page'
- s.op->newline() << "c->data = NULL;";
- s.op->newline() << "bytes = strnlen(strdata, MAXSTRINGLEN - 1);";
- s.op->newline() << "if (off >= bytes)";
- s.op->newline(1) << "*eof = 1;";
- s.op->newline(-1) << "else {";
- s.op->newline(1) << "bytes -= off;";
- s.op->newline() << "if (bytes > count)";
- s.op->newline(1) << "bytes = count;";
- s.op->newline(-1) << "memcpy(page, strdata + off, bytes);";
- s.op->newline() << "*start = page;";
- s.op->newline(-1) << "}";
-
- common_probe_entryfn_epilogue (s.op);
- s.op->newline() << "return bytes;";
-
- s.op->newline(-1) << "}";
- }
- if (has_write_probes)
- {
- s.op->newline() << "static int _stp_procfs_write(struct file *file, const char *buffer, unsigned long count, void *data) {";
-
- s.op->newline(1) << "struct stap_procfs_probe *spp = (struct stap_procfs_probe *)data;";
- s.op->newline() << "string_t strdata = {'\\0'};";
-
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "spp->write_pp");
-
- s.op->newline() << "if (count > (MAXSTRINGLEN - 1))";
- s.op->newline(1) << "count = MAXSTRINGLEN - 1;";
- s.op->newline(-1) << "_stp_copy_from_user(strdata, buffer, count);";
-
- s.op->newline() << "if (c->data == NULL)";
- s.op->newline(1) << "c->data = &strdata;";
- s.op->newline(-1) << "else {";
-
- s.op->newline(1) << "if (unlikely (atomic_inc_return (& skipped_count) > MAXSKIPPED)) {";
- s.op->newline(1) << "atomic_set (& session_state, STAP_SESSION_ERROR);";
- s.op->newline() << "_stp_exit ();";
- s.op->newline(-1) << "}";
- s.op->newline() << "atomic_dec (& c->busy);";
- s.op->newline() << "goto probe_epilogue;";
- s.op->newline(-1) << "}";
-
- // call probe function (which copies data out of strdata)
- s.op->newline() << "(*spp->write_ph) (c);";
-
- s.op->newline() << "c->data = NULL;";
- common_probe_entryfn_epilogue (s.op);
-
- s.op->newline() << "return count;";
- s.op->newline(-1) << "}";
- }
-}
-
-
-void
-procfs_derived_probe_group::emit_module_init (systemtap_session& s)
-{
- if (probes_by_path.empty())
- return;
-
- s.op->newline() << "for (i = 0; i < " << probes_by_path.size() << "; i++) {";
- s.op->newline(1) << "struct stap_procfs_probe *spp = &stap_procfs_probes[i];";
-
- s.op->newline() << "if (spp->read_pp)";
- s.op->newline(1) << "probe_point = spp->read_pp;";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "probe_point = spp->write_pp;";
-
- s.op->newline(-1) << "rc = _stp_create_procfs(spp->path, i);";
-
- s.op->newline() << "if (rc) {";
- s.op->newline(1) << "_stp_close_procfs();";
- s.op->newline() << "break;";
- s.op->newline(-1) << "}";
-
- if (has_read_probes)
- {
- s.op->newline() << "if (spp->read_pp)";
- s.op->newline(1) << "_stp_procfs_files[i]->read_proc = &_stp_procfs_read;";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "_stp_procfs_files[i]->read_proc = NULL;";
- s.op->indent(-1);
- }
- else
- s.op->newline() << "_stp_procfs_files[i]->read_proc = NULL;";
-
- if (has_write_probes)
- {
- s.op->newline() << "if (spp->write_pp)";
- s.op->newline(1) << "_stp_procfs_files[i]->write_proc = &_stp_procfs_write;";
- s.op->newline(-1) << "else";
- s.op->newline(1) << "_stp_procfs_files[i]->write_proc = NULL;";
- s.op->indent(-1);
- }
- else
- s.op->newline() << "_stp_procfs_files[i]->write_proc = NULL;";
-
- s.op->newline() << "_stp_procfs_files[i]->data = spp;";
- s.op->newline(-1) << "}"; // for loop
-}
-
-
-void
-procfs_derived_probe_group::emit_module_exit (systemtap_session& s)
-{
- if (probes_by_path.empty())
- return;
-
- s.op->newline() << "_stp_close_procfs();";
-}
-
-
-void
-procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e)
-{
- assert(e->base_name.size() > 0 && e->base_name[0] == '$');
-
- if (e->base_name != "$value")
- throw semantic_error ("invalid target symbol for procfs probe, $value expected",
- e->tok);
-
- if (e->components.size() > 0)
- {
- switch (e->components[0].first)
- {
- case target_symbol::comp_literal_array_index:
- throw semantic_error("procfs target variable '$value' may not be used as array",
- e->tok);
- break;
- case target_symbol::comp_struct_member:
- throw semantic_error("procfs target variable '$value' may not be used as a structure",
- e->tok);
- break;
- default:
- throw semantic_error ("invalid use of procfs target variable '$value'",
- e->tok);
- break;
- }
- }
-
- bool lvalue = is_active_lvalue(e);
- if (write_probe && lvalue)
- throw semantic_error("procfs $value variable is read-only in a procfs write probe", e->tok);
- else if (! write_probe && ! lvalue)
- throw semantic_error("procfs $value variable cannot be read in a procfs read probe", e->tok);
-
- // Remember that we've seen a target variable.
- target_symbol_seen = true;
-
- // Synthesize a function.
- functiondecl *fdecl = new functiondecl;
- fdecl->tok = e->tok;
- embeddedcode *ec = new embeddedcode;
- ec->tok = e->tok;
-
- string fname = (string(lvalue ? "_procfs_value_set" : "_procfs_value_get")
- + "_" + lex_cast<string>(tick++));
- string locvalue = "CONTEXT->data";
-
- if (! lvalue)
- ec->code = string("strlcpy (THIS->__retvalue, ") + locvalue
- + string(", MAXSTRINGLEN); /* pure */");
- else
- ec->code = string("strlcpy (") + locvalue
- + string(", THIS->value, MAXSTRINGLEN);");
-
- fdecl->name = fname;
- fdecl->body = ec;
- fdecl->type = pe_string;
-
- if (lvalue)
- {
- // Modify the fdecl so it carries a single pe_string formal
- // argument called "value".
-
- vardecl *v = new vardecl;
- v->type = pe_string;
- v->name = "value";
- v->tok = e->tok;
- fdecl->formal_args.push_back(v);
- }
- sess.functions[fdecl->name]=fdecl;
-
- // 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
-
- if (lvalue)
- {
- // Provide the functioncall to our parent, so that it can be
- // used to substitute for the assignment node immediately above
- // us.
- assert(!target_symbol_setter_functioncalls.empty());
- *(target_symbol_setter_functioncalls.top()) = n;
- }
-
- provide (n);
-}
-
-
-struct procfs_builder: public derived_probe_builder
-{
- procfs_builder() {}
- virtual void build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results);
-};
-
-
-void
-procfs_builder::build(systemtap_session & sess,
- probe * base,
- probe_point * location,
- literal_map_t const & parameters,
- vector<derived_probe *> & finished_results)
-{
- string path;
- bool has_procfs = get_param(parameters, TOK_PROCFS, path);
- bool has_read = (parameters.find(TOK_READ) != parameters.end());
- bool has_write = (parameters.find(TOK_WRITE) != parameters.end());
-
- // If no procfs path, default to "command". The runtime will do
- // this for us, but if we don't do it here, we'll think the
- // following 2 probes are attached to different paths:
- //
- // probe procfs("command").read {}"
- // probe procfs.write {}
-
- if (! has_procfs)
- path = "command";
- // If we have a path, we need to validate it.
- else
- {
- string::size_type start_pos, end_pos;
- string component;
- start_pos = 0;
- while ((end_pos = path.find('/', start_pos)) != string::npos)
- {
- // Make sure it doesn't start with '/'.
- if (end_pos == 0)
- throw semantic_error ("procfs path cannot start with a '/'",
- location->tok);
-
- component = path.substr(start_pos, end_pos - start_pos);
- // Make sure it isn't empty.
- if (component.size() == 0)
- throw semantic_error ("procfs path component cannot be empty",
- location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
-
- start_pos = end_pos + 1;
- }
- component = path.substr(start_pos);
- // Make sure it doesn't end with '/'.
- if (component.size() == 0)
- throw semantic_error ("procfs path cannot end with a '/'", location->tok);
- // Make sure it isn't relative.
- else if (component == "." || component == "..")
- throw semantic_error ("procfs path cannot be relative (and contain '.' or '..')", location->tok);
- }
-
- if (!(has_read ^ has_write))
- throw semantic_error ("need read/write component", location->tok);
-
- finished_results.push_back(new procfs_derived_probe(sess, base, location,
- path, has_write));
-}
-
-
-
-// ------------------------------------------------------------------------
// statically inserted macro-based derived probes
// ------------------------------------------------------------------------
@@ -10987,6 +10496,7 @@ void
register_standard_tapsets(systemtap_session & s)
{
register_tapset_been(s);
+ register_tapset_procfs(s);
register_tapset_timers(s);
s.pattern_root->bind("perfmon")->bind_str("counter")
@@ -11061,14 +10571,6 @@ register_standard_tapsets(systemtap_session & s)
s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_TRACE)
->bind(new tracepoint_builder());
- // procfs parts
- s.pattern_root->bind(TOK_PROCFS)->bind(TOK_READ)->bind(new procfs_builder());
- s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_READ)
- ->bind(new procfs_builder());
- s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder());
- s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)
- ->bind(new procfs_builder());
-
// Kprobe based probe
s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)
->bind(new kprobe_builder());
diff --git a/tapsets.h b/tapsets.h
index cf83b5e3..ba322d44 100644
--- a/tapsets.h
+++ b/tapsets.h
@@ -21,8 +21,10 @@ void common_probe_entryfn_prologue (translator_output* o, std::string statestr,
void common_probe_entryfn_epilogue (translator_output* o, bool overload_processing = true);
void register_tapset_been(systemtap_session& sess);
+void register_tapset_procfs(systemtap_session& sess);
void register_tapset_timers(systemtap_session& sess);
+
// ------------------------------------------------------------------------
// Generic derived_probe_group: contains an ordinary vector of the
// given type. It provides only the enrollment function.
@@ -36,6 +38,19 @@ public:
void enroll (DP* probe) { probes.push_back (probe); }
};
+
+// ------------------------------------------------------------------------
+// An update visitor that allows replacing assignments with a function call
+
+struct var_expanding_visitor: public update_visitor
+{
+ static unsigned tick;
+ std::stack<functioncall**> target_symbol_setter_functioncalls;
+
+ var_expanding_visitor() {}
+ void visit_assignment (assignment* e);
+};
+
#endif // TAPSETS_H
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */