| /* Copyright (C) 2021-2024 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 "StringBuilder.h" |
| |
| #define SP_GROUP_HEADER "#analyzer experiment group" |
| |
| /* 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 |
| |
| static const char *get_fstype (char *); |
| static cpu_info_t cpu_info; |
| |
| static void |
| read_str (char *from, char **to) |
| { |
| if (*to != NULL) |
| return; |
| for (char *s = from; *s; s++) |
| if (*s != ':' && *s != '\t' && *s != ' ') |
| { |
| for (int i = ((int) strlen (s)) - 1; i >= 0; i--) |
| { |
| if (s[i] != '\n' && s[i] != ' ' && s[i] != '\t') |
| { |
| *to = strndup (s, i + 1); |
| return; |
| } |
| } |
| return; // string is empty |
| } |
| } |
| |
| static int |
| read_int (char *from) |
| { |
| char *val = strchr (from, ':'); |
| if (val) |
| return (int) strtol (val + 1, NULL, 0); |
| return 0; |
| } |
| |
| cpu_info_t * |
| read_cpuinfo () |
| { |
| static int inited = 0; |
| if (inited) |
| return &cpu_info; |
| inited = 1; |
| |
| #if defined(__aarch64__) |
| asm volatile("mrs %0, cntfrq_el0" : "=r" (cpu_info.cpu_clk_freq)); |
| #elif defined(__riscv) |
| // Set 1000 MHz for minimal support RISC-V, will fix with a better method to get cpu clock frequency. |
| cpu_info.cpu_clk_freq = 1000; |
| #endif |
| |
| // Read /proc/cpuinfo to get CPU info and clock rate |
| FILE *procf = fopen ("/proc/cpuinfo", "r"); |
| if (procf != NULL) |
| { |
| char temp[1024]; |
| while (fgets (temp, (int) sizeof (temp), procf) != NULL) |
| { |
| if (strncmp (temp, "processor", 9) == 0) |
| cpu_info.cpu_cnt++; |
| else if (strncmp (temp, "cpu MHz", 7) == 0) |
| cpu_info.cpu_clk_freq = read_int (temp + 9); |
| else if (strncmp (temp, "cpu family", 10) == 0) |
| cpu_info.cpu_family = read_int (temp + 10); |
| else if (strncmp (temp, "vendor_id", 9) == 0 |
| || strncmp (temp, "mvendorid", 9) == 0) |
| { |
| if (cpu_info.cpu_vendorstr == NULL) |
| read_str (temp + 9, &cpu_info.cpu_vendorstr); |
| } |
| else if (strncmp (temp, "model name", 10) == 0) |
| { |
| if (cpu_info.cpu_modelstr == NULL) |
| read_str (temp + 10, &cpu_info.cpu_modelstr); |
| } |
| else if (strncmp (temp, "model", 5) == 0) |
| cpu_info.cpu_model = read_int (temp + 5); |
| else if (strncmp (temp, "CPU implementer", 15) == 0) |
| cpu_info.cpu_family = read_int (temp + 15); |
| else if (strncmp (temp, "CPU architecture", 16) == 0) |
| cpu_info.cpu_model = read_int (temp + 16); |
| } |
| fclose (procf); |
| } |
| if (cpu_info.cpu_vendorstr == NULL) |
| #if defined(__aarch64__) |
| cpu_info.cpu_vendorstr = strdup (AARCH64_VENDORSTR_ARM); |
| #else |
| cpu_info.cpu_vendorstr = GTXT ("Unknown processor"); |
| #endif |
| if (cpu_info.cpu_modelstr == NULL) |
| cpu_info.cpu_modelstr = GTXT ("Unknown cpu model"); |
| return &cpu_info; |
| } |
| |
| Coll_Ctrl::Coll_Ctrl (int _interactive, bool _defHWC, bool _kernelHWC) |
| { |
| char hostname[MAXPATHLEN]; |
| 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"); |
| |
| cpu_info_t *cpu_p = read_cpuinfo (); |
| ncpus = cpu_p->cpu_cnt; |
| cpu_clk_freq = cpu_p->cpu_clk_freq; |
| |
| /* check resolution of system clock */ |
| sys_resolution = sysconf (_SC_CLK_TCK); |
| |
| /* 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 |
| |
| /* 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_mode = NULL; |
| 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; |
| 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_mode = dbe_strdup(cc->heaptrace_mode); |
| 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; |
| |
| // 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); |
| free (heaptrace_mode); |
| 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_mode != NULL || 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_mode != NULL && 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_mode == NULL && 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) |
| { |
| StringBuilder sb; |
| if (i == 0) |
| { |
| sb.append (GTXT ("Collection parameters:\n")); |
| sb.append (GTXT (" experiment enabled\n")); |
| } |
| if (target_name != NULL) |
| sb.appendf (GTXT ("\ttarget = %s\n"), target_name); |
| if (uexpt_name != NULL) |
| sb.appendf (GTXT ("\tuser_expt_name = %s\n"), uexpt_name); |
| sb.appendf (GTXT ("\texpt_name = %s\n"), |
| ((expt_name != NULL) ? expt_name : "<NULL>")); |
| if (udir_name != NULL) |
| sb.appendf (GTXT ("\tdir_name = %s\n"), udir_name); |
| if (expt_group != NULL) |
| sb.appendf (GTXT ("\texpt_group = %s\n"), expt_group); |
| if (debug_mode == 1) |
| sb.append (GTXT ("\tdebug_mode enabled\n")); |
| if (clkprof_enabled != 0) |
| sb.appendf (GTXT ("\tclock profiling enabled, %.3f millisec.\n"), |
| (double) (clkprof_timer) / 1000.); |
| if (synctrace_enabled != 0) |
| { |
| if (synctrace_thresh < 0) |
| sb.append (GTXT ("\tsynchronization tracing enabled, threshold: calibrate; ")); |
| else if (synctrace_thresh == 0) |
| sb.append (GTXT ("\tsynchronization tracing enabled, threshold: all; ")); |
| else |
| sb.appendf (GTXT ("\tsynchronization tracing enabled, threshold: %d micros.; "), |
| synctrace_thresh); |
| switch (synctrace_scope) |
| { |
| case SYNCSCOPE_NATIVE: |
| sb.append (GTXT ("Native-APIs\n")); |
| break; |
| case SYNCSCOPE_JAVA: |
| sb.append (GTXT ("Java-APIs\n")); |
| break; |
| case SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA: |
| sb.append (GTXT ("Native- and Java-APIs\n")); |
| break; |
| default: |
| sb.appendf (GTXT ("ERR -- unexpected synctrace_scope %d\n"), |
| synctrace_scope); |
| break; |
| } |
| } |
| if (hwcprof_enabled_cnt != 0) |
| { |
| char ctrbuf[MAXPATHLEN]; |
| if (hwcprof_default == 1) |
| sb.append (GTXT ("\thardware counter profiling (default) enabled:\n")); |
| else |
| sb.append (GTXT ("\thardware counter profiling enabled:\n")); |
| for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) |
| sb.appendf ("\t %u. %s\n", ii + 1, |
| hwc_hwcentry_specd_string (ctrbuf, sizeof (ctrbuf), &hwctr[ii])); |
| } |
| if (heaptrace_mode != NULL) |
| sb.append (GTXT ("\theap tracing enabled\n")); |
| if (iotrace_enabled != 0) |
| sb.append (GTXT ("\tI/O tracing enabled\n")); |
| switch (count_enabled) |
| { |
| case 0: |
| break; |
| case 1: |
| sb.append (GTXT ("\tcount data enabled\n")); |
| break; |
| case -1: |
| sb.append (GTXT ("\tstatic count data will be generated (for a.out only)\n")); |
| break; |
| } |
| switch (follow_mode) |
| { |
| case FOLLOW_ON: |
| sb.append (GTXT ("\tdescendant processes will be followed\n")); |
| break; |
| case FOLLOW_ALL: |
| if (follow_spec_usr && follow_spec_cmp) |
| sb.appendf (GTXT ("\texperiments will be recorded for descendant processes that match pattern '%s'\n"), |
| follow_spec_usr); |
| else |
| sb.append (GTXT ("\tdescendant processes will all be followed\n")); |
| break; |
| case FOLLOW_NONE: |
| sb.append (GTXT ("\tdescendant processes will not be followed\n")); |
| break; |
| default: |
| sb.append (GTXT ("\tfollowing descendant processes: <UNKNOWN>\n")); |
| break; |
| } |
| if (java_mode == 0) |
| sb.append (GTXT ("\tjava profiling disabled\n")); |
| if (pauseresume_sig != 0) |
| { |
| const char *buf = strsignal (pauseresume_sig); |
| if (buf != NULL) |
| { |
| if (pauseresume_pause == 1) |
| sb.appendf (GTXT ("\tpause-resume (delayed initialization) signal %s (%d) -- paused\n"), |
| buf, pauseresume_sig); |
| else |
| sb.appendf (GTXT ("\tpause-resume (delayed initialization) signal %s (%d)\n"), |
| buf, pauseresume_sig); |
| } |
| else |
| { |
| if (pauseresume_pause == 1) |
| sb.appendf (GTXT ("\tpause-resume (delayed initialization) signal %d -- paused\n"), |
| pauseresume_sig); |
| else |
| sb.appendf (GTXT ("\tpause-resume (delayed initialization) signal %d\n"), |
| pauseresume_sig); |
| } |
| } |
| if (sample_sig != 0) |
| { |
| const char *buf = strsignal (sample_sig); |
| if (buf != NULL) |
| sb.appendf (GTXT ("\tsample signal %s (%d)\n"), buf, sample_sig); |
| else |
| sb.appendf (GTXT ("\tsample signal %d\n"), sample_sig); |
| } |
| if (time_run != 0 || start_delay != 0) |
| { |
| if (start_delay != 0) |
| { |
| if (time_run != 0) |
| sb.appendf (GTXT ("\tdata-collection duration, %d-%d secs.\n"), |
| start_delay, time_run); |
| else |
| sb.appendf (GTXT ("\tdata-collection duration, %d- secs.\n"), |
| start_delay); |
| } |
| else |
| sb.appendf (GTXT ("\tdata-collection duration, %d secs.\n"), time_run); |
| } |
| if (sample_period != 0) |
| sb.appendf (GTXT ("\tperiodic sampling, %d secs.\n"), sample_period); |
| else |
| sb.append (GTXT ("\tno periodic sampling\n")); |
| if (size_limit != 0) |
| sb.appendf (GTXT ("\texperiment size limit %d MB.\n"), size_limit); |
| else |
| sb.append (GTXT ("\tno experiment size limit set\n")); |
| sb.appendf (GTXT ("\texperiment archiving: -a %s\n"), archive_mode); |
| sb.appendf (GTXT ("\tdata descriptor: \"%s\"\n"), |
| ((data_desc != NULL) ? data_desc : NTXT ("<NULL>"))); |
| sb.appendf (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); |
| sb.appendf (GTXT ("\t\tmemory: %ld pages @ %ld bytes = %lld MB.\n"), |
| npages, page_size, memsize); |
| } |
| return sb.toString (); |
| } |
| |
| #define MAX_COLLECT_ARGS 100 |
| |
| char ** |
| Coll_Ctrl::get_collect_args () |
| { |
| 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"); |
| *p++ = dbe_sprintf ("%du", clkprof_timer); |
| } |
| if (hwcprof_enabled_cnt > 0) |
| { |
| StringBuilder sb; |
| *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!! |
| if (ii > 0) |
| sb.append (','); |
| sb.append (hwctr[ii].name); |
| if (rateString) |
| { |
| sb.append (rateString); |
| free (rateString); |
| } |
| if (ii + 1 < hwcprof_enabled_cnt) |
| sb.append (','); |
| } |
| *p++ = sb.toString (); |
| } |
| if (heaptrace_mode != NULL) |
| { |
| *p++ = strdup ("-H"); |
| *p++ = strdup (heaptrace_mode); |
| } |
| 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; |
| StringBuilder sb; |
| sb.appendf (GTXT ("Creating experiment directory %s (Process ID: %ld) ...\n"), |
| store_ptr != NULL ? store_ptr : "<NULL>", (long) getpid ()); |
| char *caller = getenv ("SP_COLLECTOR_FROM_GUI"); // Collector from GUI |
| if (caller != NULL) // Print non-localized message |
| sb.appendf ("\nCreating experiment directory %s (Process ID: %ld) ...\n", |
| store_ptr != NULL ? store_ptr : "<NULL>", (long) getpid ()); |
| return sb.toString (); |
| } |
| |
| 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') |
| 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); |
| if (*endchar != 0 || tval < 0) |
| { |
| free (val); |
| return dbe_sprintf (GTXT ("Unrecognized synchronization tracing threshold `%s'\n"), string); |
| } |
| free (val); |
| 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")); |
| free(heaptrace_mode); |
| heaptrace_mode = NULL; // Same as "off" |
| if (string != NULL && strcmp (string, "off") == 0) |
| return NULL; |
| |
| if (string == NULL || strlen (string) == 0 || strcmp (string, "on") == 0) |
| heaptrace_mode = strdup ("on"); |
| else if (isdigit (*string)) |
| { |
| char *s; |
| unsigned long long n = strtoull (string, &s, 0); |
| if (*s == '-' && isdigit (s[1])) |
| { |
| unsigned long long n1 = strtoull (s + 1, &s, 0); |
| if (n1 < n) |
| return dbe_sprintf ( |
| GTXT ("Incorrect range in heap trace parameter '%s'\n"), string); |
| } |
| if (*s != 0) |
| return dbe_sprintf ( |
| GTXT ("Incorrect range in heap trace parameter '%s'\n"), string); |
| heaptrace_mode = strdup (string); |
| } |
| else |
| return dbe_sprintf (GTXT ("Unrecognized heap tracing parameter `%s'\n"), |
| string); |
| char *ret = check_consistency (); |
| if (ret != NULL) |
| { |
| free (heaptrace_mode); |
| heaptrace_mode = NULL; |
| return ret; |
| } |
| return NULL; |
| } |
| |
| 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; |
| } |
| |
| // 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; |
| Hwcentry tmpctr[MAX_PICS]; |
| Hwcentry * ctrtable[MAX_PICS]; |
| char *emsg; |
| char *wmsg; |
| 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 */ |
| 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); |
| StringBuilder sb; |
| 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); |
| if (ii > 0) |
| sb.append (','); |
| sb.append (hwctr[ii].name); |
| sb.append (','); |
| if (rateString) |
| { |
| sb.append (rateString); |
| free (rateString); |
| } |
| } |
| hwc_string = sb.toString (); |
| 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 () |
| { |
| StringBuilder sb; |
| |
| // Put sample sig before clock profiling. Dbx uses PROF |
| // for that purpose and we want it to be processed first. |
| if (project_home) |
| sb.appendf ("P:%s;", project_home); |
| if (sample_sig != 0) |
| sb.appendf ("g:%d;", sample_sig); |
| if (pauseresume_sig != 0) |
| sb.appendf ("d:%d%s;", pauseresume_sig, pauseresume_pause == 1 ? "p" : ""); |
| if (clkprof_enabled == 1) |
| sb.appendf ("p:%d;", clkprof_timer); |
| if (synctrace_enabled == 1) |
| sb.appendf ("s:%d,%d;", synctrace_thresh, synctrace_scope); |
| if (heaptrace_mode != NULL && strcmp (heaptrace_mode, "off") != 0) |
| sb.appendf ("H:%s;", heaptrace_mode); |
| if (iotrace_enabled == 1) |
| sb.append ("i:;"); |
| if (hwcprof_enabled_cnt > 0) |
| { |
| sb.appendf ("h:%s", (hwcprof_default == true) ? "*" : ""); |
| for (int ii = 0; ii < hwcprof_enabled_cnt; ii++) |
| { |
| Hwcentry *h = hwctr + ii; |
| hrtime_t min_time = h->min_time; |
| if (min_time == HWCTIME_TBD) |
| // user did not specify any value for overflow rate |
| min_time = h->min_time_default; |
| if (ii > 0) |
| sb.append (','); |
| sb.appendf ("%d:%d:%lld:%lld:%s:%s:%lld:%d:m%lld:%d:%d:0x%x", |
| h->use_perf_event_type, h->type, (long long) h->config, |
| (long long) h->config1, |
| strcmp (h->name, h->int_name) ? h->name : "", |
| h->int_name, (long long) h->reg_num, h->val, |
| (long long) min_time, ii, /*tag*/ h->timecvt, h->memop); |
| } |
| sb.append (";"); |
| } |
| if (time_run != 0 || start_delay != 0) |
| { |
| if (start_delay != 0) |
| sb.appendf ("t:%d:%d;", start_delay, time_run); |
| else |
| sb.appendf ("t:%d;", time_run); |
| } |
| if (sample_period != 0) |
| sb.appendf ("S:%d;", sample_period); |
| if (size_limit != 0) |
| sb.appendf ("L:%d;", size_limit); |
| if (java_mode != 0) |
| sb.appendf ("j:%d;", (int) java_mode); |
| if (follow_mode != FOLLOW_NONE) |
| sb.appendf ("F:%d;", (int) follow_mode); |
| sb.appendf ("a:%s;", archive_mode); |
| free (data_desc); |
| data_desc = sb.toString (); |
| } |
| |
| 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 |
| 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 () |
| { |
| StringBuilder sb; |
| |
| /* 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); |
| } |
| if (*expt_name == '/' && udir_name != NULL) |
| sb.appendf (GTXT ("Warning: Experiment name is an absolute path; directory name %s ignored.\n"), |
| udir_name); |
| |
| // now extract the directory and basename |
| char *s = strrchr (expt_name, '/'); |
| if (s == NULL) |
| { |
| expt_dir = strdup ("."); |
| base_name = strdup (expt_name); |
| } |
| else |
| { |
| expt_dir = dbe_strndup (expt_name, s - expt_name - 1); |
| base_name = strdup (s + 1); |
| } |
| |
| 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 |
| store_dir = dbe_sprintf ("%s/%s", udir_name, expt_dir); |
| } |
| free (store_ptr); |
| if (strcmp (store_dir, ".") == 0) |
| store_ptr = strdup (base_name); |
| else |
| store_ptr = dbe_sprintf ("%s/%s", store_dir, base_name); |
| |
| // 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)) |
| sb.appendf (GTXT ("Experiment 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"), fstype); |
| } |
| if (sb.length () == 0) |
| return NULL; |
| return sb.toString (); |
| } |
| |
| 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_mode == NULL) |
| 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) |
|