]> sourceware.org Git - systemtap.git/commitdiff
PR15053: stapdyn -G global variable setting support (initial patch)
authorSerguei Makarov <smakarov@redhat.com>
Fri, 22 Mar 2013 22:11:27 +0000 (18:11 -0400)
committerSerguei Makarov <smakarov@redhat.com>
Fri, 22 Mar 2013 22:11:27 +0000 (18:11 -0400)
* buildrun.cxx (make_dyninst_run_command): pass module arguments on to stapdyn.
* runtime/dyninst/runtime.h (set_int64_t): new function.
* runtime/dyninst/stapdyn.h (stp_global_setter): function prototype.
* stapdyn/mutator.cxx (mutator::mutator): add modoptions to mutator.
  (mutator::init_modoptions): new function.
  (mutator::run_module_init): call init_modoptions to initialize globals.
* stapdyn/mutator.h (class mutator): add modoptions to mutator.
* stapdyn/stapdyn.cxx (main): collect module arguments from command line.
* translate.cxx (struct c_unparser): new emit_global_init_* functions.
  (c_unparser::emit_global_param): stapdyn no longer calls this function.
  (c_unparser::emit_global_init_setters): new function.
  (c_unparser::emit_global_init): emit default initial values in stapdyn mode.
  (c_unparser::emit_global_init_type): new function.
  (c_unparser::emit_module_init): copy initial values of stapdyn globals
     to shared memory as appropriate.
  (translate_pass): emit static-initialization struct for stapdyn,
     and also emit the setter function to access this struct.
* translate.h (struct c_unparser): document new emit_global_init_* functions.

buildrun.cxx
runtime/dyninst/runtime.h
runtime/dyninst/stapdyn.h
stapdyn/mutator.cxx
stapdyn/mutator.h
stapdyn/stapdyn.cxx
translate.cxx
translate.h

index 61bdfa6ca09458bd2bf33d8e35365928aa66539b..459580e1f5e87e71c96731ef71ad2e43c8340e1b 100644 (file)
@@ -656,6 +656,9 @@ make_dyninst_run_command (systemtap_session& s, const string& remotedir,
   cmd.push_back((remotedir.empty() ? s.tmpdir : remotedir)
                + "/" + s.module_filename());
 
+  // add module arguments
+  cmd.insert(cmd.end(), s.globalopts.begin(), s.globalopts.end());
+
   return cmd;
 }
 
index 0deceb7560a9f13631505805b6782580bc690624..c23bd544617b7c1256524afa0d1555a773087413 100644 (file)
@@ -133,6 +133,24 @@ static int stp_pthread_mutex_init_shared(pthread_mutex_t *mutex);
 #include "stat.c"
 #include "unwind.c"
 
+/* Support function for int64_t module parameters. */
+static int set_int64_t(const char *val, int64_t *mp)
+{
+  char *endp;
+  long long ll;
+
+  if (!val)
+    return -EINVAL;
+
+  ll = strtoull(val, &endp, 0);
+
+  if ((endp == val) || ((int64_t)ll != ll) || (*endp != '\0'))
+    return -EINVAL;
+
+  *mp = (int64_t)ll;
+  return 0;
+}
+
 static int systemtap_module_init(void);
 static void systemtap_module_exit(void);
 
index 0c98e2d4a6cd3424d0519751dda38fecdd893d58..6f81df4b43d3df7842ab97146b0899491c37a9e2 100644 (file)
@@ -63,6 +63,10 @@ extern int enter_dyninst_utrace_probe(uint64_t index, struct pt_regs *regs);
 extern const char* stp_dyninst_shm_init(void);
 extern int stp_dyninst_shm_connect(const char* name);
 
+/* The following function is dynamically generated by systemtap, and
+   used by stapdyn to modify global variables at module startup only
+   (that is, *before* running stp_dyninst_session_init()): */
+extern int stp_global_setter(const char *name, const char *value);
 
 #define STAPDYN_PROBE_ALL_FLAGS (uint64_t)(STAPDYN_PROBE_FLAG_RETURN   \
     | STAPDYN_PROBE_FLAG_PROC_BEGIN | STAPDYN_PROBE_FLAG_PROC_END      \
