| /* 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; | |
| } |