/* Copyright (C) 2021-2023 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 <strings.h>
#include <stdlib.h>

#include "util.h"
#include "BaseMetric.h"
#include "DbeSession.h"
#include "Expression.h"

int BaseMetric::last_id = 0;

void
BaseMetric::init (Type t)
{
  id = last_id++;
  type = t;
  aux = NULL;
  cmd = NULL;
  username = NULL;
  hw_ctr = NULL;
  cond = NULL;
  val = NULL;
  expr = NULL;
  cond_spec = NULL;
  val_spec = NULL;
  expr_spec = NULL;
  legend = NULL;
  definition = NULL;
  dependent_bm = NULL;
  zeroThreshold = 0;
  clock_unit = (Presentation_clock_unit) 0;
  for (int ii = 0; ii < NSUBTYPES; ii++)
    default_visbits[ii] = VAL_NA;
  valtype = VT_DOUBLE;
  precision = METRIC_HR_PRECISION;
  flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
  value_styles = VAL_TIMEVAL | VAL_PERCENT;
}

BaseMetric::BaseMetric (Type t)
{
  init (t);
  switch (t)
    {
    case CP_LMS_USER:
    case CP_LMS_SYSTEM:
    case CP_LMS_WAIT_CPU:
    case CP_LMS_USER_LOCK:
    case CP_LMS_TFAULT:
    case CP_LMS_DFAULT:
    case OMP_MASTER_THREAD:
    case CP_TOTAL:
    case CP_TOTAL_CPU:
    case CP_LMS_TRAP:
    case CP_LMS_KFAULT:
    case CP_LMS_SLEEP:
    case CP_LMS_STOPPED:
    case OMP_NONE:
    case OMP_OVHD:
    case OMP_WORK:
    case OMP_IBAR:
    case OMP_EBAR:
    case OMP_WAIT:
    case OMP_SERL:
    case OMP_RDUC:
    case OMP_LKWT:
    case OMP_CTWT:
    case OMP_ODWT:
    case OMP_MSTR:
    case OMP_SNGL:
    case OMP_ORDD:
    case CP_KERNEL_CPU:
      // all of these are floating point, precision = clock profile tick
      valtype = VT_DOUBLE;
      precision = METRIC_SIG_PRECISION;
      flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
      value_styles = VAL_TIMEVAL | VAL_PERCENT;
      break;
    case SYNC_WAIT_TIME:
    case IO_READ_TIME:
    case IO_WRITE_TIME:
    case IO_OTHER_TIME:
    case IO_ERROR_TIME:
      // all of these are floating point, precision = hrtime tick
      valtype = VT_DOUBLE;
      precision = METRIC_HR_PRECISION;
      flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
      value_styles = VAL_TIMEVAL | VAL_PERCENT;
      break;
    case SYNC_WAIT_COUNT:
    case HEAP_ALLOC_CNT:
    case HEAP_LEAK_CNT:
    case IO_READ_CNT:
    case IO_WRITE_CNT:
    case IO_OTHER_CNT:
    case IO_ERROR_CNT:
      valtype = VT_LLONG;
      precision = 1;
      flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
      value_styles = VAL_VALUE | VAL_PERCENT;
      break;
    case RACCESS:
    case DEADLOCKS:
      // all of these are integer
      valtype = VT_LLONG;
      precision = 1;
      flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
      value_styles = VAL_VALUE | VAL_PERCENT;
      zeroThreshold = 1;
      break;
    case HEAP_ALLOC_BYTES:
    case HEAP_LEAK_BYTES:
    case IO_READ_BYTES:
    case IO_WRITE_BYTES:
      // all of these are longlong
      valtype = VT_ULLONG;
      precision = 1;
      flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
      value_styles = VAL_VALUE | VAL_PERCENT;
      break;
    case SIZES:
      valtype = VT_LLONG;
      precision = 1;
      flavors = STATIC;
      value_styles = VAL_VALUE;
      break;
    case ADDRESS:
      valtype = VT_ADDRESS;
      precision = 1;
      flavors = STATIC;
      value_styles = VAL_VALUE;
      break;
    case ONAME:
      valtype = VT_LABEL;
      precision = 0;
      flavors = STATIC;
      value_styles = VAL_VALUE;
      break;
    case HWCNTR: // We should call the other constructor for hwc metric
    default:
      abort ();
    }
  specify ();
}

// Constructor for linked HW counters (base counter)
BaseMetric::BaseMetric (Hwcentry *ctr, const char* _aux, const char* _username,
			int v_styles, BaseMetric* _dependent_bm)
{
  hwc_init (ctr, _aux, _aux, _username, v_styles);
  dependent_bm = _dependent_bm;
}

// Constructor for linked HW counters (derived counter)

BaseMetric::BaseMetric (Hwcentry *ctr, const char *_aux, const char *_cmdname,
			const char *_username, int v_styles)
{
  hwc_init (ctr, _aux, _cmdname, _username, v_styles);
}

void
BaseMetric::hwc_init (Hwcentry *ctr, const char* _aux, const char* _cmdname,
		      const char* _username, int v_styles)
{
  init (HWCNTR);
  aux = dbe_strdup (_aux);      // HWC identifier
  cmd = dbe_strdup (_cmdname);  // may differ from _aux for cycles->time hwcs
  username = dbe_strdup (_username);
  flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
  value_styles = v_styles | VAL_PERCENT;
  if ((value_styles & (VAL_TIMEVAL | VAL_VALUE)) == VAL_TIMEVAL)
    valtype = VT_DOUBLE;
  else
    valtype = VT_ULLONG;
  if (ABST_MEMSPACE_ENABLED (ctr->memop))
    flavors |= DATASPACE; // only for ctrs with memop definitions
  hw_ctr = ctr;
  specify ();
}

// Constructor for derived metrics
BaseMetric::BaseMetric (const char *_cmd, const char *_username,
			Definition *def)
{
  init (DERIVED);
  cmd = dbe_strdup (_cmd);
  username = dbe_strdup (_username);
  aux = dbe_strdup (_cmd);
  definition = def;
  flavors = EXCLUSIVE | INCLUSIVE | ATTRIBUTED;
  clock_unit = CUNIT_NULL; // should it be CUNIT_TIME or 0 or something?

  /* we're not going to process packets for derived metrics */
  packet_type = (ProfData_type) (-1);
  value_styles = VAL_VALUE;
  valtype = VT_DOUBLE;
  precision = 1000;
}

