aboutsummaryrefslogtreecommitdiff
path: root/gprofng/src/gp-collect-app.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gprofng/src/gp-collect-app.cc')
-rw-r--r--gprofng/src/gp-collect-app.cc1598
1 files changed, 1598 insertions, 0 deletions
diff --git a/gprofng/src/gp-collect-app.cc b/gprofng/src/gp-collect-app.cc
new file mode 100644
index 0000000..afaae70
--- /dev/null
+++ b/gprofng/src/gp-collect-app.cc
@@ -0,0 +1,1598 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+ Contributed by Oracle.
+
+ This file is part of GNU Binutils.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "config.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+
+#include "gp-defs.h"
+#include "cpu_frequency.h"
+#include "util.h"
+#include "collctrl.h"
+#include "hwcdrv.h"
+#include "gp-experiment.h"
+#include "collect.h"
+#include "StringBuilder.h"
+
+#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER"
+
+extern char **environ;
+
+static volatile int interrupt = 0;
+static int saved_stdout = -1;
+static int saved_stderr = -1;
+static int no_short_usage = 0;
+static int usage_fd = 2;
+static collect *collect_obj = NULL;
+extern "C" void sigint_handler (int sig, siginfo_t *info, void *context);
+static char *outredirect = NULL;
+static int precheck;
+static int nprocesses;
+static Process **processes;
+
+int
+main (int argc, char *argv[])
+{
+ // disable any alarm that might be pending
+ int r = alarm (0);
+ if (r != 0)
+ dbe_write (2, GTXT ("collect has alarm(%d) pending\n"), r);
+ collect_obj = new collect (argc, argv, environ);
+ collect_obj->start (argc, argv);
+ delete collect_obj;
+ return 0;
+}
+
+extern "C" void
+sigint_handler (int, siginfo_t *, void *)
+{
+ interrupt = 1;
+ if (collect_obj->cc != NULL)
+ collect_obj->cc->interrupt ();
+ return;
+}
+
+extern "C" void
+sigalrm_handler (int, siginfo_t *, void *)
+{
+ dbe_write (2, GTXT ("collect: unexpected alarm clock signal received\n"));
+ return;
+}
+
+extern "C" void
+sigterm_handler (int, siginfo_t *, void *)
+{
+ for (int i = 0; i < nprocesses; i++)
+ {
+ Process *proc = processes[i];
+ if (proc != NULL)
+ kill (proc->pid, SIGTERM);
+ }
+}
+
+collect::collect (int argc, char *argv[], char **envp)
+: Application (argc, argv)
+{
+ verbose = 0;
+ disabled = 0;
+ cc = NULL;
+ collect_warnings = NULL;
+ collect_warnings_idx = 0;
+ int ii;
+ for (ii = 0; ii < MAX_LD_PRELOAD_TYPES; ii++)
+ sp_preload_list[ii] = NULL;
+ for (ii = 0; ii < MAX_LD_PRELOAD_TYPES; ii++)
+ sp_libpath_list[ii] = NULL;
+ java_path = NULL;
+ java_how = NULL;
+ jseen_global = 0;
+ nlabels = 0;
+ origargc = argc;
+ origargv = argv;
+ origenvp = envp;
+ mem_so_me = false;
+}
+
+collect::~collect ()
+{
+ delete cc;
+}
+
+struct sigaction old_sigint_handler;
+struct sigaction old_sigalrm_handler;
+
+void
+collect::start (int argc, char *argv[])
+{
+ char *ccret;
+ char *extype;
+ /* create a collector control structure, disabling aggressive warning */
+ cc = new Coll_Ctrl (0, false, false);
+ if (prog_name)
+ {
+ char *s = strrchr (prog_name, '/');
+ if (s && (s - prog_name) > 5) // Remove /bin/
+ {
+ s = dbe_sprintf (NTXT ("%.*s"), (int) (s - prog_name - 4), prog_name);
+ cc->set_project_home (s);
+ free (s);
+ }
+ }
+ char * errenable = cc->enable_expt ();
+ if (errenable)
+ {
+ writeStr (2, errenable);
+ free (errenable);
+ }
+
+ /* install a handler for SIGALRM */
+ struct sigaction act;
+ memset (&act, 0, sizeof (struct sigaction));
+ sigemptyset (&act.sa_mask);
+ act.sa_handler = (SignalHandler) sigalrm_handler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction (SIGALRM, &act, &old_sigalrm_handler) == -1)
+ {
+ writeStr (2, GTXT ("Unable to install SIGALRM handler\n"));
+ exit (-1);
+ }
+
+ /* install a handler for SIGINT */
+ sigemptyset (&act.sa_mask);
+ act.sa_handler = (SignalHandler) sigint_handler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction (SIGINT, &act, &old_sigint_handler) == -1)
+ {
+ writeStr (2, GTXT ("Unable to install SIGINT handler\n"));
+ exit (-1);
+ }
+
+ /* install a handler for SIGTERM */
+ sigemptyset (&act.sa_mask);
+ act.sa_sigaction = sigterm_handler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction (SIGTERM, &act, NULL) == -1)
+ {
+ writeStr (2, GTXT ("Unable to install SIGTERM handler\n"));
+ exit (-1);
+ }
+ if (argc > 1 && strncmp (argv[1], NTXT ("--whoami="), 9) == 0)
+ {
+ whoami = argv[1] + 9;
+ argc--;
+ argv++;
+ }
+
+ /* check for no arguments -- usage message */
+ if (argc == 1)
+ {
+ verbose = 1;
+ usage_fd = 1;
+ validate_config (0);
+ usage ();
+ exit (0);
+ }
+ else if (argc == 2 && strcmp (argv[1], NTXT ("-h")) == 0)
+ {
+ /* only one argument, -h */
+ verbose = 1;
+ validate_config (0);
+ /* now print the HWC usage message */
+ show_hwc_usage ();
+ exit (0);
+ }
+ else if (argc == 2 && (strcmp (argv[1], NTXT ("-help")) == 0 ||
+ strcmp (argv[1], NTXT ("--help")) == 0))
+ {
+ /* only one argument, -help or --help */
+ verbose = 1;
+ usage_fd = 1;
+ validate_config (0);
+ usage ();
+ exit (0);
+ }
+// Ruud
+ else if ((argc == 2) &&
+ (strcmp (argv[1], NTXT ("--version")) == 0))
+ {
+ /* only one argument, --version */
+
+ /* print the version info */
+ Application::print_version_info ();
+ exit (0);
+ }
+
+ /* precheck the arguments -- scan for -O, -M flagS */
+ precheck = 1;
+ targ_index = check_args (argc, argv);
+ if (targ_index < 0)
+ {
+ /* message has already been written */
+ usage_fd = 2;
+ short_usage ();
+ exit (1);
+ }
+ /* crack the arguments */
+ precheck = 0;
+ targ_index = check_args (argc, argv);
+ if (targ_index <= 0)
+ {
+ /* message has already been written */
+ usage_fd = 2;
+ short_usage ();
+ exit (1);
+ }
+ if (targ_index != 0)
+ check_target (argc, argv);
+ if (disabled != 0 && cc->get_count () == 0)
+ {
+ // show collection parameters; count data
+ ccret = cc->show (0);
+ writeStr (1, ccret);
+ }
+
+ // see if Java version should be checked
+ if (cc->get_java_default () == 0 && java_path != NULL)
+ validate_java (java_path, java_how, verbose);
+
+ /* if count data is requested, exec bit to do the real work */
+ /* even for a dryrun */
+ if (cc->get_count () != 0)
+ get_count_data ();
+
+ /* if a dry run, just exit */
+ if (disabled != 0)
+ {
+ writeStr (1, cc->show_expt ());
+ StringBuilder sb;
+ sb.append (GTXT ("Exec argv[] = "));
+ for (int i = 0; i < nargs; i++)
+ sb.appendf (NTXT ("%s "), arglist[i]);
+ sb.append (NTXT ("\n"));
+ char *s = sb.toString ();
+ writeStr (1, s);
+ free (s);
+ exit (0);
+ }
+
+ // If the mem_so_me flag is set, preload mem.so
+ // and launch the process
+ if (mem_so_me)
+ {
+ /* set env vars for mem.so */
+ if (putenv_memso () != 0)
+ exit (1); /* message has already been written */
+ /* ensure original outputs restored for target */
+ reset_output ();
+
+ /* now exec the target ... */
+ if (cc->get_debug_mode () == 1)
+ {
+ traceme (arglist[0], arglist);
+ extype = NTXT ("traceme");
+ }
+ else
+ {
+ execvp (arglist[0], arglist);
+ extype = NTXT ("exevcp");
+ }
+ /* oops, exec of the target failed */
+ char *em = strerror (errno);
+ set_output (); /* restore output for collector */
+ if (em == NULL)
+ dbe_write (2, GTXT ("memso %s of %s failed: errno = %d\n"), extype, argv[targ_index], errno);
+ else
+ dbe_write (2, GTXT ("memso %s of %s failed: %s\n"), extype, argv[targ_index], em);
+ exit (1);
+ }
+
+ /* normal path, setting up an experiment and launching the target */
+ /* set up the experiment */
+ ccret = cc->setup_experiment ();
+ if (ccret != NULL)
+ {
+ dbe_write (2, NTXT ("%s\n"), ccret);
+ free (ccret);
+ exit (1);
+ }
+ /* Beyond this point, the experiment is created */
+ if (collect_warnings != NULL)
+ {
+ warn_open ();
+ for (int i = 0; i < collect_warnings_idx; i++)
+ warn_comment (SP_JCMD_CWARN, COL_WARN_APP_NOT_READY, collect_warnings[i], (int) strlen (collect_warnings[i]));
+ warn_close ();
+ }
+ /* check cpu frequency variation for intel*/
+ unsigned char mode = COL_CPUFREQ_NONE;
+ int max_freq = get_cpu_frequency (&mode);
+ char freq_scaling[256];
+ char turbo_mode[256];
+ *freq_scaling = 0;
+ *turbo_mode = 0;
+ if (mode & COL_CPUFREQ_SCALING)
+ snprintf (freq_scaling, sizeof (freq_scaling), NTXT (" frequency_scaling=\"enabled\""));
+ if (mode & COL_CPUFREQ_TURBO)
+ snprintf (turbo_mode, sizeof (turbo_mode), NTXT (" turbo_mode=\"enabled\""));
+ if (mode != COL_CPUFREQ_NONE)
+ {
+ warn_open ();
+ if (warn_file != NULL)
+ {
+ warn_write ("<powerm>\n<frequency clk=\"%d\"%s%s/>\n</powerm>\n",
+ max_freq, freq_scaling, turbo_mode);
+ warn_close ();
+ }
+ }
+
+ /* check for labels to write to notes file */
+ if (nlabels != 0)
+ {
+ char *nbuf;
+ char nbuf2[MAXPATHLEN];
+ // fetch the experiment name and CWD
+ char *exp = cc->get_experiment ();
+ char *ev = getcwd (nbuf2, sizeof (nbuf2));
+
+ // format the environment variable for the experiment directory name
+ if (ev != NULL && exp[0] != '/')
+ // cwd succeeded, and experiment is a relative path
+ nbuf = dbe_sprintf (NTXT ("%s/%s/%s"), nbuf2, exp, SP_NOTES_FILE);
+ else
+ // getcwd failed or experiment is a fullpath
+ nbuf = dbe_sprintf (NTXT ("%s/%s"), exp, SP_NOTES_FILE);
+
+ FILE *f = fopen (nbuf, NTXT ("w"));
+ free (nbuf);
+ if (f != NULL)
+ {
+ for (int i = 0; i < nlabels; i++)
+ fprintf (f, NTXT ("%s\n"), label[i]);
+ fclose (f);
+ }
+ }
+ /* check for user interrupt */
+ if (interrupt == 1)
+ {
+ cc->delete_expt ();
+ writeStr (2, GTXT ("User interrupt\n"));
+ exit (0);
+ }
+
+ /* print data-collection parameters */
+ if (verbose)
+ {
+ ccret = cc->show (0);
+ if (ccret != NULL)
+ writeStr (2, ccret);
+ }
+ ccret = cc->show_expt ();
+ if (ccret != NULL)
+ writeStr (1, ccret); /* write this to stdout */
+
+ pid_t pid = (pid_t) cc->get_attach_pid ();
+ if (pid == (pid_t) 0)
+ {
+ /* No attach */
+ /* Set the environment for libcollector */
+ if (putenv_libcollector () != 0)
+ {
+ /* message has already been written */
+ cc->delete_expt ();
+ exit (1);
+ }
+ /* ensure original output fds restored for target */
+ reset_output ();
+
+ /* now exec the target ... */
+ if (cc->get_debug_mode () == 1)
+ {
+ traceme (arglist[0], arglist);
+ extype = NTXT ("traceme");
+ }
+ else
+ {
+ execvp (arglist[0], arglist);
+ extype = NTXT ("execvp");
+ }
+
+ /* we reach this point only if the target launch failed */
+ char *em = strerror (errno);
+
+ /* restore output for collector */
+ set_output ();
+
+ /* exec failed; delete experiment */
+ cc->delete_expt ();
+
+ /* print a message and exit */
+ if (em == NULL)
+ dbe_write (2, GTXT ("%s of %s failed: errno = %d\n"), extype, argv[targ_index], errno);
+ else
+ dbe_write (2, GTXT ("%s of %s failed: %s\n"), extype, argv[targ_index], em);
+ exit (1);
+ }
+ else
+ abort ();
+}
+
+/**
+ * Prepare a warning message and pass it to warn_write()
+ * @Parameters:
+ * kind Type of comment
+ * num ID
+ * s Comment sting
+ * len Length of the string
+ * @Return: none.
+ */
+void
+collect::warn_comment (const char *kind, int num, char *s, int len)
+{
+ if (len != 0)
+ warn_write (NTXT ("<event kind=\"%s\" id=\"%d\">%.*s</event>\n"),
+ kind, num, len, s);
+ else if (s == NULL)
+ warn_write (NTXT ("<event kind=\"%s\" id=\"%d\"/>\n"), kind, num);
+ else
+ warn_write (NTXT ("<event kind=\"%s\" id=\"%d\">%s</event>\n"), kind, num, s);
+}
+
+/**
+ * Open the warnings file in Append mode ("aw")
+ */
+void
+collect::warn_open ()
+{
+ // open the warnings file
+ warnfilename = dbe_sprintf (NTXT ("%s/%s"), cc->get_experiment (), SP_WARN_FILE);
+ int fd = open (warnfilename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ warn_file = fdopen (fd, NTXT ("aw"));
+}
+
+/**
+ * Close the warnings file
+ */
+void
+collect::warn_close ()
+{
+ (void) fclose (warn_file);
+}
+
+/**
+ * Format the warning message and write it to the warnings file
+ */
+void
+collect::warn_write (const char *format, ...)
+{
+ char buf[4096];
+ // format the input arguments into a string
+ va_list va;
+ va_start (va, format);
+ vsnprintf (buf, sizeof (buf), format, va);
+ va_end (va);
+ // write it to the warnings file (warnings.xml)
+ fwrite (buf, 1, strlen (buf), warn_file);
+ fflush (warn_file);
+}
+
+/* process the args, setting expt. params,
+ * and finding offset for a.out name
+ */
+int
+collect::check_args (int argc, char *argv[])
+{
+ int hseen = 0;
+ int hoffseen = 0;
+ int lseen = 0;
+ int tseen = 0;
+ int pseen = 0;
+ int sseen = 0;
+ int yseen = 0;
+ int Fseen = 0;
+ int Aseen = 0;
+ int Sseen = 0;
+ int Hseen = 0;
+ int iseen = 0;
+ int Jseen = 0;
+ int ofseen = 0;
+ char *expName = NULL;
+ bool overwriteExp = false;
+ char *ccret;
+ char *ccwarn;
+ for (targ_index = 1; targ_index < argc; targ_index++)
+ {
+ if (argv[targ_index] == NULL)
+ break;
+ if (dbe_strcmp (argv[targ_index], "--") == 0)
+ {
+ targ_index++;
+ break;
+ }
+ if (argv[targ_index][0] != '-')
+ break;
+ int param;
+ switch (argv[targ_index][1])
+ {
+ case 'y':
+ {
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ char *ptr;
+ int resume = 1;
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (yseen != 0)
+ {
+ dupflagseen ('y');
+ return -1;
+ }
+ yseen++;
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ {
+ writeStr (2, GTXT ("-y requires a signal argument\n"));
+ return -1;
+ }
+ if ((ptr = strrchr (argv[targ_index], ',')) != NULL)
+ {
+ if ((*(ptr + 1) != 'r') || (*(ptr + 2) != 0))
+ {
+ /* not the right trailer */
+ dbe_write (2, GTXT ("Invalid delay signal %s\n"), argv[targ_index]);
+ return -1;
+ }
+ resume = 0;
+ *ptr = 0;
+ }
+ param = cc->find_sig (argv[targ_index]);
+ if (param < 0)
+ {
+ /* invalid signal */
+ dbe_write (2, GTXT ("Invalid delay signal %s\n"), argv[targ_index]);
+ return -1;
+ }
+ ccret = cc->set_pauseresume_signal (param, resume);
+ if (ccret != NULL)
+ {
+ /* invalid signal; write message */
+ writeStr (2, ccret);
+ return -1;
+ }
+ break;
+ }
+ case 'l':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (lseen != 0)
+ {
+ dupflagseen ('l');
+ return -1;
+ }
+ lseen++;
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ {
+ writeStr (2, GTXT ("-l requires a signal argument\n"));
+ return -1;
+ }
+ param = cc->find_sig (argv[targ_index]);
+ if (param < 0)
+ {
+ /* invalid signal */
+ dbe_write (2, GTXT ("Invalid sample signal %s\n"), argv[targ_index]);
+ return -1;
+ }
+ ccret = cc->set_sample_signal (param);
+ if (ccret != NULL)
+ {
+ /* invalid signal; write message */
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ break;
+
+#ifdef GPROFNG_DOES_NOT_SUPPORT
+ case 'P':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (Pseen != 0)
+ {
+ dupflagseen ('P');
+ return -1;
+ }
+ Pseen++;
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ {
+ writeStr (2, GTXT ("-P requires a process pid argument\n"));
+ return -1;
+ }
+ ccret = cc->set_attach_pid (argv[targ_index]);
+ if (ccret != NULL)
+ {
+ /* error; write message */
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ break;
+#endif
+ case 't':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (tseen != 0)
+ {
+ dupflagseen ('t');
+ return -1;
+ }
+ tseen++;
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ {
+ writeStr (2, GTXT ("-t requires a run-duration argument\n"));
+ return -1;
+ }
+ ccret = cc->set_time_run (argv[targ_index]);
+ if (ccret != NULL)
+ {
+ /* error; write message */
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ break;
+ case 'p':
+ {
+ char *warnmsg;
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (pseen != 0)
+ {
+ dupflagseen ('p');
+ return -1;
+ }
+ pseen++;
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ {
+ writeStr (2, GTXT ("-p requires a clock-profiling argument\n"));
+ return -1;
+ }
+ ccret = cc->set_clkprof (argv[targ_index], &warnmsg);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ if (warnmsg != NULL)
+ {
+ writeStr (2, warnmsg);
+ free (warnmsg);
+ }
+ break;
+ }
+ case 's':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (sseen != 0)
+ {
+ dupflagseen ('s');
+ return -1;
+ }
+ sseen++;
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ {
+ writeStr (2, GTXT ("-s requires a synchronization-tracing argument\n"));
+ return -1;
+ }
+ ccret = cc->set_synctrace (argv[targ_index]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ break;
+ case 'h':
+ {
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ targ_index++;
+ if ((argv[targ_index] == NULL) || (strlen (argv[targ_index]) == 0))
+ {
+ writeStr (2, GTXT ("-h requires a HW-counter-profiling argument\n"));
+ return -1;
+ }
+ // Check for some special cases
+ char * string = argv[targ_index];
+ if (strcmp (argv[targ_index], NTXT ("off")) == 0)
+ {
+ if (hseen != 0)
+ {
+ no_short_usage = 1;
+ writeStr (2, GTXT ("-h off cannot be used with any other -h arguments\n"));
+ return -1;
+ }
+ hoffseen = 1;
+ hseen = 1;
+ cc->disable_hwc ();
+ break;
+ }
+ // Check to see if we can use HWC
+ unsigned hwc_maxregs = hwc_get_max_concurrent (false);
+ if (hwc_maxregs == 0)
+ {
+ char buf[1024];
+ char *pch = hwcfuncs_errmsg_get (buf, sizeof (buf), 0);
+ if (*pch)
+ dbe_write (2, GTXT ("HW counter profiling is not supported on this system: %s%s"),
+ pch, pch[strlen (pch) - 1] == '\n' ? "" : "\n");
+ else
+ dbe_write (2, GTXT ("HW counter profiling is not supported on this system\n"));
+ no_short_usage = 1;
+ return -1;
+ }
+ // Make sure there's no other -h after -h off
+ if (hoffseen != 0)
+ {
+ no_short_usage = 1;
+ writeStr (2, GTXT ("No -h arguments can be used after -h off\n"));
+ return -1;
+ }
+ // set up to process HW counters (to know about default counters)
+ cc->setup_hwc ();
+ hseen++;
+ char *warnmsg;
+ if (strcmp (argv[targ_index], NTXT ("on")) == 0)
+ ccret = cc->add_default_hwcstring ("on", &warnmsg, true);
+ else if (strcmp (argv[targ_index], NTXT ("hi")) == 0 ||
+ strcmp (argv[targ_index], NTXT ("high")) == 0)
+ ccret = cc->add_default_hwcstring ("hi", &warnmsg, true);
+ else if (strcmp (argv[targ_index], NTXT ("lo")) == 0 ||
+ strcmp (argv[targ_index], NTXT ("low")) == 0)
+ ccret = cc->add_default_hwcstring ("lo", &warnmsg, true);
+ else if (strcmp (argv[targ_index], NTXT ("auto")) == 0)
+ ccret = cc->add_default_hwcstring ("auto", &warnmsg, true);
+ else
+ ccret = cc->add_hwcstring (string, &warnmsg);
+ if (ccret != NULL)
+ {
+ /* set global flag to suppress the short_usage message for any subsequent HWC errors */
+ no_short_usage = 1;
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ if (warnmsg != NULL)
+ {
+ writeStr (2, warnmsg);
+ free (warnmsg);
+ }
+ break;
+ }
+ case 'O':
+ overwriteExp = true;
+ __attribute__ ((fallthrough));
+ case 'o':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s must be followed by a file name\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (expName != NULL)
+ {
+ dbe_write (2, GTXT ("Only one -o or -O argument may be used\n"));
+ dupflagseen ('o');
+ return -1;
+ }
+ expName = argv[targ_index + 1];
+ targ_index++;
+ break;
+ case 'S':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s must be followed by a sample interval name\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (Sseen != 0)
+ {
+ dupflagseen ('S');
+ return -1;
+ }
+ Sseen++;
+ ccret = cc->set_sample_period (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ targ_index++;
+ break;
+ case 'H':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s requires a heap-tracing argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (Hseen != 0)
+ {
+ dupflagseen ('H');
+ return -1;
+ }
+ Hseen++;
+ ccret = cc->set_heaptrace (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ if (cc->get_java_default () == 1)
+ cc->set_java_mode (NTXT ("off"));
+ targ_index++;
+ break;
+ case 'i':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ fprintf (stderr, GTXT ("Argument %s requires an I/O-tracing argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (iseen != 0)
+ {
+ dupflagseen ('i');
+ return -1;
+ }
+ iseen++;
+ ccret = cc->set_iotrace (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ targ_index++;
+ break;
+ case 'j':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s requires a java-profiling argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (jseen_global != 0)
+ {
+ dupflagseen ('j');
+ return -1;
+ }
+ jseen_global++;
+ ccret = cc->set_java_mode (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ targ_index++;
+ break;
+ case 'J':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1) return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s requires a java argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (Jseen != 0)
+ {
+ dupflagseen ('J');
+ return -1;
+ }
+ Jseen++;
+ ccret = cc->set_java_args (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ targ_index++;
+ break;
+ case 'F':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s requires a descendant-following argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (Fseen != 0)
+ {
+ dupflagseen ('F');
+ return -1;
+ }
+ Fseen++;
+ ccret = cc->set_follow_mode (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ targ_index++;
+ break;
+ case 'a':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s requires a load-object archiving argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (Aseen != 0)
+ {
+ dupflagseen ('a');
+ return -1;
+ }
+ Aseen++;
+ ccret = cc->set_archive_mode (argv[targ_index + 1]);
+ if (ccret != NULL)
+ {
+ writeStr (2, ccret);
+ free (ccret);
+ return -1;
+ }
+ targ_index++;
+ break;
+ case 'C':
+ if (precheck == 1)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ if (checkflagterm (argv[targ_index]) == -1)
+ return -1;
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s must be followed by a comment\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (nlabels == MAXLABELS)
+ {
+ dbe_write (2, GTXT ("No more than %d comments may be specified\n"),
+ MAXLABELS);
+ return -1;
+ }
+ label[nlabels] = argv[targ_index + 1];
+ nlabels++;
+ targ_index++;
+ break;
+ case 'n':
+ case 'v':
+ case 'V':
+ if (precheck == 1)
+ break;
+ do_flag (&argv[targ_index][1]);
+ break;
+ case 'Z':
+ // special undocumented argument for debug builds only to allow analyzer to
+ // LD_PRELOAD mem.so for the target it spawns
+ mem_so_me = true;
+ break;
+ case '-':
+ if (strcmp (argv[targ_index], NTXT ("--verbose")) == 0)
+ do_flag ("v");
+ else if (strcmp (argv[targ_index], "--outfile") == 0)
+ {
+ if (precheck == 0)
+ {
+ targ_index++;
+ if (argv[targ_index] == NULL)
+ return 0;
+ break;
+ }
+ // process this argument now
+ if (argv[targ_index + 1] == NULL)
+ {
+ dbe_write (2, GTXT ("Argument %s requires a file argument\n"),
+ argv[targ_index]);
+ return -1;
+ }
+ if (ofseen != 0)
+ {
+ dupflagseen (argv[targ_index]);
+ return -1;
+ }
+ ofseen++;
+ if (outredirect == NULL)
+ {
+ outredirect = argv[targ_index + 1];
+ set_output ();
+ } // else already redirected; ignore with no message
+ targ_index++;
+ }
+ else
+ {
+ dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), argv[targ_index]);
+ return -1;
+ }
+ break;
+ default:
+ dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), argv[targ_index]);
+ return -1;
+ }
+ }
+ if (targ_index >= argc)
+ return -1;
+ if (argv[targ_index] == NULL)
+ {
+ if (precheck == 1)
+ return 0;
+ if (cc->get_attach_pid () != 0) /* no target is OK, if we're attaching */
+ return 0;
+ writeStr (2, GTXT ("Name of target must be specified\n"));
+ return -1;
+ }
+ if (expName)
+ {
+ ccwarn = NULL;
+ ccret = cc->set_expt (expName, &ccwarn, overwriteExp);
+ if (ccwarn)
+ {
+ writeStr (2, ccwarn);
+ free (ccwarn);
+ }
+ if (ccret)
+ {
+ writeStr (2, ccret);
+ return -1;
+ }
+ }
+ if (cc->get_attach_pid () != 0)
+ {
+ writeStr (2, GTXT ("Name of target must not be specified when -P is used\n"));
+ return -1;
+ }
+ return targ_index;
+}
+
+int
+collect::checkflagterm (const char *c)
+{
+ if (c[2] != 0)
+ {
+ dbe_write (2, GTXT ("collect: unrecognized argument `%s'\n"), c);
+ return -1;
+ }
+ return 0;
+}
+
+int
+collect::do_flag (const char *flags)
+{
+ char *s;
+ for (int i = 0;; i++)
+ {
+ switch (flags[i])
+ {
+ case 0: // end of string
+ return 0;
+ case 'n':
+ disabled = 1;
+ if (verbose != 1)
+ {
+// Ruud
+ Application::print_version_info ();
+/*
+ dbe_write (2, NTXT ("GNU %s version %s\n"),
+ get_basename (prog_name), VERSION);
+*/
+ verbose = 1;
+ }
+ break;
+ case 'x':
+ s = cc->set_debug_mode (1);
+ if (s)
+ {
+ writeStr (2, s);
+ free (s);
+ }
+ break;
+ case 'v':
+ if (verbose != 1)
+ {
+// Ruud
+ Application::print_version_info ();
+/*
+ dbe_write (2, NTXT ("GNU %s version %s\n"),
+ get_basename (prog_name), VERSION);
+*/
+ verbose = 1;
+ }
+ break;
+ case 'V':
+// Ruud
+ Application::print_version_info ();
+/*
+ dbe_write (2, NTXT ("GNU %s version %s\n"),
+ get_basename (prog_name), VERSION);
+*/
+ /* no further processing.... */
+ exit (0);
+ }
+ }
+}
+
+/*
+ * traceme - cause the caller to stop at the end of the next exec()
+ * so that a debugger can attach to the new program
+ *
+ * Takes same arguments as execvp()
+ */
+int
+collect::traceme (const char *execvp_file, char *const execvp_argv[])
+{
+ int ret = -1;
+ pid_t pid = fork ();
+ if (pid == 0)
+ { // child
+ // child will set up itself to be PTRACE'd, and then exec the target executable
+ /* reset the SP_COLLECTOR_FOUNDER value to the new pid */
+ pid_t mypid = getpid ();
+ char *ev = dbe_sprintf (NTXT ("%s=%d"), SP_COLLECTOR_FOUNDER, mypid);
+ if (putenv (ev) != 0)
+ {
+ dbe_write (2, GTXT ("fork-child: Can't putenv of \"%s\": run aborted\n"), ev);
+ return 1;
+ }
+ ptrace (PTRACE_TRACEME, 0, NULL, NULL); // initiate trace
+ ret = execvp (execvp_file, execvp_argv); // execvp user command
+ return ret; // execvp failed
+ }
+ else if (pid > 0)
+ { // parent
+ int status;
+ if (waitpid (pid, &status, 0) != pid)
+ { // wait for execvp to cause signal
+ writeStr (2, GTXT ("parent waitpid() failed\n"));
+ return -2;
+ }
+ if (!WIFSTOPPED (status))
+ writeStr (2, GTXT ("WIFSTOPPED(status) failed\n"));
+
+ // originally, PTRACE_DETACH would send SIGTSTP, but now we do it here:
+ if (kill (pid, SIGTSTP) != 0)
+ writeStr (2, GTXT ("kill(pid, SIGTSTP) failed\n"));
+ if (ptrace (PTRACE_DETACH, pid, NULL, 0) != 0)
+ { // detach trace
+ writeStr (2, GTXT ("ptrace(PTRACE_DETACH) failed\n"));
+ return -4;
+ }
+ dbe_write (2, GTXT ("Waiting for attach from debugger: pid=%d\n"), (int) pid);
+
+ // wait for an external debugger to attach
+ if (waitpid (pid, &status, 0) != pid)
+ { // keep parent alive until child quits
+ writeStr (2, GTXT ("parent final waitpid() failed\n"));
+ return -5;
+ }
+ }
+ else
+ return -1; // fork failed
+ exit (0);
+}
+
+void
+collect::dupflagseen (char c)
+{
+ dbe_write (2, GTXT ("Only one -%c argument may be used\n"), c);
+}
+
+void
+collect::dupflagseen (const char *s)
+{
+ dbe_write (2, GTXT ("Only one %s argument may be used\n"), s);
+}
+
+int
+collect::set_output ()
+{
+ static int initial = 1;
+ if (outredirect)
+ {
+ int fd = open (outredirect, O_WRONLY | O_CREAT | O_APPEND,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd == -1)
+ {
+ dbe_write (2, GTXT ("Warning: Can't open collector output `%s': %s\n"),
+ outredirect, strerror (errno));
+ }
+ else
+ {
+ if ((saved_stdout = dup (1)) == -1 || dup2 (fd, 1) == -1)
+ dbe_write (2, GTXT ("Warning: Can't divert collector %s: %s\n"),
+ NTXT ("stdout"), strerror (errno));
+ if ((saved_stderr = dup (2)) == -1 || dup2 (fd, 2) == -1)
+ dbe_write (2, GTXT ("Warning: Can't divert collector %s: %s\n"),
+ NTXT ("stderr"), strerror (errno));
+ close (fd);
+ if ((saved_stdout != -1) && (saved_stderr != -1))
+ {
+ if (initial)
+ {
+ struct timeval tp;
+ gettimeofday (&tp, NULL);
+ writeStr (2, ctime (&tp.tv_sec));
+ initial = 0;
+ }
+ return 1; // diversion in place
+ }
+ }
+ }
+ return 0; // no diversion
+}
+
+void
+collect::reset_output ()
+{
+ if (saved_stdout != -1 &&
+ (dup2 (saved_stdout, 1) == -1 || close (saved_stdout)))
+ dbe_write (2, GTXT ("Warning: Can't restore collector stdout: %s\n"),
+ strerror (errno));
+ if (saved_stderr != -1 &&
+ (dup2 (saved_stderr, 2) == -1 || close (saved_stderr)))
+ dbe_write (2, GTXT ("Warning: Can't restore collector stderr: %s\n"),
+ strerror (errno));
+}
+
+void
+collect::usage ()
+{
+
+/*
+ Ruud - Isolate this line because it has an argument. Otherwise it would be at the
+ end of this long list.
+*/
+ printf ( GTXT (
+ "Usage: gprofng collect app [OPTION(S)] TARGET [TARGET_ARGUMENTS]\n")),
+
+/*
+-------------------------------------------------------------------------------
+ For a reason I don't understand, the continuation line(s) need to start at
+ column 26 in order for help2man to do the righ thing. Ruud
+-------------------------------------------------------------------------------
+*/
+ printf ( GTXT (
+ "\n"
+ "Collect performance data on the target program. In addition to Program\n"
+ "Counter PC) sampling, hardware event counters and various tracing options\n"
+ "are supported.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " --version print the version number and exit.\n"
+ " --help print usage information and exit.\n"
+ " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n"
+ "\n"
+ " -p {off|on|lo|hi|<value>} disable (off) or enable (on) clock-profiling using a default\n"
+ " sampling granularity, or enable clock-profiling implicitly by\n"
+ " setting the sampling granularity (lo, hi, or a specific value\n"
+ " in ms); by default clock profiling is enabled.\n"
+ "\n"
+ " -h {<ctr_def>...,<ctr_n_def>} enable hardware event counter profiling and select\n"
+ " the counter(s); to see the supported counters on this system use\n"
+ " the -h option without other arguments.\n"
+ "\n"
+ " -o <exp_name> specify the name for (and path to) the experiment directory; the\n"
+ " the default path is the current directory.\n"
+ "\n"
+ " -O <exp_name> the same as -o, but unlike the -o option, silently overwrite an\n"
+ " existing experiment directory with the same name.\n"
+ "\n"
+ " -C <label> add up to 10 comment labels to the experiment; comments appear in\n"
+ " the notes section of the header.\n"
+ "\n"
+ " -j {on|off|<path>} enable (on), or disable (off) Java profiling when the target\n"
+ " program is a JVM; optionally set the <path> to a non-default JVM;\n"
+ " the default is \"-j on\".\n"
+ "\n"
+ " -J <java-args> specify arguments to the JVM.\n"
+ "\n"
+ " -t <duration>[m|s] specify the duration over which to record data; the default unit\n"
+ " is seconds (s), but can be set to minutes (m).\n"
+ "\n"
+ " -n dry run; display several run-time settings, but do not run the\n"
+ " target, or collect performance data.\n"
+ "\n"
+ " -y <signal>[,r] specify delayed initialization and a pause/resume signal; by default\n"
+ " the target starts in paused mode; if the optional r keyword is\n"
+ " provided, start in resumed mode.\n"
+ "\n"
+ " -F {off|on|=<regex>} control to follow descendant processes; disable (off), enable (on),\n"
+ " or collect data on all descendant processes whose name matches the\n"
+ " specified regular expression; the default is \"-F on\".\n"
+ "\n"
+ " -a {off|on|ldobjects|src|usedldobjects|usedsrc} specify archiving of binaries and other files;\n"
+ " in addition to disable this feature (off), or enable archiving off all\n"
+ " loadobjects and sources (on), the other options support a more\n"
+ " refined selection. All of these options enable archiving, but the\n"
+ " keyword controls what exactly is selected: all load objects (ldobjects),\n"
+ " all source files (src), the loadobjects asscoiated with a program counter\n"
+ " (usedldobjects), or the source files associated with a program counter\n"
+ " (usedsrc); the default is \"-a ldobjects\".\n"
+ "\n"
+ " -S {off|on|<seconds>} disable (off) or enable (on) periodic sampling of process-wide resource\n"
+ " utilization; by default sampling occurs every second; use the <seconds>\n"
+ " option to change this; the default is \"-S on\".\n"
+ "\n"
+ " -l <signal> specify a signal that will trigger a sample of process-wide resource utilization.\n"
+ "\n"
+ " -s <option>[,<API>] enable synchronization wait tracing; <option> is used to define the specifics\n"
+ " of the tracing (on, off, <threshold>, or all); <API> is used to select the API:\n"
+ " \"n\" selects native/Pthreads, \"j\" selects Java, and \"nj\" selects both;\n"
+ " the default is \"-s off\".\n"
+ "\n"
+ " -H {off|on} disable (off), or enable (on) heap tracing; the default is \"-H off\".\n"
+ "\n"
+ " -i {off|on} disable (off), or enable (on) I/O tracing; the default is \"-i off\".\n"
+ "\n"
+ "Documentation:\n"
+ "\n"
+ "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n"
+ "gprofng programs are properly installed at your site, the command \"info gprofng\"\n"
+ "should give you access to this document.\n"
+ "\n"
+ "See also:\n"
+ "\n"
+ "gprofng(1), gp-archive(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n"));
+/*
+ char *s = dbe_sprintf (GTXT ("Usage: %s <args> target <target-args>\n"),
+ whoami);
+ writeStr (usage_fd, s);
+ free (s);
+ writeStr (usage_fd, GTXT (" -p {lo|on|hi|off|<value>}\tspecify clock-profiling\n"));
+ writeStr (usage_fd, GTXT ("\t`lo' per-thread rate of ~10 samples/second\n"));
+ writeStr (usage_fd, GTXT ("\t`on' per-thread rate of ~100 samples/second (default)\n"));
+ writeStr (usage_fd, GTXT ("\t`hi' per-thread rate of ~1000 samples/second\n"));
+ writeStr (usage_fd, GTXT ("\t`off' disable clock profiling\n"));
+ writeStr (usage_fd, GTXT ("\t<value> specify profile timer period in millisec.\n"));
+ s = dbe_sprintf (GTXT ("\t\t\tRange on this system is from %.3f to %.3f millisec.\n\t\t\tResolution is %.3f millisec.\n"),
+ (double) cc->get_clk_min () / 1000.,
+ (double) cc->get_clk_max () / 1000.,
+ (double) cc->get_clk_res () / 1000.);
+ writeStr (usage_fd, s);
+ free (s);
+ writeStr (usage_fd, GTXT (" -h <ctr_def>...[,<ctr_n_def>]\tspecify HW counter profiling\n"));
+ s = dbe_sprintf (GTXT ("\tto see the supported HW counters on this machine, run \"%s -h\" with no other arguments\n"),
+ whoami);
+ writeStr (usage_fd, s);
+ free (s);
+ writeStr (usage_fd, GTXT (" -s <threshold>[,<scope>]\tspecify synchronization wait tracing\n"));
+ writeStr (usage_fd, GTXT ("\t<scope> is \"j\" for tracing Java-APIs, \"n\" for tracing native-APIs, or \"nj\" for tracing both\n"));
+ writeStr (usage_fd, GTXT (" -H {on|off}\tspecify heap tracing\n"));
+ writeStr (usage_fd, GTXT (" -i {on|off}\tspecify I/O tracing\n"));
+ writeStr (usage_fd, GTXT (" -N <lib>\tspecify library to exclude count from instrumentation (requires -c also)\n"));
+ writeStr (usage_fd, GTXT (" \tmultiple -N arguments can be provided\n"));
+ writeStr (usage_fd, GTXT (" -j {on|off|path}\tspecify Java profiling\n"));
+ writeStr (usage_fd, GTXT (" -J <java-args>\tspecify arguments to Java for Java profiling\n"));
+ writeStr (usage_fd, GTXT (" -t <duration>\tspecify time over which to record data\n"));
+ writeStr (usage_fd, GTXT (" -n\tdry run -- don't run target or collect performance data\n"));
+ writeStr (usage_fd, GTXT (" -y <signal>[,r]\tspecify delayed initialization and pause/resume signal\n"));
+ writeStr (usage_fd, GTXT ("\tWhen set, the target starts in paused mode;\n\t if the optional r is provided, it starts in resumed mode\n"));
+ writeStr (usage_fd, GTXT (" -F {on|off|=<regex>}\tspecify following descendant processes\n"));
+ writeStr (usage_fd, GTXT (" -a {on|ldobjects|src|usedldobjects|usedsrc|off}\tspecify archiving of binaries and other files;\n"));
+ writeStr (usage_fd, GTXT (" -S {on|off|<seconds>}\t Set the interval for periodic sampling of process-wide resource utilization\n"));
+ writeStr (usage_fd, GTXT (" -l <signal>\tspecify signal that will trigger a sample of process-wide resource utilization\n"));
+ writeStr (usage_fd, GTXT (" -o <expt>\tspecify experiment name\n"));
+ writeStr (usage_fd, GTXT (" --verbose\tprint expanded log of processing\n"));
+ writeStr (usage_fd, GTXT (" -C <label>\tspecify comment label (up to 10 may appear)\n"));
+ writeStr (usage_fd, GTXT (" -V|--version\tprint version number and exit\n"));
+*/
+ /* don't document this feature */
+ // writeStr (usage_fd, GTXT(" -Z\tPreload mem.so, and launch target [no experiment]\n") );
+/*
+ writeStr (usage_fd, GTXT ("\n See the gp-collect(1) man page for more information\n"));
+*/
+
+#if 0
+ /* print an extended usage message */
+ /* find a Java for Java profiling, set Java on to check Java */
+ find_java ();
+ cc->set_java_mode (NTXT ("on"));
+
+ /* check for variable-clock rate */
+ unsigned char mode = COL_CPUFREQ_NONE;
+ get_cpu_frequency (&mode);
+ if (mode != COL_CPUFREQ_NONE)
+ writeStr (usage_fd, GTXT ("NOTE: system has variable clock frequency, which may cause variable program run times.\n"));
+
+ /* show the experiment that would be run */
+ writeStr (usage_fd, GTXT ("\n Default experiment:\n"));
+ char *ccret = cc->setup_experiment ();
+ if (ccret != NULL)
+ {
+ writeStr (usage_fd, ccret);
+ free (ccret);
+ exit (1);
+ }
+ cc->delete_expt ();
+ ccret = cc->show (1);
+ if (ccret != NULL)
+ {
+ writeStr (usage_fd, ccret);
+ free (ccret);
+ }
+#endif
+}
+
+void
+collect::short_usage ()
+{
+ if (no_short_usage == 0)
+ dbe_write (usage_fd, GTXT ("Run \"%s --help\" for a usage message.\n"), whoami);
+}
+
+void
+collect::show_hwc_usage ()
+{
+ usage_fd = 1;
+ short_usage ();
+ cc->setup_hwc ();
+ hwc_usage (false, whoami, NULL);
+}
+
+void
+collect::writeStr (int f, const char *buf)
+{
+ if (buf != NULL)
+ write (f, buf, strlen (buf));
+}