/* Copyright (C) 2021-2023 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 ("\n\n\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 ("%.*s\n"), kind, num, len, s); else if (s == NULL) warn_write (NTXT ("\n"), kind, num); else warn_write (NTXT ("%s\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|} 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 {...,} 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 specify the name for (and path to) the experiment directory; the\n" " the default path is the current directory.\n" "\n" " -O the same as -o, but unlike the -o option, silently overwrite an\n" " existing experiment directory with the same name.\n" "\n" " -C