mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-15 07:29:41 +00:00
1599 lines
42 KiB
C++
1599 lines
42 KiB
C++
/* 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));
|
|
}
|