// 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>
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 *> >
// 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
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;
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;
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)
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;
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;
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;
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;
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
}
}
// 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
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++)
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 \
// 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);
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.
// 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;
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()
<< 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];
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
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);
return rc;
}
-static int
+int
pass_5 (systemtap_session &s, vector<remote*> targets)
{
// PASS 5: RUN
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;
#endif
}
- // Clean up temporary directory. Obviously, be careful with this.
- remove_temp_dir (s);
+ s.report_suppression();
PROBE1(stap, pass6__end, &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;
}
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 : */