diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.in | 21 | ||||
-rw-r--r-- | tapset-procfs.cxx | 521 | ||||
-rw-r--r-- | tapsets.cxx | 500 | ||||
-rw-r--r-- | tapsets.h | 15 |
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()); @@ -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 : */ |