// Copy constructor
BaseMetric::BaseMetric (const BaseMetric& m)
{
  id = m.id;
  type = m.type;
  aux = dbe_strdup (m.aux);
  cmd = dbe_strdup (m.cmd);
  username = dbe_strdup (m.username);
  flavors = m.flavors;
  value_styles = m.value_styles;
  valtype = m.valtype;
  precision = m.precision;
  hw_ctr = m.hw_ctr;
  packet_type = m.packet_type;
  zeroThreshold = m.zeroThreshold;
  clock_unit = m.clock_unit;
  for (int ii = 0; ii < NSUBTYPES; ii++)
    default_visbits[ii] = m.default_visbits[ii];
  if (m.cond_spec)
    {
      cond_spec = strdup (m.cond_spec);
      cond = m.cond->copy ();
    }
  else
    {
      cond = NULL;
      cond_spec = NULL;
    }
  if (m.val_spec)
    {
      val_spec = strdup (m.val_spec);
      val = m.val->copy ();
    }
  else
    {
      val = NULL;
      val_spec = NULL;
    }
  if (m.expr_spec)
    {
      expr_spec = strdup (m.expr_spec);
      expr = m.expr->copy ();
    }
  else
    {
      expr = NULL;
      expr_spec = NULL;
    }
  legend = dbe_strdup (m.legend);
  definition = NULL;
  if (m.definition)
    definition = Definition::add_definition (m.definition->def);
  dependent_bm = m.dependent_bm;
}

BaseMetric::~BaseMetric ()
{
  free (aux);
  free (cmd);
  free (cond_spec);
  free (val_spec);
  free (expr_spec);
  free (legend);
  free (username);
  delete cond;
  delete val;
  delete expr;
  delete definition;
}

bool
BaseMetric::is_internal ()
{
  return (get_value_styles () & VAL_INTERNAL) != 0;
}

