1/* Copyright (C) 2021 Free Software Foundation, Inc.
2   Contributed by Oracle.
3
4   This file is part of GNU Binutils.
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3, or (at your option)
9   any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, USA.  */
20
21#include "config.h"
22#include "util.h"
23#include "Command.h"
24#include "DbeSession.h"
25#include "MetricList.h"
26#include "StringBuilder.h"
27
28//  Build a metric reference list
29MetricList::MetricList (Vector<BaseMetric*> *base_metrics, MetricType _mtype)
30{
31  mtype = _mtype;
32  items = new Vector<Metric*>;
33  sort_ref_index = 0;
34  sort_reverse = false;
35
36  Metric *mitem;
37  // loop over the base_metrics, and add in all the appropriate subtypes
38  for (long i = 0, sz = base_metrics ? base_metrics->size () : 0; i < sz; i++)
39    {
40      BaseMetric *mtr = base_metrics->get (i);
41      if (mtr->is_internal ())
42	continue;
43      switch (mtype)
44	{
45	case MET_DATA:
46	  if ((mtr->get_flavors () & BaseMetric::DATASPACE) != 0)
47	    {
48	      mitem = new Metric (mtr, BaseMetric::DATASPACE);
49	      items->append (mitem);
50	    }
51	  break;
52
53	case MET_INDX:
54	  {
55	    if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0
56		|| (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0)
57	      {
58		int index2;
59		Metric *item2 = NULL;
60		bool found = false;
61		Vec_loop (Metric*, items, index2, item2)
62		{
63		  if (item2->get_subtype () == BaseMetric::EXCLUSIVE
64		      && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0)
65		    {
66		      found = true;
67		      break;
68		    }
69		}
70		if (found == false)
71		  {
72		    mitem = new Metric (mtr, BaseMetric::EXCLUSIVE);
73		    items->append (mitem);
74		  }
75	      }
76	  }
77	  break;
78
79	case MET_CALL:
80	case MET_CALL_AGR:
81	  if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) != 0)
82	    {
83	      mitem = new Metric (mtr, BaseMetric::ATTRIBUTED);
84	      items->append (mitem);
85	    }
86	  // now fall through to add exclusive and inclusive
87
88	case MET_NORMAL:
89	case MET_COMMON:
90	  if (mtr->get_flavors () & BaseMetric::EXCLUSIVE)
91	    {
92	      mitem = new Metric (mtr, BaseMetric::EXCLUSIVE);
93	      items->append (mitem);
94	    }
95	  if (mtr->get_flavors () & BaseMetric::INCLUSIVE)
96	    {
97	      mitem = new Metric (mtr, BaseMetric::INCLUSIVE);
98	      items->append (mitem);
99	    }
100	  break;
101	case MET_SRCDIS:
102	  if (mtr->get_flavors () & BaseMetric::INCLUSIVE)
103	    {
104	      mitem = new Metric (mtr, BaseMetric::INCLUSIVE);
105	      items->append (mitem);
106	    }
107	  break;
108	case MET_IO:
109	  {
110	    if (mtr->get_packet_type () == DATA_IOTRACE
111		&& ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0
112		    || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0))
113	      {
114		int index2;
115		Metric *item2 = NULL;
116		bool found = false;
117		Vec_loop (Metric*, items, index2, item2)
118		{
119		  if (item2->get_subtype () == BaseMetric::EXCLUSIVE
120		      && dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0)
121		    {
122		      found = true;
123		      break;
124		    }
125		}
126		if (found == false)
127		  {
128		    mitem = new Metric (mtr, BaseMetric::EXCLUSIVE);
129		    items->append (mitem);
130		  }
131	      }
132	  }
133	  break;
134	case MET_HEAP:
135	  {
136	    if (mtr->get_packet_type () == DATA_HEAP
137		&& ((mtr->get_flavors () & BaseMetric::INCLUSIVE) != 0
138		    || (mtr->get_flavors () & BaseMetric::EXCLUSIVE) != 0))
139	      {
140		int index2;
141		Metric *item2 = NULL;
142		bool found = false;
143		Vec_loop (Metric*, items, index2, item2)
144		{
145		  if ((item2->get_subtype () == BaseMetric::EXCLUSIVE) &&
146		      (dbe_strcmp (item2->get_cmd (), mtr->get_cmd ()) == 0))
147		    {
148		      found = true;
149		      break;
150		    }
151		}
152		if (found == false)
153		  {
154		    mitem = new Metric (mtr, BaseMetric::EXCLUSIVE);
155		    items->append (mitem);
156		  }
157	      }
158	  }
159	  break;
160	}
161
162      // add the static
163      if (mtr->get_flavors () & BaseMetric::STATIC)
164	{
165	  switch (mtype)
166	    {
167	    case MET_NORMAL:
168	    case MET_COMMON:
169	    case MET_CALL:
170	    case MET_CALL_AGR:
171	    case MET_SRCDIS:
172	      mitem = new Metric (mtr, BaseMetric::STATIC);
173	      items->append (mitem);
174	      break;
175	    default:
176	      if (mtr->get_type () == BaseMetric::ONAME)
177		{
178		  mitem = new Metric (mtr, BaseMetric::STATIC);
179		  items->append (mitem);
180		}
181	      break;
182	    }
183	}
184    }
185  // set all metrics visible
186  for (long i = 0, sz = items ? items->size () : 0; i < sz; i++)
187    items->get (i)->enable_all_visbits ();
188}
189
190// Constructor for an empty list -- items will be added one at a time
191MetricList::MetricList (MetricType _mtype)
192{
193  mtype = _mtype;
194  items = new Vector<Metric*>;
195  sort_ref_index = 0;
196  sort_reverse = false;
197}
198
199MetricList::~MetricList ()
200{
201  Destroy (items);
202}
203
204// Duplicate a metric list
205MetricList::MetricList (MetricList *old)
206{
207  mtype = old->mtype;
208
209  // get an empty vector
210  items = new Vector<Metric*>;
211  Metric *item;
212  Metric *nitem;
213  int index;
214  sort_ref_index = old->get_sort_ref_index ();
215  sort_reverse = old->get_sort_rev ();
216  Vec_loop (Metric*, old->items, index, item)
217  {
218    nitem = new Metric (*item);
219    items->append (nitem);
220  }
221}
222
223// set_metrics:
224//	Sets the particular metric list, according to the metric spec
225//      If fromRcFile, updates dbeSession->get_reg_metrics_tree() with new defaults.
226char *
227MetricList::set_metrics (const char *mspec, bool fromRcFile,
228			 DerivedMetrics * /* derived_metrics */)
229{
230  BaseMetric::SubType subtypes[10];
231  int nsubtypes;
232  int dmetrics_vis; // literal translation of metrics/dmetrics %.+
233  bool parseOK = false;
234  char *errbuf;
235  Vector<Metric*> *old_items = items;
236  items = new Vector<Metric*>;
237  Vector<BaseMetric*> *base_items = dbeSession->get_base_reg_metrics ();
238
239  // and copy the input specification
240  char *buf = dbe_strdup (mspec);
241
242  // append metric items from parsing the string
243  for (char *mcmd = strtok (buf, NTXT (":")); mcmd != NULL;
244	  mcmd = strtok (NULL, NTXT (":")))
245    {
246      // parse the single metric_spec, based on the type of list being constructed, into:
247      //	a vector of SubTypes (any of [iead] or STATIC)
248      //	a integer mask for the visibility bits
249      //	and the string name of the base metric
250      // 	    it might be "all", "any", or "hwc" or it should match a metric in the list
251      //	    it might also be "bit", meaning any bit-computed metric
252      char *mname = parse_metric_spec (mcmd, subtypes, &nsubtypes,
253				       &dmetrics_vis, &parseOK);
254      if (!parseOK)
255	{
256	  // error parsing the metric specification
257	  // not from an rc file, it's an error
258	  if (!fromRcFile)
259	    {
260	      delete base_items;
261	      items->destroy ();
262	      delete items;
263	      items = old_items;
264	      free (buf);
265	      return mname;
266	    }
267	  continue;
268	}
269
270      // loop over subtypes requested
271      // set the visibility of and sort order according to the vis bits,
272      //	and the order of encounter in the processing
273      int ret = add_matching_dmetrics (base_items, mname, subtypes, nsubtypes,
274				       dmetrics_vis, fromRcFile);
275      if (ret != 0 && !fromRcFile)
276	{
277	  if (ret == 1)
278	    errbuf = dbe_sprintf (GTXT ("No data recorded to support metric specification: %s\n"),
279				  mcmd);
280	  else
281	    errbuf = dbe_sprintf (GTXT ("Metric specification for `%s' has appeared before in %s"),
282				    mcmd, mspec);
283	  delete base_items;
284	  items->destroy ();
285	  delete items;
286	  items = old_items;
287	  free (buf);
288	  return errbuf;
289	}
290    } // we've processed the entire spec
291
292  // update metric defaults
293  if (fromRcFile)
294    {
295      for (long i = 0, sz = items->size (); i < sz; i++)
296	{
297	  Metric *m = items->get (i);
298	  int visbits = m->get_visbits ();
299	  BaseMetric::SubType subtype = m->get_subtype ();
300	  BaseMetric *reg_bm = m->get_base_metric ();
301	  reg_bm->set_default_visbits (subtype, visbits);
302	  BaseMetricTreeNode *mtree = dbeSession->get_reg_metrics_tree ();
303	  BaseMetricTreeNode *bmtnode = mtree->register_metric (m);
304	  BaseMetric *tree_bm = bmtnode->get_BaseMetric ();
305	  tree_bm->set_default_visbits (subtype, visbits);
306	}
307    }
308
309  // ensure that name is present, remove hidden metrics
310  nsubtypes = 1;
311  for (long i = items->size () - 1; i >= 0; i--)
312    {
313      Metric *m = items->fetch (i);
314      if (!m->is_any_visible ())
315	{
316	  delete m;
317	  items->remove (i);
318	  continue;
319	}
320      if (m->get_type () == BaseMetric::ONAME)
321	nsubtypes = 0;
322    }
323
324  // did we get at least one valid match?
325  if (items->size () == 0 && !fromRcFile)
326    {
327      errbuf = dbe_sprintf (GTXT ("No valid metrics specified in `%s'\n"), mspec);
328      delete base_items;
329      items->destroy ();
330      delete items;
331      items = old_items;
332      free (buf);
333      return errbuf;
334    }
335
336  if (nsubtypes == 1)
337    {
338      subtypes[0] = BaseMetric::STATIC;
339      (void) add_matching_dmetrics (base_items, NTXT ("name"), subtypes, 1, VAL_VALUE, true);
340    }
341
342  // replace the old list of items, with the new set
343  if (old_items)
344    {
345      old_items->destroy ();
346      delete old_items;
347    }
348  set_fallback_sort ();
349  free (buf);
350  delete base_items;
351  return NULL;
352}
353
354void
355MetricList::set_fallback_sort ()
356{
357  // sort by first visible of the appropriate flavor
358  char *sortcmd = NULL;
359  switch (mtype)
360    {
361    case MET_NORMAL:
362    case MET_COMMON:
363      sortcmd = NTXT ("ei.any:name");
364      break;
365    case MET_SRCDIS:
366      sortcmd = NTXT ("i.any:name");
367      break;
368    case MET_CALL:
369    case MET_CALL_AGR:
370      sortcmd = NTXT ("a.any:name");
371      break;
372    case MET_DATA:
373      sortcmd = NTXT ("d.any:name");
374      break;
375    case MET_INDX:
376      sortcmd = NTXT ("e.any:name");
377      break;
378    case MET_IO:
379      sortcmd = NTXT ("e.any:name");
380      break;
381    case MET_HEAP:
382      sortcmd = NTXT ("e.any:name");
383      break;
384    }
385  if (NULL != sortcmd)
386    (void) set_sort (sortcmd, true);
387}
388
389void
390MetricList::set_metrics (MetricList *mlist)
391{
392  // verify that the type is appropriate for the call
393  if (mtype == MET_NORMAL || mtype == MET_COMMON
394      || (mlist->mtype != MET_NORMAL && mlist->mtype != MET_COMMON))
395    abort ();
396
397  Vector<Metric*> *mlist_items = mlist->get_items ();
398  items->destroy ();
399  items->reset ();
400
401  int sort_ind = mlist->get_sort_ref_index ();
402  for (int i = 0, mlist_sz = mlist_items->size (); i < mlist_sz; i++)
403    {
404      Metric *mtr = mlist_items->fetch (i);
405      if (!mtr->is_any_visible ())
406	continue;
407
408      //  Add a new Metric with probably a new sub_type to this->items:
409      //    for MET_CALL and MET_CALL_AGR the matching entry to an e. or i. is itself
410      //    for MET_DATA, the matching entry to an e. or i. is the d. metric
411      //    for MET_INDX, the matching entry to an e. or i. is the e. metric
412      //    for MET_IO, the matching entry to an e. or i. is the e. metric
413      //    for MET_HEAP, the matching entry to an e. or i. is the e. metric
414      //    Save static entries (SIZES and ADDRESS) only for MET_NORMAL, MET_CALL, MET_CALL_AGR, MET_SRCDIS
415      switch (mtr->get_type ())
416	{
417	case BaseMetric::SIZES:
418	case BaseMetric::ADDRESS:
419	  switch (mtype)
420	    {
421	    case MET_NORMAL:
422	    case MET_COMMON:
423	    case MET_CALL:
424	    case MET_CALL_AGR:
425	    case MET_SRCDIS:
426	      break;
427	    default:
428	      continue;
429	    }
430	  break;
431	default:
432	  break;
433	}
434
435      BaseMetric::SubType st = mtr->get_subtype ();
436      if (st != BaseMetric::STATIC)
437	{
438	  if (mtype == MET_CALL || mtype == MET_CALL_AGR)
439	    {
440	      if ((mtr->get_flavors () & BaseMetric::ATTRIBUTED) == 0)
441		continue;
442	      st = BaseMetric::ATTRIBUTED;
443	    }
444	  else if (mtype == MET_DATA)
445	    {
446	      if ((mtr->get_flavors () & BaseMetric::DATASPACE) == 0)
447		continue;
448	      st = BaseMetric::DATASPACE;
449	    }
450	  else if (mtype == MET_INDX)
451	    {
452	      if ((mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0)
453		continue;
454	      st = BaseMetric::EXCLUSIVE;
455	    }
456	  else if (mtype == MET_IO)
457	    {
458	      if (mtr->get_packet_type () != DATA_IOTRACE ||
459		  (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0)
460		continue;
461	      st = BaseMetric::EXCLUSIVE;
462	    }
463	  else if (mtype == MET_HEAP)
464	    {
465	      if (mtr->get_packet_type () != DATA_HEAP ||
466		  (mtr->get_flavors () & BaseMetric::EXCLUSIVE) == 0)
467		continue;
468	      st = BaseMetric::EXCLUSIVE;
469	    }
470	  else if (mtype == MET_SRCDIS)
471	    {
472	      if ((mtr->get_flavors () & BaseMetric::INCLUSIVE) == 0)
473		continue;
474	      st = BaseMetric::INCLUSIVE;
475	    }
476	}
477
478      bool found = false;
479      for (int i1 = 0, items_sz = items->size (); i1 < items_sz; i1++)
480	{
481	  Metric *m1 = items->fetch (i1);
482	  if (mtr->get_id () == m1->get_id () && st == m1->get_subtype ())
483	    {
484	      if (sort_ind == i)
485		sort_ind = i1;
486	      found = true;
487	      break;
488	    }
489	}
490      if (found)
491	continue;
492      Metric *m = new Metric (*mtr);
493      m->set_subtype (st);
494      m->set_raw_visbits (mtr->get_visbits ());
495      if (sort_ind == i)
496	sort_ind = items->size ();
497      items->append (m);
498    }
499  if (sort_ind >= items->size ())
500    sort_ind = 0;
501  if (mtype == MET_IO)
502    sort_ind = 0;
503  if (mtype == MET_HEAP)
504    sort_ind = 0;
505  sort_ref_index = sort_ind;
506
507}
508
509
510// set_sort:
511//	Sets the sort for the metric list to the first metric
512//	in mspec that is present; if fromRcFile is false, then
513//	only one metric may be specified.  The requested sort
514//	metric must be visible, or it won't  be in the metric list
515
516char *
517MetricList::set_sort (const char *mspec, bool fromRcFile)
518{
519  char *mcmd;
520  BaseMetric::SubType subtypes[10];
521  int nsubtypes;
522  int vis;
523  bool parseOK = false;
524  bool reverse = false;
525  char buf[BUFSIZ];
526  char *list = buf;
527  char *mname;
528
529  // copy the input specification
530  snprintf (buf, sizeof (buf), NTXT ("%s"), mspec);
531  char *listp = list;
532  if (*listp == '-')
533    {
534      // reverse sort specified
535      reverse = true;
536      listp++;
537    }
538
539  // search for metric items from parsing the string
540  while ((mcmd = strtok (listp, NTXT (":"))) != NULL)
541    {
542      listp = NULL; // let strtok keep track
543
544      // parse the single metric_spec, based on the type of list being constructed, into:
545      //	a vector of SubTypes (any of [iead] or STATIC)
546      //	a integer mask for the visibility bits
547      //	and the string name of the base metric
548      mname = parse_metric_spec (mcmd, subtypes, &nsubtypes, &vis, &parseOK);
549      if (!parseOK)
550	{
551	  // error parsing the metric specification
552	  // not from an rc file, it's an error
553	  if (!fromRcFile)
554	    return (mname);
555	  continue;
556	}
557      if (VAL_IS_HIDDEN (vis))
558	continue;
559
560      // loop over subtypes requested to find metric
561      // add a metric of that subtype, with specified vis.bits
562      for (int i = 0; i < nsubtypes; i++)
563	{
564	  // make sure the subtype is acceptable
565	  if ((mtype == MET_CALL || mtype == MET_CALL_AGR)
566	      && subtypes[i] != BaseMetric::ATTRIBUTED
567	      && subtypes[i] != BaseMetric::STATIC)
568	      return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Data metrics cannot be specified for caller-callee sort: %s\n"),
569				  mcmd);
570	  if (mtype == MET_DATA && subtypes[i] != BaseMetric::DATASPACE
571	      && subtypes[i] != BaseMetric::STATIC)
572	      return dbe_sprintf (GTXT ("Inclusive, Exclusive, or Attributed metrics cannot be specified for data-derived sort: %s\n"),
573				  mcmd);
574	  if (mtype == MET_INDX && subtypes[i] != BaseMetric::EXCLUSIVE
575				   && subtypes[i] != BaseMetric::STATIC)
576	    return dbe_sprintf (GTXT ("Inclusive, Data or Attributed metrics cannot be specified for index sort: %s\n"),
577				mcmd);
578	  if ((mtype == MET_NORMAL || mtype == MET_COMMON
579	       || mtype == MET_SRCDIS)
580	      && (subtypes[i] == BaseMetric::DATASPACE
581		  || subtypes[i] == BaseMetric::ATTRIBUTED))
582	    return dbe_sprintf (GTXT ("Data or Attributed metrics cannot be specified for sort: %s\n"), mcmd);
583	  if (set_sort_metric (mname, subtypes[i], reverse))
584	    return NULL;
585	}
586      // continue looking at entries
587    }
588
589  // not found on the list at all
590  switch (mtype)
591    {
592    case MET_NORMAL:
593    case MET_COMMON:
594    case MET_SRCDIS:
595      return dbe_sprintf (GTXT ("Invalid sort specification: %s\n"), mspec);
596    case MET_CALL:
597    case MET_CALL_AGR:
598      return dbe_sprintf (GTXT ("Invalid caller-callee sort specification: %s\n"),
599			  mspec);
600    case MET_DATA:
601      return dbe_sprintf (GTXT ("Invalid data-derived sort specification: %s\n"),
602			  mspec);
603    case MET_INDX:
604      return dbe_sprintf (GTXT ("Invalid index sort specification: %s\n"),
605			  mspec);
606    case MET_IO:
607      return dbe_sprintf (GTXT ("Invalid I/O sort specification: %s\n"), mspec);
608    case MET_HEAP:
609      return dbe_sprintf (GTXT ("Invalid heap sort specification: %s\n"),
610			  mspec);
611    }
612  return NULL;
613}
614
615// set_sort to the metric with the given visible index
616
617void
618MetricList::set_sort (int visindex, bool reverse)
619{
620  Metric *mitem;
621  if (visindex < items->size ())
622    {
623      mitem = items->fetch (visindex);
624      if (mitem->is_any_visible ())
625	{
626	  sort_ref_index = visindex;
627	  sort_reverse = reverse;
628	  return;
629	}
630    }
631  set_fallback_sort ();
632}
633
634bool
635MetricList::set_sort_metric (char *mname, BaseMetric::SubType mst, bool reverse)
636{
637  bool any = false, hwc = false, bit = false;
638
639  // check keywords 'any', 'all', 'bit' and 'hwc'
640  if (!strcasecmp (mname, Command::ANY_CMD))
641    any = true;
642  else if (!strcasecmp (mname, Command::ALL_CMD))
643    any = true;
644  else if (!strcasecmp (mname, Command::HWC_CMD))
645    hwc = true;
646  else if (!strcasecmp (mname, Command::BIT_CMD))
647    bit = true;
648
649  for (int i = 0, items_sz = items->size (); i < items_sz; i++)
650    {
651      Metric *m = items->fetch (i);
652      if (mst == m->get_subtype ()
653	  && (any || (hwc && m->get_type () == BaseMetric::HWCNTR)
654	      || (bit && m->get_cmd ()
655		  && strncmp (Command::BIT_CMD, m->get_cmd (),
656			      strlen (Command::BIT_CMD)) == 0)
657	      || dbe_strcmp (mname, m->get_cmd ()) == 0))
658	{
659	  sort_ref_index = i;
660	  sort_reverse = reverse;
661	  return true;
662	}
663    }
664  return false;
665}
666
667// Print to a file of a list of metrics from a supplied vector
668//	Debug flag = 1, prints the short name and address of the list
669//	Debug flag = 2, prints the details of the list
670void
671MetricList::print_metric_list (FILE *dis_file, char *leader, int debug)
672{
673  Metric *item;
674  int index;
675  char fmt_name[64];
676  fprintf (dis_file, NTXT ("%s"), leader);
677  if (items == NULL)
678    {
679      fprintf (dis_file, GTXT ("NULL metric list can not be printed; aborting"));
680      abort ();
681    }
682
683  if (items->size () == 0)
684    {
685      fprintf (dis_file, GTXT ("metric list is empty; aborting\n"));
686      abort ();
687    }
688
689  // if debugging, print list address and string, and sort name
690  if (debug != 0)
691    {
692      char *s = get_metrics ();
693      fprintf (dis_file, "\tmetriclist at 0x%lx: %s, %lld metrics; sort by %s\n",
694	       (unsigned long) this, s, (long long) items->size (),
695	       get_sort_name ());
696      free (s);
697      if (debug == 1)
698	return;
699    }
700
701  // Find the longest metric name & command
702  size_t max_len = 0;
703  size_t max_len2 = 0;
704
705  Vec_loop (Metric*, items, index, item)
706  {
707    // get the name
708    char *mn = item->get_name ();
709    size_t len = strlen (mn);
710    if (max_len < len)
711      max_len = len;
712
713    mn = item->get_mcmd (true);
714    len = strlen (mn);
715    if (max_len2 < len)
716      max_len2 = len;
717    free (mn);
718
719  }
720  if (debug == 2)
721    snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%-%ds", (int) max_len,
722	      (int) max_len2);
723  else
724    snprintf (fmt_name, sizeof (fmt_name), "%%%ds: %%s", (int) max_len);
725
726  Vec_loop (Metric*, items, index, item)
727  {
728    char *mcmd = item->get_mcmd (true);
729    fprintf (dis_file, fmt_name, item->get_name (), mcmd);
730    free (mcmd);
731    if (debug == 2)
732      fprintf (dis_file, "\t[st %2d, VT %d, vis = %4s, T=%d, sort = %c]",
733	       item->get_subtype (), item->get_vtype (),
734	       item->get_vis_str (), item->is_time_val (),
735	       sort_ref_index == index ? 'Y' : 'N');
736    fputc ('\n', dis_file);
737  }
738
739  fputc ('\n', dis_file);
740  fflush (dis_file);
741}
742
743// Return a string formatted from a vector of metrics
744//	string is in the form suitable for a "metrics <string>" command
745char *
746MetricList::get_metrics ()
747{
748  Metric *item;
749  int index;
750  StringBuilder sb;
751  Vec_loop (Metric*, items, index, item)
752  {
753    if (sb.length () != 0)
754      sb.append (':');
755    char *mcmd = item->get_mcmd (false);
756    sb.append (mcmd);
757    free (mcmd);
758  }
759  return sb.toString ();
760}
761
762int
763MetricList::get_listorder (Metric *mtr)
764{
765  for (int i = 0, items_sz = items->size (); i < items_sz; i++)
766    {
767      Metric *m = items->fetch (i);
768      if (m->get_subtype () == mtr->get_subtype ()
769	  && m->get_id () == mtr->get_id ())
770	return i;
771    }
772  return -1;
773}
774
775int
776MetricList::get_listorder (char *cmd, BaseMetric::SubType st, const char *expr)
777{
778  for (long i = 0, items_sz = items->size (); i < items_sz; i++)
779    {
780      Metric *m = items->fetch (i);
781      if (m->get_subtype () == st && dbe_strcmp (m->get_cmd (), cmd) == 0
782	  && dbe_strcmp (m->get_expr_spec (), expr) == 0)
783	return (int) i;
784    }
785  return -1;
786}
787
788Metric *
789MetricList::find_metric_by_name (char *cmd)
790{
791  for (long i = 0, items_sz = items->size (); i < items_sz; i++)
792    {
793      Metric *m = items->fetch (i);
794      if (dbe_strcmp (m->get_cmd (), cmd) == 0)
795	return m;
796    }
797  return NULL;
798}
799
800//  find a metric by name and subtype
801Metric *
802MetricList::find_metric (char *cmd, BaseMetric::SubType st)
803{
804  int i = get_listorder (cmd, st);
805  if (i < 0)
806    return NULL;
807  return items->fetch (i);
808}
809
810//  Get the sort metric from a list; forces sort by first if not set
811Metric *
812MetricList::get_sort_metric ()
813{
814  int i = get_sort_ref_index ();
815  return i >= 0 ? items->fetch (i) : NULL;
816}
817
818char *
819MetricList::get_sort_name ()
820{
821  Metric *item = get_sort_metric ();
822  if (item == NULL)
823    return dbe_strdup (NTXT (""));
824  char *n = item->get_name ();
825  return sort_reverse ? dbe_sprintf ("-%s", n) : dbe_strdup (n);
826}
827
828char *
829MetricList::get_sort_cmd ()
830{
831  char *buf;
832  Metric *item = get_sort_metric ();
833  if (item == NULL)
834    return dbe_strdup (NTXT (""));
835  char *n = item->get_mcmd (false);
836  if (sort_reverse)
837    {
838      buf = dbe_sprintf (NTXT ("-%s"), n);
839      free (n);
840    }
841  else
842    buf = n;
843  return buf;
844}
845
846Metric *
847MetricList::append (BaseMetric *bm, BaseMetric::SubType st, int visbits)
848{
849  for (long i = 0, sz = items->size (); i < sz; i++)
850    {
851      Metric *m = items->get (i);
852      if (m->get_id () == bm->get_id () && m->get_subtype () == st)
853	return NULL;
854    }
855  Metric *met = new Metric (bm, st);
856  met->set_dmetrics_visbits (visbits);
857  items->append (met);
858  return met;
859}
860
861int
862MetricList::add_matching_dmetrics (Vector<BaseMetric*> *base_items,
863				   char *mcmd, BaseMetric::SubType *_subtypes,
864				   int nsubtypes, int dmetrics_visbits,
865				   bool fromRcFile)
866{
867  bool any = false, hwc = false, bit = false;
868  int got_metric = 1;
869
870  // check keywords 'any', 'all', 'bit', and 'hwc'
871  if (!strcasecmp (mcmd, Command::ANY_CMD))
872    any = true;
873  else if (!strcasecmp (mcmd, Command::ALL_CMD))
874    any = true;
875  else if (!strcasecmp (mcmd, Command::HWC_CMD))
876    hwc = true;
877  else if (!strcasecmp (mcmd, Command::BIT_CMD))
878    bit = true;
879
880  BaseMetric::SubType *subtypes = _subtypes;
881  BaseMetric::SubType all_subtypes[2] =
882    { BaseMetric::EXCLUSIVE, BaseMetric::INCLUSIVE };
883
884  if (nsubtypes == 0 || (nsubtypes == 1 && subtypes[0] == BaseMetric::STATIC))
885    {
886      // user did not specify ei; treat as wildcard and supply both.
887      subtypes = all_subtypes;
888      nsubtypes = 2;
889    }
890
891  // scan the metrics to find all matches
892  for (int i = 0, base_sz = base_items->size (); i < base_sz; i++)
893    {
894      BaseMetric *item = base_items->fetch (i);
895      if (!(any || (hwc && item->get_type () == BaseMetric::HWCNTR)
896	    || (bit && item->get_cmd ()
897		&& strncmp (item->get_cmd (), Command::BIT_CMD,
898			    strlen (Command::BIT_CMD)) == 0)
899	    || dbe_strcmp (item->get_cmd (), mcmd) == 0))
900	continue;
901      if (item->is_internal ())
902	continue;
903      if (item->get_flavors () & BaseMetric::STATIC)
904	{
905	  got_metric = 0;
906	  int vis = item->get_type () != BaseMetric::ONAME ?
907		  dmetrics_visbits : VAL_VALUE;
908	  if (append (item, BaseMetric::STATIC, vis) == NULL && !fromRcFile)
909	    return 2;
910	  continue;
911	}
912
913      // special case for omp metrics: make visible only if
914      // omp data has been collected
915      if (!dbeSession->is_omp_available ()
916	  && (strcasecmp (mcmd, "ompwork") == 0
917	      || strcasecmp (mcmd, "ompwait") == 0))
918	  continue;
919
920      for (int j = 0; j < nsubtypes; j++)
921	{
922	  if (append (item, subtypes[j], dmetrics_visbits) == NULL
923	      && !fromRcFile)
924	    return 2;
925	}
926      got_metric = 0;
927      if (!(any || hwc || bit))
928	break;
929    }
930  return got_metric;
931}
932
933// parse a single metric specification, to give:
934//	a vector of subtypes, and a count of the number of them
935//	an integer visibility
936//	return the string for the metric name
937
938char *
939MetricList::parse_metric_spec (char *mcmd, BaseMetric::SubType *subtypes,
940			       int *nsubtypes, int *dmetrics_visb, bool *isOK)
941{
942  size_t len_vtype;
943  int index;
944  int vis;
945  bool got_e, got_i, got_a, got_d;
946  char *str = mcmd;
947  char *str2;
948
949  *isOK = true;
950
951  // For dynamic metrics, each keyword is of the form  <flavor><visibility><metric-name>
952  // For static metrics, each keyword is of the form [<visibility>]<metric-name>
953  // <flavor> can be either "i" for inclusive or "e" for exclusive
954  // <visibility> can be any combination of "." (to show the metric as a time),
955  //    "%" (to show it as a percentage), "+" (to show it as a count), and "!" (turn off the metric)
956
957  // find subtype
958  index = 0;
959  size_t len_subtype = strspn (str, NTXT ("eiad"));
960  str2 = str + len_subtype;
961
962  // find vis
963  if (len_subtype == 0)
964    {
965      // only a . or ! is possible if no subtypes
966      len_vtype = strspn (str2, NTXT (".!"));
967      vis = VAL_VALUE;
968    }
969  else
970    {
971      len_vtype = strspn (str2, NTXT (".+%!"));
972      vis = VAL_NA;
973    }
974
975  // if no visibility bits, there can't be a subtype
976  if (len_vtype == 0)
977    len_subtype = 0;
978
979  if (len_subtype == 0)
980    {
981      // must be a static metric
982      subtypes[index++] = BaseMetric::STATIC;
983      vis = VAL_VALUE;
984    }
985  else
986    {
987      // figure out which subtypes are specified
988      got_e = got_i = got_a = got_d = false;
989      for (size_t i = 0; i < len_subtype; i++)
990	{
991	  str += len_subtype;
992	  if (mcmd[i] == 'e')
993	    { // exclusive
994	      if (mtype == MET_DATA)
995		{
996		  *isOK = false;
997		  return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"),
998				      mcmd);
999		}
1000	      if (!got_e)
1001		{
1002		  got_e = true;
1003		  subtypes[index++] = BaseMetric::EXCLUSIVE;
1004		}
1005	    }
1006	  else if (mcmd[i] == 'i')
1007	    { // inclusive
1008	      if (mtype == MET_DATA)
1009		{
1010		  *isOK = false;
1011		  return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for data metrics\n"),
1012				      mcmd);
1013		}
1014	      if (mtype == MET_INDX)
1015		{
1016		  *isOK = false;
1017		  return dbe_sprintf (GTXT ("Invalid metric specification: %s inapplicable for index metrics\n"),
1018				      mcmd);
1019		}
1020	      if (!got_i)
1021		{
1022		  got_i = true;
1023		  subtypes[index++] = BaseMetric::INCLUSIVE;
1024		}
1025	    }
1026	  else if (mcmd[i] == 'a')
1027	    { // attributed
1028	      if (mtype != MET_CALL && mtype != MET_CALL_AGR)
1029		{
1030		  *isOK = false;
1031		  return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for caller-callee metrics only\n"),
1032				      mcmd);
1033		}
1034	      if (!got_a)
1035		{
1036		  got_a = true;
1037		  subtypes[index++] = BaseMetric::ATTRIBUTED;
1038		}
1039	    }
1040	  else if (mcmd[i] == 'd')
1041	    { // data-space
1042	      if (mtype != MET_DATA)
1043		{
1044		  *isOK = false;
1045		  return dbe_sprintf (GTXT ("Invalid metric specification: %s applicable for data-derived metrics only\n"),
1046				      mcmd);
1047		}
1048	      if (!got_d)
1049		{
1050		  got_d = true;
1051		  subtypes[index++] = BaseMetric::DATASPACE;
1052		}
1053	    }
1054	}
1055    }
1056  *nsubtypes = index;
1057
1058  // now determine the visiblity bits
1059  if (len_vtype > 0)
1060    {
1061      for (size_t i = 0; i < len_vtype; i++)
1062	{
1063	  if (str2[i] == '+')
1064	    vis = (vis | VAL_VALUE);
1065	  else if (str2[i] == '.')
1066	    vis = (vis | VAL_TIMEVAL);
1067	  else if (str2[i] == '%')
1068	    vis = (vis | VAL_PERCENT);
1069	  else if (str2[i] == '!')
1070	    vis = (vis | VAL_HIDE_ALL);
1071	}
1072    }
1073  *dmetrics_visb = vis;
1074  return mcmd + len_subtype + len_vtype;
1075}
1076