|  | /* 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<BaseMetric*> *base_metrics, MetricType _mtype) | 
|  | { | 
|  | mtype = _mtype; | 
|  | items = new Vector<Metric*>; | 
|  | 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<Metric*>; | 
|  | 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*>; | 
|  | 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<Metric*> *old_items = items; | 
|  | items = new Vector<Metric*>; | 
|  | Vector<BaseMetric*> *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<Metric*> *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 <string>" 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<BaseMetric*> *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  <flavor><visibility><metric-name> | 
|  | // For static metrics, each keyword is of the form [<visibility>]<metric-name> | 
|  | // <flavor> can be either "i" for inclusive or "e" for exclusive | 
|  | // <visibility> 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; | 
|  | } |