int
BaseMetric::get_default_visbits (SubType subtype)
{
  int rc = VAL_NA;
  switch (subtype)
    {
    case STATIC:
    case EXCLUSIVE:
      rc = default_visbits[0];
      break;
    case INCLUSIVE:
      rc = default_visbits[1];
      break;
    default:
      break;
    }
  return rc;
}

void
BaseMetric::set_default_visbits (SubType subtype, int _visbits)
{
  switch (subtype)
    {
    case STATIC:
    case EXCLUSIVE:
      default_visbits[0] = _visbits;
      break;
    case INCLUSIVE:
      default_visbits[1] = _visbits;
      break;
    default:
      break;
    }
}

void
BaseMetric::set_cond_spec (char *_cond_spec)
{
  if (cond_spec)
    {
      free (cond_spec);
      delete cond;
      cond_spec = NULL;
      cond = NULL;
    }
  if (_cond_spec)
    {
      cond = dbeSession->ql_parse (_cond_spec);
      if (cond == NULL)
	{
	  fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _cond_spec);
	  abort ();
	}
      cond_spec = dbe_strdup (_cond_spec);
    }
}

void
BaseMetric::set_val_spec (char *_val_spec)
{
  if (val_spec)
    {
      free (val_spec);
      delete val;
      val_spec = NULL;
      val = NULL;
    }
  if (_val_spec)
    {
      val = dbeSession->ql_parse (_val_spec);
      if (val == NULL)
	{
	  fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _val_spec);
	  abort ();
	}
      val_spec = dbe_strdup (_val_spec);
    }
}

void
BaseMetric::set_expr_spec (char *_expr_spec)
{
  id = last_id++;
  if (expr_spec)
    {
      free (expr_spec);
      delete expr;
      expr_spec = NULL;
      expr = NULL;
    }
  if (_expr_spec)
    {
      expr = dbeSession->ql_parse (_expr_spec);
      if (expr == NULL)
	{
	  fprintf (stderr, GTXT ("Invalid expression in metric specification `%s'\n"), _expr_spec);
	  return;
	}
      expr_spec = dbe_strdup (_expr_spec);
    }
}

void
BaseMetric::specify_mstate_metric (int st)
{
  char buf[128];
  snprintf (buf, sizeof (buf), NTXT ("MSTATE==%d"), st);
  specify_prof_metric (buf);
}

void
BaseMetric::specify_ompstate_metric (int st)
{
  char buf[128];
  snprintf (buf, sizeof (buf), NTXT ("OMPSTATE==%d"), st);
  specify_prof_metric (buf);
}

void
BaseMetric::specify_prof_metric (char *_cond_spec)
{
  packet_type = DATA_CLOCK;
  specify_metric (_cond_spec, NTXT ("NTICK_USEC")); // microseconds
}

void
BaseMetric::specify_metric (char *_cond_spec, char *_val_spec)
{
  set_cond_spec (_cond_spec);
  set_val_spec (_val_spec);
}

