/* 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 #include #include #include #include "enums.h" #include "Settings.h" #include "DbeSession.h" #include "Command.h" #include "Application.h" #include "MemorySpace.h" #include "StringBuilder.h" #include "Table.h" #include "Emsg.h" #include "util.h" #include "i18n.h" // Commands for compiler commentary static const char *comp_cmd[] = { NTXT ("basic"), NTXT ("version"), NTXT ("warn"), NTXT ("parallel"), NTXT ("query"), NTXT ("loop"), NTXT ("pipe"), NTXT ("inline"), NTXT ("memops"), NTXT ("fe"), NTXT ("codegen"), NTXT ("src"), NTXT ("asrc"), NTXT ("nosrc"), NTXT ("hex"), NTXT ("nohex"), NTXT ("threshold"), NTXT ("cf") }; static const int comp_vis[] = { CCMV_BASIC, CCMV_VER, CCMV_WARN, CCMV_PAR, CCMV_QUERY, CCMV_LOOP, CCMV_PIPE, CCMV_INLINE, CCMV_MEMOPS, CCMV_FE, CCMV_CG, COMP_SRC, COMP_SRC_METRIC, COMP_NOSRC, COMP_HEX, COMP_NOHEX, COMP_THRESHOLD, COMP_CMPLINE }; const int comp_size = sizeof (comp_cmd) / sizeof (char *); // Commands for timeline typedef enum { TLCMD_INVALID, TLCMD_ENTITY_MODE, TLCMD_ALIGN, TLCMD_DEPTH } TLModeSubcommand; typedef struct { const char * cmdText; TLModeSubcommand cmdType; int cmdId; } TLModeCmd; static const TLModeCmd tlmode_cmd[] = { // MODE commands {NTXT ("lwp"), TLCMD_ENTITY_MODE, PROP_LWPID}, {NTXT ("thread"), TLCMD_ENTITY_MODE, PROP_THRID}, {NTXT ("cpu"), TLCMD_ENTITY_MODE, PROP_CPUID}, {NTXT ("experiment"), TLCMD_ENTITY_MODE, PROP_EXPID}, // ALIGN commands {NTXT ("root"), TLCMD_ALIGN, TLSTACK_ALIGN_ROOT}, {NTXT ("leaf"), TLCMD_ALIGN, TLSTACK_ALIGN_LEAF}, // DEPTH commands {NTXT ("depth"), TLCMD_DEPTH, 0 /* don't care */} }; static const int tlmode_size = sizeof (tlmode_cmd) / sizeof (TLModeCmd); // Constructor Settings::Settings (Application *_app) { // Remember the application app = _app; // Clear all default strings str_vmode = NULL; str_en_desc = NULL; str_datamode = NULL; str_scompcom = NULL; str_sthresh = NULL; str_dcompcom = NULL; str_dthresh = NULL; str_dmetrics = NULL; str_dsort = NULL; str_tlmode = NULL; str_tldata = NULL; str_tabs = NULL; str_rtabs = NULL; str_search_path = NULL; str_name_format = NULL; str_limit = NULL; str_printmode = NULL; str_compare = NULL; preload_libdirs = NULL; pathmaps = new Vector; lo_expands = new Vector; lo_expand_default = LIBEX_SHOW; is_loexpand_default = true; tabs_processed = false; // set default-default values name_format = Histable::NA; view_mode = VMODE_USER; en_desc = false; en_desc_cmp = NULL; en_desc_usr = NULL; src_compcom = 2147483647; dis_compcom = 2147483647; #define DEFAULT_SRC_DIS_THRESHOLD 75 threshold_src = DEFAULT_SRC_DIS_THRESHOLD; threshold_dis = DEFAULT_SRC_DIS_THRESHOLD; src_visible = true; srcmetric_visible = false; hex_visible = false; cmpline_visible = true; funcline_visible = true; tldata = NULL; tlmode = 0; stack_align = 0; stack_depth = 0; limit = 0; // print mode is initialized after the .rc files are read print_delim = ','; compare_mode = CMP_DISABLE; machinemodel = NULL; ignore_no_xhwcprof = false; ignore_fs_warn = false; // construct the master list of tabs buildMasterTabList (); indx_tab_state = new Vector; indx_tab_order = new Vector; mem_tab_state = new Vector; mem_tab_order = new Vector; // note that the .rc files are not read here, but later } // Constructor for duplicating an existing Settings class Settings::Settings (Settings * _settings) { int index; app = _settings->app; // Copy all default strings str_vmode = dbe_strdup (_settings->str_vmode); str_en_desc = dbe_strdup (_settings->str_en_desc); str_datamode = dbe_strdup (_settings->str_datamode); str_scompcom = dbe_strdup (_settings->str_scompcom); str_sthresh = dbe_strdup (_settings->str_sthresh); str_dcompcom = dbe_strdup (_settings->str_dcompcom); str_dthresh = dbe_strdup (_settings->str_dthresh); str_dmetrics = dbe_strdup (_settings->str_dmetrics); str_dsort = dbe_strdup (_settings->str_dsort); str_tlmode = dbe_strdup (_settings->str_tlmode); str_tldata = dbe_strdup (_settings->str_tldata); str_tabs = dbe_strdup (_settings->str_tabs); str_rtabs = dbe_strdup (_settings->str_rtabs); str_search_path = dbe_strdup (_settings->str_search_path); str_name_format = dbe_strdup (_settings->str_name_format); str_limit = dbe_strdup (_settings->str_limit); str_printmode = dbe_strdup (_settings->str_printmode); str_compare = dbe_strdup (_settings->str_compare); preload_libdirs = dbe_strdup (_settings->preload_libdirs); // replicate the pathmap vector pathmap_t *thismap; pathmap_t *newmap; pathmaps = new Vector; Vec_loop (pathmap_t*, _settings->pathmaps, index, thismap) { newmap = new pathmap_t; newmap->old_prefix = dbe_strdup (thismap->old_prefix); newmap->new_prefix = dbe_strdup (thismap->new_prefix); pathmaps->append (newmap); } // replicate the lo_expand vector and default lo_expand_t *this_lo_ex; lo_expand_t *new_lo_ex; lo_expand_default = _settings->lo_expand_default; is_loexpand_default = _settings->is_loexpand_default; lo_expands = new Vector; Vec_loop (lo_expand_t*, _settings->lo_expands, index, this_lo_ex) { new_lo_ex = new lo_expand_t; new_lo_ex->libname = dbe_strdup (this_lo_ex->libname); new_lo_ex->expand = this_lo_ex->expand; lo_expands->append (new_lo_ex); } tabs_processed = _settings->tabs_processed; // Copy the various values from the _settings instance name_format = _settings->name_format; view_mode = _settings->view_mode; en_desc = false; en_desc_cmp = NULL; en_desc_usr = NULL; if (_settings->en_desc_usr) set_en_desc (_settings->en_desc_usr, true); src_compcom = _settings->src_compcom; dis_compcom = _settings->dis_compcom; threshold_src = _settings->threshold_src; threshold_dis = _settings->threshold_dis; src_visible = _settings->src_visible; srcmetric_visible = _settings->srcmetric_visible; hex_visible = _settings->hex_visible; cmpline_visible = _settings->cmpline_visible; funcline_visible = _settings->funcline_visible; tldata = dbe_strdup (_settings->tldata); tlmode = _settings->tlmode; stack_align = _settings->stack_align; stack_depth = _settings->stack_depth; limit = _settings->limit; print_mode = _settings->print_mode; print_delim = _settings->print_delim; compare_mode = _settings->compare_mode; machinemodel = dbe_strdup (_settings->machinemodel); ignore_no_xhwcprof = _settings->ignore_no_xhwcprof; ignore_fs_warn = _settings->ignore_fs_warn; // copy the tab list, too tab_list = new Vector; DispTab *dsptab; Vec_loop (DispTab*, _settings->tab_list, index, dsptab) { DispTab *ntab; ntab = new DispTab (dsptab->type, dsptab->order, dsptab->visible, dsptab->cmdtoken); ntab->setAvailability (dsptab->available); tab_list->append (ntab); } // construct the master list of memory tabs & copy order index = _settings->mem_tab_state->size (); mem_tab_state = new Vector(index); mem_tab_order = new Vector(index); for (int i = 0; i < index; i++) { mem_tab_state->append (false); mem_tab_order->append (_settings->mem_tab_order->fetch (i)); } // construct the master list of index tabs & copy order index = _settings->indx_tab_state->size (); indx_tab_state = new Vector(index); indx_tab_order = new Vector(index); for (int i = 0; i < index; i++) indx_tab_order->append (_settings->indx_tab_order->fetch (i)); set_IndxTabState (_settings->indx_tab_state); } Settings::~Settings () { for (int i = 0; i < pathmaps->size (); ++i) { pathmap_t *pmap = pathmaps->fetch (i); free (pmap->old_prefix); free (pmap->new_prefix); delete pmap; } delete pathmaps; for (int i = 0; i < lo_expands->size (); ++i) { lo_expand_t *lo_ex = lo_expands->fetch (i); free (lo_ex->libname); delete lo_ex; } delete lo_expands; tab_list->destroy (); delete tab_list; delete indx_tab_state; delete indx_tab_order; delete mem_tab_state; delete mem_tab_order; free (str_vmode); free (str_en_desc); free (str_datamode); free (str_scompcom); free (str_sthresh); free (str_dcompcom); free (str_dthresh); free (str_dmetrics); free (str_dsort); free (str_tlmode); free (str_tldata); free (str_tabs); free (str_rtabs); free (str_search_path); free (str_name_format); free (str_limit); free (str_compare); free (str_printmode); free (preload_libdirs); free (tldata); free (en_desc_usr); if (en_desc_cmp) { regfree (en_desc_cmp); delete en_desc_cmp; } } /** * Read .er.rc file from the specified location * @param path * @return */ char * Settings::read_rc (char *path) { StringBuilder sb; Emsgqueue *commentq = new Emsgqueue (NTXT ("setting_commentq")); // Check file name if (NULL == path) return dbe_strdup (GTXT ("Error: empty file name")); bool override = true; set_rc (path, true, commentq, override); Emsg *msg = commentq->fetch (); while (msg != NULL) { char *str = msg->get_msg (); sb.append (str); msg = msg->next; } return sb.toString (); } void Settings::read_rc (bool ipc_or_rdt_mode) { bool override = false; // Read file from the current working directory char *rc_path = realpath (NTXT ("./.gprofng.rc"), NULL); if (rc_path) set_rc (rc_path, true, app->get_comments_queue (), override, ipc_or_rdt_mode); // Read file from the user's home directory char *home = getenv (NTXT ("HOME")); if (home) { char *strbuf = dbe_sprintf (NTXT ("%s/.gprofng.rc"), home); char *home_rc_path = realpath (strbuf, NULL); if (home_rc_path) { if (rc_path == NULL || strcmp (rc_path, home_rc_path) != 0) set_rc (home_rc_path, true, app->get_comments_queue (), override, ipc_or_rdt_mode); free (home_rc_path); } free (strbuf); } free (rc_path); // Read system-wide file const char *sysconfdir = getenv("GPROFNG_SYSCONFDIR"); if (sysconfdir == NULL) sysconfdir = SYSCONFDIR; rc_path = dbe_sprintf (NTXT ("%s/gprofng.rc"), sysconfdir); if (access (rc_path, R_OK | F_OK) != 0) { StringBuilder sb; sb.sprintf (GTXT ("Warning: Default gprofng.rc file (%s) missing; configuration error "), rc_path); Emsg *m = new Emsg (CMSG_COMMENT, sb); app->get_comments_queue ()->append (m); } else set_rc (rc_path, false, app->get_comments_queue (), override); free (rc_path); is_loexpand_default = true; if (str_printmode == NULL) { // only if there's none set print_mode = PM_TEXT; str_printmode = dbe_strdup (NTXT ("text")); } } // Handle various settings from reading the name .rc file // This function is called for each .rc file read, and, for // some settings, it accumulates the strings from the files. // For others, it accepts the first appearance for a setting in a // .rc file, and ignores subsequent appearances from other files. // Error messages are appended to the Emsgqueue specified by the caller #define MAXARGS 20 void Settings::set_rc (const char *path, bool msg, Emsgqueue *commentq, bool override, bool ipc_or_rdt_mode) { CmdType cmd_type; int arg_count, cparam; char *cmd, *end_cmd, *strbuf; char *arglist[MAXARGS]; StringBuilder sb; FILE *fptr = fopen (path, NTXT ("r")); if (fptr == NULL) return; if (msg) { sb.sprintf (GTXT ("Processed %s for default settings"), path); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); } int line_no = 0; end_cmd = NULL; while (!feof (fptr)) { char *script = read_line (fptr); if (script == NULL) continue; line_no++; strtok (script, NTXT ("\n")); // extract the command cmd = strtok (script, NTXT (" \t")); if (cmd == NULL || *cmd == '#' || *cmd == '\n') { free (script); continue; } char *remainder = strtok (NULL, NTXT ("\n")); // now extract the arguments int nargs = 0; for (;;) { if (nargs >= MAXARGS) { if (!msg) { msg = true; // suppress repeats of header Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); commentq->append (m); } sb.sprintf (GTXT ("Warning: more than %d arguments to %s command, line %d\n"), MAXARGS, cmd, line_no); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); break; } char *nextarg = strtok (remainder, NTXT ("\n")); if (nextarg == NULL || *nextarg == '#') break; arglist[nargs++] = parse_qstring (nextarg, &end_cmd); remainder = end_cmd; if (remainder == NULL) break; // skip any blanks or tabs to get to next argument while (*remainder == ' ' || *remainder == '\t') remainder++; } cmd_type = Command::get_command (cmd, arg_count, cparam); // check for extra arguments if ((cmd_type != UNKNOWN_CMD && cmd_type != INDXOBJDEF) && (nargs > arg_count)) { if (!msg) { msg = true; // suppress repeats of header Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); commentq->append (m); } sb.sprintf (GTXT ("Warning: extra arguments to %s command, line %d\n"), cmd, line_no); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); } if (nargs < arg_count) { if (!msg) { msg = true; // suppress repeats of header Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); commentq->append (m); } sb.sprintf (GTXT ("Error: missing arguments to %s command, line %d\n"), cmd, line_no); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); // ignore this command free (script); continue; } if (ipc_or_rdt_mode && (cmd_type != ADDPATH) && (cmd_type != PATHMAP)) { free (script); continue; } switch (cmd_type) { case SCOMPCOM: if (!str_scompcom || override) { str_scompcom = dbe_strdup (arglist[0]); proc_compcom (arglist[0], true, true); } break; case STHRESH: if (!str_sthresh || override) { str_sthresh = dbe_strdup (arglist[0]); proc_thresh (arglist[0], true, true); break; } break; case DCOMPCOM: if (!str_dcompcom || override) { str_dcompcom = dbe_strdup (arglist[0]); proc_compcom (arglist[0], false, true); } break; case COMPCOM: // process as if it were for both source and disassembly // note that if it is set, subsequent SCOMPCOM and DCOMPCOM // will be ignored if (!str_scompcom || override) { str_scompcom = dbe_strdup (arglist[0]); proc_compcom (arglist[0], true, true); } if (!str_dcompcom || override) { str_dcompcom = dbe_strdup (arglist[0]); proc_compcom (arglist[0], false, true); } break; case DTHRESH: if (!str_dthresh || override) { str_dthresh = dbe_strdup (arglist[0]); proc_thresh (arglist[0], false, true); } break; case DMETRICS: // append new settings to old, if necessary if (str_dmetrics) { char *name = strstr (str_dmetrics, ":name"); if (name == NULL) strbuf = dbe_sprintf ("%s:%s", str_dmetrics, arglist[0]); else { char * next = strstr (name + 1, ":"); if (next == NULL) { name[0] = '\0'; strbuf = dbe_sprintf ("%s:%s:name", str_dmetrics, arglist[0]); } else strbuf = dbe_sprintf ("%s:%s", str_dmetrics, arglist[0]); } free (str_dmetrics); str_dmetrics = strbuf; } else str_dmetrics = dbe_strdup (arglist[0]); break; case DSORT: // append new settings to old, if necessary if (str_dsort) { strbuf = dbe_sprintf (NTXT ("%s:%s"), str_dsort, arglist[0]); free (str_dsort); str_dsort = strbuf; } else str_dsort = dbe_strdup (arglist[0]); break; case TLMODE: if (!str_tlmode || override) { str_tlmode = dbe_strdup (arglist[0]); proc_tlmode (arglist[0], true); } break; case TLDATA: if (!str_tldata || override) { str_tldata = dbe_strdup (arglist[0]); proc_tldata (arglist[0], true); } break; case TABS: if (!str_tabs || override) // the string is processed later, after all .rc files are read str_tabs = dbe_strdup (arglist[0]); break; case RTABS: if (!str_rtabs || override) // the string is processed later, after all .rc files are read str_rtabs = dbe_strdup (arglist[0]); break; case ADDPATH: if (str_search_path) { strbuf = dbe_sprintf (NTXT ("%s:%s"), str_search_path, arglist[0]); free (str_search_path); str_search_path = strbuf; } else str_search_path = dbe_strdup (arglist[0]); break; case PATHMAP: { char *err = add_pathmap (pathmaps, arglist[0], arglist[1]); free (err); // XXX error is not reported break; } case LIBDIRS: if (preload_libdirs == NULL) preload_libdirs = dbe_strdup (arglist[0]); break; case NAMEFMT: if (name_format == Histable::NA) set_name_format (arglist[0]); break; case VIEWMODE: if (!str_vmode || override) { str_vmode = dbe_strdup (arglist[0]); set_view_mode (arglist[0], true); } break; case EN_DESC: if (!str_en_desc || override) { str_en_desc = dbe_strdup (arglist[0]); set_en_desc (arglist[0], true); } break; case LIMIT: if (!str_limit || override) { str_limit = dbe_strdup (arglist[0]); set_limit (arglist[0], true); } break; case PRINTMODE: if (!str_printmode || override) set_printmode (arglist[0]); break; case COMPARE: if (!str_compare || override) { char *s = arglist[0]; if (s) str_compare = dbe_strdup (s); else s = NTXT (""); if (strcasecmp (s, NTXT ("OFF")) == 0 || strcmp (s, NTXT ("0")) == 0) set_compare_mode (CMP_DISABLE); else if (strcasecmp (s, NTXT ("ON")) == 0 || strcmp (s, NTXT ("1")) == 0) set_compare_mode (CMP_ENABLE); else if (strcasecmp (s, NTXT ("DELTA")) == 0) set_compare_mode (CMP_DELTA); else if (strcasecmp (s, NTXT ("RATIO")) == 0) set_compare_mode (CMP_RATIO); else { sb.sprintf (GTXT (" .er.rc:%d The argument of 'compare' should be 'on', 'off', 'delta', or 'ratio'"), (int) line_no); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); } } break; case INDXOBJDEF: { char *ret = dbeSession->indxobj_define (arglist[0], NULL, arglist[1], (nargs >= 3) ? PTXT (arglist[2]) : NULL, (nargs >= 4) ? PTXT (arglist[3]) : NULL); if (ret != NULL) { sb.sprintf (GTXT (" %s: line %d `%s %s %s'\n"), ret, line_no, cmd, arglist[0], arglist[1]); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); } break; } #ifdef sparc //XXX: should be conditional on the experiment ARCH, not dbe ARCH case IGNORE_NO_XHWCPROF: // ignore absence of -xhwcprof info for dataspace profiling set_ignore_no_xhwcprof (true); break; #endif // sparc case IGNORE_FS_WARN: // ignore file system warning in experiments set_ignore_fs_warn (true); break; case OBJECT_SHOW: // Add the named libraries to the lib_expands array set_libexpand (arglist[0], LIBEX_SHOW, true); break; case OBJECT_HIDE: // Add the named libraries to the lib_expands array set_libexpand (arglist[0], LIBEX_HIDE, true); break; case OBJECT_API: // Add the named libraries to the lib_expands array set_libexpand (arglist[0], LIBEX_API, true); break; case COMMENT: // ignore the line break; default: { // unexpected command in an rc file if (!msg) { // if quiet, can remain so no longer msg = true; Emsg *m = new Emsg (CMSG_COMMENT, GTXT ("Processed system gprofng.rc file for default settings")); commentq->append (m); } sb.sprintf (GTXT (" Unrecognized .gprofng.rc command on line %d: `%.64s'"), line_no, cmd); Emsg *m = new Emsg (CMSG_COMMENT, sb); commentq->append (m); break; } } free (script); } fclose (fptr); } Cmd_status Settings::set_view_mode (char *arg, bool rc) { if (!strcasecmp (arg, NTXT ("user"))) view_mode = VMODE_USER; else if (!strcasecmp (arg, NTXT ("expert"))) view_mode = VMODE_EXPERT; else if (!strcasecmp (arg, NTXT ("machine"))) view_mode = VMODE_MACHINE; else if (!rc) return CMD_BAD_ARG; return CMD_OK; } Cmd_status Settings::set_en_desc (char *arg, bool rc) { regex_t *regex_desc = NULL; // cases below should be similar to Coll_Ctrl::set_follow_mode() cases if (!strcasecmp (arg, NTXT ("on"))) en_desc = true; else if (!strcasecmp (arg, NTXT ("off"))) en_desc = false; else if (arg[0] == '=' && arg[1] != 0) { // user has specified a string matching specification int ercode; { // compile regex_desc char * str = dbe_sprintf (NTXT ("^%s$"), arg + 1); regex_desc = new regex_t; memset (regex_desc, 0, sizeof (regex_t)); ercode = regcomp (regex_desc, str, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); free (str); } if (ercode) { // syntax error in parsing string delete regex_desc; if (!rc) return CMD_BAD_ARG; return CMD_OK; } en_desc = true; } else { if (!rc) return CMD_BAD_ARG; return CMD_OK; } free (en_desc_usr); en_desc_usr = dbe_strdup (arg); if (en_desc_cmp) { regfree (en_desc_cmp); delete en_desc_cmp; } en_desc_cmp = regex_desc; return CMD_OK; } // See if a descendant matches either the lineage or the executable name bool Settings::check_en_desc (const char *lineage, const char *targname) { bool rc; if (en_desc_cmp == NULL) return en_desc; // no specification was set, use the binary on/off value if (lineage == NULL) // user doesn't care about specification return en_desc; // use the binary on/off specification if (!regexec (en_desc_cmp, lineage, 0, NULL, 0)) rc = true; // this one matches user specification else if (targname == NULL) rc = false; //a NULL name does not match any expression else if (!regexec (en_desc_cmp, targname, 0, NULL, 0)) rc = true; // this one matches the executable name else rc = false; return rc; } char * Settings::set_limit (char *arg, bool) { limit = (int) strtol (arg, (char **) NULL, 10); return NULL; } char * Settings::set_printmode (char *arg) { if (arg == NULL) return dbe_sprintf (GTXT ("The argument to '%s' must be '%s' or '%s' or a single-character"), NTXT ("printmode"), NTXT ("text"), NTXT ("html")); if (strlen (arg) == 1) { print_mode = PM_DELIM_SEP_LIST; print_delim = arg[0]; } else if (!strcasecmp (arg, NTXT ("text"))) print_mode = PM_TEXT; else if (!strcasecmp (arg, NTXT ("html"))) print_mode = PM_HTML; else return dbe_sprintf (GTXT ("The argument to '%s' must be '%s' or '%s' or a single-character"), NTXT ("printmode"), NTXT ("text"), NTXT ("html")); free (str_printmode); str_printmode = dbe_strdup (arg); return NULL; } Cmd_status Settings::proc_compcom (const char *cmd, bool isSrc, bool rc) { int ck_compcom_bits, ck_threshold; bool ck_hex_visible = false; bool ck_src_visible = false; bool ck_srcmetric_visible = false; bool got_compcom_bits, got_threshold, got_src_visible, got_srcmetric_visible; bool got_hex_visible, got; int len, i; char *mcmd, *param; int flag, value = 0; Cmd_status status; char buf[BUFSIZ], *list; if (cmd == NULL) return CMD_BAD; ck_compcom_bits = 0; ck_threshold = 0; got_compcom_bits = got_threshold = got_src_visible = false; got_srcmetric_visible = got_hex_visible = false; snprintf (buf, sizeof (buf), NTXT ("%s"), cmd); list = buf; while ((mcmd = strtok (list, NTXT (":"))) != NULL) { list = NULL; // if "all" or "none" if (!strcasecmp (mcmd, Command::ALL_CMD)) { got_compcom_bits = true; ck_compcom_bits = CCMV_ALL; continue; } else if (!strcasecmp (mcmd, Command::NONE_CMD)) { got_compcom_bits = true; ck_compcom_bits = 0; continue; } // Find parameter after '=' param = strchr (mcmd, '='); if (param) { *param = '\0'; param++; } status = CMD_OK; got = false; flag = 0; len = (int) strlen (mcmd); for (i = 0; status == CMD_OK && i < comp_size; i++) if (!strncasecmp (mcmd, comp_cmd[i], len)) { if (got) // Ambiguous comp_com command status = CMD_AMBIGUOUS; else { got = true; flag = comp_vis[i]; // Check argument if (flag == COMP_THRESHOLD) { if (param == NULL) status = CMD_BAD_ARG; else { value = (int) strtol (param, ¶m, 10); if (value < 0 || value > 100) status = CMD_OUTRANGE; } } else if (param != NULL) status = CMD_BAD_ARG; } } // Not valid comp_com command if (!got) status = CMD_INVALID; if (status != CMD_OK) { if (!rc) return status; continue; } // Set bits switch (flag) { case COMP_CMPLINE: cmpline_visible = true; break; case COMP_FUNCLINE: funcline_visible = true; break; case COMP_THRESHOLD: got_threshold = true; ck_threshold = value; break; case COMP_SRC: got_src_visible = true; ck_src_visible = true; break; case COMP_SRC_METRIC: got_srcmetric_visible = true; ck_srcmetric_visible = true; got_src_visible = true; ck_src_visible = true; break; case COMP_NOSRC: got_src_visible = true; ck_src_visible = false; break; case COMP_HEX: got_hex_visible = true; ck_hex_visible = true; break; case COMP_NOHEX: got_hex_visible = true; ck_hex_visible = false; break; case CCMV_BASIC: got_compcom_bits = true; ck_compcom_bits = CCMV_BASIC; break; default: got_compcom_bits = true; ck_compcom_bits |= flag; } } // No error, update if (got_compcom_bits) { if (isSrc) src_compcom = ck_compcom_bits; else dis_compcom = ck_compcom_bits; } if (got_threshold) { if (isSrc) threshold_src = ck_threshold; else threshold_dis = ck_threshold; } if (got_src_visible) src_visible = ck_src_visible; if (got_srcmetric_visible) srcmetric_visible = ck_srcmetric_visible; if (got_hex_visible) hex_visible = ck_hex_visible; return CMD_OK; } // Process a threshold setting Cmd_status Settings::proc_thresh (char *cmd, bool isSrc, bool rc) { int value; if (cmd == NULL) value = DEFAULT_SRC_DIS_THRESHOLD; // the default else value = (int) strtol (cmd, &cmd, 10); if (value < 0 || value > 100) { if (!rc) return CMD_OUTRANGE; value = DEFAULT_SRC_DIS_THRESHOLD; } if (isSrc) threshold_src = value; else threshold_dis = value; return CMD_OK; } // return any error string from processing visibility settings char * Settings::get_compcom_errstr (Cmd_status status, const char *cmd) { int i; StringBuilder sb; switch (status) { case CMD_BAD: sb.append (GTXT ("No commentary classes has been specified.")); break; case CMD_AMBIGUOUS: sb.append (GTXT ("Ambiguous commentary classes: ")); break; case CMD_BAD_ARG: sb.append (GTXT ("Invalid argument for commentary classes: ")); break; case CMD_OUTRANGE: sb.append (GTXT ("Out of range commentary classes argument: ")); break; case CMD_INVALID: sb.append (GTXT ("Invalid commentary classes: ")); break; case CMD_OK: break; } if (cmd) sb.append (cmd); sb.append (GTXT ("\nAvailable commentary classes: ")); for (i = 0; i < comp_size; i++) { sb.append (comp_cmd[i]); if (i == comp_size - 1) sb.append (NTXT ("=#\n")); else sb.append (NTXT (":")); } return sb.toString (); } // Process a timeline-mode setting Cmd_status Settings::proc_tlmode (char *cmd, bool rc) { bool got_tlmode, got_stack_align, got_stack_depth, got; int ck_tlmode = 0, ck_stack_align = 0, ck_stack_depth = 0; int len, i; char *mcmd, *param; int cmd_id, value = 0; TLModeSubcommand cmd_type; Cmd_status status; char buf[BUFSIZ], *list; if (cmd == NULL) return CMD_BAD; got_tlmode = got_stack_align = got_stack_depth = false; snprintf (buf, sizeof (buf), NTXT ("%s"), cmd); list = buf; while ((mcmd = strtok (list, NTXT (":"))) != NULL) { list = NULL; // Find parameter after '=' param = strchr (mcmd, '='); if (param) { *param = '\0'; param++; } status = CMD_OK; got = false; cmd_id = 0; cmd_type = TLCMD_INVALID; len = (int) strlen (mcmd); for (i = 0; status == CMD_OK && i < tlmode_size; i++) { if (!strncasecmp (mcmd, tlmode_cmd[i].cmdText, len)) { if (got) // Ambiguous timeline mode status = CMD_AMBIGUOUS; else { got = true; cmd_type = tlmode_cmd[i].cmdType; cmd_id = tlmode_cmd[i].cmdId; // Check argument if (cmd_type == TLCMD_DEPTH) { if (param == NULL) status = CMD_BAD_ARG; else { value = (int) strtol (param, ¶m, 10); if (value <= 0 || value > 256) status = CMD_OUTRANGE; } } else if (param != NULL) status = CMD_BAD_ARG; } } } // Not valid timeline mode if (!got) status = CMD_INVALID; if (status != CMD_OK) { if (!rc) return status; continue; } // Set bits switch (cmd_type) { case TLCMD_ENTITY_MODE: got_tlmode = true; ck_tlmode = cmd_id; break; case TLCMD_ALIGN: got_stack_align = true; ck_stack_align = cmd_id; break; case TLCMD_DEPTH: got_stack_depth = true; ck_stack_depth = value; break; default: break; } } // No error, update if (got_tlmode) tlmode = ck_tlmode; if (got_stack_align) stack_align = ck_stack_align; if (got_stack_depth) stack_depth = ck_stack_depth; return CMD_OK; } // Process timeline data specification Cmd_status Settings::proc_tldata (const char *cmd, bool /* if true, ignore any error */) { free (tldata); tldata = dbe_strdup (cmd); // let GUI parse it return CMD_OK; } void Settings::set_tldata (const char* _tldata_str) { free (tldata); tldata = dbe_strdup (_tldata_str); } char* Settings::get_tldata () { return dbe_strdup (tldata); } Cmd_status Settings::set_name_format (char *arg) { char *colon = strchr (arg, ':'); size_t arg_len = (colon) ? (colon - arg) : strlen (arg); Histable::NameFormat fname_fmt = Histable::NA; if (!strncasecmp (arg, NTXT ("long"), arg_len)) fname_fmt = Histable::LONG; else if (!strncasecmp (arg, NTXT ("short"), arg_len)) fname_fmt = Histable::SHORT; else if (!strncasecmp (arg, NTXT ("mangled"), arg_len)) fname_fmt = Histable::MANGLED; else return CMD_BAD_ARG; bool soname_fmt = false; if (colon) { colon++; if (!strcasecmp (colon, NTXT ("soname"))) soname_fmt = true; else if (!strcasecmp (colon, NTXT ("nosoname"))) soname_fmt = false; else return CMD_BAD_ARG; } name_format = Histable::make_fmt (fname_fmt, soname_fmt); return CMD_OK; } void Settings::buildMasterTabList () { tab_list = new Vector; int i = -1; // Add tabs for all the known reports tab_list->append (new DispTab (DSP_DEADLOCKS, i, false, DEADLOCK_EVNTS)); tab_list->append (new DispTab (DSP_FUNCTION, i, false, FUNCS)); tab_list->append (new DispTab (DSP_TIMELINE, i, false, TIMELINE)); tab_list->append (new DispTab (DSP_CALLTREE, i, false, CALLTREE)); tab_list->append (new DispTab (DSP_CALLFLAME, i, false, CALLFLAME)); tab_list->append (new DispTab (DSP_DUALSOURCE, i, false, DUALSOURCE)); tab_list->append (new DispTab (DSP_SOURCE_DISASM, i, false, SOURCEDISAM)); tab_list->append (new DispTab (DSP_SOURCE, i, false, SOURCE)); tab_list->append (new DispTab (DSP_LINE, i, false, HOTLINES)); tab_list->append (new DispTab (DSP_DISASM, i, false, DISASM)); tab_list->append (new DispTab (DSP_PC, i, false, HOTPCS)); tab_list->append (new DispTab (DSP_LEAKLIST, i, false, LEAKS)); tab_list->append (new DispTab (DSP_IOACTIVITY, i, false, IOACTIVITY)); tab_list->append (new DispTab (DSP_HEAPCALLSTACK, i, false, HEAP)); tab_list->append (new DispTab (DSP_IFREQ, i, false, IFREQ)); tab_list->append (new DispTab (DSP_CALLER, i, false, GPROF)); tab_list->append (new DispTab (DSP_STATIS, i, false, STATISTICS)); tab_list->append (new DispTab (DSP_EXP, i, false, HEADER)); } // Update tablist based on data availability void Settings::updateTabAvailability () { int index; DispTab *dsptab; Vec_loop (DispTab*, tab_list, index, dsptab) { if (dsptab->type == DSP_DATAOBJ) dsptab->setAvailability (dbeSession->is_datamode_available ()); else if (dsptab->type == DSP_DLAYOUT) dsptab->setAvailability (dbeSession->is_datamode_available ()); else if (dsptab->type == DSP_LEAKLIST) dsptab->setAvailability (false); else if (dsptab->type == DSP_IOACTIVITY) dsptab->setAvailability (dbeSession->is_iodata_available ()); else if (dsptab->type == DSP_HEAPCALLSTACK) dsptab->setAvailability (dbeSession->is_heapdata_available ()); else if (dsptab->type == DSP_TIMELINE) dsptab->setAvailability (dbeSession->is_timeline_available ()); else if (dsptab->type == DSP_IFREQ) dsptab->setAvailability (dbeSession->is_ifreq_available ()); else if (dsptab->type == DSP_RACES) dsptab->setAvailability (dbeSession->is_racelist_available ()); else if (dsptab->type == DSP_DEADLOCKS) dsptab->setAvailability (dbeSession->is_deadlocklist_available ()); else if (dsptab->type == DSP_DUALSOURCE) dsptab->setAvailability (dbeSession->is_racelist_available () || dbeSession->is_deadlocklist_available ()); } } // Process a tab setting Cmd_status Settings::proc_tabs (bool _rdtMode) { int arg_cnt, cparam; int count = 0; int index; DispTab *dsptab; char *cmd; if (tabs_processed == true) return CMD_OK; tabs_processed = true; if (_rdtMode == true) { if (str_rtabs == NULL) str_rtabs = xstrdup ("header"); cmd = str_rtabs; } else { if (str_tabs == NULL) str_tabs = xstrdup ("header"); cmd = str_tabs; } if (strcmp (cmd, NTXT ("none")) == 0) return CMD_OK; Vector *tokens = split_str (cmd, ':'); for (long j = 0, sz = VecSize (tokens); j < sz; j++) { char *tabname = tokens->get (j); // search for this tab command token CmdType c = Command::get_command (tabname, arg_cnt, cparam); if (c == INDXOBJ) { // set the bit for this subtype indx_tab_state->store (cparam, true); indx_tab_order->store (cparam, count++); } else { // search for this tab type in the regular tabs Vec_loop (DispTab*, tab_list, index, dsptab) { if (dsptab->cmdtoken == c) { dsptab->visible = true; dsptab->order = count++; break; } } } free (tabname); } delete tokens; return CMD_OK; } void Settings::set_MemTabState (Vector*selected) { if (selected->size () == 0) return; for (int j = 0; j < mem_tab_state->size (); j++) mem_tab_state->store (j, selected->fetch (j)); } // define a new memory object type void Settings::mobj_define (MemObjType_t */* mobj */, bool state) { if (mem_tab_state->size () == 0) state = true; mem_tab_state->append (state); mem_tab_order->append (-1); } void Settings::set_IndxTabState (Vector*selected) { for (int j = 0; j < selected->size (); j++) indx_tab_state->store (j, selected->fetch (j)); } // define a new index object type void Settings::indxobj_define (int type, bool state) { indx_tab_state->store (type, state); indx_tab_order->store (type, -1); } void Settings::set_pathmaps (Vector *newPathMap) { if (pathmaps) { pathmaps->destroy (); delete pathmaps; } pathmaps = newPathMap; } static char * get_canonical_name (const char *fname) { char *nm = dbe_strdup (fname); for (size_t len = strlen (nm); (len > 0) && (nm[len - 1] == '/'); len--) nm[len - 1] = 0; return nm; } char * Settings::add_pathmap (Vector *v, const char *from, const char *to) { // Check for errors if (from == NULL || to == NULL) return dbe_strdup (GTXT ("Pathmap can have neither from nor to as NULL\n")); if (strcmp (from, to) == 0) return dbe_strdup (GTXT ("Pathmap from must differ from to\n")); char *old_prefix = get_canonical_name (from); char *new_prefix = get_canonical_name (to); // Check the pathmap list for (int i = 0, sz = v->size (); i < sz; i++) { pathmap_t *pmp = v->get (i); if ((strcmp (pmp->old_prefix, old_prefix) == 0) &&(strcmp (pmp->new_prefix, new_prefix) == 0)) { char *s = dbe_sprintf (GTXT ("Pathmap from `%s' to `%s' already exists\n"), old_prefix, new_prefix); free (old_prefix); free (new_prefix); return s; } } // construct a map for this pair pathmap_t *thismap = new pathmap_t; thismap->old_prefix = old_prefix; thismap->new_prefix = new_prefix; v->append (thismap); return NULL; } // Set all shared object expands back to .rc file defaults, // as stored in the DbeSession Settings bool Settings::set_libdefaults () { // See if this is unchanged if (is_loexpand_default == true) return false; // no change // replicate the DbeSession's lo_expand vector and default settings lo_expand_t *this_lo_ex; lo_expand_t *new_lo_ex; int index; lo_expand_default = dbeSession->get_settings ()->lo_expand_default; lo_expands = new Vector; Vec_loop (lo_expand_t*, dbeSession->get_settings ()->lo_expands, index, this_lo_ex) { new_lo_ex = new lo_expand_t; new_lo_ex->libname = dbe_strdup (this_lo_ex->libname); new_lo_ex->expand = this_lo_ex->expand; lo_expands->append (new_lo_ex); } is_loexpand_default = true; return true; } bool Settings::set_libexpand (char *cov, enum LibExpand expand, bool rc) { int index; lo_expand_t *loe; bool change = false; if (cov == NULL || !strcasecmp (cov, Command::ALL_CMD)) { // set all libraries // set the default if (lo_expand_default != expand) { lo_expand_default = expand; change = true; is_loexpand_default = false; } // and force any explicit settings to match, too Vec_loop (lo_expand_t*, lo_expands, index, loe) { if (loe->expand != expand) { loe->expand = expand; change = true; is_loexpand_default = false; } } } else { // parsing coverage Vector *tokens = split_str (cov, ','); for (long j = 0, sz = VecSize (tokens); j < sz; j++) { char *lo_name = tokens->get (j); char *newname = get_basename (lo_name); bool found = false; Vec_loop (lo_expand_t*, lo_expands, index, loe) { if (strcmp (loe->libname, newname) == 0) { if (loe->expand != expand) { if (rc == false) { loe->expand = expand; change = true; is_loexpand_default = false; } } found = true; break; } } if (found == false) { // construct a map for this pair lo_expand_t *thisloe; thisloe = new lo_expand_t; thisloe->libname = dbe_strdup (newname); thisloe->expand = expand; change = true; is_loexpand_default = false; // add it to the vector lo_expands->append (thisloe); } free (lo_name); } delete tokens; } return change; } enum LibExpand Settings::get_lo_setting (char *name) { int index; lo_expand_t *loe; char *lo_name = get_basename (name); Vec_loop (lo_expand_t*, lo_expands, index, loe) { if (strcmp (loe->libname, lo_name) == 0) return loe->expand; } return lo_expand_default; }