aboutsummaryrefslogtreecommitdiff
path: root/gprofng/src/collctrl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gprofng/src/collctrl.cc')
-rw-r--r--gprofng/src/collctrl.cc3149
1 files changed, 3149 insertions, 0 deletions
diff --git a/gprofng/src/collctrl.cc b/gprofng/src/collctrl.cc
new file mode 100644
index 0000000..48c65ff
--- /dev/null
+++ b/gprofng/src/collctrl.cc
@@ -0,0 +1,3149 @@
+/* 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 <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <assert.h>
+#include <regex.h> /* regcomp() */
+
+#include "util.h"
+#include "libiberty.h"
+#include "collctrl.h"
+#include "hwcdrv.h"
+//#include "hwcfuncs.h"
+
+#define SP_GROUP_HEADER "#analyzer experiment group"
+#define DD_MAXPATHLEN (MAXPATHLEN * 4) /* large, to build up data descriptor */
+
+/* If the system doesn't provide strsignal, we get it defined in
+ libiberty but no declaration is supplied. */
+#if !defined (HAVE_STRSIGNAL) && !defined (strsignal)
+extern const char *strsignal (int);
+#endif
+
+// _SC_CPUID_MAX is not available on 2.6/2.7
+#ifndef _SC_CPUID_MAX
+#define _SC_CPUID_MAX 517
+#endif
+
+const char *get_fstype (char *);
+
+Coll_Ctrl::Coll_Ctrl (int _interactive, bool _defHWC, bool _kernelHWC)
+{
+ char hostname[MAXPATHLEN];
+ long ncpumax;
+ interactive = _interactive;
+ defHWC = _defHWC;
+ kernelHWC = _kernelHWC;
+
+ /* set this host's parameters */
+ gethostname (hostname, 1023);
+ node_name = strdup (hostname);
+ char *p = strchr (node_name, (int) '.');
+ if (p != NULL)
+ *p = 0;
+ default_stem = strdup ("test");
+
+ /* get CPU count and processor clock rate */
+ ncpumax = sysconf (_SC_CPUID_MAX);
+ if (ncpumax == -1)
+ {
+ ncpus = sysconf (_SC_NPROCESSORS_CONF);
+ /* add 2048 to count, since on some systems CPUID does not start at zero */
+ ncpumax = ncpus + 2048;
+ }
+ ncpus = 0;
+ cpu_clk_freq = 0;
+
+ // On Linux, read /proc/cpuinfo to get CPU count and clock rate
+ // Note that parsing is different on SPARC and x86
+#if defined(sparc)
+ FILE *procf = fopen ("/proc/cpuinfo", "r");
+ if (procf != NULL)
+ {
+ char temp[1024];
+ while (fgets (temp, (int) sizeof (temp), procf) != NULL)
+ {
+ if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0'
+ && strncmp ((strchr (temp + 1, 'C')) ? strchr (temp + 1, 'C')
+ : (temp + 4), "ClkTck", 6) == 0)
+ {
+ ncpus++;
+ char *val = strchr (temp, ':');
+ if (val)
+ {
+ unsigned long long freq;
+ sscanf (val + 2, "%llx", &freq);
+ cpu_clk_freq = (unsigned int) (((double) freq) / 1000000.0 + 0.5);
+ }
+ else
+ cpu_clk_freq = 0;
+ }
+ }
+ fclose (procf);
+ }
+
+#elif defined(__aarch64__)
+ asm volatile("mrs %0, cntfrq_el0" : "=r" (cpu_clk_freq));
+ dbe_write (2, GTXT ("CPU clock frequency: %d\n"), cpu_clk_freq);
+
+#else
+ FILE *procf = fopen ("/proc/cpuinfo", "r");
+ if (procf != NULL)
+ {
+ char temp[1024];
+ while (fgets (temp, (int) sizeof (temp), procf) != NULL)
+ {
+ // x86 Linux
+ if (strncmp (temp, "processor", 9) == 0)
+ ncpus++;
+ else if (strncmp (temp, "cpu MHz", 7) == 0)
+ {
+ char *val = strchr (temp, ':');
+ cpu_clk_freq = val ? atoi (val + 1) : 0;
+ }
+ }
+ fclose (procf);
+ }
+#endif
+
+ /* check resolution of system clock */
+ sys_resolution = sysconf (_SC_CLK_TCK);
+ if (sys_resolution == 0)
+ sys_period = 10000;
+ else
+ sys_period = MICROSEC / (int) sys_resolution;
+
+ /* determine memory page size and number of pages */
+ npages = sysconf (_SC_PHYS_PAGES);
+ page_size = sysconf (_SC_PAGE_SIZE);
+
+ /* set default clock parameters */
+ hwcprof_enabled_cnt = 0; // must be set before calling determine_profile_params();
+ determine_profile_params (); // inits clk_params which is used by clock profiling AND HWCs
+ cpc_cpuver = CPUVER_UNDEFINED;
+
+ /* set default control values */
+ debug_mode = 0;
+#if defined(GPROFNG_JAVA_PROFILING)
+ java_mode = 1;
+#else
+ java_mode = 0;
+#endif
+ java_default = 1;
+ java_path = NULL;
+ java_args = NULL;
+ njava_args = 0;
+ follow_mode = FOLLOW_ON;
+ follow_default = 1;
+ follow_spec_usr = NULL;
+ follow_spec_cmp = NULL;
+ prof_idle = 1;
+ archive_mode = strdup ("on");
+ pauseresume_sig = 0;
+ sample_sig = 0;
+ uinterrupt = 0;
+ attach_pid = 0;
+ time_run = 0;
+ start_delay = 0;
+
+ /* clear the string pointers */
+ uexpt_name = NULL;
+ expt_name = NULL;
+ expt_dir = NULL;
+ base_name = NULL;
+ udir_name = NULL;
+ store_dir = NULL;
+ prev_store_dir = strdup ("");
+ store_ptr = NULL;
+ expt_group = NULL;
+ target_name = NULL;
+ data_desc = NULL;
+ lockname = NULL;
+ hwc_string = NULL;
+ project_home = NULL;
+ lockfd = -1;
+
+ /* set default data collection values */
+ enabled = 0;
+ opened = 0;
+ clkprof_enabled = 1;
+ clkprof_default = 1;
+ for (unsigned ii = 0; ii < MAX_PICS; ii++)
+ {
+ memset (&hwctr[ii], 0, sizeof (Hwcentry));
+ hwctr[ii].reg_num = -1;
+ }
+ hwcprof_default = 0;
+ if (defHWC == true)
+ {
+ setup_hwc ();
+ hwcprof_default = 1;
+ }
+ else // disable the default, and reset the counters
+ hwcprof_enabled_cnt = 0;
+ synctrace_enabled = 0;
+ synctrace_thresh = -1;
+ synctrace_scope = 0;
+ heaptrace_enabled = 0;
+ heaptrace_checkenabled = 0;
+ iotrace_enabled = 0;
+ count_enabled = 0;
+ Iflag = 0;
+ Nflag = 0;
+ sample_period = 1;
+ sample_default = 1;
+ size_limit = 0;
+ nofswarn = 0;
+ expno = 1;
+
+ // ensure that the default name is updated
+ // but don't print any message
+ (void) preprocess_names ();
+ (void) update_expt_name (false, false);
+}
+
+/* Copy constructor */
+Coll_Ctrl::Coll_Ctrl (Coll_Ctrl * cc)
+{
+ uinterrupt = 0;
+ interactive = cc->interactive;
+ defHWC = cc->defHWC;
+ kernelHWC = cc->kernelHWC;
+ node_name = strdup (cc->node_name);
+ default_stem = strdup (cc->default_stem);
+ ncpus = cc->ncpus;
+ cpu_clk_freq = cc->cpu_clk_freq;
+ npages = cc->npages;
+ page_size = cc->page_size;
+ cpc_cpuver = cc->cpc_cpuver;
+ debug_mode = cc->debug_mode;
+ java_mode = cc->java_mode;
+ java_default = cc->java_default;
+ java_path = NULL;
+ java_args = NULL;
+ njava_args = 0;
+ follow_mode = cc->follow_mode;
+ follow_default = cc->follow_default;
+ if (cc->follow_spec_usr)
+ {
+ follow_spec_usr = strdup (cc->follow_spec_usr);
+ follow_spec_cmp = strdup (cc->follow_spec_cmp);
+ }
+ else
+ {
+ follow_spec_usr = NULL;
+ follow_spec_cmp = NULL;
+ }
+ archive_mode = strdup (cc->archive_mode);
+ pauseresume_sig = cc->pauseresume_sig;
+ sample_sig = cc->sample_sig;
+ time_run = cc->time_run;
+ start_delay = cc->start_delay;
+ clk_params = cc->clk_params;
+ clkprof_enabled = cc->clkprof_enabled;
+ clkprof_default = cc->clkprof_default;
+ clkprof_timer = cc->clkprof_timer;
+ clkprof_timer_target = cc->clkprof_timer_target;
+
+ // copy HW counter information
+ hwcprof_default = cc->hwcprof_default;
+ hwcprof_enabled_cnt = cc->hwcprof_enabled_cnt;
+ if (cc->hwc_string != NULL)
+ hwc_string = strdup (cc->hwc_string);
+ else
+ hwc_string = NULL;
+ for (int i = 0; i < hwcprof_enabled_cnt; i++)
+ hwcentry_dup (&hwctr[i], &(cc->hwctr[i]));
+ project_home = cc->project_home ? strdup (cc->project_home) : NULL;
+ synctrace_enabled = cc->synctrace_enabled;
+ synctrace_thresh = cc->synctrace_thresh;
+ synctrace_scope = cc->synctrace_scope;
+ heaptrace_enabled = cc->heaptrace_enabled;
+ heaptrace_checkenabled = cc->heaptrace_checkenabled;
+ iotrace_enabled = cc->iotrace_enabled;
+ count_enabled = cc->count_enabled;
+ Iflag = cc->Iflag;
+ Nflag = cc->Nflag;
+ sample_period = cc->sample_period;
+ sample_default = cc->sample_default;
+ size_limit = cc->size_limit;
+ nofswarn = cc->nofswarn;
+
+ // these will get reset during preprocess_names()
+ expt_name = NULL;
+ expt_dir = NULL;
+ store_dir = NULL;
+ base_name = NULL;
+ expno = 1;
+
+ // these represent user settings
+ expt_group = NULL;
+ if (cc->expt_group != NULL)
+ expt_group = strdup (cc->expt_group);
+ uexpt_name = NULL;
+ if (cc->uexpt_name != NULL)
+ uexpt_name = strdup (cc->uexpt_name);
+ udir_name = NULL;
+ if (cc->udir_name != NULL)
+ udir_name = strdup (cc->udir_name);
+
+ /* clear the string pointers */
+ prev_store_dir = strdup ("");
+ store_ptr = NULL;
+ target_name = NULL;
+ data_desc = NULL;
+ lockname = NULL;
+ lockfd = -1;
+
+ /* set default data collection values */
+ enabled = cc->enabled;
+ opened = 0;
+ nofswarn = cc->nofswarn;
+ sys_resolution = cc->sys_resolution;
+ sys_period = cc->sys_period;
+
+ // ensure that the default name is updated
+ (void) preprocess_names ();
+ (void) update_expt_name (false, false);
+ build_data_desc ();
+}
+
+Coll_Ctrl::~Coll_Ctrl ()
+{
+ free (node_name);
+ free (expt_name);
+ free (expt_dir);
+ free (base_name);
+ free (udir_name);
+ free (store_dir);
+ free (store_ptr);
+ free (expt_group);
+ free (target_name);
+ free (data_desc);
+ free (lockname);
+ free (hwc_string);
+ free (project_home);
+ free (java_path);
+ hwcprof_enabled_cnt = 0;
+}
+
+/* set up the experiment */
+char *
+Coll_Ctrl::setup_experiment ()
+{
+ char *ret;
+ if (enabled == 0)
+ return NULL;
+ build_data_desc ();
+
+ /* create the experiment directory */
+ ret = create_exp_dir ();
+ if (ret != NULL)
+ return ret;
+
+ /* if an experiment-group, join it */
+ ret = join_group ();
+ if (ret != NULL)
+ {
+ remove_exp_dir ();
+ return ret;
+ }
+ /* all is OK, return 0 */
+ opened = 1;
+ return NULL;
+}
+
+void
+Coll_Ctrl::interrupt ()
+{
+ uinterrupt = 1;
+}
+
+char *
+Coll_Ctrl::enable_expt ()
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (cpu_clk_freq == 0)
+ return strdup (GTXT ("Can not determine CPU clock frequency.\n"));
+ if (sys_resolution == 0)
+ return strdup (GTXT ("System clock profile resolution can not be determined.\n"));
+ enabled = 1;
+ return NULL;
+}
+
+/* close the experiment */
+void
+Coll_Ctrl::close_expt ()
+{
+ opened = 0;
+ (void) update_expt_name (false, false);
+}
+
+/* close and delete the experiment */
+void
+Coll_Ctrl::delete_expt ()
+{
+ if (opened == 0)
+ return;
+ remove_exp_dir ();
+
+ /* The order of removing the directory and closing
+ * the experiment may seem unnatural, but it's not.
+ * We do need to update names when we close the experiment
+ * (actually Coll_Ctrl object) and we can't remove anything
+ * after that.
+ */
+ close_expt ();
+}
+
+// Check the experiment settings for consistency. Returns NULL if OK,
+// or an error message if there are invalid combinations of settings
+char *
+Coll_Ctrl::check_consistency ()
+{
+ /* check for Java arguments, but not Java profiling */
+ if (java_args != NULL && java_mode == 0)
+ return strdup (GTXT ("Java arguments can not be set if Java profiling is not enabled.\n"));
+
+ /* if count data, no other data is allowed */
+ if (count_enabled != 0
+ && ((clkprof_default != 1 && clkprof_enabled != 0)
+ || hwcprof_enabled_cnt != 0 || synctrace_enabled != 0
+ || heaptrace_enabled != 0 || iotrace_enabled != 0))
+ return strdup (GTXT ("Count data cannot be collected along with any other data.\n"));
+
+ /* if count data, various other options are not allowed */
+ if (count_enabled != 0
+ && ((java_mode != 0 && java_default != 1)
+ || java_args != NULL || debug_mode != 0
+ || (follow_mode != 0 && follow_default != 1)
+ || pauseresume_sig != 0 || sample_sig != 0
+ || (sample_default != 1 && sample_period != 0) || time_run != 0))
+ return strdup (GTXT ("Count data cannot be collected with any of -F -S -y -l -j -J -x -t .\n"));
+ /* if not count data, I and N options are not allowed */
+ if (count_enabled == 0 && (Iflag != 0 || Nflag != 0))
+ return strdup (GTXT ("-I or -N can only be specified with count data.\n"));
+ return NULL;
+}
+
+char *
+Coll_Ctrl::check_expt (char **warn)
+{
+ char *ret;
+ *warn = NULL;
+ ret = check_consistency ();
+ if (ret != NULL) /* something is wrong, return the error */
+ return ret;
+ /* check for heaptrace and java -- warn that it covers native allocations only */
+ if (heaptrace_enabled == 1 && java_mode == 1 && java_default == 0)
+ *warn = strdup (GTXT ("Note: Heap profiling will only trace native allocations, not Java allocations.\n"));
+
+ /* if no profiling data selected, warn the user */
+ if (clkprof_enabled == 0 && hwcprof_enabled_cnt == 0 && synctrace_enabled == 0
+ && heaptrace_enabled == 0 && iotrace_enabled == 0 && count_enabled == 0)
+ *warn = strdup (GTXT ("Warning: No function level data requested; only statistics will be collected.\n\n"));
+ build_data_desc ();
+
+ /* verify that the directory exists */
+ struct stat statbuf;
+ if (stat (store_dir, &statbuf) != 0)
+ return dbe_sprintf (GTXT ("Store directory %s is not accessible: %s\n"),
+ store_dir, strerror (errno));
+ if (access (store_dir, W_OK) != 0)
+ return dbe_sprintf (GTXT ("Store directory %s is not writeable: %s\n"),
+ store_dir, strerror (errno));
+
+ /* if an experiment-group, verify that it can be written */
+ ret = check_group ();
+ if (ret != NULL)
+ return ret;
+ return NULL;
+}
+
+char *
+Coll_Ctrl::show (int i)
+{
+ char UEbuf[4096];
+ UEbuf[0] = 0;
+ if (i == 0)
+ {
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("Collection parameters:\n"));
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT (" experiment enabled\n"));
+ }
+ if (target_name != NULL)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\ttarget = %s\n"), target_name);
+ if (uexpt_name != NULL)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tuser_expt_name = %s\n"), uexpt_name);
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\texpt_name = %s\n"),
+ ((expt_name != NULL) ? expt_name : NTXT ("<NULL>")));
+ if (udir_name != NULL)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdir_name = %s\n"), udir_name);
+ if (expt_group != NULL)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\texpt_group = %s\n"), expt_group);
+ if (debug_mode == 1)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdebug_mode enabled\n"));
+ if (clkprof_enabled != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tclock profiling enabled, %.3f millisec.\n"),
+ (double) (clkprof_timer) / 1000.);
+ if (synctrace_enabled != 0)
+ {
+ if (synctrace_thresh < 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tsynchronization tracing enabled, threshold: calibrate; "));
+ else if (synctrace_thresh == 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tsynchronization tracing enabled, threshold: all; "));
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tsynchronization tracing enabled, threshold: %d micros.; "), synctrace_thresh);
+ switch (synctrace_scope)
+ {
+ case SYNCSCOPE_NATIVE:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("Native-APIs\n"));
+ break;
+ case SYNCSCOPE_JAVA:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("Java-APIs\n"));
+ break;
+ case SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("Native- and Java-APIs\n"));
+ break;
+ default:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("ERR -- unexpected synctrace_scope %d\n"), synctrace_scope);
+ break;
+ }
+ }
+ if (hwcprof_enabled_cnt != 0)
+ {
+ char ctrbuf[MAXPATHLEN];
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\thardware counter profiling%s enabled:\n"),
+ (hwcprof_default == 1 ? GTXT (" (default)") : ""));
+ for (int ii = 0; ii < hwcprof_enabled_cnt; ii++)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t %u. %s\n"), ii + 1,
+ hwc_hwcentry_specd_string (ctrbuf, MAXPATHLEN, &hwctr[ii]));
+ }
+ if (heaptrace_enabled != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\theap tracing enabled, %s\n"),
+ (heaptrace_checkenabled == 0 ? GTXT ("no checking") :
+ (heaptrace_checkenabled == 1 ? GTXT ("over/underrun checking") :
+ GTXT ("over/underrun checking and pattern storing"))));
+ if (iotrace_enabled != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tI/O tracing enabled\n"));
+ switch (count_enabled)
+ {
+ case 0:
+ break;
+ case 1:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tcount data enabled\n"));
+ break;
+ case -1:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tstatic count data will be generated (for a.out only)\n"));
+ break;
+ }
+ switch (follow_mode)
+ {
+ case FOLLOW_ON:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdescendant processes will be followed\n"));
+ break;
+ case FOLLOW_ALL:
+ if (follow_spec_usr && follow_spec_cmp)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\texperiments will be recorded for descendant processes that match pattern '%s'\n"),
+ follow_spec_usr);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdescendant processes will all be followed\n"));
+ break;
+ case FOLLOW_NONE:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdescendant processes will not be followed\n"));
+ break;
+ default:
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tfollowing descendant processes: <UNKNOWN>\n"));
+ break;
+ }
+ if (java_mode == 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tjava profiling disabled\n"));
+ if (pauseresume_sig != 0)
+ {
+ const char *buf = strsignal (pauseresume_sig);
+ if (buf != NULL)
+ {
+ if (pauseresume_pause == 1)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tpause-resume (delayed initialization) signal %s (%d) -- paused\n"), buf, pauseresume_sig);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tpause-resume (delayed initialization) signal %s (%d)\n"), buf, pauseresume_sig);
+ }
+ else
+ {
+ if (pauseresume_pause == 1)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tpause-resume (delayed initialization) signal %d -- paused\n"), pauseresume_sig);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tpause-resume (delayed initialization) signal %d\n"), pauseresume_sig);
+ }
+ }
+ if (sample_sig != 0)
+ {
+ const char *buf = strsignal (sample_sig);
+ if (buf != NULL)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tsample signal %s (%d)\n"), buf, sample_sig);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tsample signal %d\n"), sample_sig);
+ }
+ if (time_run != 0 || start_delay != 0)
+ {
+ if (start_delay != 0)
+ {
+ if (time_run != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdata-collection duration, %d-%d secs.\n"), start_delay, time_run);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdata-collection duration, %d- secs.\n"), start_delay);
+ }
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdata-collection duration, %d secs.\n"), time_run);
+ }
+ if (sample_period != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tperiodic sampling, %d secs.\n"), sample_period);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tno periodic sampling\n"));
+ if (size_limit != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\texperiment size limit %d MB.\n"), size_limit);
+ else
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tno experiment size limit set\n"));
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\texperiment archiving: -a %s\n"), archive_mode);
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\tdata descriptor: \"%s\"\n"),
+ ((data_desc != NULL) ? data_desc : NTXT ("<NULL>")));
+#if 0
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t expt_dir: %s\n"),
+ ((expt_dir != NULL) ? expt_dir : NTXT ("<NULL>")));
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t base_name: %s\n"),
+ ((base_name != NULL) ? base_name : NTXT ("<NULL>")));
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t store_dir: %s\n"),
+ ((store_dir != NULL) ? store_dir : NTXT ("<NULL>")));
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t store_ptr: %s\n"),
+ ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")));
+#endif
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t\thost: `%s', ncpus = %d, clock frequency %d MHz.\n"),
+ ((node_name != NULL) ? node_name : NTXT ("<NULL>")),
+ (int) ncpus, (int) cpu_clk_freq);
+ if (npages > 0)
+ {
+ long long memsize = ((long long) npages * (long long) page_size) / (1024 * 1024);
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("\t\tmemory: %ld pages @ %ld bytes = %lld MB.\n"),
+ npages, page_size, memsize);
+ }
+ return strdup (UEbuf);
+}
+
+#define MAX_COLLECT_ARGS 100
+
+char **
+Coll_Ctrl::get_collect_args ()
+{
+ char buf[DD_MAXPATHLEN];
+ char **p;
+ char **argv = (char **) calloc (MAX_COLLECT_ARGS, sizeof (char *));
+ if (argv == NULL) // poor way of dealing with calloc failure
+ abort ();
+ p = argv;
+ *p++ = strdup ("collect");
+ if (debug_mode == 1)
+ *p++ = strdup ("-x");
+ if (clkprof_enabled != 0)
+ {
+ *p++ = strdup ("-p");
+ snprintf (buf, sizeof (buf), "%du", clkprof_timer);
+ *p++ = strdup (buf);
+ }
+ if (hwcprof_enabled_cnt > 0)
+ {
+ *buf = 0;
+ *p++ = strdup ("-h");
+ for (int ii = 0; ii < hwcprof_enabled_cnt; ii++)
+ {
+ char*rateString = hwc_rate_string (&hwctr[ii], 1); //"1" is for temporary goldfile compatibility. TBR YXXX!!
+ snprintf (buf + strlen (buf), sizeof (buf) - strlen (buf),
+ "%s%s,%s%s", ii ? "," : "", hwctr[ii].name,
+ rateString ? rateString : "",
+ (ii + 1 < hwcprof_enabled_cnt) ? "," : "");
+ free (rateString);
+ }
+ if (strlen (buf) + 1 >= sizeof (buf))
+ abort ();
+ *p++ = strdup (buf);
+ }
+ if (heaptrace_enabled != 0)
+ {
+ *p++ = strdup ("-H");
+ *p++ = strdup ("on");
+ }
+ if (iotrace_enabled != 0)
+ {
+ *p++ = strdup ("-i");
+ *p++ = strdup ("on");
+ }
+ if (synctrace_enabled != 0)
+ {
+ *p++ = strdup ("-s");
+ if (synctrace_thresh < 0)
+ *p++ = strdup ("calibrate");
+ else if (synctrace_thresh < 0)
+ *p++ = strdup ("all");
+ else
+ *p++ = dbe_sprintf ("%d", synctrace_thresh);
+ *p++ = dbe_sprintf (",%d", synctrace_scope);
+ }
+ if (follow_mode != 0)
+ {
+ *p++ = strdup ("-F");
+ char * fs = get_follow_usr_spec ();
+ if (fs)
+ *p++ = strdup (fs);
+ else
+ {
+ switch (get_follow_mode ())
+ {
+ case FOLLOW_ON:
+ *p++ = strdup ("on");
+ break;
+ case FOLLOW_ALL:
+ *p++ = strdup ("all");
+ break;
+ case FOLLOW_NONE:
+ default:
+ *p++ = strdup ("off");
+ break;
+ }
+ }
+ }
+ *p++ = strdup ("-a");
+ *p++ = strdup (get_archive_mode ());
+ if (java_mode != 0)
+ {
+ *p++ = strdup ("-j");
+ *p++ = strdup ("on");
+ }
+ if (pauseresume_sig != 0)
+ {
+ *p++ = strdup ("-y");
+ *p++ = dbe_sprintf ("%d%s", pauseresume_sig,
+ (pauseresume_pause == 0 ? ",r" : ""));
+ }
+ if (sample_sig != 0)
+ {
+ *p++ = strdup ("-l");
+ *p++ = dbe_sprintf ("%d", sample_sig);
+ }
+ if (sample_period != 0)
+ {
+ *p++ = strdup ("-S");
+ *p++ = dbe_sprintf ("%d", sample_period);
+ }
+ if (size_limit != 0)
+ {
+ *p++ = strdup ("-L");
+ *p++ = dbe_sprintf ("%d", size_limit);
+ }
+ if (expt_group != NULL)
+ {
+ *p++ = strdup ("-g");
+ *p++ = strdup (expt_group);
+ }
+ if (udir_name != 0)
+ {
+ *p++ = strdup ("-d");
+ *p++ = strdup (udir_name);
+ }
+ if (expt_name != 0)
+ {
+ *p++ = strdup ("-o");
+ *p++ = strdup (expt_name);
+ }
+ if (p - argv >= MAX_COLLECT_ARGS) // argument list too small -- fatal error
+ abort ();
+ return argv;
+}
+
+char *
+Coll_Ctrl::show_expt ()
+{
+ if (enabled == 0)
+ return NULL;
+ char UEbuf[4096];
+ UEbuf[0] = 0;
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("Creating experiment directory %s (Process ID: %ld) ...\n"),
+ ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")), (long) getpid ());
+ char *caller = getenv ("SP_COLLECTOR_FROM_GUI"); // Collector from GUI
+ if (caller != NULL) // Print non-localized message
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ NTXT ("\nCreating experiment directory %s (Process ID: %ld) ...\n"),
+ ((store_ptr != NULL) ? store_ptr : NTXT ("<NULL>")), (long) getpid ());
+#if 0
+ char *fstype = get_fstype (store_dir);
+ if ((fstype != NULL) && (nofswarn == 0))
+ {
+ // only warn if clock or hwc profiling is turned on
+ if (clkprof_enabled || hwcprof_enabled_cnt != 0)
+ snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf),
+ GTXT ("this experiment is being recorded to a file system \nof type \"%s\", which may distort the measured performance."),
+ fstype);
+ }
+#endif
+ return strdup (UEbuf);
+}
+
+void
+Coll_Ctrl::set_clk_params (int min, int res, int max, int hi, int norm, int lo)
+{
+ clk_params.min = min;
+ clk_params.res = res;
+ clk_params.max = max;
+ clk_params.hival = hi;
+ clk_params.normval = norm;
+ clk_params.lowval = lo;
+ set_clkprof_timer_target (clk_params.normval); // note: requires clk_params to be initialized!
+}
+
+char *
+Coll_Ctrl::reset_clkprof (int val)
+{
+ if (val != clkprof_timer)
+ {
+ // profiler has had to reset to a different value; warn user
+ char *msg = dbe_sprintf (
+ GTXT ("Warning: Clock profiling timer reset from %.3f millisec. to %.3f millisec. as required by profiling driver\n\n"),
+ (double) (clkprof_timer) / 1000., (double) (val) / 1000.);
+ adjust_clkprof_timer (val);
+ return msg;
+ }
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_clkprof (const char *string, char** warn)
+{
+ int ticks;
+ int nclkprof_timer;
+ int prevclkprof_enabled;
+ int prevclkprof_default;
+ *warn = NULL;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ /* if the first character is a +, warn user that it is no longer supported */
+ if (string[0] == '+')
+ return strdup (GTXT ("Warning: clock-based memoryspace and dataspace profiling is no longer supported\n"));
+ if (strcmp (string, "off") == 0)
+ {
+ clkprof_enabled = 0;
+ clkprof_default = 0;
+ return NULL;
+ }
+ else if (string == NULL || strcmp (string, "on") == 0)
+ nclkprof_timer = clk_params.normval;
+ else if (strcmp (string, "lo") == 0 || strcmp (string, "low") == 0)
+ nclkprof_timer = clk_params.lowval;
+ else if (strcmp (string, "hi") == 0 || strcmp (string, "high") == 0
+ || strcmp (string, "h") == 0)
+ nclkprof_timer = clk_params.hival;
+ else
+ {
+ /* the remaining string should be a number > 0 */
+ char *endchar = NULL;
+ double dval = strtod (string, &endchar);
+ if (*endchar == 'm' || *endchar == 0) /* user specified milliseconds */
+ dval = dval * 1000.;
+ else if (*endchar == 'u') /* user specified microseconds */
+ dval = dval;
+ else
+ return dbe_sprintf (GTXT ("Unrecognized clock-profiling interval `%s'\n"), string);
+ nclkprof_timer = (int) (dval + 0.5);
+ }
+ // we now have the proposed value; ensure it's within limits
+ if (nclkprof_timer <= 0)
+ return dbe_sprintf (GTXT ("Unrecognized clock-profiling interval `%s'\n"), string);
+
+ // Check consistency with experiment
+ prevclkprof_enabled = clkprof_enabled;
+ prevclkprof_default = clkprof_default;
+ clkprof_enabled = 1;
+ clkprof_default = 0;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ clkprof_default = prevclkprof_default;
+ clkprof_enabled = prevclkprof_enabled;
+ return ret;
+ }
+ int ref_nclkprof_timer = nclkprof_timer;
+
+ // check for minimum value
+ if (nclkprof_timer < clk_params.min)
+ {
+ /* value too small, use minimum value, with warning */
+ *warn = dbe_sprintf (
+ GTXT ("Warning: Clock profiling at %.3f millisec. interval is not supported on this system; minimum %.3f millisec. used\n"),
+ (double) (nclkprof_timer) / 1000., (double) (clk_params.min) / 1000.);
+ nclkprof_timer = clk_params.min;
+ }
+
+ // check for maximum value
+ if (nclkprof_timer > clk_params.max)
+ {
+ *warn = dbe_sprintf (
+ GTXT ("Clock profiling at %.3f millisec. interval is not supported on this system; maximum %.3f millisec. used\n"),
+ (double) (nclkprof_timer) / 1000., (double) (clk_params.max) / 1000.);
+ nclkprof_timer = clk_params.max;
+ }
+
+ /* see if setting is a multiple of the period */
+ if (nclkprof_timer > clk_params.res)
+ {
+ ticks = ((nclkprof_timer / clk_params.res) * clk_params.res);
+ if (ticks != nclkprof_timer)
+ {
+ /* no, we need to reset to a multiple */
+ *warn = dbe_sprintf (
+ GTXT ("Clock profile interval rounded from %.3f to %.3f (system resolution = %.3f) millisec."),
+ (double) (nclkprof_timer) / 1000., (double) (ticks) / 1000.,
+ (double) (clk_params.res) / 1000.);
+ nclkprof_timer = ticks;
+ }
+ }
+
+ // limit reference "target" rate. Target rate is also used for HWCS.
+ if (ref_nclkprof_timer > PROFINT_MAX)
+ ref_nclkprof_timer = PROFINT_MAX;
+ if (ref_nclkprof_timer < PROFINT_MIN)
+ ref_nclkprof_timer = PROFINT_MIN;
+ set_clkprof_timer_target (ref_nclkprof_timer);
+ adjust_clkprof_timer (nclkprof_timer);
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_synctrace (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ char *comma_p = NULL;
+ if (string == NULL)
+ {
+ /* no argument provided, use default: calibrate and native */
+ synctrace_enabled = 1;
+ synctrace_thresh = -1;
+ synctrace_scope = SYNCSCOPE_NATIVE;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ synctrace_enabled = 0;
+ return ret;
+ }
+ return NULL;
+ }
+ char *val = strdup (string);
+ /* see if there's a comma in the string */
+ char *next = strchr (val, (int) ',');
+ if (next != NULL)
+ {
+ /* remember where the comma was */
+ comma_p = next;
+
+ /* set the scope based on the characters following the comma */
+ synctrace_scope = 0;
+ next++;
+ while (*next != 0)
+ {
+ if (*next == 'n')
+ synctrace_scope |= SYNCSCOPE_NATIVE;
+ else if (*next == 'j')
+ synctrace_scope |= SYNCSCOPE_JAVA;
+ else
+ return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string);
+ next++;
+ }
+ if (synctrace_scope == 0)
+ synctrace_scope = SYNCSCOPE_NATIVE;
+ /* clear the comma for the threshold determination */
+ *comma_p = 0;
+ }
+ else /* no ",<scope>" -- default to native and Java */
+ synctrace_scope = SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA;
+ if (!strlen (val) || !strcmp (val, "calibrate") || !strcmp (val, "on"))
+ {
+ /* use default: calibrate and native */
+ synctrace_enabled = 1;
+ synctrace_thresh = -1;
+ free (val);
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ synctrace_enabled = 0;
+ return ret;
+ }
+ return NULL;
+ }
+ if (strcmp (val, "off") == 0)
+ {
+ synctrace_enabled = 0;
+ free (val);
+ return NULL;
+ }
+ if (strcmp (val, "all") == 0)
+ {
+ /* set to record all events */
+ synctrace_thresh = 0;
+ synctrace_enabled = 1;
+ char *ret = check_consistency ();
+ free (val);
+ if (ret != NULL)
+ {
+ synctrace_enabled = 0;
+ return ret;
+ }
+ return NULL;
+ }
+ /* the remaining string should be a number >= 0 */
+ char *endchar = NULL;
+ int tval = (int) strtol (val, &endchar, 0);
+ free (val);
+ if (*endchar != 0 || tval < 0)
+ {
+ /* invalid setting */
+ /* restore the comma, if it was zeroed out */
+ if (comma_p != NULL)
+ *comma_p = ',';
+ return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string);
+ }
+ synctrace_thresh = tval;
+ synctrace_enabled = 1;
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_heaptrace (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0)
+ {
+ heaptrace_enabled = 1;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ heaptrace_enabled = 0;
+ return ret;
+ }
+ return NULL;
+ }
+ if (strcmp (string, "off") == 0)
+ {
+ heaptrace_enabled = 0;
+ return NULL;
+ }
+#if 0
+ if (strcmp (string, "check") == 0)
+ {
+ /* set to check for over/underruns */
+ heaptrace_checkenabled = 1;
+ heaptrace_enabled = 1;
+ return NULL;
+ }
+ if (strcmp (string, "clear") == 0)
+ {
+ /* set to check for over/underruns, and store patterns */
+ heaptrace_checkenabled = 2;
+ heaptrace_enabled = 1;
+ return NULL;
+ }
+#endif
+ return dbe_sprintf (GTXT ("Unrecognized heap tracing parameter `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_iotrace (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0)
+ {
+ iotrace_enabled = 1;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ iotrace_enabled = 0;
+ return ret;
+ }
+ return NULL;
+ }
+ if (strcmp (string, "off") == 0)
+ {
+ iotrace_enabled = 0;
+ return NULL;
+ }
+ return dbe_sprintf (GTXT ("Unrecognized I/O tracing parameter `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_count (const char *string)
+{
+ int ret = -1;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0 || strcmp (string, "off") == 0)
+ {
+ count_enabled = 0;
+ ret = 0;
+ }
+ if (strcmp (string, "on") == 0)
+ {
+ count_enabled = 1;
+ char *cret = check_consistency ();
+ if (cret != NULL)
+ {
+ count_enabled = 0;
+ return cret;
+ }
+ ret = 0;
+ }
+ if (strcmp (string, "static") == 0)
+ {
+ count_enabled = -1;
+ char *cret = check_consistency ();
+ if (cret != NULL)
+ {
+ count_enabled = 0;
+ return cret;
+ }
+ ret = 0;
+ }
+ if (ret == 0)
+ {
+ if (count_enabled != 0)
+ {
+ /* ensure that sample period is 0, if set by default */
+ if (sample_default == 1)
+ sample_period = 0;
+ /* ensure that clock profiling is off, if set by default */
+ if (clkprof_default == 1)
+ {
+ clkprof_default = 0;
+ clkprof_enabled = 0;
+ }
+ if (hwcprof_default == 1)
+ hwcprof_default = 0;
+ }
+ return NULL;
+ }
+ return dbe_sprintf (GTXT ("Unrecognized count parameter `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_time_run (const char *valarg)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (valarg == NULL) /* invalid setting */
+ return strdup (GTXT ("time parameter can not be NULL\n"));
+ /* the string should be a number >= 0 */
+ int prev_start_delay = start_delay;
+ int prev_time_run = time_run;
+ const char *endchar = valarg;
+ char *newchar = NULL;
+ int val = 0;
+ if (*endchar != '-')
+ {
+ val = (int) strtol (endchar, &newchar, 0);
+ endchar = newchar;
+ if (val < 0)
+ return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg);
+ if (*endchar == 'm')
+ {
+ val = val * 60; /* convert to seconds */
+ endchar++;
+ }
+ else if (*endchar == 's') /* no conversion needed */
+ endchar++;
+ if (*endchar == 0)
+ {
+ time_run = val;
+ return NULL;
+ }
+ else if (*endchar != '-')
+ return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg);
+ }
+ /* a second number is provided */
+ start_delay = val;
+ endchar++;
+ val = (int) strtol (endchar, &newchar, 0);
+ endchar = newchar;
+ if (val < 0)
+ {
+ start_delay = prev_start_delay;
+ return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg);
+ }
+ if (*endchar == 'm')
+ {
+ val = val * 60; /* convert to seconds */
+ endchar++;
+ }
+ else if (*endchar == 's') /* no conversion needed */
+ endchar++;
+ if (*endchar != 0)
+ {
+ start_delay = prev_start_delay;
+ return dbe_sprintf (GTXT ("Unrecognized time parameter `%s'\n"), valarg);
+ }
+ time_run = val;
+ if (time_run != 0 && start_delay >= time_run)
+ {
+ start_delay = prev_start_delay;
+ return dbe_sprintf (GTXT ("Invalid time parameter `%s': start time must be earlier than end time\n"), valarg);
+ }
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ start_delay = prev_start_delay;
+ time_run = prev_time_run;
+ return ret;
+ }
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_attach_pid (char *valarg)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (valarg == NULL)
+ return strdup (GTXT ("Specified PID can not be NULL\n"));
+
+ /* the string should be a number corresponding to an active process' pid */
+ char *endchar = NULL;
+ int val = (int) strtol (valarg, &endchar, 0);
+ if (*endchar != 0 || val < 0)
+ return dbe_sprintf (GTXT ("Invalid process pid `%s'\n"), valarg);
+ int prev_attach_pid = attach_pid;
+ attach_pid = val;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ attach_pid = prev_attach_pid;
+ return ret;
+ }
+ return NULL;
+}
+
+void
+Coll_Ctrl::free_hwc_fields (Hwcentry * tmpctr)
+{
+ if (tmpctr->name != NULL)
+ free (tmpctr->name);
+ if (tmpctr->int_name != NULL)
+ free (tmpctr->int_name);
+ memset (tmpctr, 0, sizeof (Hwcentry));
+ tmpctr->reg_num = -1;
+}
+
+void
+Coll_Ctrl::hwcentry_dup (Hwcentry *hnew, Hwcentry *_hwc)
+{
+ *hnew = *_hwc;
+ if (_hwc->name != NULL)
+ hnew->name = strdup (_hwc->name);
+ else
+ hnew->name = NULL;
+ if (_hwc->int_name != NULL)
+ hnew->int_name = strdup (_hwc->int_name);
+ else
+ hnew->int_name = NULL;
+ if (_hwc->metric != NULL)
+ hnew->metric = strdup (_hwc->metric);
+ else
+ hnew->metric = NULL;
+ if (_hwc->short_desc != NULL)
+ hnew->short_desc = strdup (_hwc->short_desc);
+ else
+ hnew->short_desc = NULL;
+ if (_hwc->reg_list != NULL)
+ {
+ hnew->reg_list = (regno_t*) malloc (sizeof (regno_t*) * MAX_PICS);
+ // poor way of dealing with malloc failure
+ if (hnew->reg_list)
+ {
+ for (int i = 0; i < MAX_PICS; i++)
+ {
+ hnew->reg_list[i] = _hwc->reg_list[i];
+ if (hnew->reg_list[i] == REGNO_ANY)
+ break;
+ }
+ }
+ }
+}
+
+// Routine to initialize the HWC tables, set up the default experiment, etc.
+void
+Coll_Ctrl::setup_hwc ()
+{
+ static bool is_hwc_setup = false;
+ if (is_hwc_setup == true)
+ return;
+ // try to set the default counters
+ is_hwc_setup = true;
+ set_hwcdefault ();
+}
+
+hrtime_t
+Coll_Ctrl::clkprof_timer_2_hwcentry_min_time (int target_clkprof_usec)
+{
+ hrtime_t hwc_nanosec;
+ if (target_clkprof_usec == clk_params.normval)
+ hwc_nanosec = HWCTIME_ON;
+ else if (target_clkprof_usec == clk_params.lowval)
+ hwc_nanosec = HWCTIME_LO;
+ else if (target_clkprof_usec == clk_params.hival)
+ hwc_nanosec = HWCTIME_HI;
+ else
+ hwc_nanosec = 1000LL * target_clkprof_usec; // nanoseconds
+ return hwc_nanosec;
+}
+
+void
+Coll_Ctrl::set_clkprof_timer_target (int microseconds)
+{
+ clkprof_timer = microseconds;
+ clkprof_timer_target = microseconds;
+ hrtime_t hwc_min_time_nanosec = clkprof_timer_2_hwcentry_min_time (microseconds);
+ for (int ii = 0; ii < hwcprof_enabled_cnt; ii++)
+ {
+ hwctr[ii].min_time_default = hwc_min_time_nanosec;
+ hwc_update_val (&hwctr[ii]);
+ }
+}
+
+void
+Coll_Ctrl::adjust_clkprof_timer (int use)
+{
+ clkprof_timer = use;
+}
+
+/* set HWC counter set from a string */
+char * /* return an error string */
+Coll_Ctrl::set_hwcstring (const char *string, char **warnmsg)
+{
+ *warnmsg = NULL;
+ if (string == NULL || strcmp (string, "off") == 0)
+ {
+ hwcprof_enabled_cnt = 0;
+ return NULL;
+ }
+ setup_hwc ();
+ int old_cnt = hwcprof_enabled_cnt;
+ int old_hwcprof_default = hwcprof_default;
+
+ /* reset any previous count to zero */
+ hwcprof_enabled_cnt = 0;
+ char *ret = add_hwcstring (string, warnmsg);
+ if (ret != NULL)
+ {
+ // restore previous setting
+ hwcprof_enabled_cnt = old_cnt;
+ hwcprof_default = old_hwcprof_default;
+ }
+ return ret;
+}
+
+/* add additional HWC counters to counter set from string */
+char * /* return an error string */
+Coll_Ctrl::add_hwcstring (const char *string, char **warnmsg)
+{
+ *warnmsg = NULL;
+ if (string == NULL || strcmp (string, "off") == 0)
+ {
+ hwcprof_enabled_cnt = 0;
+ return NULL;
+ }
+ setup_hwc ();
+ int rc = 0;
+ int old_cnt = hwcprof_enabled_cnt;
+ int prev_cnt = hwcprof_enabled_cnt;
+ // int old_hwcprof_default = hwcprof_default;
+ char UEbuf[MAXPATHLEN * 5];
+ int UEsz;
+ Hwcentry tmpctr[MAX_PICS];
+ Hwcentry * ctrtable[MAX_PICS];
+ char *emsg;
+ char *wmsg;
+ UEbuf[0] = 0;
+ UEsz = sizeof (UEbuf);
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (hwcprof_default == 0)
+ {
+ /* Copy the counters already defined */
+ for (int ii = 0; ii < prev_cnt; ii++)
+ tmpctr[ii] = hwctr[ii];
+ }
+ else /* the previously-defined counters were defaulted; don't copy them */
+ prev_cnt = 0;
+
+ /* look up the CPU version */
+ cpc_cpuver = hwc_get_cpc_cpuver ();
+ if (string && *string)
+ {
+ /* lookup counters */
+ /* set up a pointer array */
+ for (unsigned ii = 0; ii < MAX_PICS; ii++)
+ ctrtable[ii] = &tmpctr[ii];
+ hrtime_t global_min_time = clkprof_timer_2_hwcentry_min_time (clkprof_timer_target);
+ rc = hwc_lookup (kernelHWC, global_min_time, string, &ctrtable[prev_cnt], MAX_PICS - prev_cnt, &emsg, &wmsg);
+ if (wmsg != NULL)
+ *warnmsg = wmsg;
+ if (rc < 0)
+ return emsg;
+ /* set count for sum of old and new counters */
+ rc = rc + prev_cnt;
+ }
+
+ /* even though the actual hwctr[] array is not updated, we can check consistency */
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ hwcprof_enabled_cnt = old_cnt;
+ return ret;
+ }
+
+ /* finally, validate the full counter set */
+ emsg = hwc_validate_ctrs (kernelHWC, ctrtable, rc);
+ if (emsg != NULL)
+ {
+ hwcprof_enabled_cnt = old_cnt;
+ return emsg;
+ }
+
+ /* success, update real counters and the string for them */
+ /* turn off the default */
+ hwcprof_default = 0;
+ hwcprof_enabled_cnt = rc;
+ free (hwc_string);
+ for (int ii = 0; ii < hwcprof_enabled_cnt; ii++)
+ {
+ /* shallow copy of new counters */
+ hwctr[ii] = tmpctr[ii];
+ char *rateString = hwc_rate_string (&hwctr[ii], 0);
+ snprintf (UEbuf + strlen (UEbuf), UEsz - strlen (UEbuf),
+ NTXT (",%s,%s"), hwctr[ii].name,
+ rateString ? rateString : "");
+ free (rateString);
+ }
+ /* now duplicate that string, skipping the leading comma */
+ hwc_string = strdup (&UEbuf[1]);
+ return NULL;
+}
+
+/* add default HWC counters to counter set with resolution (on, hi, or lo) */
+/* Note that the resultion will also be used to set the clock-profiling default */
+char * /* return an error string */
+Coll_Ctrl::add_default_hwcstring (const char *resolution, char **warnmsg, bool add, bool forKernel)
+{
+ setup_hwc ();
+ *warnmsg = NULL;
+ char *def_string = hwc_get_default_cntrs2 (forKernel, 1);
+ if (def_string == NULL)
+ {
+ /* no string defined, format and return an error message */
+ char cpuname[128];
+ hwc_get_cpuname (cpuname, sizeof (cpuname));
+ return dbe_sprintf (GTXT ("No default HW counter set is defined for %s\n"), cpuname);
+ }
+ int len = strlen (def_string);
+ if (len == 0)
+ {
+ /* string zero-length, meaning default counters can't be used */
+ char cpuname[128];
+ hwc_get_cpuname (cpuname, sizeof (cpuname));
+ return dbe_sprintf (GTXT ("HW counter set for %s cannot be loaded on this system\n"), cpuname);
+ }
+ /* allocate return string */
+ int retsize = 2 * len + 10;
+ char *ret = (char *) malloc (retsize);
+ if (ret == NULL)
+ return strdup (GTXT ("internal error formating HW counter set; malloc failed\n"));
+ *ret = 0;
+ char *retp = ret;
+ char *stringp = def_string;
+ int first = 1;
+ char *hwc_defaultx = strdup (def_string);
+
+ /* now massage the string in order to insert resolution for each counter */
+ for (;;)
+ {
+ /* find the next comma */
+ char * next;
+ char *nextp;
+ if (first == 1)
+ nextp = stringp;
+ else
+ nextp = stringp + 1;
+ first = 0;
+ if ((next = strchr (nextp, (int) ',')) != NULL)
+ {
+ if (next == nextp)
+ {
+ /* next counter is zero-length -- invalid string */
+ char cpuname[128];
+ hwc_get_cpuname (cpuname, sizeof (cpuname));
+ free (ret);
+ ret = dbe_sprintf (GTXT ("HW counter set for %s, \"%s\", format error\n"), cpuname, hwc_defaultx);
+ free (hwc_defaultx);
+ return ret;
+ }
+ /* another field found */
+ *next = 0;
+ char nextc = *(next + 1);
+ if ((nextc == 0) || (nextc == ','))
+ {
+ /* either ,, between fields, or string ends in comma */
+ /* append the string */
+ strncat (retp, stringp, (retsize - strlen (retp) - 1));
+ strncat (retp, ",", (retsize - strlen (retp) - 1));
+ strncat (retp, resolution, (retsize - strlen (retp) - 1));
+ if (nextc == 0) /* string ended in comma; we're done */
+ break;
+ }
+ else
+ {
+ /* string had only one comma between counter names; that's not valid */
+ char cpuname[128];
+ hwc_get_cpuname (cpuname, sizeof (cpuname));
+ free (ret);
+ ret = dbe_sprintf (GTXT ("HW counter set for %s, \"%s\", format error\n"), cpuname, hwc_defaultx);
+ free (hwc_defaultx);
+ return ret;
+ }
+ /* string had ,, between fields; move to next field */
+ stringp = next + 1;
+ if (* (stringp + 1) == 0) /* name ended in ,, -- we're done */
+ break;
+ continue;
+ }
+ else
+ {
+ /* no comma found, add the last counter and the comma and resolution */
+ strncat (retp, stringp, (retsize - strlen (retp) - 1));
+ strncat (retp, ",", (retsize - strlen (retp) - 1));
+ strncat (retp, resolution, (retsize - strlen (retp) - 1));
+ break;
+ }
+ }
+
+ /* we have now formatted the new string, with resolution inserted */
+ char *ccret;
+ if (add == true)
+ ccret = add_hwcstring (ret, warnmsg);
+ else
+ ccret = set_hwcstring (ret, warnmsg);
+ free (hwc_defaultx);
+ free (ret);
+
+ /* now set the clock-profiling timer, if on by default */
+ if (clkprof_default == 1)
+ {
+ if (strcmp (resolution, NTXT ("on")) == 0)
+ set_clkprof_timer_target (clk_params.normval);
+ else if (strcmp (resolution, NTXT ("lo")) == 0)
+ set_clkprof_timer_target (clk_params.lowval);
+ else if (strcmp (resolution, NTXT ("hi")) == 0)
+ set_clkprof_timer_target (clk_params.hival);
+ }
+ return ccret;
+}
+
+void
+Coll_Ctrl::set_hwcdefault ()
+{
+ char *string = hwc_get_default_cntrs2 (kernelHWC, 1);
+ if (string != NULL)
+ {
+ if (strlen (string) == 0)
+ hwcprof_default = 0;
+ else
+ {
+ char * warnmsg = NULL;
+ char *ccret = add_hwcstring (string, &warnmsg);
+ if (ccret != NULL)
+ {
+#if 0
+ /* set string to zero-length so that it won't be used again */
+ hwc_set_default_cntrs (kernelHWC, NTXT (""));
+#endif
+ hwcprof_default = 0;
+ }
+ else
+ hwcprof_default = 1;
+ }
+ free (string);
+ }
+ else
+ hwcprof_default = 0;
+}
+
+void
+Coll_Ctrl::disable_hwc ()
+{
+ hwcprof_enabled_cnt = 0;
+ hwcprof_default = 0;
+ free (hwc_string);
+ hwc_string = NULL;
+}
+
+char *
+Coll_Ctrl::set_sample_period (const char *string)
+{
+ int val;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strcmp (string, "on") == 0)
+ val = 1;
+ else if (strcmp (string, "off") == 0)
+ val = 0;
+ else
+ {
+ /* string should be a number > 0 */
+ char *endchar = NULL;
+ val = (int) strtol (string, &endchar, 0);
+ if (*endchar != 0 || val <= 0)
+ return dbe_sprintf (GTXT ("Unrecognized sample period `%s'\n"), string);
+ }
+ /* set that value */
+ int prev_sample_period = sample_period;
+ sample_period = val;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ sample_period = prev_sample_period;
+ return ret;
+ }
+ sample_default = 0;
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_size_limit (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0
+ || strcmp (string, "unlimited") == 0 || strcmp (string, "none") == 0)
+ {
+ size_limit = 0;
+ return NULL;
+ }
+ /* string should be a number >0; 0 is an error */
+ char *endchar = NULL;
+ int val = (int) strtol (string, &endchar, 0);
+ if (*endchar != 0 || val <= 0)
+ return dbe_sprintf (GTXT ("Unrecognized size limit `%s'\n"), string);
+ size_limit = val;
+ return 0;
+}
+
+void
+Coll_Ctrl::build_data_desc ()
+{
+ char spec[DD_MAXPATHLEN];
+ spec[0] = 0;
+
+ // Put sample sig before clock profiling. Dbx uses PROF
+ // for that purpose and we want it to be processed first.
+ if (project_home)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "P:%s;", project_home);
+ if (sample_sig != 0)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "g:%d;", sample_sig);
+ if (pauseresume_sig != 0)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "d:%d%s;", pauseresume_sig,
+ (pauseresume_pause == 1 ? "p" : ""));
+ if (clkprof_enabled == 1)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "p:%d;", clkprof_timer);
+ if (synctrace_enabled == 1)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "s:%d,%d;", synctrace_thresh, synctrace_scope);
+ if (heaptrace_enabled == 1)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "H:%d;", heaptrace_checkenabled);
+ if (iotrace_enabled == 1)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "i:;");
+ if (hwcprof_enabled_cnt > 0)
+ {
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "h:%s",
+ (hwcprof_default == true) ? "*" : "");
+ for (int ii = 0; ii < hwcprof_enabled_cnt; ii++)
+ {
+ /* min_time is a "new" field.
+ *
+ * To help process_data_descriptor() in hwcfuncs.c parse
+ * the HWC portion of this string -- specifically, to
+ * recognize min_time when it's present and skip over
+ * when it's not -- we prepend 'm' to the min_time value.
+ *
+ * When we no longer worry about, say, an old dbx
+ * writing this string and a new libcollector looking for
+ * the min_time field, the 'm' character can be
+ * removed and process_data_descriptor() simplified.
+ */
+ hrtime_t min_time = hwctr[ii].min_time;
+ if (min_time == HWCTIME_TBD)
+ // user did not specify any value for overflow rate
+ min_time = hwctr[ii].min_time_default;
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec),
+ "%s%s:%s:%d:%d:m%lld:%d:%d:0x%x", ii ? "," : "",
+ strcmp (hwctr[ii].name, hwctr[ii].int_name) ? hwctr[ii].name : "",
+ hwctr[ii].int_name, hwctr[ii].reg_num, hwctr[ii].val,
+ min_time, ii, /*tag*/ hwctr[ii].timecvt, hwctr[ii].memop);
+ }
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), ";");
+ }
+ if ((time_run != 0) || (start_delay != 0))
+ {
+ if (start_delay != 0)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d:%d;", start_delay, time_run);
+ else
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "t:%d;", time_run);
+ }
+ if (sample_period != 0)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "S:%d;",
+ sample_period);
+ if (size_limit != 0)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "L:%d;",
+ size_limit);
+ if (java_mode != 0)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "j:%d;", (int) java_mode);
+ if (follow_mode != FOLLOW_NONE)
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "F:%d;", (int) follow_mode);
+ snprintf (spec + strlen (spec), sizeof (spec) - strlen (spec), "a:%s;", archive_mode);
+ if (strlen (spec) + 1 >= sizeof (spec))
+ abort ();
+ free (data_desc);
+ data_desc = strdup (spec);
+}
+
+char *
+Coll_Ctrl::check_group ()
+{
+ char group_file[MAXPATHLEN];
+ if (expt_group == NULL)
+ return NULL;
+ // Is the group an relative path, with a store directory set?
+ if ((expt_group[0] == '/') || ((udir_name == NULL) || (udir_name[0] == '0')))
+ snprintf (group_file, sizeof (group_file), "%s", expt_group);
+ else // relative path, store directory; make group_file in that directory
+ snprintf (group_file, sizeof (group_file), "%s/%s", udir_name, expt_group);
+ // See if we can write the group file
+ int ret = access (group_file, W_OK);
+ if (ret != 0)
+ {
+ if (errno == ENOENT)
+ {
+ char *stmp = group_file;
+ char *dir = dirname (stmp);
+ ret = access (dir, W_OK);
+ if (ret != 0) // group file does not exist;
+ return dbe_sprintf (GTXT ("Directory (%s) for group file %s is not writeable: %s\n"),
+ dir, group_file, strerror (errno));
+ }
+ else
+ return dbe_sprintf (GTXT ("Group file %s is not writeable: %s\n"),
+ group_file, strerror (errno));
+ }
+ return NULL;
+}
+
+char *
+Coll_Ctrl::join_group ()
+{
+ int tries = 0;
+ int groupfd;
+ FILE *file;
+ char group_file[MAXPATHLEN];
+ struct stat statbuf;
+ struct flock flockbuf;
+ flockbuf.l_type = F_WRLCK;
+ flockbuf.l_whence = SEEK_SET;
+ flockbuf.l_start = 0;
+ flockbuf.l_len = 0;
+ if (expt_group == NULL)
+ return NULL;
+ // Is the group an relative path, with a store directory set?
+ if (expt_group[0] == '/' || udir_name == NULL || udir_name[0] == '0')
+ snprintf (group_file, sizeof (group_file), "%s", expt_group);
+ else // relative path, store directory; make group_file in that directory
+ snprintf (group_file, sizeof (group_file), "%s/%s", udir_name, expt_group);
+ for (;;)
+ {
+ tries++;
+ // try to open the group file
+ while ((groupfd = open (group_file, O_RDWR)) >= 0)
+ {
+ if (uinterrupt == 1)
+ {
+ close (groupfd);
+ return strdup (GTXT ("user interrupt\n"));
+ }
+ // it's opened, now lock it
+ if (fcntl (groupfd, F_SETLK, &flockbuf) != -1)
+ {
+ // we got the lock; check the file size
+ if (fstat (groupfd, &statbuf) != 0)
+ {
+ // can't stat the file -- give up
+ close (groupfd);
+ return dbe_sprintf (GTXT ("Can't fstat group file %s\n"), group_file);
+ }
+ if (statbuf.st_size == 0)
+ {
+ // size is zero: we got the lock just as someone
+ // else created the group file
+ // close the file and release the lock; try again
+ close (groupfd);
+ continue;
+ }
+ else
+ {
+ // size is non-zero, add our record
+ file = fdopen (groupfd, "a");
+ if (file == NULL)
+ {
+ close (groupfd);
+ return dbe_sprintf (GTXT ("Can't access group file %s\n"), group_file);
+ }
+ if (fprintf (file, "%s\n", store_ptr) <= 0)
+ {
+ fclose (file);
+ return dbe_sprintf (GTXT ("Can't update group file %s\n"), group_file);
+ }
+ // close the file, releasing our lock
+ fclose (file);
+ return NULL;
+ }
+ }
+ else
+ {
+ // can't get the lock, close the file and try again
+ close (groupfd);
+ if (uinterrupt == 1)
+ return strdup (GTXT ("user interrupt\n"));
+ if (tries == 11900)
+ return dbe_sprintf (GTXT ("Timed out: waiting for group file %s\n"), group_file);
+#if 0
+ if (tries % 500 == 0)
+ USR_WARN (GTXT ("Waiting for group file %s . . ."), group_file);
+#endif
+ usleep (10000U);
+ continue;
+ }
+ }
+ // If the error was not that the file did not exist, report it
+ if (errno != ENOENT)
+ return dbe_sprintf (GTXT ("Can't open group file %s: %s\n"),
+ group_file, strerror (errno));
+ // the file did not exist, try to create it
+ groupfd = open (group_file, O_CREAT | O_EXCL | O_RDWR, 0666);
+ if (groupfd < 0)
+ {
+ // we could not create the file
+ if (errno == EEXIST)
+ continue;
+ return dbe_sprintf (GTXT ("Can't create group file %s: %s\n"),
+ group_file, strerror (errno));
+ }
+ // we created the group file, now lock it, waiting for the lock
+ while (fcntl (groupfd, F_SETLKW, &flockbuf) == -1)
+ {
+ // we created the file, but couldn't lock it
+ if (errno != EINTR)
+ return dbe_sprintf (GTXT ("Unable to lock group file %s\n"), group_file);
+ }
+ // we created and locked the file, write to it
+ file = fdopen (groupfd, "a");
+ if (file == NULL)
+ {
+ close (groupfd);
+ return dbe_sprintf (GTXT ("Can't access group file %s\n"), group_file);
+ }
+ // write the header line
+ if (fprintf (file, "%s\n", SP_GROUP_HEADER) <= 0)
+ {
+ fclose (file);
+ return dbe_sprintf (GTXT ("Can't initialize group file %s\n"), group_file);
+ }
+ if (fprintf (file, "%s\n", store_ptr) <= 0)
+ {
+ fclose (file);
+ return dbe_sprintf (GTXT ("Can't update group file %s\n"), group_file);
+ }
+ // finally, close the file, releasing the lock
+ fclose (file);
+ return NULL;
+ }
+ // never reached
+}
+
+char *
+Coll_Ctrl::set_directory (char *dir, char **warn)
+{
+ struct stat statbuf;
+ *warn = NULL;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (stat (dir, &statbuf) != 0)
+ return dbe_sprintf (GTXT ("Can't set directory `%s': %s\n"),
+ dir, strerror (errno));
+ if (!S_ISDIR (statbuf.st_mode))
+ return dbe_sprintf (GTXT ("Can't set directory `%s': %s\n"),
+ dir, strerror (ENOTDIR));
+ free (udir_name);
+ udir_name = strdup (dir);
+
+ // Process new setting
+ *warn = preprocess_names ();
+ if ((uexpt_name != NULL) || (interactive != 0))
+ {
+ char *ret = update_expt_name (true, true);
+ if (ret != NULL)
+ {
+ if (*warn != NULL)
+ {
+ char *msg = dbe_sprintf ("%s%s", *warn, ret);
+ free (*warn);
+ free (ret);
+ *warn = msg;
+ }
+ else
+ *warn = ret;
+ }
+ }
+ else
+ (void) update_expt_name (false, false);
+ return NULL; // All is OK
+}
+
+int
+Coll_Ctrl::set_target (char* targetname)
+{
+ free (target_name);
+ target_name = NULL;
+ if (targetname != NULL)
+ target_name = strdup (targetname);
+ return 0;
+}
+
+void
+Coll_Ctrl::set_default_stem (const char* stem)
+{
+ default_stem = strdup (stem);
+ preprocess_names ();
+ (void) update_expt_name (false, false); // no warnings
+}
+
+char *
+Coll_Ctrl::set_expt (const char *ename, char **warn, bool overwriteExp)
+{
+ *warn = NULL;
+ if (ename == NULL)
+ {
+ free (uexpt_name);
+ uexpt_name = NULL;
+ return NULL;
+ }
+ char *exptname = canonical_path(strdup(ename));
+ size_t i = strlen (exptname);
+ if (i < 4 || strcmp (&exptname[i - 3], ".er") != 0)
+ {
+ free (exptname);
+ return dbe_sprintf (GTXT ("Experiment name `%s' must end in `.er'\n"),
+ ename);
+ }
+ // Name is OK
+ free (uexpt_name);
+ uexpt_name = exptname;
+ preprocess_names ();
+ char *err = update_expt_name (true, true, overwriteExp);
+ if (err != NULL)
+ return err;
+ if (overwriteExp)
+ {
+ char *nm = dbe_sprintf ("%s/%s", store_dir, base_name);
+ struct stat statbuf;
+ char *cmd = dbe_sprintf ("/bin/rm -rf %s >/dev/null 2>&1", nm);
+ system (cmd);
+ free (cmd);
+ if (stat (nm, &statbuf) == 0)
+ return dbe_sprintf (GTXT ("Cannot remove experiment `%s'\n"), nm);
+ if (errno != ENOENT)
+ return dbe_sprintf (GTXT ("Cannot remove experiment `%s'\n"), nm);
+ free (nm);
+ }
+ *warn = update_expt_name (true, false);
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_group (char *groupname)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (expt_group != NULL)
+ {
+ free (expt_group);
+ expt_group = NULL;
+ }
+ if (groupname == NULL)
+ {
+ // reset the name
+ preprocess_names ();
+ (void) update_expt_name (true, false);
+ return NULL;
+ }
+ int i = (int) strlen (groupname);
+ if (i < 5 || strcmp (&groupname[i - 4], ".erg") != 0)
+ return dbe_sprintf (GTXT ("Experiment group name `%s'must end in `.erg'\n"), groupname);
+ expt_group = strdup (groupname);
+ preprocess_names ();
+ (void) update_expt_name (true, false);
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_java_mode (const char *string)
+{
+ struct stat statbuf;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0)
+ {
+#if defined(GPROFNG_JAVA_PROFILING)
+ int prev_java_mode = java_mode;
+ int prev_java_default = java_default;
+ java_mode = 1;
+ java_default = 0;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ java_mode = prev_java_mode;
+ java_default = prev_java_default;
+ return ret;
+ }
+ return NULL;
+#else
+ return strdup (GTXT ("gprofng was built without support for profiling Java applications\n"));
+#endif
+ }
+ if (strcmp (string, "off") == 0)
+ {
+ int prev_java_mode = java_mode;
+ int prev_java_default = java_default;
+ java_mode = 0;
+ java_default = 0;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ java_mode = prev_java_mode;
+ java_default = prev_java_default;
+ return ret;
+ }
+ free (java_path);
+ java_path = NULL;
+ return NULL;
+ }
+ /* any other value should be a path to Java installation directory */
+ if (stat (string, &statbuf) == 0)
+ {
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+ {
+ // it's a directory -- set the Java path to it
+ int prev_java_mode = java_mode;
+ int prev_java_default = java_default;
+ java_mode = 1;
+ java_default = 0;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ java_mode = prev_java_mode;
+ java_default = prev_java_default;
+ return ret;
+ }
+ return set_java_path (string);
+ }
+ }
+ return dbe_sprintf (GTXT ("Java-profiling parameter is neither \"on\", nor \"off\", nor is it a directory: `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_java_path (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ free (java_path);
+ java_path = strdup (string);
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_java_args (char *string)
+{
+ char *next;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ char *prev_java_args = java_args;
+ if (string == NULL || strlen (string) == 0)
+ java_args = strdup ("");
+ else
+ java_args = strdup (string);
+ // now count the number of Java arguments
+ for (next = java_args; *next; next++)
+ {
+ if (*next == ' ' || *next == '\t')
+ continue;
+ njava_args++;
+ for (++next; *next; next++)
+ if (*next == ' ' || *next == '\t')
+ break;
+ if (!*next)
+ break;
+ }
+ if (njava_args == 0)
+ java_args = NULL;
+ char *ret = check_consistency ();
+ if (ret != NULL)
+ {
+ java_args = prev_java_args;
+ return ret;
+ }
+ free (prev_java_args);
+ return NULL;
+}
+
+char *
+Coll_Ctrl::set_follow_mode (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ free (follow_spec_usr);
+ free (follow_spec_cmp);
+ follow_spec_usr = NULL;
+ follow_spec_cmp = NULL;
+ if (string == NULL || strlen (string) == 0 || strcmp (string, "all") == 0
+ || strcmp (string, "on") == 0)
+ {
+ follow_mode = FOLLOW_ON;
+ follow_default = 0;
+ return NULL;
+ }
+ if (strcmp (string, "off") == 0)
+ {
+ follow_mode = FOLLOW_NONE;
+ follow_default = 0;
+ return NULL;
+ }
+
+ /* compile regular expression if string starts with "=" */
+ if (string[0] == '=' && string[1] != 0)
+ {
+ // user has specified a string matching specification
+ regex_t regex_desc;
+ int ercode;
+ const char *userspec = &string[1];
+ size_t newstrlen = strlen (userspec) + 3;
+ char * str = (char *) malloc (newstrlen);
+ if (str)
+ {
+ snprintf (str, newstrlen, "^%s$", userspec);
+ assert (strlen (str) == newstrlen - 1);
+ ercode = regcomp (&regex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
+ }
+ else
+ ercode = 1;
+ if (!ercode)
+ {
+ follow_spec_usr = strdup (string);
+ /* Ideally, follow_spec_cmp = [serialized regex_desc], */
+ /* so that libcollector wouldn't have to recompile it. */
+ /* For now, just copy the regular expression into follow_spec_cmp */
+ follow_spec_cmp = str;
+ follow_mode = FOLLOW_ALL;
+ follow_default = 0;
+ return NULL;
+ }
+ // syntax error in parsing string
+#if 0
+ char errbuf[256];
+ regerror (ercode, &regex_desc, errbuf, sizeof (errbuf));
+ fprintf (stderr, "Coll_Ctrl::set_follow_mode: regerror()=%s\n", errbuf);
+#endif
+ free (str);
+ }
+ return dbe_sprintf (GTXT ("Unrecognized follow-mode parameter `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_prof_idle (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0)
+ {
+ prof_idle = 1;
+ return NULL;
+ }
+ if (strcmp (string, "off") == 0)
+ {
+ prof_idle = 0;
+ return NULL;
+ }
+ return dbe_sprintf (GTXT ("Unrecognized profiling idle cpus parameter `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_archive_mode (const char *string)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (string == NULL || strlen (string) == 0)
+ string = "on";
+ if (strcasecmp (string, "on") == 0 || strcasecmp (string, "off") == 0
+ || strcasecmp (string, "ldobjects") == 0
+ || strcasecmp (string, "usedldobjects") == 0
+ || strcasecmp (string, "src") == 0 || strcasecmp (string, "usedsrc") == 0
+ || strcasecmp (string, "all") == 0)
+ {
+ free (archive_mode);
+ archive_mode = strdup (string);
+ return NULL;
+ }
+ return dbe_sprintf (GTXT ("Unrecognized archive-mode parameter `%s'\n"), string);
+}
+
+char *
+Coll_Ctrl::set_sample_signal (int value)
+{
+ const char *buf;
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (value == 0)
+ {
+ sample_sig = 0;
+ return NULL;
+ }
+ if (value == pauseresume_sig)
+ return report_signal_conflict (value);
+ if ((buf = strsignal (value)) != NULL)
+ sample_sig = value;
+ else
+ return dbe_sprintf (GTXT ("Invalid sample signal %d\n"), value);
+ return NULL;
+}
+
+/* find a signal by name */
+int
+Coll_Ctrl::find_sig (const char *string)
+{
+ int val;
+ char *signame_alloc = NULL;
+ const char *signame;
+ val = -1;
+ if (strcmp (string, "off") == 0)
+ return 0;
+ // see if the name begins with SIG
+ if (strncmp (string, "SIG", 3) != 0)
+ {
+ // no: add it
+ signame_alloc = (char *) malloc (strlen (string) + 3 + 1);
+ if (signame_alloc == NULL)
+ return -1;
+ strcpy (signame_alloc, "SIG");
+ strcpy (&signame_alloc[3], string);
+ signame = signame_alloc;
+ }
+ else
+ signame = string;
+
+ /* see if the string is a number */
+ char *endchar = NULL;
+ val = (int) strtol (signame, &endchar, 0);
+ if (*endchar != 0)
+ val = strtosigno (signame);
+ free (signame_alloc);
+ if (val == SIGKILL)
+ return -1;
+ return val;
+}
+
+char *
+Coll_Ctrl::set_pauseresume_signal (int value, int resume)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ if (value == 0)
+ {
+ pauseresume_sig = 0;
+ return NULL;
+ }
+ if (value == sample_sig)
+ return report_signal_conflict (value);
+ if (strsignal (value) != NULL)
+ {
+ pauseresume_sig = value;
+ pauseresume_pause = resume;
+ }
+ else
+ return dbe_sprintf (GTXT ("Invalid pause-resume (delayed initialization) signal %d\n"), value);
+ return NULL;
+}
+
+char *
+Coll_Ctrl::report_signal_conflict (int value)
+{
+ const char *xbuf = strsignal (value);
+ if (xbuf != NULL)
+ return dbe_sprintf (GTXT ("Signal %s (%d) can not be used for both sample and pause-resume (delayed initialization)\n"),
+ xbuf, value);
+ return dbe_sprintf (GTXT ("Signal %d can not be used for both sample and pause-resume (delayed initialization)\n"),
+ value);
+}
+
+char *
+Coll_Ctrl::set_debug_mode (int value)
+{
+ if (opened == 1)
+ return strdup (GTXT ("Experiment is active; command ignored.\n"));
+ debug_mode = value;
+ return NULL;
+}
+
+char *
+Coll_Ctrl::create_exp_dir ()
+{
+ int max = 4095; // 0xFFF - can be increased if it seems too low
+ for (int i = 0; i < max; i++)
+ {
+ if (mkdir (store_ptr,
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
+ {
+ int err = errno;
+ if (err == EACCES)
+ return dbe_sprintf (GTXT ("Store directory %s is not writeable: %s\n"),
+ store_dir, strerror (err));
+ if (i + 1 >= max) // no more attempts
+ return dbe_sprintf (GTXT ("Unable to create directory `%s' -- %s\n%s: %d\n"),
+ store_ptr, strerror (err),
+ GTXT ("collect: Internal error: loop count achieved"),
+ max);
+ char *ermsg = update_expt_name (false, false, true);
+ if (ermsg != NULL)
+ {
+ char *msg = dbe_sprintf (GTXT ("Unable to create directory `%s' -- %s\n"),
+ store_ptr, ermsg);
+ free (ermsg);
+ return msg;
+ }
+ continue;
+ }
+ return NULL; // All is OK
+ }
+ return dbe_sprintf (GTXT ("Unable to create directory `%s'\n"), store_ptr);
+}
+
+char *
+Coll_Ctrl::get_exp_name (const char *stembase)
+{
+ expno = 1;
+ return dbe_sprintf ("%s.%d.er", stembase, expno);
+}
+
+char *
+Coll_Ctrl::preprocess_names ()
+{
+ char buf[MAXPATHLEN];
+ char msgbuf[MAXPATHLEN];
+ char *ret = NULL;
+
+ /* convert the experiment name and directory into store name/dir */
+ /* free the old strings */
+ if (store_dir != NULL)
+ {
+ free (store_dir);
+ store_dir = NULL;
+ }
+ if (expt_dir != NULL)
+ {
+ free (expt_dir);
+ expt_dir = NULL;
+ }
+ if (base_name != NULL)
+ {
+ free (base_name);
+ base_name = NULL;
+ }
+ if (expt_name != NULL)
+ {
+ free (expt_name);
+ expt_name = NULL;
+ }
+ expno = 1;
+ if (uexpt_name != NULL)
+ expt_name = strdup (uexpt_name);
+ else
+ {
+ // no user name -- pick a default
+ char *c;
+ char *stem;
+ char *stembase;
+ if (expt_group == NULL)
+ {
+ stem = strdup (default_stem);
+ stembase = stem;
+ }
+ else
+ {
+ stem = strdup (expt_group);
+ stem[strlen (stem) - 4] = 0;
+ stembase = stem;
+ // now remove any leading directory
+ for (int i = 0;; i++)
+ {
+ if (stem[i] == 0)
+ break;
+ if (stem[i] == '/')
+ stembase = &stem[i + 1];
+ }
+ if (strlen (stembase) == 0)
+ {
+ free (stem);
+ stem = strdup (default_stem);
+ stembase = stem;
+ }
+ }
+ c = get_exp_name (stembase);
+ expt_name = c;
+ free (stem);
+ }
+ snprintf (buf, sizeof (buf), NTXT ("%s"), expt_name);
+ if (buf[0] == '/')
+ {
+ // it's a full path name
+ if (udir_name != NULL)
+ {
+ snprintf (msgbuf, sizeof (msgbuf),
+ GTXT ("Warning: Experiment name is an absolute path; directory name %s ignored.\n"),
+ udir_name);
+ ret = strdup (msgbuf);
+ }
+ }
+
+ // now extract the directory and basename
+ int lastslash = 0;
+ for (int i = 0;; i++)
+ {
+ if (buf[i] == 0)
+ break;
+ if (buf[i] == '/')
+ lastslash = i;
+ }
+ expt_dir = strdup (buf);
+ if (lastslash != 0)
+ base_name = strdup (&buf[lastslash + 1]);
+ else
+ base_name = strdup (buf);
+ expt_dir[lastslash] = 0;
+ if (expt_dir[0] == '/')
+ store_dir = strdup (expt_dir);
+ else if ((udir_name == NULL) || (udir_name[0] == 0))
+ {
+ if (expt_dir[0] == 0)
+ store_dir = strdup (".");
+ else
+ store_dir = strdup (expt_dir);
+ }
+ else
+ {
+ /* udir_name is a non-empty string */
+ if (expt_dir[0] == 0)
+ store_dir = strdup (udir_name);
+ else
+ {
+ snprintf (buf, sizeof (buf), "%s/%s", udir_name, expt_dir);
+ store_dir = strdup (buf);
+ }
+ }
+ free (store_ptr);
+ if (strcmp (store_dir, ".") == 0)
+ store_ptr = strdup (base_name);
+ else
+ {
+ snprintf (buf, sizeof (buf), "%s/%s", store_dir, base_name);
+ store_ptr = strdup (buf);
+ }
+
+ // determine the file system type
+ if (strcmp (store_dir, prev_store_dir) != 0)
+ {
+ free (prev_store_dir);
+ prev_store_dir = strdup (store_dir);
+ const char *fstype = get_fstype (store_dir);
+ if (interactive && enabled && (fstype != NULL) && (nofswarn == 0))
+ {
+ snprintf (msgbuf, sizeof (msgbuf),
+ GTXT ("%sExperiment directory is set to a file system of type \"%s\",\n which may distort the measured performance;\n it is preferable to record to a local disk.\n"),
+ (ret == NULL ? "" : ret), fstype);
+ free (ret);
+ ret = strdup (msgbuf);
+ }
+ }
+ return ret;
+}
+
+char *
+Coll_Ctrl::update_expt_name (bool chgmsg, bool chkonly, bool newname)
+{
+ char *ret = NULL;
+ struct stat statbuf;
+ // make sure the name ends in .er
+ // set count to the length of the name
+ int count = (int) strlen (base_name);
+
+ // this should have been checked already, so we can abort
+ if (count < 4 || strcmp (&base_name[count - 3], ".er") != 0)
+ abort ();
+ int pcount = count - 4;
+ if (!newname)
+ { // check if old name can be used
+ char fullname[MAXPATHLEN];
+ snprintf (fullname, sizeof (fullname), "%s/%s", store_dir, base_name);
+ if (stat (fullname, &statbuf) != 0)
+ if (errno == ENOENT) // name does not exist, we can use it
+ return NULL;
+ }
+ else if (chkonly)
+ return NULL;
+
+ // current name will not work, update the name
+ DIR *dir;
+ struct dirent *dir_entry;
+
+ // see if there's a numeric field in front of the .er of the name
+ int digits = 0;
+ while (isdigit ((int) (base_name[pcount])) != 0)
+ {
+ pcount--;
+ if (pcount == 0) // name is of the form 12345.er; don't update it
+ return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"),
+ base_name);
+ digits++;
+ }
+ if (digits == 0) // name is of form xyz.er (or xyz..er); don't update it
+ return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"),
+ base_name);
+ if (base_name[pcount] != '.') // name is of form xyz123.er; don't update it
+ return dbe_sprintf (GTXT ("name %s is in use and cannot be updated\n"),
+ base_name);
+ if (chkonly)
+ return NULL;
+
+ // save the name for a changed message
+ char *oldbase = strdup (base_name);
+
+ // the name is of the from prefix.nnn.er; extract the value of nnn
+ int version = atoi (&base_name[pcount + 1]);
+ if (newname) // do not try to use old name
+ version++;
+ int max_version = version - 1;
+
+ // terminate the base_name string after that . yielding "prefix."
+ base_name[pcount + 1] = 0;
+ if ((dir = opendir (store_dir)) == NULL)
+ {
+ // ignore error -- we'll hit it again later
+ free (oldbase);
+ return NULL;
+ }
+
+ // find the maximum version in the directory
+ // count is the number of characters before the number
+ //
+ while ((dir_entry = readdir (dir)) != NULL)
+ {
+ count = (int) strlen (dir_entry->d_name);
+ if ((count < 4) || (strcmp (&dir_entry->d_name[count - 3], ".er") != 0))
+ continue;
+ // check that the name is of the form prefix.nnn.er; if not, skip it
+ if (strncmp (base_name, dir_entry->d_name, pcount + 1) == 0)
+ {
+ // the "prefix." part matches, terminate the entry name before the .er
+ dir_entry->d_name[count - 3] = 0;
+ char *lastchar;
+ int dversion = (int) strtol (&dir_entry->d_name[pcount + 1], &lastchar, 10);
+
+ // if it did not end where the .er was, skip it
+ if (*lastchar != 0)
+ continue;
+ if (dversion > max_version)
+ max_version = dversion;
+ }
+ }
+
+ // we now have the maximum version determined
+ char newbase[MAXPATHLEN];
+ base_name[pcount + 1] = 0;
+ version = max_version + 1;
+ snprintf (newbase, sizeof (newbase), "%s%d.er", base_name, version);
+ if ((strcmp (oldbase, newbase) != 0) && chgmsg)
+ {
+ ret = dbe_sprintf (GTXT ("name %s is in use; changed to %s\n"),
+ oldbase, newbase);
+ free (oldbase);
+ }
+ else
+ free (oldbase);
+ free (base_name);
+ base_name = strdup (newbase);
+
+ // now, reset expt_name to reflect new setting
+ free (expt_name);
+ if (expt_dir[0] == 0)
+ expt_name = strdup (base_name);
+ else
+ expt_name = dbe_sprintf ("%s/%s", expt_dir, base_name);
+ free (store_ptr);
+ if (strcmp (store_dir, ".") == 0)
+ store_ptr = strdup (base_name);
+ else
+ store_ptr = dbe_sprintf ("%s/%s", store_dir, base_name);
+ closedir (dir);
+ return ret;
+}
+
+void
+Coll_Ctrl::remove_exp_dir ()
+{
+ if (store_ptr == NULL)
+ return;
+ rmdir (store_ptr);
+ free (store_ptr);
+ store_ptr = NULL;
+ return;
+}
+
+void
+Coll_Ctrl::determine_profile_params ()
+{
+ struct itimerval itimer;
+ struct itimerval otimer;
+ int period;
+ long nperiod;
+ struct sigaction act;
+ struct sigaction old_handler;
+ memset (&act, 0, sizeof (struct sigaction));
+ period = 997;
+
+ // set SIGPROF handler to SIG_IGN
+ sigemptyset (&act.sa_mask);
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction (SIGPROF, &act, &old_handler) == -1)
+ {
+ /* couldn't set signal */
+ fprintf (stderr, GTXT ("Can't set SIGPROF: %s\n"), strerror (errno));
+ exit (1);
+ }
+
+ // set the timer to arbitrary resolution
+ itimer.it_interval.tv_sec = period / MICROSEC;
+ itimer.it_interval.tv_usec = period % MICROSEC;
+ itimer.it_value = itimer.it_interval;
+ setitimer (ITIMER_REALPROF, &itimer, &otimer);
+
+ // now reset the timer to turn it off
+ itimer.it_value.tv_sec = 0;
+ itimer.it_value.tv_usec = 0;
+ if (setitimer (ITIMER_REALPROF, &itimer, &otimer) == -1) // call failed
+ nperiod = -1;
+ else
+ nperiod = otimer.it_interval.tv_sec * MICROSEC + otimer.it_interval.tv_usec;
+
+ // check the returned value: is the what we asked for?
+ if (period == nperiod) // arbitrary precision is OK
+ set_clk_params (PROFINT_MIN, 1, PROFINT_MAX, PROFINT_HIGH, PROFINT_NORM, PROFINT_LOW);
+ else if (nperiod < 10000) // hi resolution allowed, but not arbitrary precision
+ set_clk_params ((int) nperiod, 1000, PROFINT_MAX, 1000, 10000, 100000);
+ else // low resolution only allowed
+ set_clk_params (10000, 10000, PROFINT_MAX, 1000, 10000, 100000);
+
+ // If old handler was default, ignore it; otherwise restore it
+ if (old_handler.sa_handler != SIG_DFL)
+ {
+ act.sa_handler = old_handler.sa_handler;
+ if (sigaction (SIGPROF, &act, &old_handler) == -1)
+ {
+ /* couldn't reset signal */
+ fprintf (stderr, GTXT ("Can't reset SIGPROF: %s\n"), strerror (errno));
+ exit (1);
+ }
+ }
+}
+
+const char *
+get_fstype (char *)
+{
+ /* On Linux, statvfs() doesn't return any information that seems to indicate
+ the filetype. The structure statvfs does not have any field/flag that
+ gives this information. Comparing the fields from
+ /usr/include/bits/statvfs.h:
+ unsigned long int f_fsid;
+ int __f_unused;
+ ^^^^ On Solaris, this is where f_basetype is
+ unsigned long int f_flag;
+ unsigned long int f_namemax;
+ XXX Need to revisit this XXX
+ */
+ return NULL; // no NFS warning on Linux for now
+}
+
+//========== Special functions to communicate with the Collector GUI ==========//
+
+/* Interface strings GUI <-> CLI */
+const char *ipc_str_exp_limit = "exp_limit";
+const char *ipc_str_time_limit = "time_limit";
+const char *ipc_str_arch_exp = "arch_exp";
+const char *ipc_str_descendant = "descendant";
+const char *ipc_str_clkprof = "clkprof";
+const char *ipc_str_hwcprof = "hwcprof";
+const char *ipc_str_hwc2_prof = "hwc2_prof";
+const char *ipc_str_javaprof = "javaprof";
+const char *ipc_str_sample = "sample";
+const char *ipc_str_sample_sig = "sample_sig";
+const char *ipc_str_pause_resume_sig = "pause_resume_sig";
+const char *ipc_str_synctrace = "synctrace";
+const char *ipc_str_heaptrace = "heaptrace";
+const char *ipc_str_iotrace = "iotrace";
+const char *ipc_str_count = "count";
+const char *ipc_str_prof_idle = "prof_idle"; // -x option
+// Standard answers
+const char *ipc_str_empty = "";
+const char *ipc_str_on = "on";
+const char *ipc_str_off = "off";
+const char *ipc_str_src = "src";
+const char *ipc_str_usedsrc = "usedsrc";
+const char *ipc_str_usedldobjects = "usedldobjects";
+const char *ipc_str_unlimited = "unlimited";
+const char *ipc_str_unknown_control = "Unknown control";
+const char *ipc_str_internal_error = "Internal error";
+
+/**
+ * Finds signal name
+ * @param signal
+ * @return NULL or signal name (pointer to allocated memory)
+ */
+char *
+Coll_Ctrl::find_signal_name (int signal)
+{
+ char *str_signal = NULL;
+ const char *buf = strsignal (signal);
+ if (buf != NULL)
+ str_signal = strdup (buf);
+ return str_signal;
+}
+
+/**
+ * Gets control's value
+ * @param control
+ * @return value
+ */
+char *
+Coll_Ctrl::get (char * control)
+{
+ int len = strlen (control);
+ if (!strncmp (control, ipc_str_exp_limit, len))
+ {
+ if ((size_limit > 0))
+ return dbe_sprintf ("%d", size_limit);
+ return strdup (ipc_str_unlimited);
+ }
+ if (!strncmp (control, ipc_str_time_limit, len))
+ {
+ if ((time_run != 0) || (start_delay != 0))
+ {
+ if (start_delay != 0)
+ {
+ if (time_run != 0)
+ return dbe_sprintf ("%ds-%ds", start_delay, start_delay + time_run);
+ return dbe_sprintf ("%ds-0s", start_delay);
+ }
+ return dbe_sprintf ("0s-%ds", time_run);
+ }
+ return strdup (ipc_str_unlimited);
+ }
+ if (strncmp (control, ipc_str_arch_exp, len) == 0)
+ return strdup (get_archive_mode ());
+ if (!strncmp (control, ipc_str_descendant, len))
+ {
+ switch (get_follow_mode ())
+ {
+ case FOLLOW_ON:
+ return strdup (ipc_str_on);
+ case FOLLOW_ALL:
+ return strdup (ipc_str_on);
+ case FOLLOW_NONE:
+ default:
+ return strdup (ipc_str_off);
+ }
+ }
+ if (!strncmp (control, ipc_str_prof_idle, len))
+ {
+ if (prof_idle == 0)
+ return strdup (ipc_str_off);
+ return strdup (ipc_str_on);
+ }
+ if (!strncmp (control, ipc_str_clkprof, len))
+ {
+ if (clkprof_default == 1 && clkprof_enabled == 1) // Default value
+ return strdup (ipc_str_empty);
+ if (clkprof_enabled == 0)
+ return strdup (ipc_str_off);
+ if ((clkprof_timer > 0))
+ return dbe_sprintf ("%d", clkprof_timer / 1000);
+ return strdup (ipc_str_internal_error);
+ }
+ if (!strncmp (control, ipc_str_hwcprof, len))
+ {
+ if (hwcprof_enabled_cnt == 0)
+ return strdup (ipc_str_off);
+ if (hwc_string != NULL)
+ return dbe_sprintf ("on\n%s", hwc_string);
+ return strdup (ipc_str_on); // XXX need more details?
+ }
+ if (!strncmp (control, ipc_str_javaprof, len))
+ {
+ if ((java_mode == 0))
+ return strdup (ipc_str_off);
+ return strdup (ipc_str_on);
+ }
+ if (!strncmp (control, ipc_str_sample, len))
+ {
+ if (sample_default == 1 && sample_period == 1) // Default value
+ return strdup (ipc_str_empty);
+ if (sample_period == 0)
+ return strdup (ipc_str_off);
+ if (sample_period > 0)
+ return dbe_sprintf ("%d", sample_period);
+ return strdup (ipc_str_internal_error);
+ }
+ if (!strncmp (control, ipc_str_sample_sig, len))
+ {
+ if ((sample_sig == 0))
+ return strdup (ipc_str_off);
+ char *str_signal = find_signal_name (sample_sig);
+ if (str_signal != NULL)
+ return str_signal;
+ return dbe_sprintf (GTXT ("Invalid sample signal %d\n"), sample_sig);
+ }
+ if (!strncmp (control, ipc_str_pause_resume_sig, len))
+ {
+ if (pauseresume_sig == 0)
+ return strdup (ipc_str_off);
+ char *str_signal = find_signal_name (pauseresume_sig);
+ if (str_signal != NULL)
+ return str_signal;
+ return dbe_sprintf (GTXT ("Invalid pause/resume signal %d\n"), pauseresume_sig);
+ }
+ if (!strncmp (control, ipc_str_synctrace, len))
+ {
+ if (synctrace_enabled == 0)
+ return strdup (ipc_str_off);
+ if (synctrace_thresh < 0)
+ return strdup ("on\nthreshold: calibrate");
+ if (synctrace_thresh == 0)
+ return strdup ("on\nthreshold: all");
+ return dbe_sprintf ("on\nthreshold: %d", synctrace_thresh);
+ }
+ if (!strncmp (control, ipc_str_heaptrace, len))
+ {
+ if ((heaptrace_enabled == 0))
+ return strdup (ipc_str_off);
+ return strdup (ipc_str_on);
+ }
+ if (!strncmp (control, ipc_str_iotrace, len))
+ {
+ if ((iotrace_enabled == 0))
+ return strdup (ipc_str_off);
+ return strdup (ipc_str_on);
+ }
+ if (!strncmp (control, ipc_str_count, len))
+ {
+ if ((count_enabled == 0))
+ return strdup (ipc_str_off);
+ if ((count_enabled < 0))
+ return strdup ("on\nstatic");
+ return strdup (ipc_str_on);
+ }
+ return strdup (ipc_str_unknown_control);
+}
+
+/**
+ * Resets control's value (restores the default value)
+ * @param control
+ * @param value
+ * @return error or warning or NULL (done)
+ */
+char *
+Coll_Ctrl::set (char * control, const char * value)
+{
+ char * ret;
+ char * warn = NULL;
+ int len = strlen (control);
+ if (!strncmp (control, ipc_str_exp_limit, len))
+ return set_size_limit (value);
+ if (!strncmp (control, ipc_str_time_limit, len))
+ return set_time_run (value);
+ if (!strncmp (control, ipc_str_arch_exp, len))
+ return set_archive_mode (value);
+ if (!strncmp (control, ipc_str_descendant, len))
+ return set_follow_mode (value);
+ if (!strncmp (control, ipc_str_prof_idle, len))
+ return set_prof_idle (value);
+ if (!strncmp (control, ipc_str_clkprof, len))
+ {
+ ret = set_clkprof (value, &warn);
+ if (ret == NULL)
+ {
+ if (warn != NULL)
+ return warn; // Warning
+ return NULL; // Done
+ }
+ return ret; // Error
+ }
+ if (!strncmp (control, ipc_str_hwcprof, len))
+ {
+ ret = set_hwcstring (value, &warn);
+ if (ret == NULL)
+ {
+ if (warn != NULL)
+ return warn; // Warning
+ return NULL; // Done
+ }
+ return ret; // Error
+ }
+ if (!strncmp (control, ipc_str_hwc2_prof, len))
+ {
+ ret = set_hwcstring (value, &warn);
+ if (ret == NULL)
+ {
+ if (warn != NULL)
+ return warn; // Warning
+ return NULL; // Done
+ }
+ return ret; // Error
+ }
+ if (!strncmp (control, ipc_str_javaprof, len))
+ return set_java_mode (value);
+ if (!strncmp (control, ipc_str_sample, len))
+ return set_sample_period (value);
+ if (!strncmp (control, ipc_str_sample_sig, len))
+ return set_sample_signal (find_sig (value));
+ if (!strncmp (control, ipc_str_pause_resume_sig, len))
+ {
+ char *str_signal = strdup (value);
+ char *str_state = strchr (str_signal, (int) '\n');
+ if (str_state != NULL)
+ {
+ *str_state = 0;
+ str_state++;
+ }
+ int signal = atoi (str_signal);
+ int state = 0;
+ if (str_state != NULL)
+ state = atoi (str_state);
+ free (str_signal);
+ return set_pauseresume_signal (signal, state);
+ }
+ if (!strncmp (control, ipc_str_synctrace, len))
+ return set_synctrace (value);
+ if (!strncmp (control, ipc_str_heaptrace, len))
+ return set_heaptrace (value);
+ if (!strncmp (control, ipc_str_iotrace, len))
+ return set_iotrace (value);
+ if (!strncmp (control, ipc_str_count, len))
+ return set_count (value);
+ return strdup (ipc_str_unknown_control);
+}
+
+/**
+ * Resets control's value (restores the default value)
+ * @param control
+ * @return error or NULL (done)
+ */
+char *
+Coll_Ctrl::unset (char * control)
+{
+ int len = strlen (control);
+ if (!strncmp (control, ipc_str_exp_limit, len))
+ size_limit = 0;
+ if (!strncmp (control, ipc_str_time_limit, len))
+ {
+ time_run = 0;
+ start_delay = 0;
+ }
+ if (!strncmp (control, ipc_str_arch_exp, len))
+ {
+ archive_mode = strdup ("on");
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_descendant, len))
+ {
+ follow_mode = FOLLOW_NONE;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_prof_idle, len))
+ {
+ prof_idle = 1;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_clkprof, len))
+ {
+ clkprof_default = 1;
+ clkprof_enabled = 1;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_hwcprof, len))
+ {
+ setup_hwc ();
+ set_hwcdefault ();
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_javaprof, len))
+ {
+ java_mode = 0;
+ java_default = 0;
+ free (java_path);
+ java_path = NULL;
+ free (java_args);
+ java_args = NULL;
+ }
+ if (!strncmp (control, ipc_str_sample, len))
+ {
+ sample_period = 1;
+ sample_default = 1;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_sample_sig, len))
+ {
+ sample_sig = 0;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_pause_resume_sig, len))
+ {
+ pauseresume_sig = 0;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_synctrace, len))
+ {
+ synctrace_enabled = 0;
+ synctrace_thresh = -1;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_heaptrace, len))
+ {
+ heaptrace_enabled = 0;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_iotrace, len))
+ {
+ iotrace_enabled = 0;
+ return NULL;
+ }
+ if (!strncmp (control, ipc_str_count, len))
+ {
+ count_enabled = 0;
+ Iflag = 0;
+ Nflag = 0;
+ return NULL;
+ }
+ return strdup (ipc_str_unknown_control);
+}
+
+void
+Coll_Ctrl::set_project_home (char *s)
+{
+ if (s)
+ project_home = strdup (s);
+}