void
BaseMetric::specify ()
{
  enum
  {
    IDLE_STATE_BITS =
	(1 << OMP_IDLE_STATE) | (1 << OMP_IBAR_STATE) | (1 << OMP_EBAR_STATE) |
	(1 << OMP_LKWT_STATE) | (1 << OMP_CTWT_STATE) | (1 << OMP_ODWT_STATE) |
	(1 << OMP_ATWT_STATE) | (1 << OMP_TSKWT_STATE),
    LMS_USER_BITS =
	(1 << OMP_NO_STATE) | (1 << OMP_WORK_STATE) | (1 << OMP_SERL_STATE) |
	(1 << OMP_RDUC_STATE)
  };

  char buf[256];
  char buf2[256];
  packet_type = (ProfData_type) - 1; // illegal value
  clock_unit = CUNIT_TIME;
  switch (type)
    {
    case SIZES:
      username = dbe_strdup (GTXT ("Size"));
      clock_unit = CUNIT_BYTES;
      cmd = dbe_strdup (NTXT ("size"));
      break;
    case ADDRESS:
      username = dbe_strdup (GTXT ("PC Address"));
      cmd = dbe_strdup (NTXT ("address"));
      break;
    case ONAME:
      username = dbe_strdup (GTXT ("Name"));
      cmd = dbe_strdup (NTXT ("name"));
      break;
    case CP_LMS_SYSTEM:
      username = dbe_strdup (GTXT ("System CPU Time"));
      specify_mstate_metric (LMS_SYSTEM);
      cmd = dbe_strdup (NTXT ("system"));
      break;
    case CP_TOTAL_CPU:
      username = dbe_strdup (GTXT ("Total CPU Time"));
      snprintf (buf, sizeof (buf),
		"(MSTATE==%d)||(MSTATE==%d)||(MSTATE==%d)||(MSTATE==%d)",
		LMS_USER, LMS_SYSTEM, LMS_TRAP, LMS_LINUX_CPU);
      specify_prof_metric (buf);
      cmd = dbe_strdup (NTXT ("totalcpu"));
      break;
    case CP_TOTAL:
      username = dbe_strdup (GTXT ("Total Thread Time"));
      snprintf (buf, sizeof (buf), NTXT ("(MSTATE!=%d)&&(MSTATE!=%d)"),
		LMS_KERNEL_CPU, LMS_LINUX_CPU);
      specify_prof_metric (buf);
      cmd = dbe_strdup (NTXT ("total"));
      break;
    case CP_KERNEL_CPU:
      username = dbe_strdup (GTXT ("Kernel CPU Time"));
      specify_mstate_metric (LMS_KERNEL_CPU);
      cmd = dbe_strdup (NTXT ("kcpu"));
      break;
    case OMP_MASTER_THREAD:
      username = dbe_strdup (GTXT ("Master Thread Time"));
      specify_prof_metric (NTXT ("LWPID==1"));
      cmd = dbe_strdup (NTXT ("masterthread"));
      break;
    case CP_LMS_USER:
      username = dbe_strdup (GTXT ("User CPU Time"));
      specify_mstate_metric (LMS_USER);
      cmd = dbe_strdup (NTXT ("user"));
      break;
    case CP_LMS_WAIT_CPU:
      username = dbe_strdup (GTXT ("Wait CPU Time"));
      specify_mstate_metric (LMS_WAIT_CPU);
      cmd = dbe_strdup (NTXT ("wait"));
      break;
    case CP_LMS_USER_LOCK:
      username = dbe_strdup (GTXT ("User Lock Time"));
      specify_mstate_metric (LMS_USER_LOCK);
      cmd = dbe_strdup (NTXT ("lock"));
      break;
    case CP_LMS_TFAULT:
      username = dbe_strdup (GTXT ("Text Page Fault Time"));
      specify_mstate_metric (LMS_TFAULT);
      cmd = dbe_strdup (NTXT ("textpfault"));
      break;
    case CP_LMS_DFAULT:
      username = dbe_strdup (GTXT ("Data Page Fault Time"));
      specify_mstate_metric (LMS_DFAULT);
      cmd = dbe_strdup (NTXT ("datapfault"));
      break;
    case CP_LMS_TRAP:
      username = dbe_strdup (GTXT ("Trap CPU Time"));
      specify_mstate_metric (LMS_TRAP);
      cmd = dbe_strdup (NTXT ("trap"));
      break;
    case CP_LMS_KFAULT:
      username = dbe_strdup (GTXT ("Kernel Page Fault Time"));
      specify_mstate_metric (LMS_KFAULT);
      cmd = dbe_strdup (NTXT ("kernelpfault"));
      break;
    case CP_LMS_SLEEP:
      username = dbe_strdup (GTXT ("Sleep Time"));
      specify_mstate_metric (LMS_SLEEP);
      cmd = dbe_strdup (NTXT ("sleep"));
      break;
    case CP_LMS_STOPPED:
      username = dbe_strdup (GTXT ("Stopped Time"));
      specify_mstate_metric (LMS_STOPPED);
      cmd = dbe_strdup (NTXT ("stop"));
      break;
    case OMP_OVHD:
      username = dbe_strdup (GTXT ("OpenMP Overhead Time"));
      specify_ompstate_metric (OMP_OVHD_STATE);
      cmd = dbe_strdup (NTXT ("ompovhd"));
      break;
    case OMP_WORK:
      username = dbe_strdup (GTXT ("OpenMP Work Time"));
      snprintf (buf, sizeof (buf),
		NTXT ("(OMPSTATE>=0) && (MSTATE==%d) && ((1<<OMPSTATE) & %d)"),
		LMS_USER, LMS_USER_BITS);
      specify_prof_metric (buf);
      cmd = dbe_strdup (NTXT ("ompwork"));
      break;
    case OMP_WAIT:
      username = dbe_strdup (GTXT ("OpenMP Wait Time"));
      snprintf (buf, sizeof (buf),
		"OMPSTATE>=0 && ((1<<OMPSTATE) & ((MSTATE!=%d) ? %d : %d))",
		LMS_USER, (LMS_USER_BITS | IDLE_STATE_BITS), IDLE_STATE_BITS);
      specify_prof_metric (buf);
      cmd = dbe_strdup (NTXT ("ompwait"));
      break;
    case OMP_IBAR:
      username = dbe_strdup (GTXT ("OpenMP Implicit Barrier Time"));
      specify_ompstate_metric (OMP_IBAR_STATE);
      cmd = dbe_strdup (NTXT ("ompibar"));
      break;
    case OMP_EBAR:
      username = dbe_strdup (GTXT ("OpenMP Explicit Barrier Time"));
      specify_ompstate_metric (OMP_EBAR_STATE);
      cmd = dbe_strdup (NTXT ("ompebar"));
      break;
    case OMP_SERL:
      username = dbe_strdup (GTXT ("OpenMP Serial Time"));
      specify_ompstate_metric (OMP_SERL_STATE);
      cmd = dbe_strdup (NTXT ("ompserl"));
      break;
    case OMP_RDUC:
      username = dbe_strdup (GTXT ("OpenMP Reduction Time"));
      specify_ompstate_metric (OMP_RDUC_STATE);
      cmd = dbe_strdup (NTXT ("omprduc"));
      break;
    case OMP_LKWT:
      username = dbe_strdup (GTXT ("OpenMP Lock Wait Time"));
      specify_ompstate_metric (OMP_LKWT_STATE);
      cmd = dbe_strdup (NTXT ("omplkwt"));
      break;
    case OMP_CTWT:
      username = dbe_strdup (GTXT ("OpenMP Critical Section Wait Time"));
      specify_ompstate_metric (OMP_CTWT_STATE);
      cmd = dbe_strdup (NTXT ("ompctwt"));
      break;
    case OMP_ODWT:
      username = dbe_strdup (GTXT ("OpenMP Ordered Section Wait Time"));
      specify_ompstate_metric (OMP_ODWT_STATE);
      cmd = dbe_strdup (NTXT ("ompodwt"));
      break;
    case SYNC_WAIT_TIME:
      packet_type = DATA_SYNCH;
      username = dbe_strdup (GTXT ("Sync Wait Time"));
      snprintf (buf, sizeof (buf), NTXT ("(EVT_TIME)/%lld"),
		(long long) (NANOSEC / METRIC_HR_PRECISION));
      specify_metric (NULL, buf);
      cmd = dbe_strdup (NTXT ("sync"));
      break;
    case SYNC_WAIT_COUNT:
      packet_type = DATA_SYNCH;
      username = dbe_strdup (GTXT ("Sync Wait Count"));
      specify_metric (NULL, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("syncn"));
      break;
    case HEAP_ALLOC_CNT:
      packet_type = DATA_HEAP;
      username = dbe_strdup (GTXT ("Allocations"));
      snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"),
		FREE_TRACE, MUNMAP_TRACE);
      specify_metric (buf, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("heapalloccnt"));
      break;
    case HEAP_ALLOC_BYTES:
      packet_type = DATA_HEAP;
      username = dbe_strdup (GTXT ("Bytes Allocated"));
      snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"),
		FREE_TRACE, MUNMAP_TRACE);
      specify_metric (buf, NTXT ("HSIZE"));
      cmd = dbe_strdup (NTXT ("heapallocbytes"));
      break;
    case HEAP_LEAK_CNT:
      packet_type = DATA_HEAP;
      username = dbe_strdup (GTXT ("Leaks"));
      snprintf (buf, sizeof (buf), "(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR&&HLEAKED",
		FREE_TRACE, MUNMAP_TRACE);
      specify_metric (buf, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("heapleakcnt"));
      break;
    case HEAP_LEAK_BYTES:
      packet_type = DATA_HEAP;
      username = dbe_strdup (GTXT ("Bytes Leaked"));
      snprintf (buf, sizeof (buf), NTXT ("(HTYPE!=%d)&&(HTYPE!=%d)&&HVADDR"),
		FREE_TRACE, MUNMAP_TRACE);
      specify_metric (buf, NTXT ("HLEAKED"));
      cmd = dbe_strdup (NTXT ("heapleakbytes"));
      break;

    case IO_READ_CNT:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Read Count"));
      snprintf (buf, sizeof (buf), "(IOTYPE==%d)", READ_TRACE);
      specify_metric (buf, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("ioreadcnt"));
      break;
    case IO_WRITE_CNT:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Write Count"));
      snprintf (buf, sizeof (buf), "(IOTYPE==%d)", WRITE_TRACE);
      specify_metric (buf, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("iowritecnt"));
      break;
    case IO_OTHER_CNT:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Other I/O Count"));
      snprintf (buf, sizeof (buf), "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)",
		OPEN_TRACE, CLOSE_TRACE, OTHERIO_TRACE);
      specify_metric (buf, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("ioothercnt"));
      break;
    case IO_ERROR_CNT:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("I/O Error Count"));
      snprintf (buf, sizeof (buf),
	 "(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)",
	 READ_TRACE_ERROR, WRITE_TRACE_ERROR, OPEN_TRACE_ERROR,
	 CLOSE_TRACE_ERROR, OTHERIO_TRACE_ERROR);
      specify_metric (buf, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("ioerrorcnt"));
      break;
    case IO_READ_BYTES:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Read Bytes"));
      snprintf (buf, sizeof (buf), NTXT ("(IOTYPE==%d)&&IONBYTE"),
		READ_TRACE);
      specify_metric (buf, NTXT ("IONBYTE"));
      cmd = dbe_strdup (NTXT ("ioreadbytes"));
      break;
    case IO_WRITE_BYTES:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Write Bytes"));
      snprintf (buf, sizeof (buf), "(IOTYPE==%d)&&IONBYTE", WRITE_TRACE);
      specify_metric (buf, NTXT ("IONBYTE"));
      cmd = dbe_strdup (NTXT ("iowritebytes"));
      break;
    case IO_READ_TIME:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Read Time"));
      snprintf (buf, sizeof (buf), "(IOTYPE==%d)&&EVT_TIME", READ_TRACE);
      snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"),
		(long long) (NANOSEC / METRIC_HR_PRECISION));
      specify_metric (buf, buf2);
      cmd = dbe_strdup (NTXT ("ioreadtime"));
      break;
    case IO_WRITE_TIME:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Write Time"));
      snprintf (buf, sizeof (buf), NTXT ("(IOTYPE==%d)&&EVT_TIME"),
		WRITE_TRACE);
      snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"),
		(long long) (NANOSEC / METRIC_HR_PRECISION));
      specify_metric (buf, buf2);
      cmd = dbe_strdup (NTXT ("iowritetime"));
      break;
    case IO_OTHER_TIME:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("Other I/O Time"));
      snprintf (buf, sizeof (buf),
		"(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)&&EVT_TIME",
		OPEN_TRACE, CLOSE_TRACE, OTHERIO_TRACE);
      snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"),
		(long long) (NANOSEC / METRIC_HR_PRECISION));
      specify_metric (buf, buf2);
      cmd = dbe_strdup (NTXT ("ioothertime"));
      break;
    case IO_ERROR_TIME:
      packet_type = DATA_IOTRACE;
      username = dbe_strdup (GTXT ("I/O Error Time"));
      snprintf (buf, sizeof (buf),
		"(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)||(IOTYPE==%d)&&EVT_TIME",
		READ_TRACE_ERROR, WRITE_TRACE_ERROR, OPEN_TRACE_ERROR,
		CLOSE_TRACE_ERROR, OTHERIO_TRACE_ERROR);
      snprintf (buf2, sizeof (buf2), NTXT ("(EVT_TIME)/%lld"),
		(long long) (NANOSEC / METRIC_HR_PRECISION));
      specify_metric (buf, buf2);
      cmd = dbe_strdup (NTXT ("ioerrortime"));
      break;
    case RACCESS:
      packet_type = DATA_RACE;
      username = dbe_strdup (GTXT ("Race Accesses"));
      specify_metric (NULL, NTXT ("RCNT"));
      cmd = dbe_strdup (NTXT ("raccess"));
      break;
    case DEADLOCKS:
      packet_type = DATA_DLCK;
      username = dbe_strdup (GTXT ("Deadlocks"));
      specify_metric (NULL, NTXT ("1"));
      cmd = dbe_strdup (NTXT ("deadlocks"));
      break;
    case HWCNTR:
      packet_type = DATA_HWC;
      // username, cmd, and aux set by hwc constructor
      if (valtype == VT_DOUBLE)
	{
	  if (hw_ctr->timecvt > 0)  // CPU cycles
	    specify_metric (NULL, NTXT ("((HWCINT*1000000)/FREQ_MHZ)"));
	  else if (hw_ctr->timecvt < 0)
	    { // reference clock (frequency is -timecvt MHz)
	      snprintf (buf, sizeof (buf), NTXT ("((HWCINT*1000000)/%d)"), -hw_ctr->timecvt);
	      specify_metric (NULL, buf);
	    }
	  else  // shouldn't happen
	    specify_metric (NULL, NTXT ("0"));
	  // resulting unit: seconds * 1e12
	  precision = 1000000LL * 1000000LL; // Seconds * 1e12
	}
      else
	{
	  specify_metric (NULL, NTXT ("HWCINT"));
	  precision = 1;
	}
      break;
    case OMP_MSTR:
    case OMP_SNGL:
    case OMP_ORDD:
    case OMP_NONE:
    default:
      username = dbe_strdup (GTXT ("****"));
      fprintf (stderr, "BaseMetric::init Undefined basemetric %s\n",
	       get_basetype_name ());
    }
}

