]> sourceware.org Git - systemtap.git/blobdiff - main.cxx
Use std::thread for threading and CPU counts
[systemtap.git] / main.cxx
index 8410372eb44da88329f97e8c173f76c68501a700..9c22f8633689c0d44e022b4783fba1f72b9d6859 100644 (file)
--- a/main.cxx
+++ b/main.cxx
@@ -1,5 +1,5 @@
 // systemtap translator/driver
-// Copyright (C) 2005-2011 Red Hat Inc.
+// Copyright (C) 2005-2016 Red Hat Inc.
 // Copyright (C) 2005 IBM Corp.
 // Copyright (C) 2006 Intel Corporation.
 //
 #include "remote.h"
 #include "tapsets.h"
 #include "setupdwfl.h"
+#ifdef HAVE_LIBREADLINE
+#include "interactive.h"
+#endif
 
+#if ENABLE_NLS
 #include <libintl.h>
 #include <locale.h>
+#endif
 
 #include "stap-probe.h"
 
 #include <cstdlib>
+#include <thread>
 
 extern "C" {
 #include <glob.h>
@@ -63,7 +69,8 @@ uniq_list(list<string>& l)
 static void
 printscript(systemtap_session& s, ostream& o)
 {
-  if (s.listing_mode)
+  if (s.dump_mode == systemtap_session::dump_matched_probes ||
+      s.dump_mode == systemtap_session::dump_matched_probes_vars)
     {
       // We go through some heroic measures to produce clean output.
       // Record the alias and probe pointer as <name, set<derived_probe *> >
@@ -72,55 +79,47 @@ printscript(systemtap_session& s, ostream& o)
       // Pre-process the probe alias
       for (unsigned i=0; i<s.probes.size(); i++)
         {
-          if (pending_interrupts) return;
+          assert_no_interrupts();
 
           derived_probe* p = s.probes[i];
-          // NB: p->basest() is not so interesting;
-          // p->almost_basest() doesn't quite work, so ...
           vector<probe*> chain;
           p->collect_derivation_chain (chain);
-          probe* second = (chain.size()>1) ? chain[chain.size()-2] : chain[0];
 
-          if (s.verbose > 5) {
-          p->printsig(cerr); cerr << endl;
-          cerr << "chain[" << chain.size() << "]:" << endl;
-          for (unsigned j=0; j<chain.size(); j++)
-            {
-              cerr << "  [" << j << "]: " << endl;
-              cerr << "\tlocations[" << chain[j]->locations.size() << "]:" << endl;
-              for (unsigned k=0; k<chain[j]->locations.size(); k++)
-                {
-                  cerr << "\t  [" << k << "]: ";
-                  chain[j]->locations[k]->print(cerr);
-                  cerr << endl;
-                }
-              const probe_alias *a = chain[j]->get_alias();
-              if (a)
-                {
-                  cerr << "\taliases[" << a->alias_names.size() << "]:" << endl;
-                  for (unsigned k=0; k<a->alias_names.size(); k++)
-                    {
-                      cerr << "\t  [" << k << "]: ";
-                      a->alias_names[k]->print(cerr);
-                      cerr << endl;
-                    }
-                }
-            }
+          if (s.verbose > 2) {
+            p->printsig(cerr); cerr << endl;
+            cerr << "chain[" << chain.size() << "]:" << endl;
+            for (unsigned j=0; j<chain.size(); j++)
+              {
+                cerr << "  [" << j << "]: " << endl;
+                cerr << "\tlocations[" << chain[j]->locations.size() << "]:" << endl;
+                for (unsigned k=0; k<chain[j]->locations.size(); k++)
+                  {
+                    cerr << "\t  [" << k << "]: ";
+                    chain[j]->locations[k]->print(cerr);
+                    cerr << endl;
+                  }
+                const probe_alias *a = chain[j]->get_alias();
+                if (a)
+                  {
+                    cerr << "\taliases[" << a->alias_names.size() << "]:" << endl;
+                    for (unsigned k=0; k<a->alias_names.size(); k++)
+                      {
+                        cerr << "\t  [" << k << "]: ";
+                        a->alias_names[k]->print(cerr);
+                        cerr << endl;
+                      }
+                  }
+              }
           }
 
-          stringstream tmps;
-          const probe_alias *a = second->get_alias();
-          if (a)
-            {
-              assert (a->alias_names.size() >= 1);
-              a->alias_names[0]->print(tmps); // XXX: [0] is arbitrary; perhaps print all
-            }
-          else
-            {
-              assert (second->locations.size() >= 1);
-              second->locations[0]->print(tmps); // XXX: [0] is less arbitrary here, but still ...
-            }
-          string pp = tmps.str();
+          const string& pp = lex_cast(*p->script_location());
+
+          // PR16730: We should only list probes that can be traced back to the
+          // user's spec, not any auxiliary probes in the tapsets.
+          // Also, do not want to the probes that are from the additional
+          // scripts (-E SCRIPT) to be listed.
+          if (!s.is_primary_probe(p))
+            continue;
 
           // Now duplicate-eliminate.  An alias may have expanded to
           // several actual derived probe points, but we only want to
@@ -134,7 +133,7 @@ printscript(systemtap_session& s, ostream& o)
           o << it->first; // probe name or alias
 
           // Print the locals and arguments for -L mode only
-          if (s.listing_mode_vars)
+          if (s.dump_mode == systemtap_session::dump_matched_probes_vars)
             {
               map<string,unsigned> var_count; // format <"name:type",count>
               map<string,unsigned> arg_count;
@@ -182,7 +181,7 @@ printscript(systemtap_session& s, ostream& o)
         o << _("# global embedded code") << endl;
       for (unsigned i=0; i<s.embeds.size(); i++)
         {
-          if (pending_interrupts) return;
+          assert_no_interrupts();
           embeddedcode* ec = s.embeds[i];
           ec->print (o);
           o << endl;
@@ -192,7 +191,7 @@ printscript(systemtap_session& s, ostream& o)
         o << _("# globals") << endl;
       for (unsigned i=0; i<s.globals.size(); i++)
         {
-          if (pending_interrupts) return;
+          assert_no_interrupts();
           vardecl* v = s.globals[i];
           v->printsig (o);
           if (s.verbose && v->init)
@@ -207,7 +206,7 @@ printscript(systemtap_session& s, ostream& o)
         o << _("# functions") << endl;
       for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
         {
-          if (pending_interrupts) return;
+          assert_no_interrupts();
           functiondecl* f = it->second;
           f->printsig (o);
           o << endl;
@@ -231,7 +230,7 @@ printscript(systemtap_session& s, ostream& o)
         o << _("# probes") << endl;
       for (unsigned i=0; i<s.probes.size(); i++)
         {
-          if (pending_interrupts) return;
+          assert_no_interrupts();
           derived_probe* p = s.probes[i];
           p->printsig (o);
           o << endl;
@@ -257,7 +256,7 @@ printscript(systemtap_session& s, ostream& o)
 int pending_interrupts;
 
 extern "C"
-void handle_interrupt (int sig)
+void handle_interrupt (int)
 {
   // This might be nice, but we don't know our current verbosity...
   // clog << _F("Received signal %d", sig) << endl << flush;
@@ -304,136 +303,59 @@ setup_signals (sighandler_t handler)
   sigaction (SIGXCPU, &sa, NULL);
 }
 
-int parse_kernel_config (systemtap_session &s)
-{
-  // PR10702: pull config options
-  string kernel_config_file = s.kernel_build_tree + "/.config";
-  struct stat st;
-  int rc = stat(kernel_config_file.c_str(), &st);
-  if (rc != 0)
-    {
-        clog << _F("Checking \"%s\" failed with error: %s",
-                   kernel_config_file.c_str(), strerror(errno)) << endl;
-       find_devel_rpms(s, s.kernel_build_tree.c_str());
-       missing_rpm_list_print(s,"-devel");
-       return rc;
-    }
-
-  ifstream kcf (kernel_config_file.c_str());
-  string line;
-  while (getline (kcf, line))
-    {
-      if (!startswith(line, "CONFIG_")) continue;
-      size_t off = line.find('=');
-      if (off == string::npos) continue;
-      string key = line.substr(0, off);
-      string value = line.substr(off+1, string::npos);
-      s.kernel_config[key] = value;
-    }
-  if (s.verbose > 2)
-    clog << _F("Parsed kernel \"%s\", ", kernel_config_file.c_str())
-         << _F(ngettext("containing %zu tuple", "containing %zu tuples",
-                s.kernel_config.size()), s.kernel_config.size()) << endl;
-
-  kcf.close();
-  return 0;
-}
-
 
-int parse_kernel_exports (systemtap_session &s)
+static void
+sdt_benchmark_thread(unsigned long i)
 {
-  string kernel_exports_file = s.kernel_build_tree + "/Module.symvers";
-  struct stat st;
-  int rc = stat(kernel_exports_file.c_str(), &st);
-  if (rc != 0)
-    {
-        clog << _F("Checking \"%s\" failed with error: %s\nEnsure kernel development headers & makefiles are installed",
-                   kernel_exports_file.c_str(), strerror(errno)) << endl;
-       return rc;
-    }
-
-  ifstream kef (kernel_exports_file.c_str());
-  string line;
-  while (getline (kef, line))
-    {
-      vector<string> tokens;
-      tokenize (line, tokens, "\t");
-      if (tokens.size() == 4 &&
-          tokens[2] == "vmlinux" &&
-          tokens[3].substr(0,13) == string("EXPORT_SYMBOL"))
-        s.kernel_exports.insert (tokens[1]);
-      // RHEL4 Module.symvers file only has 3 tokens.  No
-      // 'EXPORT_SYMBOL' token at the end of the line.
-      else if (tokens.size() == 3 && tokens[2] == "vmlinux")
-        s.kernel_exports.insert (tokens[1]);
-    }
-  if (s.verbose > 2)
-    clog << _F(ngettext("Parsed kernel %s, which contained one vmlinux export",
-                        "Parsed kernel %s, which contained %zu vmlinux exports",
-                         s.kernel_exports.size()), kernel_exports_file.c_str(),
-                         s.kernel_exports.size()) << endl;
-
-  kef.close();
-  return 0;
+  PROBE(stap, benchmark__thread__start);
+  while (i--)
+    PROBE1(stap, benchmark, i);
+  PROBE(stap, benchmark__thread__end);
 }
 
 
 static int
-create_temp_dir (systemtap_session &s)
+run_sdt_benchmark(systemtap_session& s)
 {
-  if (!s.tmpdir.empty())
-    return 0;
-
-  // Create a temporary directory to build within.
-  // Be careful with this, as "tmpdir" is "rm -rf"'d at the end.
-  const char * tmpdir_env = getenv("TMPDIR");
-  if (!tmpdir_env)
-    tmpdir_env = "/tmp";
-
-  string stapdir = "/stapXXXXXX";
-  string tmpdirt = tmpdir_env + stapdir;
-  const char *tmpdir_name = mkdtemp((char *)tmpdirt.c_str());
-  if (! tmpdir_name)
-    {
-      const char* e = strerror(errno);
-      //TRANSLATORS: we can't make the directory due to the error
-      cerr << _F("ERROR: cannot create temporary directory (\" %s \"): %s", tmpdirt.c_str(), e) << endl;
-      return 1;
-    }
-  else
-    s.tmpdir = tmpdir_name;
+  unsigned long loops = s.benchmark_sdt_loops ?: 10000000;
+  unsigned long threads = s.benchmark_sdt_threads ?: 1;
 
-  if (s.verbose>1)
-    clog << _F("Created temporary directory \"%s\"", s.tmpdir.c_str()) << endl;
-  return 0;
-}
+  if (s.verbose > 0)
+    clog << _F("Beginning SDT benchmark with %lu loops in %lu threads.",
+               loops, threads) << endl;
 
-static void
-remove_temp_dir(systemtap_session &s)
-{
-  if (!s.tmpdir.empty())
+  struct tms tms_before, tms_after;
+  struct timeval tv_before, tv_after;
+  unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
+  times (& tms_before);
+  gettimeofday (&tv_before, NULL);
+
+  PROBE(stap, benchmark__start);
     {
-      if (s.keep_tmpdir && !s.tmpdir_opt_set)
-          clog << _F("Keeping temporary directory \"%s\"", s.tmpdir.c_str()) << endl;
-      else if (!s.tmpdir_opt_set)
-        {
-          // Mask signals while we're deleting the temporary directory.
-          stap_sigmasker masked;
+      vector<thread> handles;
+      for (unsigned long i = 0; i < threads; ++i)
+        handles.push_back(thread(sdt_benchmark_thread, loops));
+      for (unsigned long i = 0; i < threads; ++i)
+        handles[i].join();
+    }
+  PROBE(stap, benchmark__end);
 
-          // Remove the temporary directory.
-          vector<string> cleanupcmd;
-          cleanupcmd.push_back("rm");
-          cleanupcmd.push_back("-rf");
-          cleanupcmd.push_back(s.tmpdir);
+  times (& tms_after);
+  gettimeofday (&tv_after, NULL);
+  if (s.verbose > 0)
+    clog << _F("Completed SDT benchmark in %ldusr/%ldsys/%ldreal ms.",
+               (long)(tms_after.tms_utime - tms_before.tms_utime) * 1000 / _sc_clk_tck,
+               (long)(tms_after.tms_stime - tms_before.tms_stime) * 1000 / _sc_clk_tck,
+               (long)((tv_after.tv_sec - tv_before.tv_sec) * 1000 +
+                ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000))
+         << endl;
 
-          (void) stap_system(s.verbose, cleanupcmd);
-          s.tmpdir.clear();
-        }
-    }
+  return EXIT_SUCCESS;
 }
 
+
 // Compilation passes 0 through 4
-static int
+int
 passes_0_4 (systemtap_session &s)
 {
   int rc = 0;
@@ -449,30 +371,20 @@ passes_0_4 (systemtap_session &s)
       return 1;
     }
 
-  // Create a temporary directory to build within.
-  // Be careful with this, as "s.tmpdir" is "rm -rf"'d at the end.
-  rc = create_temp_dir (s);
-  if (rc)
-    return rc;
-
   // Perform passes 0 through 4 using a compile server?
   if (! s.specified_servers.empty ())
     {
 #if HAVE_NSS
       compile_server_client client (s);
-      int rc = client.passes_0_4 ();
-      // Need to give a user a better diagnostic, if she didn't
-      // even ask for a server
-      if (rc && s.automatic_server_mode) {
-        cerr << _("Note: --use-server --unprivileged was selected because of stapusr membership.") << endl;
-      }
-      return rc;
+      return client.passes_0_4 ();
 #else
-      s.print_warning("Without NSS, using a compile-server is not supported by this version of systemtap");
+      s.print_warning(_("Without NSS, using a compile-server is not supported by this version of systemtap"));
+
       // This cannot be an attempt to use a server after a local compile failed
       // since --use-server-on-error is locked to 'no' if we don't have
       // NSS.
       assert (! s.try_server ());
+      s.print_warning(_("Ignoring --use-server"));
 #endif
     }
 
@@ -497,19 +409,16 @@ passes_0_4 (systemtap_session &s)
     }
 
   // Now that no further changes to s.kernel_build_tree can occur, let's use it.
-  if ((rc = parse_kernel_config (s)) != 0)
-    {
-      // Try again with a server
-      s.set_try_server ();
-      return rc;
-    }
-
-  if ((rc = parse_kernel_exports (s)) != 0)
-    {
-      // Try again with a server
-      s.set_try_server ();
-      return rc;
-    }
+  if (s.runtime_mode == systemtap_session::kernel_runtime) {
+    if ((rc = s.parse_kernel_config ()) != 0
+        || (rc = s.parse_kernel_exports ()) != 0
+        || (rc = s.parse_kernel_functions ()) != 0)
+      {
+        // Try again with a server
+        s.set_try_server ();
+        return rc;
+      }
+  }
 
   // Create the name of the C source file within the temporary
   // directory.  Note the _src prefix, explained in
@@ -523,132 +432,365 @@ passes_0_4 (systemtap_session &s)
   struct timeval tv_before;
   gettimeofday (&tv_before, NULL);
 
-  // PASS 1a: PARSING USER SCRIPT
+  // PASS 1a: PARSING LIBRARY SCRIPTS
   PROBE1(stap, pass1a__start, &s);
 
-  struct stat user_file_stat;
-  int user_file_stat_rc = -1;
-
-  if (s.script_file == "-")
-    {
-      s.user_file = parse (s, cin, s.guru_mode);
-      user_file_stat_rc = fstat (STDIN_FILENO, & user_file_stat);
-    }
-  else if (s.script_file != "")
-    {
-      s.user_file = parse (s, s.script_file, s.guru_mode);
-      user_file_stat_rc = stat (s.script_file.c_str(), & user_file_stat);
-    }
-  else
-    {
-      istringstream ii (s.cmdline_script);
-      s.user_file = parse (s, ii, s.guru_mode);
-    }
-  if (s.user_file == 0)
+  if (! s.pass_1a_complete)
     {
-      // Syntax errors already printed.
-      rc ++;
-    }
+      // We need to handle the library scripts first because this pass
+      // gathers information on .stpm files that might be needed to
+      // parse the user script.
 
-  // Construct arch / kernel-versioning search path
-  vector<string> version_suffixes;
-  string kvr = s.kernel_release;
-  const string& arch = s.architecture;
-  // add full kernel-version-release (2.6.NN-FOOBAR) + arch
-  version_suffixes.push_back ("/" + kvr + "/" + arch);
-  version_suffixes.push_back ("/" + kvr);
-  // add kernel version (2.6.NN) + arch
-  if (kvr != s.kernel_base_release) {
-    kvr = s.kernel_base_release;
-    version_suffixes.push_back ("/" + kvr + "/" + arch);
-    version_suffixes.push_back ("/" + kvr);
-  }
-  // add kernel family (2.6) + arch
-  string::size_type dot1_index = kvr.find ('.');
-  string::size_type dot2_index = kvr.rfind ('.');
-  while (dot2_index > dot1_index && dot2_index != string::npos) {
-    kvr.erase(dot2_index);
-    version_suffixes.push_back ("/" + kvr + "/" + arch);
-    version_suffixes.push_back ("/" + kvr);
-    dot2_index = kvr.rfind ('.');
+      // We need to first ascertain the status of the user script, though.
+      struct stat user_file_stat;
+      int user_file_stat_rc = -1;
+
+      if (s.script_file == "-")
+        {
+         user_file_stat_rc = fstat (STDIN_FILENO, & user_file_stat);
+       }
+      else if (s.script_file != "")
+        {
+         user_file_stat_rc = stat (s.script_file.c_str(), & user_file_stat);
+       }
+      // otherwise, rc is 0 for a command line script
+
+      vector<string> version_suffixes;
+      if (s.runtime_mode == systemtap_session::kernel_runtime)
+        {
+         // Construct kernel-versioning search path
+         string kvr = s.kernel_release;
+
+         // add full kernel-version-release (2.6.NN-FOOBAR)
+         version_suffixes.push_back ("/" + kvr);
+
+         // add kernel version (2.6.NN)
+         if (kvr != s.kernel_base_release)
+           {
+             kvr = s.kernel_base_release;
+             version_suffixes.push_back ("/" + kvr);
+           }
+
+         // add kernel family (2.6)
+         string::size_type dot1_index = kvr.find ('.');
+         string::size_type dot2_index = kvr.rfind ('.');
+         while (dot2_index > dot1_index && dot2_index != string::npos)
+           {
+             kvr.erase(dot2_index);
+             version_suffixes.push_back ("/" + kvr);
+             dot2_index = kvr.rfind ('.');
+           }
+       }
+
+      // add empty string as last element
+      version_suffixes.push_back ("");
+
+      // Add arch variants of every path, just before each
+      const string& arch = s.architecture;
+      for (unsigned i=0; i<version_suffixes.size(); i+=2)
+       version_suffixes.insert(version_suffixes.begin() + i,
+                               version_suffixes[i] + "/" + arch);
+
+      // Add runtime variants of every path, before everything else
+      string runtime_prefix;
+      if (s.runtime_mode == systemtap_session::kernel_runtime)
+       runtime_prefix = "/linux";
+      else if (s.runtime_mode == systemtap_session::dyninst_runtime)
+       runtime_prefix = "/dyninst";
+      if (!runtime_prefix.empty())
+       for (unsigned i=0; i<version_suffixes.size(); i+=2)
+           version_suffixes.insert(version_suffixes.begin() + i/2,
+                                   runtime_prefix + version_suffixes[i]);
+
+      // First, parse .stpm files on the include path. We need to have the
+      // resulting macro definitions available for parsing library files,
+      // but since .stpm files can consist only of '@define' constructs,
+      // we can parse each one without reference to the others.
+      set<pair<dev_t, ino_t> > seen_library_macro_files;
+      set<string> seen_library_macro_files_names;
+
+      for (unsigned i=0; i<s.include_path.size(); i++)
+        {
+         // now iterate upon it
+         for (unsigned k=0; k<version_suffixes.size(); k++)
+           {
+             glob_t globbuf;
+             string dir = s.include_path[i] + version_suffixes[k] + "/*.stpm";
+             int r = glob(dir.c_str (), 0, NULL, & globbuf);
+             if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
+               rc ++;
+             // GLOB_NOMATCH is acceptable
+
+             unsigned prev_s_library_files = s.library_files.size();
+
+             for (unsigned j=0; j<globbuf.gl_pathc; j++)
+               {
+                 assert_no_interrupts();
+
+                 struct stat tapset_file_stat;
+                 int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat);
+                 if (stat_rc == 0 && user_file_stat_rc == 0 &&
+                     user_file_stat.st_dev == tapset_file_stat.st_dev &&
+                     user_file_stat.st_ino == tapset_file_stat.st_ino)
+                   {
+                     cerr
+                         << _F("usage error: macro tapset file '%s' cannot be run directly as a session script.",
+                               globbuf.gl_pathv[j]) << endl;
+                     rc ++;
+                   }
+
+                 // PR11949: duplicate-eliminate tapset files
+                 if (stat_rc == 0)
+                   {
+                     pair<dev_t,ino_t> here = make_pair(tapset_file_stat.st_dev,
+                                                        tapset_file_stat.st_ino);
+                     if (seen_library_macro_files.find(here) != seen_library_macro_files.end())
+                       {
+                         if (s.verbose>2)
+                           clog << _F("Skipping tapset \"%s\", duplicate inode.", globbuf.gl_pathv[j]) << endl;
+                         continue; 
+                       }
+                     seen_library_macro_files.insert (here);
+                   }
+
+                 // PR12443: duplicate-eliminate harder
+                 string full_path = globbuf.gl_pathv[j];
+                 string tapset_base = s.include_path[i]; // not dir; it has arch suffixes too
+                 if (full_path.size() > tapset_base.size())
+                   {
+                     string tail_part = full_path.substr(tapset_base.size());
+                     if (seen_library_macro_files_names.find (tail_part) != seen_library_macro_files_names.end())
+                       {
+                         if (s.verbose>2)
+                           clog << _F("Skipping tapset \"%s\", duplicate name.", globbuf.gl_pathv[j]) << endl;
+                         continue;
+                       }
+                     seen_library_macro_files_names.insert (tail_part);
+                   }
+
+                 if (s.verbose>2)
+                   clog << _F("Processing tapset \"%s\"", globbuf.gl_pathv[j]) << endl;
+
+                 stapfile* f = parse_library_macros (s, globbuf.gl_pathv[j]);
+                 if (f == 0)
+                   s.print_warning(_F("macro tapset \"%s\" has errors, and will be skipped.", string(globbuf.gl_pathv[j]).c_str()));
+                 else
+                   s.library_files.push_back (f);
+               }
+
+             unsigned next_s_library_files = s.library_files.size();
+             if (s.verbose>1 && globbuf.gl_pathc > 0)
+                 //TRANSLATORS: Searching through directories, 'processed' means 'examined so far'
+               clog << _F("Searched for library macro files: \"%s\", found: %zu, processed: %u",
+                          dir.c_str(), globbuf.gl_pathc,
+                          (next_s_library_files-prev_s_library_files)) << endl;
+
+             globfree (&globbuf);
+           }
+       }
+
+      // Next, gather and parse the library files.
+      set<pair<dev_t, ino_t> > seen_library_files;
+      set<string> seen_library_files_names;
+
+      for (unsigned i=0; i<s.include_path.size(); i++)
+        {
+         unsigned tapset_flags = pf_guru | pf_squash_errors;
+
+         // The first path is special, as it's the builtin tapset.
+         // Allow all features no matter what s.compatible says.
+         if (i == 0)
+           tapset_flags |= pf_no_compatible;
+
+         // now iterate upon it
+         for (unsigned k=0; k<version_suffixes.size(); k++)
+           {
+             glob_t globbuf;
+             string dir = s.include_path[i] + version_suffixes[k] + "/*.stp";
+             int r = glob(dir.c_str (), 0, NULL, & globbuf);
+             if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
+                 rc ++;
+             // GLOB_NOMATCH is acceptable
+
+             unsigned prev_s_library_files = s.library_files.size();
+
+             for (unsigned j=0; j<globbuf.gl_pathc; j++)
+               {
+                 assert_no_interrupts();
+
+                 struct stat tapset_file_stat;
+                 int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat);
+                 if (stat_rc == 0 && user_file_stat_rc == 0 &&
+                     user_file_stat.st_dev == tapset_file_stat.st_dev &&
+                     user_file_stat.st_ino == tapset_file_stat.st_ino)
+                   {
+                     cerr 
+                         << _F("usage error: tapset file '%s' cannot be run directly as a session script.",
+                               globbuf.gl_pathv[j]) << endl;
+                     rc ++;
+                   }
+
+                 // PR11949: duplicate-eliminate tapset files
+                 if (stat_rc == 0)
+                   {
+                     pair<dev_t,ino_t> here = make_pair(tapset_file_stat.st_dev,
+                                                        tapset_file_stat.st_ino);
+                     if (seen_library_files.find(here) != seen_library_files.end())
+                       {
+                         if (s.verbose>2)
+                           clog << _F("Skipping tapset \"%s\", duplicate inode.", globbuf.gl_pathv[j]) << endl;
+                         continue; 
+                       }
+                     seen_library_files.insert (here);
+                   }
+
+                 // PR12443: duplicate-eliminate harder
+                 string full_path = globbuf.gl_pathv[j];
+                 string tapset_base = s.include_path[i]; // not dir; it has arch suffixes too
+                 if (full_path.size() > tapset_base.size())
+                   {
+                     string tail_part = full_path.substr(tapset_base.size());
+                     if (seen_library_files_names.find (tail_part) != seen_library_files_names.end())
+                       {
+                         if (s.verbose>2)
+                           clog << _F("Skipping tapset \"%s\", duplicate name.", globbuf.gl_pathv[j]) << endl;
+                         continue;
+                       }
+                     seen_library_files_names.insert (tail_part);
+                   }
+
+                 if (s.verbose>2)
+                   clog << _F("Processing tapset \"%s\"", globbuf.gl_pathv[j]) << endl;
+
+                 // NB: we don't need to restrict privilege only for
+                 // /usr/share/systemtap, i.e., excluding
+                 // user-specified $XDG_DATA_DIRS.  That's because
+                 // stapdev gets root-equivalent privileges anyway;
+                 // stapsys and stapusr use a remote compilation with
+                 // a trusted environment, where client-side
+                 // $XDG_DATA_DIRS are not passed.
+
+                 stapfile* f = parse (s, globbuf.gl_pathv[j], tapset_flags);
+                 if (f == 0)
+                   s.print_warning(_F("tapset \"%s\" has errors, and will be skipped", string(globbuf.gl_pathv[j]).c_str()));
+                 else
+                   s.library_files.push_back (f);
+               }
+
+             unsigned next_s_library_files = s.library_files.size();
+             if (s.verbose>1 && globbuf.gl_pathc > 0)
+                 //TRANSLATORS: Searching through directories, 'processed' means 'examined so far'
+               clog << _F("Searched: \"%s\", found: %zu, processed: %u",
+                          dir.c_str(), globbuf.gl_pathc,
+                          (next_s_library_files-prev_s_library_files)) << endl;
+
+             globfree (& globbuf);
+           }
+       }
+      if (s.num_errors())
+       rc ++;
+
+      // Now that we've made it through pass 1a, remember this so we
+      // don't have to do this again in interactive mode. This doesn't
+      // effect non-interactive mode.
+      s.pass_1a_complete = true;
   }
-  // add architecture search path
-  version_suffixes.push_back("/" + arch);
-  // add empty string as last element
-  version_suffixes.push_back ("");
 
-  // PASS 1b: PARSING LIBRARY SCRIPTS
+  // PASS 1b: PARSING USER SCRIPT
   PROBE1(stap, pass1b__start, &s);
 
-  set<pair<dev_t, ino_t> > seen_library_files;
-
-  for (unsigned i=0; i<s.include_path.size(); i++)
+  // Only try to parse a user script if the user provided one, or if we have to
+  // make one (as is the case for listing mode). Otherwise, s.user_script
+  // remains NULL.
+  if (!s.script_file.empty() ||
+      !s.cmdline_script.empty() ||
+      s.dump_mode == systemtap_session::dump_matched_probes ||
+      s.dump_mode == systemtap_session::dump_matched_probes_vars)
     {
-      // now iterate upon it
-      for (unsigned k=0; k<version_suffixes.size(); k++)
+      unsigned user_flags = s.guru_mode ? pf_guru : 0;
+      user_flags |= pf_user_file;
+      if (s.script_file == "-")
+        {
+          s.user_files.push_back (parse (s, "<input>", cin, user_flags));
+        }
+      else if (s.script_file != "")
         {
-          glob_t globbuf;
-          string dir = s.include_path[i] + version_suffixes[k] + "/*.stp";
-          int r = glob(dir.c_str (), 0, NULL, & globbuf);
-          if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
-           rc ++;
-         // GLOB_NOMATCH is acceptable
+          s.user_files.push_back (parse (s, s.script_file, user_flags));
+        }
+      else if (s.cmdline_script != "")
+        {
+          istringstream ii (s.cmdline_script);
+          s.user_files.push_back(parse (s, "<input>", ii, user_flags));
+        }
+      else // listing mode
+        {
+          istringstream ii ("probe " + s.dump_matched_pattern + " {}");
+          s.user_files.push_back (parse (s, "<input>", ii, user_flags));
+        }
 
-          unsigned prev_s_library_files = s.library_files.size();
+      // parses the additional script(s) (-E script). does so even if in listing
+      // mode, incase there is something special in the additional script(s),
+      // like a macro or alias. give them a unique name to differentiate the
+      // scripts that were inputted.
+      unsigned count = 1;
+      for (vector<string>::iterator script = s.additional_scripts.begin(); script != s.additional_scripts.end(); script++)
+        {
+          string input_name = "<input" + lex_cast(count) + ">";
+          istringstream ii (*script);
+          s.user_files.push_back(parse (s, input_name, ii, user_flags));
+          count ++;
+        }
 
-          for (unsigned j=0; j<globbuf.gl_pathc; j++)
+      for(vector<stapfile*>::iterator it = s.user_files.begin(); it != s.user_files.end(); it++)
+        {
+          if (!(*it))
             {
-              if (pending_interrupts)
-                break;
-
-              struct stat tapset_file_stat;
-              int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat);
-              if (stat_rc == 0 && user_file_stat_rc == 0 &&
-                  user_file_stat.st_dev == tapset_file_stat.st_dev &&
-                  user_file_stat.st_ino == tapset_file_stat.st_ino)
-                {
-                  cerr 
-                  << _F("usage error: tapset file '%s' cannot be run directly as a session script.",
-                        globbuf.gl_pathv[j]) << endl;
-                  rc ++;
-                }
-
-              // PR11949: duplicate-eliminate tapset files
-              if (stat_rc == 0)
-                {
-                  pair<dev_t,ino_t> here = make_pair(tapset_file_stat.st_dev,
-                                                     tapset_file_stat.st_ino);
-                  if (seen_library_files.find(here) != seen_library_files.end())
-                    continue;
-                  seen_library_files.insert (here);
-                }
-
-              // XXX: privilege only for /usr/share/systemtap?
-              stapfile* f = parse (s, globbuf.gl_pathv[j], true);
-              if (f == 0)
-                s.print_warning("tapset '" + string(globbuf.gl_pathv[j])
-                                + "' has errors, and will be skipped.");
-              else
-                s.library_files.push_back (f);
+              // Syntax errors already printed.
+              rc ++;
             }
+        }
+    }
+  else if (s.cmdline_script.empty() &&
+           s.dump_mode == systemtap_session::dump_none) // -e ''
+    {
+      cerr << _("Input file '<input>' is empty.") << endl;
+      rc++;
+    }
 
-          unsigned next_s_library_files = s.library_files.size();
-          if (s.verbose>1 && globbuf.gl_pathc > 0)
-            //TRANSLATORS: Searching through directories, 'processed' means 'examined so far'
-            clog << _F("Searched: \" %s \", found: %zu, processed: %u",
-                       dir.c_str(), globbuf.gl_pathc,
-                       (next_s_library_files-prev_s_library_files)) << endl;
+  // Dump a list of probe aliases picked up, if requested
+  if (s.dump_mode == systemtap_session::dump_probe_aliases)
+    {
+      set<string> aliases;
+      vector<stapfile*>::const_iterator file;
+      for (file  = s.library_files.begin();
+           file != s.library_files.end(); ++file)
+        {
+          vector<probe_alias*>::const_iterator alias;
+          for (alias  = (*file)->aliases.begin();
+               alias != (*file)->aliases.end(); ++alias)
+            {
+              stringstream ss;
+              (*alias)->printsig(ss);
+              string str = ss.str();
+              if (!s.verbose && startswith(str, "_"))
+                continue;
+              aliases.insert(str);
+            }
+        }
 
-          globfree (& globbuf);
+      set<string>::iterator alias;
+      for (alias  = aliases.begin();
+           alias != aliases.end(); ++alias)
+        {
+          cout << *alias << endl;
         }
     }
-  if (s.num_errors())
-    rc ++;
-
-  if (rc == 0 && s.last_pass == 1)
+  // Dump the parse tree if this is the last pass
+  else if (rc == 0 && s.last_pass == 1)
     {
       cout << _("# parse tree dump") << endl;
-      s.user_file->print (cout);
+      for (vector<stapfile*>::iterator it = s.user_files.begin(); it != s.user_files.end(); it++)
+        (*it)->print (cout);
       cout << endl;
       if (s.verbose)
         for (unsigned i=0; i<s.library_files.size(); i++)
@@ -664,7 +806,7 @@ passes_0_4 (systemtap_session &s)
   struct timeval tv_after;
   gettimeofday (&tv_after, NULL);
 
-#define TIMESPRINT "in " << \
+#define TIMESPRINT _("in ") << \
            (tms_after.tms_cutime + tms_after.tms_utime \
             - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \
         << (tms_after.tms_cstime + tms_after.tms_stime \
@@ -675,23 +817,24 @@ passes_0_4 (systemtap_session &s)
   // syntax errors, if any, are already printed
   if (s.verbose)
     {
-      clog << "Pass 1: parsed user script and "
-           << s.library_files.size()
-           << " library script(s) "
+      // XXX also include a count of helper macro files loaded (.stpm)?
+      int n = int(s.library_files.size());
+      clog << _("Pass 1: parsed user script and ")
+           << _NF("%d library script ", "%d library scripts ", n, n)
            << getmemusage()
            << TIMESPRINT
            << endl;
     }
 
-  if (rc && !s.listing_mode)
-    cerr << _("Pass 1: parse failed.  Try again with another '--vp 1' option.") << endl;
-    //cerr << "Pass 1: parse failed.  "
-    //     << "Try again with another '--vp 1' option."
-    //     << endl;
+  if (rc && !s.dump_mode)
+    cerr << _("Pass 1: parse failed.  [man error::pass1]") << endl;
 
   PROBE1(stap, pass1__end, &s);
 
-  if (rc || s.last_pass == 1 || pending_interrupts) return rc;
+  assert_no_interrupts();
+  if (rc || s.last_pass == 1 ||
+      s.dump_mode == systemtap_session::dump_probe_aliases)
+    return rc;
 
   times (& tms_before);
   gettimeofday (&tv_before, NULL);
@@ -702,39 +845,66 @@ passes_0_4 (systemtap_session &s)
   rc = semantic_pass (s);
 
   // Dump a list of known probe point types, if requested.
-  if (s.dump_probe_types)
+  if (s.dump_mode == systemtap_session::dump_probe_types)
     s.pattern_root->dump (s);
-
-  if (s.listing_mode || (rc == 0 && s.last_pass == 2))
+  // Dump a list of functions we picked up, if requested.
+  else if (s.dump_mode == systemtap_session::dump_functions)
+    {
+      map<string,functiondecl*>::const_iterator func;
+      for (func  = s.functions.begin();
+           func != s.functions.end(); ++func)
+        {
+          functiondecl& curfunc = *func->second;
+          if (curfunc.synthetic)
+            continue;
+          if (!startswith(curfunc.name, "__global_"))
+            continue;
+          if (!s.verbose && startswith(curfunc.name, "__global__"))
+            continue;
+          curfunc.printsigtags(cout, s.verbose>0 /* all_tags */ );
+          cout << endl;
+        }
+    }
+  // Dump the whole script if requested, or if we stop at 2
+  else if (s.dump_mode == systemtap_session::dump_matched_probes ||
+           s.dump_mode == systemtap_session::dump_matched_probes_vars ||
+           (rc == 0 && s.last_pass == 2))
     printscript(s, cout);
 
   times (& tms_after);
   gettimeofday (&tv_after, NULL);
 
-  if (s.verbose) clog << "Pass 2: analyzed script: "
-                      << s.probes.size() << " probe(s), "
-                      << s.functions.size() << " function(s), "
-                      << s.embeds.size() << " embed(s), "
-                      << s.globals.size() << " global(s) "
-                      << getmemusage()
-                      << TIMESPRINT
-                      << endl;
+  if (s.verbose) {
+    int np = s.probes.size();
+    int nf = s.functions.size();
+    int ne = s.embeds.size();
+    int ng = s.globals.size();
+    clog << _("Pass 2: analyzed script: ")
+         << _NF("%d probe, ", "%d probes, ", np, np)
+         << _NF("%d function, ", "%d functions, ", nf, nf)
+         << _NF("%d embed, ", "%d embeds, ", ne, ne)
+         << _NF("%d global ", "%d globals ", ng, ng)
+         << getmemusage()
+         << TIMESPRINT
+         << endl;
+  }
 
-  if (rc && !s.listing_mode && !s.try_server ())
-    cerr << _("Pass 2: analysis failed.  Try again with another '--vp 01' option.") << endl;
-    //cerr << "Pass 2: analysis failed.  "
-    //     << "Try again with another '--vp 01' option."
-    //     << endl;
+  missing_rpm_list_print(s, "-debuginfo");
 
-  /* Print out list of missing files.  XXX should be "if (rc)" ? */
-  missing_rpm_list_print(s,"-debuginfo");
+  if (rc && !s.dump_mode && !s.try_server ())
+    cerr << _("Pass 2: analysis failed.  [man error::pass2]") << endl;
 
   PROBE1(stap, pass2__end, &s);
 
-  if (rc || s.listing_mode || s.last_pass == 2 || pending_interrupts) return rc;
+  assert_no_interrupts();
+  // NB: none of the dump modes need to go beyond pass-2. If this changes, break
+  // into individual modes here.
+  if (rc || s.last_pass == 2 || s.dump_mode)
+    return rc;
 
   rc = prepare_translate_pass (s);
-  if (rc || pending_interrupts) return rc;
+  assert_no_interrupts();
+  if (rc) return rc;
 
   // Generate hash.  There isn't any point in generating the hash
   // if last_pass is 2, since we'll quit before using it.
@@ -764,8 +934,8 @@ passes_0_4 (systemtap_session &s)
 
          // If our last pass isn't 5, we're done (since passes 3 and
          // 4 just generate what we just pulled out of the cache).
-         if (rc || s.last_pass < 5 || pending_interrupts)
-            return rc;
+         assert_no_interrupts();
+         if (rc || s.last_pass < 5) return rc;
 
          // Short-circuit to pass 5.
          return 0;
@@ -789,7 +959,7 @@ passes_0_4 (systemtap_session &s)
   gettimeofday (&tv_after, NULL);
 
   if (s.verbose) 
-    clog << "Pass 3: translated to C into \""
+    clog << _("Pass 3: translated to C into \"")
          << s.translated_source
          << "\" "
          << getmemusage()
@@ -797,14 +967,12 @@ passes_0_4 (systemtap_session &s)
          << endl;
 
   if (rc && ! s.try_server ())
-    cerr << _("Pass 3: translation failed.  Try again with another '--vp 001' option.") << endl;
-    //cerr << "Pass 3: translation failed.  "
-    //     << "Try again with another '--vp 001' option."
-    //     << endl;
+    cerr << _("Pass 3: translation failed.  [man error::pass3]") << endl;
 
   PROBE1(stap, pass3__end, &s);
 
-  if (rc || s.last_pass == 3 || pending_interrupts) return rc;
+  assert_no_interrupts();
+  if (rc || s.last_pass == 3) return rc;
 
   // PASS 4: COMPILATION
   s.verbose = s.perpass_verbose[3];
@@ -827,23 +995,21 @@ passes_0_4 (systemtap_session &s)
   times (& tms_after);
   gettimeofday (&tv_after, NULL);
 
-  if (s.verbose) clog << "Pass 4: compiled C into \""
+  if (s.verbose) clog << _("Pass 4: compiled C into \"")
                       << s.module_filename()
                       << "\" "
                       << TIMESPRINT
                       << endl;
 
   if (rc && ! s.try_server ())
-    cerr << _("Pass 4: compilation failed.  Try again with another '--vp 0001' option.") << endl;
-    //cerr << "Pass 4: compilation failed.  "
-    //     << "Try again with another '--vp 0001' option."
-    //     << endl;
+    cerr << _("Pass 4: compilation failed.  [man error::pass4]") << endl;
+
   else
     {
       // Update cache. Cache cleaning is kicked off at the beginning of this function.
       if (s.use_script_cache)
         add_script_to_cache(s);
-      if (s.use_cache && !s.is_usermode())
+      if (s.use_cache && !s.runtime_usermode_p())
         add_stapconf_to_cache(s);
 
       // We may need to save the module in $CWD if the cache was
@@ -858,6 +1024,14 @@ passes_0_4 (systemtap_session &s)
          string module_dest_path = s.module_filename();
          copy_file(module_src_path, module_dest_path, s.verbose > 1);
        }
+
+      // Copy uprobes module to the current directory.
+      if (s.save_uprobes && !s.uprobes_path.empty() && !pending_interrupts)
+        {
+          rc = create_dir("uprobes");
+          if (! rc)
+            copy_file(s.uprobes_path, "uprobes/uprobes.ko", s.verbose > 1);
+        }
     }
 
   PROBE1(stap, pass4__end, &s);
@@ -865,7 +1039,7 @@ passes_0_4 (systemtap_session &s)
   return rc;
 }
 
-static int
+int
 pass_5 (systemtap_session &s, vector<remote*> targets)
 {
   // PASS 5: RUN
@@ -885,15 +1059,12 @@ pass_5 (systemtap_session &s, vector<remote*> targets)
   unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
   struct timeval tv_after;
   gettimeofday (&tv_after, NULL);
-  if (s.verbose) clog << "Pass 5: run completed "
+  if (s.verbose) clog << _("Pass 5: run completed ")
                       << TIMESPRINT
                       << endl;
 
   if (rc)
-    cerr << _("Pass 5: run failed.  Try again with another '--vp 00001' option.") << endl;
-    //cerr << "Pass 5: run failed.  "
-    //     << "Try again with another '--vp 00001' option."
-    //     << endl;
+    cerr << _("Pass 5: run failed.  [man error::pass5]") << endl;
   else
     // Interrupting pass-5 to quit is normal, so we want an EXIT_SUCCESS below.
     pending_interrupts = 0;
@@ -922,8 +1093,7 @@ cleanup (systemtap_session &s, int rc)
 #endif
   }
 
-  // Clean up temporary directory.  Obviously, be careful with this.
-  remove_temp_dir (s);
+  s.report_suppression();
 
   PROBE1(stap, pass6__end, &s);
 }
@@ -938,12 +1108,13 @@ passes_0_4_again_with_server (systemtap_session &s)
   // Specify default server(s).
   s.specified_servers.push_back ("");
 
-  // Remove the previous temporary directory and start fresh.
-  remove_temp_dir (s);
+  // Reset the previous temporary directory and start fresh
+  s.reset_tmp_dir();
 
   // Try to compile again, using the server
   clog << _("Attempting compilation using a compile server")
        << endl;
+
   int rc = passes_0_4 (s);
   return rc;
 }
@@ -952,142 +1123,185 @@ int
 main (int argc, char * const argv [])
 {
   // Initialize defaults.
-  systemtap_session s;
-
-  setlocale (LC_ALL, "");
-  bindtextdomain (PACKAGE, LOCALEDIR);
-  textdomain (PACKAGE);
-
-  // Set up our handler to catch routine signals, to allow clean
-  // and reasonably timely exit.
-  setup_signals(&handle_interrupt);
-
-  // PR13520: Parse $SYSTEMTAP_DIR/rc for extra options
-  string rc_file = s.data_path + "/rc";
-  ifstream rcf (rc_file.c_str());
-  string rcline;
-  wordexp_t words;
-  memset (& words, 0, sizeof(words));
-  int rc = 0;
-  int linecount = 0;
-  while (getline (rcf, rcline))
-    {
-      rc = wordexp (rcline.c_str(), & words, WRDE_NOCMD|WRDE_UNDEF|
-                    (linecount > 0 ? WRDE_APPEND : 0)); 
-      // NB: WRDE_APPEND automagically reallocates words.* as more options are added.
-      linecount ++;
-      if (rc) break;
-    }
-  int extended_argc = words.we_wordc + argc;
-  char **extended_argv = (char**) calloc (extended_argc + 1, sizeof(char*));
-  if (rc || !extended_argv)
-    {
-      clog << _F("Error processing extra options in %s", rc_file.c_str());
-      exit (1);
-    }
-  // Copy over the arguments *by reference*, first the ones from the rc file.
-  char **p = & extended_argv[0];
-  *p++ = argv[0];
-  for (unsigned i=0; i<words.we_wordc; i++) *p++ = words.we_wordv[i];
-  for (int j=1; j<argc; j++) *p++ = argv[j];
-  *p++ = NULL;
-
-  // Process the command line.
-  rc = s.parse_cmdline (extended_argc, extended_argv);
-  if (rc != 0)
-    exit (rc);
-
-  if (words.we_wordc > 0 && s.verbose > 1)
-    clog << _F("Extra options in %s: %d\n", rc_file.c_str(), (int)words.we_wordc);
-
-  // Check for options conflicts. Exits if errors are detected.
-  s.check_options (extended_argc, extended_argv);
-
-  // We don't need these strings any more.
-  wordfree (& words);
-  free (extended_argv);
-
-  // arguments parsed; get down to business
-  if (s.verbose > 1)
-    s.version ();
-
-  // Some of the remote methods need to write temporary data, so go ahead
-  // and create the main tempdir now.
-  rc = create_temp_dir (s);
-
-  // Prepare connections for each specified remote target.
-  vector<remote*> targets;
-  bool fake_remote=false;
-  if (s.remote_uris.empty())
-    {
-      fake_remote=true;
-      s.remote_uris.push_back("direct:");
-    }
-  for (unsigned i = 0; rc == 0 && i < s.remote_uris.size(); ++i)
-    {
-      // PR13354: pass remote id#/url only in non --remote=HOST cases
-      remote *target = remote::create(s, s.remote_uris[i],
-                                      fake_remote ? -1 : (int)i);
-      if (target)
-        targets.push_back(target);
-      else
-        rc = 1;
-    }
+  try {
+    systemtap_session s;
+#if ENABLE_NLS
+    setlocale (LC_ALL, "");
+    bindtextdomain (PACKAGE, LOCALEDIR);
+    textdomain (PACKAGE);
+#endif
 
-  // Discover and loop over each unique session created by the remote targets.
-  set<systemtap_session*> sessions;
-  for (unsigned i = 0; i < targets.size(); ++i)
-    sessions.insert(targets[i]->get_session());
-  for (set<systemtap_session*>::iterator it = sessions.begin();
-       rc == 0 && !pending_interrupts && it != sessions.end(); ++it)
-    {
-      systemtap_session& ss = **it;
-      if (ss.verbose > 1)
-        clog << _F("Session arch: %s release: %s",
-                   ss.architecture.c_str(), ss.kernel_release.c_str()) << endl;
+    // Set up our handler to catch routine signals, to allow clean
+    // and reasonably timely exit.
+    setup_signals(&handle_interrupt);
+
+    // PR13520: Parse $SYSTEMTAP_DIR/rc for extra options
+    string rc_file = s.data_path + "/rc";
+    ifstream rcf (rc_file.c_str());
+    string rcline;
+    wordexp_t words;
+    memset (& words, 0, sizeof(words));
+    int rc = 0;
+    int linecount = 0;
+    while (getline (rcf, rcline))
+      {
+        rc = wordexp (rcline.c_str(), & words, WRDE_NOCMD|WRDE_UNDEF|
+                      (linecount > 0 ? WRDE_APPEND : 0)); 
+        // NB: WRDE_APPEND automagically reallocates words.* as more options are added.
+        linecount ++;
+        if (rc) break;
+      }
+    rcf.close();
 
-#if HAVE_NSS
-      // If requested, query server status. This is independent of other tasks.
-      query_server_status (ss);
+    int extended_argc = words.we_wordc + argc;
+    char **extended_argv = (char**) calloc (extended_argc + 1, sizeof(char*));
+    if (rc || !extended_argv)
+      {
+        clog << _F("Error processing extra options in %s", rc_file.c_str());
+        return EXIT_FAILURE;
+      }
+    // Copy over the arguments *by reference*, first the ones from the rc file.
+    char **p = & extended_argv[0];
+    *p++ = argv[0];
+    for (unsigned i=0; i<words.we_wordc; i++) *p++ = words.we_wordv[i];
+    for (int j=1; j<argc; j++) *p++ = argv[j];
+    *p++ = NULL;
+
+    // Process the command line.
+    rc = s.parse_cmdline (extended_argc, extended_argv);
+    if (rc != 0)
+      return rc;
+
+    // Create the temp dir.
+    s.create_tmp_dir();
+
+    if (words.we_wordc > 0 && s.verbose > 1)
+      clog << _F("Extra options in %s: %d\n", rc_file.c_str(), (int)words.we_wordc);
+
+    // Check for options conflicts. Exits if errors are detected.
+    s.check_options (extended_argc, extended_argv);
+
+    // We don't need these strings any more.
+    wordfree (& words);
+    free (extended_argv);
+
+    // arguments parsed; get down to business
+    if (s.verbose > 1)
+      s.version ();
+
+    // Need to send the verbose message here, rather than in the session ctor, since
+    // we didn't know if verbose was set.
+    if (rc == 0 && s.verbose>1)
+      clog << _F("Created temporary directory \"%s\"", s.tmpdir.c_str()) << endl;
+
+    // Run the benchmark and quit right away.
+    if (s.benchmark_sdt_loops || s.benchmark_sdt_threads)
+      return run_sdt_benchmark(s);
+
+    // Prepare connections for each specified remote target.
+    vector<remote*> targets;
+    bool fake_remote=false;
+    if (s.remote_uris.empty())
+      {
+        fake_remote=true;
+        s.remote_uris.push_back("direct:");
+      }
+    for (unsigned i = 0; rc == 0 && i < s.remote_uris.size(); ++i)
+      {
+        // PR13354: pass remote id#/url only in non --remote=HOST cases
+        remote *target = remote::create(s, s.remote_uris[i],
+                                        fake_remote ? -1 : (int)i);
+        if (target)
+          targets.push_back(target);
+        else
+          rc = 1;
+      }
 
-      // If requested, manage trust of servers. This is independent of other tasks.
-      manage_server_trust (ss);
+    // Discover and loop over each unique session created by the remote targets.
+    set<systemtap_session*> sessions;
+    for (unsigned i = 0; i < targets.size(); ++i)
+      sessions.insert(targets[i]->get_session());
+
+    // FIXME: For now, only attempt local interactive use.
+    if (s.interactive_mode && fake_remote)
+      {
+#ifdef HAVE_LIBREADLINE
+       rc = interactive_mode (s, targets);
 #endif
+      }
+    else
+      {
+       for (set<systemtap_session*>::iterator it = sessions.begin();
+            rc == 0 && !pending_interrupts && it != sessions.end(); ++it)
+         {
+           systemtap_session& ss = **it;
+            if (ss.verbose > 1)
+             clog << _F("Session arch: %s release: %s",
+                        ss.architecture.c_str(), ss.kernel_release.c_str())
+                  << endl;
 
-      // Run the passes only if a script has been specified. The requirement for
-      // a script has already been checked in systemtap_session::check_options.
-      // Run the passes also if a dump of supported probe types has been requested via a server.
-      if (ss.have_script || (ss.dump_probe_types && ! s.specified_servers.empty ()))
-        {
-          // Run passes 0-4 for each unique session,
-          // either locally or using a compile-server.
-          ss.init_try_server ();
-          if ((rc = passes_0_4 (ss)))
-            {
-              // Compilation failed.
-              // Try again using a server if appropriate.
-              if (ss.try_server ())
-                rc = passes_0_4_again_with_server (ss);
-            }
-        }
-      else if (ss.dump_probe_types)
-       {
-         // Dump a list of known probe point types, if requested.
-         register_standard_tapsets(ss);
-         ss.pattern_root->dump (ss);
-       }
-    }
+#if HAVE_NSS
+           // If requested, query server status. This is independent
+           // of other tasks.
+           query_server_status (ss);
 
-  // Run pass 5, if requested
-  if (rc == 0 && s.have_script && s.last_pass >= 5 && ! pending_interrupts)
-    rc = pass_5 (s, targets);
+           // If requested, manage trust of servers. This is
+           // independent of other tasks.
+           manage_server_trust (ss);
+#endif
+
+           // Run the passes only if a script has been specified or
+           // if we're dumping something. The requirement for a
+           // script has already been checked in
+           // systemtap_session::check_options.
+           if (ss.have_script || ss.dump_mode)
+             {
+               // Run passes 0-4 for each unique session, either
+               // locally or using a compile-server.
+               ss.init_try_server ();
+               if ((rc = passes_0_4 (ss)))
+                 {
+                   // Compilation failed.
+                   // Try again using a server if appropriate.
+                   if (ss.try_server ())
+                     rc = passes_0_4_again_with_server (ss);
+                 }
+               if (rc || s.perpass_verbose[0] >= 1)
+                 s.explain_auto_options ();
+             }
+         }
+
+       // Run pass 5, if requested
+       if (rc == 0 && s.have_script && s.last_pass >= 5 && ! pending_interrupts)
+         rc = pass_5 (s, targets);
+      }
 
-  // Pass 6. Cleanup
-  for (unsigned i = 0; i < targets.size(); ++i)
-    delete targets[i];
-  cleanup (s, rc);
+    // Pass 6. Cleanup
+    for (unsigned i = 0; i < targets.size(); ++i)
+      delete targets[i];
+    cleanup (s, rc);
 
-  return (rc||pending_interrupts) ? EXIT_FAILURE : EXIT_SUCCESS;
+    assert_no_interrupts();
+    return (rc) ? EXIT_FAILURE : EXIT_SUCCESS;
+  }
+  catch (const interrupt_exception& e) {
+      // User entered ctrl-c, exit quietly.
+      return EXIT_FAILURE;
+  }
+  catch (const exit_exception& e) {
+      // Exiting for any quiet reason.
+      return e.rc;
+  }
+  catch (const exception &e) {
+      // Some other uncaught exception.
+      cerr << e.what() << endl;
+      return EXIT_FAILURE;
+  }
+  catch (...) {
+      // Catch all other unknown exceptions.
+      cerr << _("ERROR: caught unknown exception!") << endl;
+      return EXIT_FAILURE;
+  }
 }
 
 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.054478 seconds and 5 git commands to generate.