index d1445ed3d51aa1d84e43fc49d760adfb60e25bb6..9d95bfac39f27b04384ddf871e6153f7a7101e3b 100644 (file)
@@ -110,9 +110,11 @@ setup_signals (void)
 }
 
 
-mutator:: mutator (const string& module_name):
+mutator:: mutator (const string& module_name,
+                   vector<string>& module_options):
   module(NULL), module_name(resolve_path(module_name)),
-  p_target_created(false), signal_count(0), utrace_enter_fn(NULL)
+  modoptions(module_options), p_target_created(false),
+  signal_count(0), utrace_enter_fn(NULL)
 {
   // NB: dlopen does a library-path search if the filename doesn't have any
   // path components, which is why we use resolve_path(module_name)
@@ -279,6 +281,46 @@ mutator::attach_process(pid_t pid)
   return true;
 }
 
+bool
+mutator::init_modoptions()
+{
+  typeof(&stp_global_setter) global_setter = NULL;
+  set_dlsym(global_setter, module, "stp_global_setter", false);
+
+  if (global_setter == NULL)
+    {
+      // Hypothetical backwards compatibility with older stapdyn:
+      stapwarn() << "compiled module does not support -G globals" << endl;
+      return false;
+    }
+
+  for (vector<string>::iterator it = modoptions.begin();
+       it != modoptions.end(); it++)
+    {
+      string modoption = *it;
+
+      // Parse modoption as "name=value"
+      // XXX: compare whether this behaviour fits safety regex in buildrun.cxx
+      string::size_type separator = modoption.find('=');
+      if (separator == string::npos)
+        {
+          stapwarn() << "could not parse module option '" << modoption << "'" << endl;
+          return false; // XXX: perhaps ignore the option instead?
+        }
+      string name = modoption.substr(0, separator);
+      string value = modoption.substr(separator+1);
+
+      int rc = global_setter(name.c_str(), value.c_str());
+      if (rc != 0)
+        {
+          stapwarn() << "incorrect module option '" << modoption << "'" << endl;
+          return false; // XXX: perhaps ignore the option instead?
+        }
+    }
+
+  return true;
+}
+
 // Initialize the module session
 bool
 mutator::run_module_init()
@@ -325,6 +367,10 @@ mutator::run_module_init()
       return false;
     }
 