#define CASE_S(x)   case x: s = (char *) #x; break
char *
BaseMetric::get_basetype_name ()
{
  static char buf[128];
  char *s;
  switch (type)
    {
      CASE_S (CP_LMS_SYSTEM);
      CASE_S (CP_TOTAL_CPU);
      CASE_S (CP_TOTAL);
      CASE_S (OMP_MASTER_THREAD);
      CASE_S (CP_LMS_USER);
      CASE_S (CP_LMS_WAIT_CPU);
      CASE_S (CP_LMS_USER_LOCK);
      CASE_S (CP_LMS_TFAULT);
      CASE_S (CP_LMS_DFAULT);
      CASE_S (CP_LMS_TRAP);
      CASE_S (CP_LMS_KFAULT);
      CASE_S (CP_LMS_SLEEP);
      CASE_S (CP_LMS_STOPPED);
      CASE_S (OMP_NONE);
      CASE_S (OMP_OVHD);
      CASE_S (OMP_WORK);
      CASE_S (OMP_IBAR);
      CASE_S (OMP_EBAR);
      CASE_S (OMP_WAIT);
      CASE_S (OMP_SERL);
      CASE_S (OMP_RDUC);
      CASE_S (OMP_LKWT);
      CASE_S (OMP_CTWT);
      CASE_S (OMP_ODWT);
      CASE_S (OMP_MSTR);
      CASE_S (OMP_SNGL);
      CASE_S (OMP_ORDD);
      CASE_S (CP_KERNEL_CPU);
      CASE_S (SYNC_WAIT_TIME);
      CASE_S (IO_READ_TIME);
      CASE_S (IO_WRITE_TIME);
      CASE_S (IO_OTHER_TIME);
      CASE_S (IO_ERROR_TIME);
      CASE_S (HWCNTR);
      CASE_S (SYNC_WAIT_COUNT);
      CASE_S (HEAP_ALLOC_CNT);
      CASE_S (HEAP_LEAK_CNT);
      CASE_S (IO_READ_CNT);
      CASE_S (IO_WRITE_CNT);
      CASE_S (IO_OTHER_CNT);
      CASE_S (IO_ERROR_CNT);
      CASE_S (RACCESS);
      CASE_S (DEADLOCKS);
      CASE_S (HEAP_ALLOC_BYTES);
      CASE_S (HEAP_LEAK_BYTES);
      CASE_S (IO_READ_BYTES);
      CASE_S (IO_WRITE_BYTES);
      CASE_S (SIZES);
      CASE_S (ADDRESS);
      CASE_S (ONAME);
      CASE_S (DERIVED);
    default:
      s = NTXT ("???");
      break;
    }
  snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, type);
  buf[sizeof (buf) - 1] = 0;
  return buf;
}

