/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 (""))); 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: \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 (""))); #if 0 snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), GTXT ("\t expt_dir: %s\n"), ((expt_dir != NULL) ? expt_dir : NTXT (""))); snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), GTXT ("\t base_name: %s\n"), ((base_name != NULL) ? base_name : NTXT (""))); snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), GTXT ("\t store_dir: %s\n"), ((store_dir != NULL) ? store_dir : NTXT (""))); snprintf (UEbuf + strlen (UEbuf), sizeof (UEbuf) - strlen (UEbuf), GTXT ("\t store_ptr: %s\n"), ((store_ptr != NULL) ? store_ptr : NTXT (""))); #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 ("")), (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 ("")), (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 ("")), (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 "," -- 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 (®ex_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, ®ex_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); }