+  // Before init runs, set any custom variables
+  if (!modoptions.empty() && !init_modoptions())
+    return false;
+
   int rc = session_init();
   if (rc)
     {
index b990bfc9100b5e232dd6786c42a9ba582192267d..280c6080fdffe1710fa73adf6f0dbdf8e9718968 100644 (file)
@@ -34,6 +34,7 @@ class mutator {
 
     void* module; // the locally dlopened probe module
     std::string module_name; // the filename of the probe module
+    std::vector<std::string> modoptions; // custom globals from -G option
     std::string module_shmem; // the global name of this module's shared memory
     std::vector<dynprobe_target> targets; // the probe targets in the module
 
@@ -47,6 +48,9 @@ class mutator {
     mutator (const mutator& other);
     mutator& operator= (const mutator& other);
 
+    // Initialize the module global variables
+    bool init_modoptions();
+
     // Initialize the module session
     bool run_module_init();
 
@@ -66,7 +70,8 @@ class mutator {
     typeof(&enter_dyninst_utrace_probe) utrace_enter_fn;
   public:
 
-    mutator (const std::string& module_name);
+    mutator (const std::string& module_name,
+             std::vector<std::string>& module_options);
     ~mutator ();
 
     // Load the stap module and initialize all probe info.
index 9dab4779053367bfea32646b2143b825f8ac2518..da43578b0c5e1fbb6292914b3c315b589bfcbe8e 100644 (file)
@@ -91,8 +91,15 @@ main(int argc, char * const argv[])
     }
 
   // The first non-option is our stap module, required.
-  if (optind == argc - 1)
-    module = argv[optind];
+  if (optind < argc)
+    module = argv[optind++];
+
+  // Remaining non-options, if any, specify global variables.
+  vector<string> modoptions;
+  while (optind < argc)
+    {
+      modoptions.push_back(string(argv[optind++]));
+    }
 
   if (!module || (command && pid))
     usage (1);
@@ -103,7 +110,7 @@ main(int argc, char * const argv[])
   if (!check_dyninst_sebools())
     return 1;
 
-  auto_ptr<mutator> session(new mutator(module));
+  auto_ptr<mutator> session(new mutator(module, modoptions));
   if (!session.get() || !session->load())
     {
       staperror() << "failed to create the mutator!" << endl;
index 04e1f80ffcdc5f4902c01163fa2cc79c74ce7479..72ffb2ea8e994e82f250820e3f2f86a533a0bc2f 100644 (file)
@@ -90,7 +90,9 @@ struct c_unparser: public unparser, public visitor
   void emit_common_header ();
   void emit_global (vardecl* v);
   void emit_global_init (vardecl* v);
+  void emit_global_init_type (vardecl *v);
   void emit_global_param (vardecl* v);
+  void emit_global_init_setters ();
   void emit_functionsig (functiondecl* v);
   void emit_module_init ();
   void emit_module_refresh ();
@@ -1480,9 +1482,8 @@ c_unparser::emit_global_param (vardecl *v)
 {
   string vn = c_globalname (v->name);
 
-  // XXX: No module parameters with dyninst.
-  if (session->runtime_usermode_p())
-    return;
+  // For dyninst, use the emit_global_init_* functionality instead.
+  assert (!session->runtime_usermode_p());
 
   // NB: systemtap globals can collide with linux macros,
   // e.g. VM_FAULT_MAJOR.  We want the parameter name anyway.  This
@@ -1506,6 +1507,41 @@ c_unparser::emit_global_param (vardecl *v)
 }
 
 
+void
+c_unparser::emit_global_init_setters ()
+{
+  // Hack for dyninst module params: setter function forms a little
+  // linear lookup table ditty to find a global variable by name.
+  o->newline() << "int stp_global_setter (const char *name, const char *value) {";
+  o->newline(1);
+  for (unsigned i=0; i<session->globals.size(); i++)
+    {
+      vardecl* v = session->globals[i];
+      if (v->arity > 0) continue;
+      if (v->type != pe_string && v->type != pe_long) continue;
+
+      // Do not mangle v->name for the comparison!
+      o->line() << "if (0 == strcmp(name,\"" << v->name << "\"))" << " {";
+
+      o->indent(1);
+      if (v->type == pe_string)
+        {
+          c_assign("stp_global_init." + c_globalname(v->name), "value", pe_string, "BUG: global module param", v->tok);
+          o->newline() << "return 0;";
+        }
+      else
+        {
+          o->newline() << "return set_int64_t(value, &stp_global_init." << c_globalname(v->name) << ");";
+        }
+
+      o->newline(-1) << "} else ";
+    }
+  o->line() << "return -EINVAL;";
+  o->newline(-1) << "}";
+  o->newline();
+}
+
+
 void
 c_unparser::emit_global (vardecl *v)
 {
@@ -1555,10 +1591,30 @@ c_unparser::emit_global_init (vardecl *v)
       v->init->visit(this);
       o->line() << ",";
     }
+  else if (v->arity == 0 && session->runtime_usermode_p())
+    {
+      // For dyninst: always try to put a default value into the initial
+      // static structure, so we don't have to guess if it was customized.
+      if (v->type == pe_long)
+        o->newline() << "." << c_globalname (v->name) << " = 0,";
+      else if (v->type == pe_string)
+        o->newline() << "." << c_globalname (v->name) << " = { '\\0' },"; // XXX: ""
+    }
   // The lock and lock_skip_count are handled in emit_module_init.
 }
 
 
+void
+c_unparser::emit_global_init_type (vardecl *v)
+{
+  // We can only statically initialize some scalars.
+  if (v->arity == 0) // ... although we still allow !v->init here.
+    {
+      o->newline() << c_typename(v->type) << " " << c_globalname(v->name) << ";";
+    }
+}
+
+
 
 void
 c_unparser::emit_functionsig (functiondecl* v)
@@ -1705,8 +1761,9 @@ c_unparser::emit_module_init ()
       vardecl* v = session->globals[i];
       if (v->index_types.size() > 0)
        o->newline() << getmap (v).init();
-      else if (v->init && session->runtime_usermode_p())
-       c_assign(getvar (v).value(), v->init, "global initialization");
+      else if (session->runtime_usermode_p() && v->arity == 0
+               && (v->type == pe_long || v->type == pe_string))
+       c_assign(getvar (v).value(), "stp_global_init." + c_globalname(v->name), v->type, "BUG: global initialization", v->tok);
       else
        o->newline() << getvar (v).init();
       // NB: in case of failure of allocation, "rc" will be set to non-zero.
@@ -6690,19 +6747,29 @@ translate_pass (systemtap_session& s)
 
          // We only need to statically initialize globals in kernel modules,
          // where module parameters may want to override the script's value.  In
-         // stapdyn, the globals are actually part of the dynamic shared memory.
-         if (!s.runtime_usermode_p())
+         // stapdyn, the globals are actually part of the dynamic shared memory,
+         // and the static structure is merely used as a source of default values.
+         s.op->newline();
+         if (!s.runtime_usermode_p ())
+           s.op->newline() << "static struct stp_globals stp_global = {";
+         else
+          {
+            s.op->newline() << "static struct {";
+            s.op->indent(1);
+            for (unsigned i=0; i<s.globals.size(); i++)
+              {
+                assert_no_interrupts();
+                 s.up->emit_global_init_type (s.globals[i]);
+              }
+            s.op->newline(-1) << "} stp_global_init = {";
+          }
+         s.op->newline(1);
+         for (unsigned i=0; i<s.globals.size(); i++)
            {
-             s.op->newline();
-             s.op->newline() << "static struct stp_globals stp_global = {";
-             s.op->newline(1);
-             for (unsigned i=0; i<s.globals.size(); i++)
-               {
-                 assert_no_interrupts();
-                 s.up->emit_global_init (s.globals[i]);
-               }
-             s.op->newline(-1) << "};";
+             assert_no_interrupts();
+              s.up->emit_global_init (s.globals[i]);
            }
+         s.op->newline(-1) << "};";
 
          s.op->assert_0_indent();
        }
@@ -6883,12 +6950,15 @@ translate_pass (systemtap_session& s)
 
       s.op->assert_0_indent();
 
-      // PR10298: attempt to avoid collisions with symbols
-      for (unsigned i=0; i<s.globals.size(); i++)
-        {
-          s.op->newline();
-          s.up->emit_global_param (s.globals[i]);
-        }
+      if (s.runtime_usermode_p())
+        s.up->emit_global_init_setters();
+      else
+        // PR10298: attempt to avoid collisions with symbols
+        for (unsigned i=0; i<s.globals.size(); i++)
+          {
+            s.op->newline();
+            s.up->emit_global_param (s.globals[i]);
+          }
       s.op->assert_0_indent();
     }
   catch (const semantic_error& e)
index 72ed3fab82d06300cb982084fa3e031d908e4879..34124c1b52c4c2054cda0cdac285229e38580f8c 100644 (file)
@@ -76,9 +76,20 @@ struct unparser
   virtual void emit_global_init (vardecl* v) = 0;
   // };
 
-  virtual void emit_global_param (vardecl* v) = 0;
+  // struct {
+  virtual void emit_global_init_type (vardecl* v) = 0;
+  // TYPE s_NAME;
+  // } global_init = {
+  //   ...
+  // };  // variation of the above for dyninst static-initialization
+
+  virtual void emit_global_param (vardecl* v) = 0; // for kernel
   // module_param_... -- at end of file
 
+  virtual void emit_global_init_setters () = 0; // for dyninst
+  // int stp_global_setter (const char *name, const char *value);
+  // -- at end of file; returns -EINVAL on error
+
   virtual void emit_functionsig (functiondecl* v) = 0;
   // static void function_NAME (context* c);
 
This page took 0.044983 seconds and 5 git commands to generate.