char *
BaseMetric::dump ()
{
  int len = 4;
  char *msg = dbe_sprintf (NTXT ("id=%d %s aux='%s' cmd='%s' user_name='%s' expr_spec='%s'\n"
				 "%*c cond_spec='%s' val_spec='%s'"),
			   id, get_basetype_name (), STR (aux), STR (cmd),
			   STR (username), STR (expr_spec),
			   len, ' ', STR (cond_spec), STR (val_spec));
  return msg;
}

Histable *
BaseMetric::get_comparable_obj (Histable *obj)
{
  if (obj == NULL || expr == NULL)
    return obj;
  if (strncmp (expr_spec, NTXT ("EXPGRID=="), 9) == 0)
    {
      int n = atoi (expr_spec + 9);
      Vector<Histable *> *cmpObjs = obj->get_comparable_objs ();
      if (cmpObjs && cmpObjs->size () >= n)
	return cmpObjs->get (n - 1);
      return NULL;
    }
  return obj;
}

Definition::Definition (opType _op)
{
  op = _op;
  bm = NULL;
  arg1 = NULL;
  arg2 = NULL;
  def = NULL;
  dependencies = NULL;
  map = NULL;
  index = 0;
}

Definition::~Definition ()
{
  delete arg1;
  delete arg2;
  delete dependencies;
  delete[] map;
}

