/* Copyright (C) 2021 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "util.h" #include "Command.h" #include "DbeSession.h" #include "MetricList.h" #include "StringBuilder.h" // Build a metric reference list MetricList::MetricList (Vector *base_metrics, MetricType _mtype) { mtype = _mtype; items = new Vector; sort_ref_index = 0; sort_reverse = false; Metric *mitem; // loop over the base_metrics, and add in all the appropriate subtypes for (long i = 0, sz = base_metrics ? base_metrics->size () : 0; i < sz; i++) { BaseMetric *mtr = base_metrics->get (i); if (mtr->is_internal ()) continue; switch (mtype) { case MET_DATA: if ((mtr->get_flavors () & BaseMetric::DATASPACE) != 0) { mitem = new Metric (mtr, BaseMetric::DATASPACE); items->append (mitem); } break; case MET_INDX: { if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0) { int index2; Metric *item2 = NULL; bool found = false; Vec_loop (Metric*, items, index2, item2) { if (item2->get_subtype () == BaseMetric::EXCLUSIVE && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0) { found = true; break; } } if (found == false) { mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); items->append (mitem); } } } break; case MET_CALL: case MET_CALL_AGR: if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) != 0) { mitem = new Metric (mtr, BaseMetric::ATTRIBUTED); items->append (mitem); } // now fall through to add exclusive and inclusive case MET_NORMAL: case MET_COMMON: if (mtr->get_flavors () & BaseMetric::EXCLUSIVE) { mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); items->append (mitem); } if (mtr->get_flavors () & BaseMetric::INCLUSIVE) { mitem = new Metric (mtr, BaseMetric::INCLUSIVE); items->append (mitem); } break; case MET_SRCDIS: if (mtr->get_flavors () & BaseMetric::INCLUSIVE) { mitem = new Metric (mtr, BaseMetric::INCLUSIVE); items->append (mitem); } break; case MET_IO: { if (mtr->get_packet_type () == DATA_IOTRACE && ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)) { int index2; Metric *item2 = NULL; bool found = false; Vec_loop (Metric*, items, index2, item2) { if (item2->get_subtype () == BaseMetric::EXCLUSIVE && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0) { found = true; break; } } if (found == false) { mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); items->append (mitem); } } } break; case MET_HEAP: { if (mtr->get_packet_type () == DATA_HEAP && ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0 || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)) { int index2; Metric *item2 = NULL; bool found = false; Vec_loop (Metric*, items, index2, item2) { if ((item2->get_subtype () == BaseMetric::EXCLUSIVE) && (dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0)) { found = true; break; } } if (found == false) { mitem = new Metric (mtr, BaseMetric::EXCLUSIVE); items->append (mitem); } } } break; } // add the static if (mtr->get_flavors () & BaseMetric::STATIC) { switch (mtype) { case MET_NORMAL: case MET_COMMON: case MET_CALL: case MET_CALL_AGR: case MET_SRCDIS: mitem = new Metric (mtr, BaseMetric::STATIC); items->append (mitem); break; default: if (mtr->get_type () == BaseMetric::ONAME) { mitem = new Metric (mtr, BaseMetric::STATIC); items->append (mitem); } break; } } } // set all metrics visible for (long i = 0, sz = items ? items->size () : 0; i < sz; i++) items->get (i)->enable_all_visbits (); } // Constructor for an empty list -- items will be added one at a time MetricList::MetricList (MetricType _mtype) { mtype = _mtype; items = new Vector; sort_ref_index = 0; sort_reverse = false; } MetricList::~MetricList () { Destroy (items); } // Duplicate a metric list MetricList::MetricList (MetricList *old) { mtype = old->mtype; // get an empty vector items = new Vector; Metric *item; Metric *nitem; int index; sort_ref_index = old->get_sort_ref_index (); sort_reverse = old->get_sort_rev (); Vec_loop (Metric*, old->items, index, item) { nitem = new Metric (*item); items->append (nitem); } } // set_metrics: // Sets the particular metric list, according to the metric spec // If fromRcFile, updates dbeSession->get_reg_metrics_tree() with new defaults. char * MetricList::set_metrics (const char *mspec, bool fromRcFile, DerivedMetrics * /* derived_metrics */) { BaseMetric::SubType subtypes[10]; int nsubtypes; int dmetrics_vis; // literal translation of metrics/dmetrics %.+ bool parseOK = false; char *errbuf; Vector *old_items = items; items = new Vector; Vector *base_items = dbeSession->get_base_reg_metrics (); // and copy the input specification char *buf = dbe_strdup (mspec); // append metric items from parsing the string for (char *mcmd = strtok (buf, NTXT (":")); mcmd != NULL; mcmd = strtok (NULL, NTXT (":"))) { // parse the single metric_spec, based on the type of list being constructed, into: // a vector of SubTypes (any of [iead] or STATIC) // a integer mask for the visibility bits // and the string name of the base metric // it might be "all", "any", or "hwc" or it should match a metric in the list // it might also be "bit", meaning any bit-computed metric char *mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, &dmetrics_vis, &parseOK); if (!parseOK) { // error parsing the metric specification // not from an rc file, it's an error if (!fromRcFile) { delete base_items; items->destroy (); delete items; items = old_items; free (buf); return mname; } continue; } // loop over subtypes requested // set the visibility of and sort order according to the vis bits, // and the order of encounter in the processing int ret = add_matching_dmetrics (base_items, mname, subtypes, nsubtypes, dmetrics_vis, fromRcFile); if (ret != 0 && !fromRcFile) { if (ret == 1) errbuf = dbe_sprintf (GTXT ("No data recorded to support metric specification: %s\n"), mcmd); else errbuf = dbe_sprintf (GTXT ("Metric specification for `%s' has appeared before in %s"), mcmd, mspec); delete base_items; items->destroy (); delete items; items = old_items; free (buf); return errbuf; } } // we've processed the entire spec // update metric defaults if (fromRcFile) { for (long i = 0, sz = items->size (); i < sz; i++) { Metric *m = items->get (i); int visbits = m->get_visbits (); BaseMetric::SubType subtype = m->get_subtype (); BaseMetric *reg_bm = m->get_base_metric (); reg_bm->set_default_visbits (subtype, visbits); BaseMetricTreeNode *mtree = dbeSession->get_reg_metrics_tree (); BaseMetricTreeNode *bmtnode = mtree->register_metric (m); BaseMetric *tree_bm = bmtnode->get_BaseMetric (); tree_bm->set_default_visbits (subtype, visbits); } } // ensure that name is present, remove hidden metrics nsubtypes = 1; for (long i = items->size () - 1; i >= 0; i--) { Metric *m = items->fetch (i); if (!m->is_any_visible ()) { delete m; items->remove (i); continue; } if (m->get_type () == BaseMetric::ONAME) nsubtypes = 0; } // did we get at least one valid match? if (items->size () == 0 && !fromRcFile) { errbuf = dbe_sprintf (GTXT ("No valid metrics specified in `%s'\n"), mspec); delete base_items; items->destroy (); delete items; items = old_items; free (buf); return errbuf; } if (nsubtypes == 1) { subtypes[0] = BaseMetric::STATIC; (void) add_matching_dmetrics (base_items, NTXT ("name"), subtypes, 1, VAL_VALUE, true); } // replace the old list of items, with the new set if (old_items) { old_items->destroy (); delete old_items; } set_fallback_sort (); free (buf); delete base_items; return NULL; } void MetricList::set_fallback_sort () { // sort by first visible of the appropriate flavor char *sortcmd = NULL; switch (mtype) { case MET_NORMAL: case MET_COMMON: sortcmd = NTXT ("ei.any:name"); break; case MET_SRCDIS: sortcmd = NTXT ("i.any:name"); break; case MET_CALL: case MET_CALL_AGR: sortcmd = NTXT ("a.any:name"); break; case MET_DATA: sortcmd = NTXT ("d.any:name"); break; case MET_INDX: sortcmd = NTXT ("e.any:name"); break; case MET_IO: sortcmd = NTXT ("e.any:name"); break; case MET_HEAP: sortcmd = NTXT ("e.any:name"); break; } if (NULL != sortcmd) (void) set_sort (sortcmd, true); } void MetricList::set_metrics (MetricList *mlist) { // verify that the type is appropriate for the call if (mtype == MET_NORMAL || mtype == MET_COMMON || (mlist->mtype != MET_NORMAL && mlist->mtype != MET_COMMON)) abort (); Vector *mlist_items = mlist->get_items (); items->destroy (); items->reset (); int sort_ind = mlist->get_sort_ref_index (); for (int i = 0, mlist_sz = mlist_items->size (); i < mlist_sz; i++) { Metric *mtr = mlist_items->fetch (i); if (!mtr->is_any_visible ()) continue; // Add a new Metric with probably a new sub_type to this->items: // for MET_CALL and MET_CALL_AGR the matching entry to an e. or i. is itself // for MET_DATA, the matching entry to an e. or i. is the d. metric // for MET_INDX, the matching entry to an e. or i. is the e. metric // for MET_IO, the matching entry to an e. or i. is the e. metric // for MET_HEAP, the matching entry to an e. or i. is the e. metric // Save static entries (SIZES and ADDRESS) only for MET_NORMAL, MET_CALL, MET_CALL_AGR, MET_SRCDIS switch (mtr->get_type ()) { case BaseMetric::SIZES: case BaseMetric::ADDRESS: switch (mtype) { case MET_NORMAL: case MET_COMMON: case MET_CALL: case MET_CALL_AGR: case MET_SRCDIS: break; default: continue; } break; default: break; } BaseMetric::SubType st = mtr->get_subtype (); if (st != BaseMetric::STATIC) { if (mtype == MET_CALL || mtype == MET_CALL_AGR) { if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) == 0) continue; st = BaseMetric::ATTRIBUTED; } else if (mtype == MET_DATA) { if ((mtr->get_flavors () & BaseMetric::DATASPACE) == 0) continue; st = BaseMetric::DATASPACE; } else if (mtype == MET_INDX) { if ((mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) continue; st = BaseMetric::EXCLUSIVE; } else if (mtype == MET_IO) { if (mtr->get_packet_type () != DATA_IOTRACE || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) continue; st = BaseMetric::EXCLUSIVE; } else if (mtype == MET_HEAP) { if (mtr->get_packet_type () != DATA_HEAP || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0) continue; st = BaseMetric::EXCLUSIVE; } else if (mtype == MET_SRCDIS) { if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) == 0) continue; st = BaseMetric::INCLUSIVE; } } bool found = false; for (int i1 = 0, items_sz = items->size (); i1 < items_sz; i1++) { Metric *m1 = items->fetch (i1); if (mtr->get_id () == m1->get_id () && st == m1->get_subtype ()) { if (sort_ind == i) sort_ind = i1; found = true; break; } } if (found) continue; Metric *m = new Metric (*mtr); m->set_subtype (st); m->set_raw_visbits (mtr->get_visbits ()); if (sort_ind == i) sort_ind = items->size (); items->append (m); } if (sort_ind >= items->size ()) sort_ind = 0; if (mtype == MET_IO) sort_ind = 0; if (mtype == MET_HEAP) sort_ind = 0; sort_ref_index = sort_ind; } // set_sort: // Sets the sort for the metric list to the first metric // in mspec that is present; if fromRcFile is false, then // only one metric may be specified. The requested sort // metric must be visible, or it won't be in the metric list char * MetricList::set_sort (const char *mspec, bool fromRcFile) { char *mcmd; BaseMetric::SubType subtypes[10]; int nsubtypes; int vis; bool parseOK = false; bool reverse = false; char buf[BUFSIZ]; char *list = buf; char *mname; // copy the input specification snprintf (buf, sizeof (buf), NTXT ("%s"), mspec); char *listp = list; if (*listp == '-') { // reverse sort specified reverse = true; listp++; } // search for metric items from parsing the string while ((mcmd = strtok (listp, NTXT (":"))) != NULL) { listp = NULL; // let strtok keep track // parse the single metric_spec, based on the type of list being constructed, into: // a vector of SubTypes (any of [iead] or STATIC) // a integer mask for the visibility bits // and the string name of the base metric mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, &vis, &parseOK); if (!parseOK) { // error parsing the metric specification // not from an rc file, it's an error if (!fromRcFile) return (mname); continue; } if (VAL_IS_HIDDEN (vis)) continue; // loop over subtypes requested to find metric // add a metric of that subtype, with specified vis.bits for (int i = 0; i < nsubtypes; i++) { // make sure the subtype is acceptable if ((mtype == MET_CALL || mtype == MET_CALL_AGR) && subtypes[i] != BaseMetric::ATTRIBUTED && subtypes[i] != BaseMetric::STATIC) return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Data metrics cannot be specified for caller-callee sort: %s\n"), mcmd); if (mtype == MET_DATA && subtypes[i] != BaseMetric::DATASPACE && subtypes[i] != BaseMetric::STATIC) return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Attributed metrics cannot be specified for data-derived sort: %s\n"), mcmd); if (mtype == MET_INDX && subtypes[i] != BaseMetric::EXCLUSIVE && subtypes[i] != BaseMetric::STATIC) return dbe_sprintf (GTXT ("Inclusive, Data or Attributed metrics cannot be specified for index sort: %s\n"), mcmd); if ((mtype == MET_NORMAL || mtype == MET_COMMON || mtype == MET_SRCDIS) && (subtypes[i] == BaseMetric::DATASPACE || subtypes[i] == BaseMetric::ATTRIBUTED)) return dbe_sprintf (GTXT ("Data or Attributed metrics cannot be specified for sort: %s\n"), mcmd); if (set_sort_metric (mname, subtypes[i], reverse)) return NULL; } // continue looking at entries } // not found on the list at all switch (mtype) { case MET_NORMAL: case MET_COMMON: case MET_SRCDIS: return dbe_sprintf (GTXT ("Invalid sort specification: %s\n"), mspec); case MET_CALL: case MET_CALL_AGR: return dbe_sprintf (GTXT ("Invalid caller-callee sort specification: %s\n"), mspec); case MET_DATA: return dbe_sprintf (GTXT ("Invalid data-derived sort specification: %s\n"), mspec); case MET_INDX: return dbe_sprintf (GTXT ("Invalid index sort specification: %s\n"), mspec); case MET_IO: return dbe_sprintf (GTXT ("Invalid I/O sort specification: %s\n"), mspec); case MET_HEAP: return dbe_sprintf (GTXT ("Invalid heap sort specification: %s\n"), mspec); } return NULL; } // set_sort to the metric with the given visible index void MetricList::set_sort (int visindex, bool reverse) { Metric *mitem; if (visindex < items->size ()) { mitem = items->fetch (visindex); if (mitem->is_any_visible ()) { sort_ref_index = visindex; sort_reverse = reverse; return; } } set_fallback_sort (); } bool MetricList::set_sort_metric (char *mname, BaseMetric::SubType mst, bool reverse) { bool any = false, hwc = false, bit = false; // check keywords 'any', 'all', 'bit' and 'hwc' if (!strcasecmp (mname, Command::ANY_CMD)) any = true; else if (!strcasecmp (mname, Command::ALL_CMD)) any = true; else if (!strcasecmp (mname, Command::HWC_CMD)) hwc = true; else if (!strcasecmp (mname, Command::BIT_CMD)) bit = true; for (int i = 0, items_sz = items->size (); i < items_sz; i++) { Metric *m = items->fetch (i); if (mst == m->get_subtype () && (any || (hwc && m->get_type () == BaseMetric::HWCNTR) || (bit && m->get_cmd () && strncmp (Command::BIT_CMD, m->get_cmd (), strlen (Command::BIT_CMD)) == 0) || dbe_strcmp (mname, m->get_cmd ()) == 0)) { sort_ref_index = i; sort_reverse = reverse; return true; } } return false; } // Print to a file of a list of metrics from a supplied vector // Debug flag = 1, prints the short name and address of the list // Debug flag = 2, prints the details of the list void MetricList::print_metric_list (FILE *dis_file, char *leader, int debug) { Metric *item; int index; char fmt_name[64]; fprintf (dis_file, NTXT ("%s"), leader); if (items == NULL) { fprintf (dis_file, GTXT ("NULL metric list can not be printed; aborting")); abort (); } if (items->size () == 0) { fprintf (dis_file, GTXT ("metric list is empty; aborting\n")); abort (); } // if debugging, print list address and string, and sort name if (debug != 0) { char *s = get_metrics (); fprintf (dis_file, "\tmetriclist at 0x%lx: %s, %lld metrics; sort by %s\n", (unsigned long) this, s, (long long) items->size (), get_sort_name ()); free (s); if (debug == 1) return; } // Find the longest metric name & command size_t max_len = 0; size_t max_len2 = 0; Vec_loop (Metric*, items, index, item) { // get the name char *mn = item->get_name (); size_t len = strlen (mn); if (max_len < len) max_len = len; mn = item->get_mcmd (true); len = strlen (mn); if (max_len2 < len) max_len2 = len; free (mn); } if (debug == 2) snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%-%ds", (int) max_len, (int) max_len2); else snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%s", (int) max_len); Vec_loop (Metric*, items, index, item) { char *mcmd = item->get_mcmd (true); fprintf (dis_file, fmt_name, item->get_name (), mcmd); free (mcmd); if (debug == 2) fprintf (dis_file, "\t[st %2d, VT %d, vis = %4s, T=%d, sort = %c]", item->get_subtype (), item->get_vtype (), item->get_vis_str (), item->is_time_val (), sort_ref_index == index ? 'Y' : 'N'); fputc ('\n', dis_file); } fputc ('\n', dis_file); fflush (dis_file); } // Return a string formatted from a vector of metrics // string is in the form suitable for a "metrics " command char * MetricList::get_metrics () { Metric *item; int index; StringBuilder sb; Vec_loop (Metric*, items, index, item) { if (sb.length () != 0) sb.append (':'); char *mcmd = item->get_mcmd (false); sb.append (mcmd); free (mcmd); } return sb.toString (); } int MetricList::get_listorder (Metric *mtr) { for (int i = 0, items_sz = items->size (); i < items_sz; i++) { Metric *m = items->fetch (i); if (m->get_subtype () == mtr->get_subtype () && m->get_id () == mtr->get_id ()) return i; } return -1; } int MetricList::get_listorder (char *cmd, BaseMetric::SubType st, const char *expr) { for (long i = 0, items_sz = items->size (); i < items_sz; i++) { Metric *m = items->fetch (i); if (m->get_subtype () == st && dbe_strcmp (m->get_cmd (), cmd) == 0 && dbe_strcmp (m->get_expr_spec (), expr) == 0) return (int) i; } return -1; } Metric * MetricList::find_metric_by_name (char *cmd) { for (long i = 0, items_sz = items->size (); i < items_sz; i++) { Metric *m = items->fetch (i); if (dbe_strcmp (m->get_cmd (), cmd) == 0) return m; } return NULL; } // find a metric by name and subtype Metric * MetricList::find_metric (char *cmd, BaseMetric::SubType st) { int i = get_listorder (cmd, st); if (i < 0) return NULL; return items->fetch (i); } // Get the sort metric from a list; forces sort by first if not set Metric * MetricList::get_sort_metric () { int i = get_sort_ref_index (); return i >= 0 ? items->fetch (i) : NULL; } char * MetricList::get_sort_name () { Metric *item = get_sort_metric (); if (item == NULL) return dbe_strdup (NTXT ("")); char *n = item->get_name (); return sort_reverse ? dbe_sprintf ("-%s", n) : dbe_strdup (n); } char * MetricList::get_sort_cmd () { char *buf; Metric *item = get_sort_metric (); if (item == NULL) return dbe_strdup (NTXT ("")); char *n = item->get_mcmd (false); if (sort_reverse) { buf = dbe_sprintf (NTXT ("-%s"), n); free (n); } else buf = n; return buf; } Metric * MetricList::append (BaseMetric *bm, BaseMetric::SubType st, int visbits) { for (long i = 0, sz = items->size (); i < sz; i++) { Metric *m = items->get (i); if (m->get_id () == bm->get_id () && m->get_subtype () == st) return NULL; } Metric *met = new Metric (bm, st); met->set_dmetrics_visbits (visbits); items->append (met); return met; } int MetricList::add_matching_dmetrics (Vector *base_items, char *mcmd, BaseMetric::SubType *_subtypes, int nsubtypes, int dmetrics_visbits, bool fromRcFile) { bool any = false, hwc = false, bit = false; int got_metric = 1; // check keywords 'any', 'all', 'bit', and 'hwc' if (!strcasecmp (mcmd, Command::ANY_CMD)) any = true; else if (!strcasecmp (mcmd, Command::ALL_CMD)) any = true; else if (!strcasecmp (mcmd, Command::HWC_CMD)) hwc = true; else if (!strcasecmp (mcmd, Command::BIT_CMD)) bit = true; BaseMetric::SubType *subtypes = _subtypes; BaseMetric::SubType all_subtypes[2] = { BaseMetric::EXCLUSIVE, BaseMetric::INCLUSIVE }; if (nsubtypes == 0 || (nsubtypes == 1 && subtypes[0] == BaseMetric::STATIC)) { // user did not specify ei; treat as wildcard and supply both. subtypes = all_subtypes; nsubtypes = 2; } // scan the metrics to find all matches for (int i = 0, base_sz = base_items->size (); i < base_sz; i++) { BaseMetric *item = base_items->fetch (i); if (!(any || (hwc && item->get_type () == BaseMetric::HWCNTR) || (bit && item->get_cmd () && strncmp (item->get_cmd (), Command::BIT_CMD, strlen (Command::BIT_CMD)) == 0) || dbe_strcmp (item->get_cmd (), mcmd) == 0)) continue; if (item->is_internal ()) continue; if (item->get_flavors () & BaseMetric::STATIC) { got_metric = 0; int vis = item->get_type () != BaseMetric::ONAME ? dmetrics_visbits : VAL_VALUE; if (append (item, BaseMetric::STATIC, vis) == NULL && !fromRcFile) return 2; continue; } // special case for omp metrics: make visible only if // omp data has been collected if (!dbeSession->is_omp_available () && (strcasecmp (mcmd, "ompwork") == 0 || strcasecmp (mcmd, "ompwait") == 0)) continue; for (int j = 0; j < nsubtypes; j++) { if (append (item, subtypes[j], dmetrics_visbits) == NULL && !fromRcFile) return 2; } got_metric = 0; if (!(any || hwc || bit)) break; } return got_metric; } // parse a single metric specification, to give: // a vector of subtypes, and a count of the number of them // an integer visibility // return the string for the metric name char * MetricList::parse_metric_spec (char *mcmd, BaseMetric::SubType *subtypes, int *nsubtypes, int *dmetrics_visb, bool *isOK) { size_t len_vtype; int index; int vis; bool got_e, got_i, got_a, got_d; char *str = mcmd; char *str2; *isOK = true; // For dynamic metrics, each keyword is of the form // For static metrics, each keyword is of the form [] // can be either "i" for inclusive or "e" for exclusive // can be any combination of "." (to show the metric as a time), // "%" (to show it as a percentage), "+" (to show it as a count), and "!" (turn off the metric) // find subtype index = 0; size_t len_subtype = strspn (str, NTXT ("eiad")); str2 = str + len_subtype; // find vis if (len_subtype == 0) { // only a . or ! is possible if no subtypes len_vtype = strspn (str2, NTXT (".!")); vis = VAL_VALUE; } else { len_vtype = strspn (str2, NTXT (".+%!")); vis = VAL_NA; } // if no visibility bits, there can't be a subtype if (len_vtype == 0) len_subtype = 0; if (len_subtype == 0) { // must be a static metric subtypes[index++] = BaseMetric::STATIC; vis = VAL_VALUE; } else { // figure out which subtypes are specified got_e = got_i = got_a = got_d = false; for (size_t i = 0; i < len_subtype; i++) { str += len_subtype; if (mcmd[i] == 'e') { // exclusive if (mtype == MET_DATA) { *isOK = false; return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"), mcmd); } if (!got_e) { got_e = true; subtypes[index++] = BaseMetric::EXCLUSIVE; } } else if (mcmd[i] == 'i') { // inclusive if (mtype == MET_DATA) { *isOK = false; return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"), mcmd); } if (mtype == MET_INDX) { *isOK = false; return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for index metrics\n"), mcmd); } if (!got_i) { got_i = true; subtypes[index++] = BaseMetric::INCLUSIVE; } } else if (mcmd[i] == 'a') { // attributed if (mtype != MET_CALL && mtype != MET_CALL_AGR) { *isOK = false; return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for caller-callee metrics only\n"), mcmd); } if (!got_a) { got_a = true; subtypes[index++] = BaseMetric::ATTRIBUTED; } } else if (mcmd[i] == 'd') { // data-space if (mtype != MET_DATA) { *isOK = false; return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for data-derived metrics only\n"), mcmd); } if (!got_d) { got_d = true; subtypes[index++] = BaseMetric::DATASPACE; } } } } *nsubtypes = index; // now determine the visiblity bits if (len_vtype > 0) { for (size_t i = 0; i < len_vtype; i++) { if (str2[i] == '+') vis = (vis | VAL_VALUE); else if (str2[i] == '.') vis = (vis | VAL_TIMEVAL); else if (str2[i] == '%') vis = (vis | VAL_PERCENT); else if (str2[i] == '!') vis = (vis | VAL_HIDE_ALL); } } *dmetrics_visb = vis; return mcmd + len_subtype + len_vtype; }