Vector<BaseMetric *> *
Definition::get_dependencies ()
{
  if (dependencies == NULL)
    {
      if (arg1 && arg1->bm && arg2 && arg2->bm)
	{
	  dependencies = new Vector<BaseMetric *>(2);
	  arg1->index = dependencies->size ();
	  dependencies->append (arg1->bm);
	  arg2->index = dependencies->size ();
	  dependencies->append (arg2->bm);
	  map = new long[2];
	}
    }
  return dependencies;
}

long *
Definition::get_map ()
{
  get_dependencies ();
  return map;
}

Definition *
Definition::add_definition (char *_def)
{
  // parse the definition
  char *op_ptr = strchr (_def, '/');
  if (op_ptr == NULL)
    {
      // it's a primitive metric
      BaseMetric *bm = dbeSession->find_base_reg_metric (_def);
      if (bm)
	{
	  Definition *p = new Definition (opPrimitive);
	  p->bm = bm;
	  return p;
	}
      return NULL; // BaseMetric is not yet specified
    }
  Definition *p2 = add_definition (op_ptr + 1);
  if (p2 == NULL)
    return NULL;
  _def = dbe_strdup (_def);
  op_ptr = strchr (_def, '/');
  *op_ptr = 0;
  Definition *p1 = add_definition (_def);
  if (p1)
    {
      *op_ptr = '/';
      Definition *p = new Definition (opDivide);
      p->arg1 = p1;
      p->arg2 = p2;
      p->def = _def;
      return p;
    }
  free (_def);
  delete p1;
  delete p2;
  return NULL;
}

double
Definition::eval (long *indexes, TValue *values)
{
  switch (op)
    {
    case opPrimitive:
      return values[indexes[index]].to_double ();
    case opDivide:
      {
	double x2 = arg2->eval (indexes, values);
	if (x2 == 0)
	  return 0.;
	double x1 = arg1->eval (indexes, values);
	return x1 / x2;
      }
    default:
      fprintf (stderr, GTXT ("unknown expression\n"));
      return 0.;
    }
}