/* Copyright (C) 2021-2025 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 "Application.h"
#include "DbeSession.h"
#include "CallStack.h"
#include "Command.h"
#include "DataObject.h"
#include "Experiment.h"
#include "ExpGroup.h"
#include "FilterExp.h"
#include "FilterSet.h"
#include "Function.h"
#include "DbeView.h"
#include "PathTree.h"
#include "DataSpace.h"
#include "MemorySpace.h"
#include "IOActivity.h"
#include "HeapActivity.h"
#include "Print.h"
#include "MetricList.h"
#include "Module.h"
#include "Filter.h"
#include "LoadObject.h"
#include "dbe_types.h"
#include "StringBuilder.h"

DbeView::DbeView (Application *_app, Settings *_settings, int _vindex)
{
  init ();
  phaseIdx = 0;
  settings = new Settings (_settings);
  ptree = new PathTree (this);
  dspace = new DataSpace (this);
  memspaces = new Vector<MemorySpace*>;
  iospace = new IOActivity (this);
  heapspace = new HeapActivity (this);
  filters = new Vector<FilterSet*>;
  lo_expands = new Vector<enum LibExpand>;
  cur_filter_str = NULL;
  prev_filter_str = NULL;
  cur_filter_expr = NULL;
  filter_active = false;
  noParFilter = false;
  dataViews = new Vector<Vector<DataView*>*>;
  names_src[0] = NULL;
  names_src[1] = NULL;
  names_src[2] = NULL;
  names_dis[0] = NULL;
  names_dis[1] = NULL;
  names_dis[2] = NULL;
  marks = new Vector<int>;
  marks2dsrc = new Vector<int_pair_t>;
  marks2dsrc_inc = new Vector<int_pair_t>;
  marks2ddis = new Vector<int_pair_t>;
  marks2ddis_inc = new Vector<int_pair_t>;
  app = _app;

  // set the view's index
  vindex = _vindex;

  // clear the precomputed data
  func_data = NULL;
  line_data = NULL;
  pc_data = NULL;
  src_data = NULL;
  dis_data = NULL;
  fitem_data = NULL;
  callers = NULL;
  callees = NULL;
  dobj_data = NULL;
  dlay_data = NULL;
  iofile_data = NULL;
  iovfd_data = NULL;
  iocs_data = NULL;
  heapcs_data = NULL;

  // and clear the selections
  sel_obj = NULL;
  sel_dobj = NULL;
  sel_binctx = NULL;
  func_scope = false;
  lastSelInstr = NULL;
  lastSelFunc = NULL;

  // Initialize index spaces
  int sz = settings->get_IndxTabState ()->size ();
  indxspaces = new Vector<PathTree*>(sz);
  indx_data = new Vector<Hist_data*>(sz);
  sel_idxobj = new Vector<Histable*>(sz);
  for (int i = 0; i < sz; i++)
    {
      PathTree *is = new PathTree (this, i);
      indxspaces->store (i, is);
      indx_data->store (i, NULL);
      sel_idxobj->store (i, NULL);
    }
  reset ();

  lobjectsNoJava = NULL;

  // set lo_expands for already existing LoadObjects
  int idx;
  LoadObject *lo;
  Vector<LoadObject*> *lobjs = dbeSession->get_text_segments ();
  Vec_loop (LoadObject*, lobjs, idx, lo)
  {
    lo_expands->store (lo->seg_idx, LIBEX_SHOW);
    set_lo_expand (lo->seg_idx, LIBEX_SHOW);
  }
  delete lobjs;
}

DbeView::DbeView (DbeView *dbev, int _vindex)
{
  init ();
  phaseIdx = 0;
  settings = new Settings (dbev->settings);
  ptree = new PathTree (this);
  dspace = new DataSpace (this);
  iospace = new IOActivity (this);
  heapspace = new HeapActivity (this);
  memspaces = new Vector<MemorySpace*>;
  filters = new Vector<FilterSet*>;
  lo_expands = new Vector<enum LibExpand>;
  cur_filter_str = NULL;
  prev_filter_str = NULL;
  cur_filter_expr = NULL;
  noParFilter = false;
  dataViews = new Vector<Vector<DataView*>*>;
  names_src[0] = NULL;
  names_src[1] = NULL;
  names_src[2] = NULL;
  names_dis[0] = NULL;
  names_dis[1] = NULL;
  names_dis[2] = NULL;
  marks = new Vector<int>;
  marks2dsrc = new Vector<int_pair_t>;
  marks2dsrc_inc = new Vector<int_pair_t>;
  marks2ddis = new Vector<int_pair_t>;
  marks2ddis_inc = new Vector<int_pair_t>;
  app = dbev->app;

  // set the view's index
  vindex = _vindex;

  // clear the precomputed data
  func_data = NULL;
  line_data = NULL;
  pc_data = NULL;
  src_data = NULL;
  dis_data = NULL;
  fitem_data = NULL;
  callers = NULL;
  callees = NULL;
  dobj_data = NULL;
  dlay_data = NULL;
  iofile_data = NULL;
  iovfd_data = NULL;
  iocs_data = NULL;
  heapcs_data = NULL;

  // and clear the selections
  sel_obj = NULL;
  sel_dobj = NULL;
  sel_binctx = NULL;
  func_scope = false;
  lastSelInstr = NULL;
  lastSelFunc = NULL;

  // create the vector of IndexSpaces
  int sz = dbev->indxspaces->size ();
  indxspaces = new Vector<PathTree*>(sz);
  indx_data = new Vector<Hist_data*>(sz);
  sel_idxobj = new Vector<Histable*>(sz);
  for (int i = 0; i < sz; i++)
    {
      PathTree *is = new PathTree (this, i);
      indxspaces->store (i, is);
      indx_data->store (i, NULL);
      sel_idxobj->store (i, NULL);
    }
  reset ();

  // now copy the relevant information from the original view
  for (int i = 0; i < dbeSession->nexps (); i++)
    add_experiment (i, dbev->get_exp_enable (i));
  update_advanced_filter ();
  delete lo_expands;
  lo_expands = dbev->lo_expands->copy ();
  lobjectsNoJava = NULL;
}

DbeView::~DbeView ()
{
  delete settings;
  delete ptree;
  delete dspace;
  delete iospace;
  delete heapspace;
  Destroy (memspaces);
  Destroy (filters);
  delete lo_expands;
  free (cur_filter_str);
  free (prev_filter_str);
  delete cur_filter_expr;
  for (int exp_id = 0; exp_id < dataViews->size (); ++exp_id)
    {
      Vector<DataView*> *expDataViewList = dataViews->fetch (exp_id);
      Destroy (expDataViewList);
    }
  delete dataViews;
  delete reg_metrics;
  metrics_lists->destroy ();
  delete metrics_lists;
  metrics_ref_lists->destroy ();
  delete metrics_ref_lists;
  delete derived_metrics;
  delete marks;
  delete marks2dsrc;
  delete marks2dsrc_inc;
  delete marks2ddis;
  delete marks2ddis_inc;

  // Index spaces
  indxspaces->destroy ();
  delete indxspaces;

  indx_data->destroy ();
  delete indx_data;
  delete sel_idxobj;
  delete lobjectsNoJava;
}

void
DbeView::init ()
{
  phaseIdx = 0;
  reg_metrics = new Vector<BaseMetric*>;
  metrics_lists = new Vector<MetricList*>;
  metrics_ref_lists = new Vector<MetricList*>;
  for (int i = 0; i <= MET_HEAP; i++)
    {
      metrics_lists->append (NULL);
      metrics_ref_lists->append (NULL);
    }
  derived_metrics = new DerivedMetrics;
  derived_metrics->add_definition (GTXT ("CPI"), GTXT ("Cycles Per Instruction"), GTXT ("cycles/insts"));
  derived_metrics->add_definition (GTXT ("IPC"), GTXT ("Instructions Per Cycle"), GTXT ("insts/cycles"));
  derived_metrics->add_definition (GTXT ("K_CPI"), GTXT ("Kernel Cycles Per Instruction"), GTXT ("K_cycles/K_insts"));
  derived_metrics->add_definition (GTXT ("K_IPC"), GTXT ("Kernel Instructions Per Cycle"), GTXT ("K_insts/K_cycles"));
}

bool
DbeView::set_libexpand (char *liblist, enum LibExpand flag)
{
  bool changed = settings->set_libexpand (liblist, flag, false);
  // Show/hide performance optimization:
  // No need to call update_lo_expand for every library because dbev->set_libexpand()
  // is called from a loop in Dbe.cc SetLoadObjectState for every load object.
  // It is sufficient to call update_lo_expand() just once at the end of the loop.
  //  At all other places such as er_print.cc which calls specific set_libexpand()
  //  explicitly call update_lo_expands();
  return changed;
}

bool
DbeView::set_libdefaults ()
{
  bool changed = settings->set_libdefaults ();
  if (changed == true)
    update_lo_expands ();
  return changed;
}

void
DbeView::update_lo_expands ()
{
  int index;
  LoadObject *lo;

  // search all load objects
  Vector<LoadObject*> *lobjs = dbeSession->get_text_segments ();
  Vec_loop (LoadObject*, lobjs, index, lo)
  {
    // now search the settings list for this one
    enum LibExpand flag = settings->get_lo_setting (lo->get_pathname ());
    set_lo_expand (lo->seg_idx, flag);
  }
  delete lobjs;
}

enum LibExpand
DbeView::get_lo_expand (int idx)
{
  if (idx < lo_expands->size ())
    return lo_expands->get (idx);
  return LIBEX_SHOW;
}

bool
DbeView::set_lo_expand (int idx, enum LibExpand flag)
{
  // LIBRARY_VISIBILITY
  if (flag == LIBEX_HIDE)
    {
      resetShowAll ();
      dbeSession->set_lib_visibility_used ();
    }
  // if no change
  if (idx < lo_expands->size () && flag == get_lo_expand (idx))
    return false;
  setShowHideChanged (); // this is necessary if called from er_print

  // change the flag
  lo_expands->store (idx, flag);

  // and reset the data
  fflush (stderr);
  purge_events ();
  reset_data (true);
  return true;
}

void
DbeView::reset ()
{
  phaseIdx++;

  // reset all the per-experiment arrays
  filters->destroy ();
  lo_expands->reset ();
  free (cur_filter_str);
  cur_filter_str = NULL;
  free (prev_filter_str);
  prev_filter_str = NULL;
  delete cur_filter_expr;
  cur_filter_expr = NULL;
  noParFilter = false;
  for (int exp_id = 0; exp_id < dataViews->size (); ++exp_id)
    {
      Vector<DataView*> *expDataViewList = dataViews->fetch (exp_id);
      if (expDataViewList)
	expDataViewList->destroy ();
    }
  dataViews->destroy ();
  reset_metrics ();

  // now reset any cached data
  reset_data (true);
  ompDisMode = false;
  showAll = true;
  showHideChanged = false;
  newViewMode = false;
}

void
DbeView::reset_data (bool all)
{
  // clear the precomputed data
  if (func_data != NULL)
    {
      delete func_data;
      func_data = NULL;
    }
  if (line_data != NULL)
    {
      delete line_data;
      line_data = NULL;
    }
  if (pc_data != NULL)
    {
      delete pc_data;
      pc_data = NULL;
    }
  if (src_data != NULL)
    {
      delete src_data;
      src_data = NULL;
    }
  if (dis_data != NULL)
    {
      delete dis_data;
      dis_data = NULL;
    }
  if (fitem_data != NULL)
    {
      delete fitem_data;
      fitem_data = NULL;
    }
  if (callers != NULL)
    {
      delete callers;
      callers = NULL;
    }
  if (callees != NULL)
    {
      delete callees;
      callees = NULL;
    }
  if (dobj_data != NULL)
    {
      delete dobj_data;
      dobj_data = NULL;
    }
  if (dlay_data != NULL)
    {
      delete dlay_data;
      dlay_data = NULL;
    }
  if (iofile_data != NULL)
    {
      delete iofile_data;
      iofile_data = NULL;
    }
  if (iovfd_data != NULL)
    {
      delete iovfd_data;
      iovfd_data = NULL;
    }
  if (iocs_data != NULL)
    {
      delete iocs_data;
      iocs_data = NULL;
    }
  if (heapcs_data != NULL)
    {
      delete heapcs_data;
      heapcs_data = NULL;
    }

  // and reset the selections
  if (all)
    {
      sel_obj = NULL;
      sel_dobj = NULL;
      lastSelInstr = NULL;
      lastSelFunc = NULL;
      // Set selected object <Total> if possible
      Function * ft = dbeSession->get_Total_Function ();
      set_sel_obj (ft);
    }
  sel_binctx = NULL;

  dspace->reset ();
  iospace->reset ();
  heapspace->reset ();

  // loop over MemorySpaces, resetting each one
  for (long i = 0, sz = VecSize (memspaces); i < sz; i++)
    {
      MemorySpace *ms = memspaces->get (i);
      ms->reset ();
    }

  // loop over IndexSpaces, resetting cached data
  indx_data->destroy ();
  for (long i = 0, sz = VecSize (indxspaces); i < sz; i++)
    {
      indx_data->store (i, NULL);
      sel_idxobj->store (i, NULL);
    }
}

Vector<BaseMetric*> *
DbeView::get_all_reg_metrics ()
{
  Vector<BaseMetric*> *mlist = dbeSession->get_all_reg_metrics ();
  return mlist;
}

BaseMetric *
DbeView::register_metric_expr (BaseMetric::Type type, char *cmd, char *expr_spec)
{
  BaseMetric *bm = dbeSession->register_metric_expr (type, cmd, expr_spec);
  return bm;
}

Metric *
DbeView::get_compare_metric (Metric *mtr, int groupNum)
{
  if (groupNum == 0 || !mtr->comparable ())
    return new Metric (*mtr);
  ExpGroup *gr = dbeSession->expGroups->get (groupNum - 1);
  char buf[128];
  snprintf (buf, sizeof (buf), NTXT ("EXPGRID==%d"), gr->groupId);
  BaseMetric *bm = register_metric_expr (mtr->get_type (), mtr->get_cmd (), buf);
  Metric *m = new Metric (bm, mtr->get_subtype ());
  m->set_raw_visbits (mtr->get_visbits ());
  if (m->legend == NULL)
    m->legend = dbe_strdup (get_basename (gr->name));
  return m;
}

MetricList *
DbeView::get_metric_ref (MetricType mtype)
{
  if (metrics_ref_lists->fetch (MET_COMMON) == NULL)
    {
      Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics ();
      metrics_ref_lists->store (MET_SRCDIS, new MetricList (base_metrics, MET_SRCDIS));
      metrics_ref_lists->store (MET_COMMON, new MetricList (base_metrics, MET_COMMON));
      metrics_ref_lists->store (MET_NORMAL, new MetricList (base_metrics, MET_NORMAL));
      metrics_ref_lists->store (MET_CALL, new MetricList (base_metrics, MET_CALL));
      metrics_ref_lists->store (MET_CALL_AGR, new MetricList (base_metrics, MET_CALL_AGR));
      metrics_ref_lists->store (MET_DATA, new MetricList (base_metrics, MET_DATA));
      metrics_ref_lists->store (MET_INDX, new MetricList (base_metrics, MET_INDX));
      metrics_ref_lists->store (MET_IO, new MetricList (base_metrics, MET_IO));
      metrics_ref_lists->store (MET_HEAP, new MetricList (base_metrics, MET_HEAP));
      delete base_metrics;
    }
  return metrics_ref_lists->fetch (mtype);
}

// logically, the function list must be created first, and it
//	will create the other two;
MetricList *
DbeView::get_metric_list (MetricType mtype)
{
  if (metrics_lists->fetch (MET_COMMON) == NULL)
    {
      Vector<BaseMetric*> *base_metrics = dbeSession->get_base_reg_metrics ();
      metrics_lists->store (MET_SRCDIS, new MetricList (base_metrics, MET_SRCDIS));
      metrics_lists->store (MET_COMMON, new MetricList (base_metrics, MET_COMMON));
      metrics_lists->store (MET_NORMAL, new MetricList (base_metrics, MET_NORMAL));
      metrics_lists->store (MET_CALL, new MetricList (base_metrics, MET_CALL));
      metrics_lists->store (MET_CALL_AGR, new MetricList (base_metrics, MET_CALL_AGR));
      metrics_lists->store (MET_DATA, new MetricList (base_metrics, MET_DATA));
      metrics_lists->store (MET_INDX, new MetricList (base_metrics, MET_INDX));
      metrics_lists->store (MET_IO, new MetricList (base_metrics, MET_IO));
      metrics_lists->store (MET_HEAP, new MetricList (base_metrics, MET_HEAP));
      delete base_metrics;

      // set the defaults
      if (settings->str_dmetrics == NULL)
	settings->str_dmetrics = xstrdup (Command::DEFAULT_METRICS);
      char *status = setMetrics (settings->str_dmetrics, true);
      if (status != NULL)
	{
	  fprintf (stderr, "XXX setMetrics(\"%s\") failed: %s\n", settings->str_dmetrics, status);
	  abort ();
	}

      // set the default sort
      setSort (settings->str_dsort, MET_NORMAL, true);
    }
  return metrics_lists->fetch (mtype);
}

MetricList *
DbeView::get_metric_list (int dsptype, int subtype)
{
  MetricList *mlist;
  switch (dsptype)
    {
    case DSP_DISASM:
    case DSP_SOURCE:
    case DSP_SOURCE_DISASM:
      mlist = get_metric_list (MET_COMMON);
      mlist = new MetricList (mlist);
      if (subtype != 0)
	{
	  for (long i = 0, sz = mlist->size (); i < sz; i++)
	    {
	      Metric *m = mlist->get (i);
	      if (m->comparable ())
		{
		  Metric *m1 = get_compare_metric (m, subtype);
		  mlist->put (i, m1);
		  delete m;
		}
	    }
	}
      break;
    default:
      mlist = get_metric_list (MET_NORMAL);
      mlist = new MetricList (mlist);
      break;
    }
  return mlist;
}

void
DbeView::reset_metrics ()
{
  for (int i = 0, sz = metrics_lists->size (); i < sz; i++)
    {
      delete metrics_lists->fetch (i);
      metrics_lists->store (i, NULL);
    }
  for (int i = 0, sz = metrics_ref_lists->size (); i < sz; i++)
    {
      delete metrics_ref_lists->fetch (i);
      metrics_ref_lists->store (i, NULL);
    }
}

bool
DbeView::comparingExperiments ()
{
  if (dbeSession->expGroups->size () <= 1)
    return false;
  return 0 != (settings->get_compare_mode () & (CMP_DELTA | CMP_ENABLE | CMP_RATIO));
}

void
DbeView::set_compare_mode (int mode)
{
  if (mode == get_compare_mode ())
    return;
  settings->set_compare_mode (mode);
  if (comparingExperiments ())
    {
      Vector<BaseMetric*> *bm_list = dbeSession->get_base_reg_metrics ();
      for (int i = 0, sz = bm_list->size (); i < sz; i++)
	{
	  BaseMetric *m = bm_list->fetch (i);
	  if (m->get_expr_spec () || !m->comparable ())
	    continue;
	  for (int i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++)
	    {
	      ExpGroup *gr = dbeSession->expGroups->fetch (i1);
	      char buf[128];
	      snprintf (buf, sizeof (buf), NTXT ("EXPGRID==%d"), gr->groupId);
	      register_metric_expr (m->get_type (), m->get_cmd (), buf);
	    }
	}
    }
  MetricList *mlist = get_metric_list (MET_NORMAL);
  MetricList *gmlist = get_metric_list (MET_CALL);
  MetricList *dmlist = get_metric_list (MET_DATA);
  MetricList *imlist = get_metric_list (MET_INDX);
  if (comparingExperiments ())
    {
      add_compare_metrics (mlist);
      add_compare_metrics (gmlist);
      add_compare_metrics (dmlist);
      add_compare_metrics (imlist);
    }
  else
    {
      remove_compare_metrics (mlist);
      remove_compare_metrics (gmlist);
      remove_compare_metrics (dmlist);
      remove_compare_metrics (imlist);
    }
}

void
DbeView::ifreq (FILE *outfile)
{
  if (!dbeSession->is_ifreq_available ())
    {
      fprintf (outfile, GTXT ("No instruction frequency data available\n"));
      return;
    }
  for (int index = 0; index < filters->size (); index++)
    {
      Experiment *exp = dbeSession->get_exp (index);
      if (exp->broken || !get_exp_enable (index) || !exp->ifreqavail)
	continue;

      // this experiment has the data; print it
      fprintf (outfile,
	       GTXT ("Instruction frequency data from experiment %s\n\n"),
	       exp->get_expt_name ());
      fprintf (outfile, NTXT ("%s"), pr_mesgs (exp->fetch_ifreq (), "", ""));
    }
}

// When adding multiple sub-experiments of an experiment, it is
// not necessary to do the following every-time. It is sufficient to call reset_metrics()
// and call get_metric_ref() and get_metric_list() in the end after all the sub-experiments
// have been added
void
DbeView::add_experiment_epilogue ()
{
  bool flag_LIBEX_HIDE = false;
  bool flag_ShowHideChanged = false;
  Vector<LoadObject*> *lobjs = dbeSession->get_LoadObjects ();
  for (long i = lo_expands->size (), sz = lobjs ? lobjs->size () : 0; i < sz; i++)
    {
      flag_ShowHideChanged = true;
      LoadObject *lo = lobjs->get (i);
      enum LibExpand flag = settings->get_lo_setting (lo->get_pathname ());
      if (flag == LIBEX_HIDE)
	flag_LIBEX_HIDE = true;
      lo_expands->store (lo->seg_idx, flag);
    }
  if (flag_LIBEX_HIDE)
    {
      resetShowAll ();
      dbeSession->set_lib_visibility_used ();
    }
  if (flag_ShowHideChanged)
    {
      setShowHideChanged (); // this is necessary if called from er_print
      purge_events ();
      reset_data (true);
    }
  reset_metrics ();
  (void) get_metric_ref (MET_NORMAL);
  (void) get_metric_ref (MET_CALL);
  (void) get_metric_ref (MET_CALL_AGR);
  (void) get_metric_ref (MET_DATA);
  (void) get_metric_ref (MET_INDX);
  (void) get_metric_ref (MET_IO);
  (void) get_metric_ref (MET_HEAP);
  (void) get_metric_list (MET_NORMAL);
  (void) get_metric_list (MET_CALL);
  (void) get_metric_list (MET_CALL_AGR);
  (void) get_metric_list (MET_DATA);
  (void) get_metric_list (MET_INDX);
  (void) get_metric_list (MET_IO);
  (void) get_metric_list (MET_HEAP);
}

// When adding multiple sub-experiments of an experiment, avoid invoking the steps in
// add_experiment_epilogue() every time and instead call it separately in the end
// after all sub-experiments have been added
void
DbeView::add_subexperiment (int index, bool enabled)
{
  // phaseIdx doesn't change, PathTree can handle adding
  // new experiments without reset

  // Set up the FilterSet for the experiments
  Experiment *exp = dbeSession->get_exp (index);
  FilterSet *filterset = new FilterSet (this, exp);
  filterset->set_enabled (enabled);
  filters->store (index, filterset);

  assert (index == dataViews->size ());
  Vector<DataView*> *expDataViewList = new Vector<DataView*>;
  for (int data_id = 0; data_id < DATA_LAST; ++data_id)
    expDataViewList->append (NULL); //experiment data_id's are not known yet
  dataViews->store (index, expDataViewList);
}

void
DbeView::add_experiment (int index, bool enabled)
{
  // phaseIdx doesn't change, PathTree can handle adding
  // new experiments without reset

  // delete any cached data
  reset_data (true);

  // Set up the FilterSet for the experiments
  Experiment *exp = dbeSession->get_exp (index);
  FilterSet *filterset = new FilterSet (this, exp);
  filterset->set_enabled (enabled);
  filters->store (index, filterset);

  assert (index == dataViews->size ());
  Vector<DataView*> *expDataViewList = new Vector<DataView*>;
  for (int data_id = 0; data_id < DATA_LAST; ++data_id)
    expDataViewList->append (NULL); //experiment data_id's are not known yet
  dataViews->store (index, expDataViewList);

  reset_metrics ();
  (void) get_metric_ref (MET_NORMAL);
  (void) get_metric_ref (MET_CALL);
  (void) get_metric_ref (MET_CALL_AGR);
  (void) get_metric_ref (MET_DATA);
  (void) get_metric_ref (MET_INDX);
  (void) get_metric_ref (MET_IO);
  (void) get_metric_ref (MET_HEAP);
  (void) get_metric_list (MET_NORMAL);
  (void) get_metric_list (MET_CALL);
  (void) get_metric_list (MET_CALL_AGR);
  (void) get_metric_list (MET_DATA);
  (void) get_metric_list (MET_INDX);
  (void) get_metric_list (MET_IO);
  (void) get_metric_list (MET_HEAP);
}

void
DbeView::drop_experiment (int index)
{
  phaseIdx++;
  filters->remove (index);

  // reset any cached data
  reset_data (true);

  Vector<DataView*> *expDataViewList = dataViews->remove (index);
  if (expDataViewList)
    {
      expDataViewList->destroy ();
      delete expDataViewList;
    }
}

bool
DbeView::get_exp_enable (int n)
{
  return filters ? filters->fetch (n)->get_enabled () : true;
}

void
DbeView::set_exp_enable (int n, bool e)
{
  FilterSet *fs = filters->fetch (n);
  if (fs->get_enabled () != e)
    {
      fs->set_enabled (e);
      purge_events (n);
      phaseIdx++;
    }
}

void
DbeView::reset_metric_list (MetricList *mlist, int cmp_mode)
{
  MetricType mtype = mlist->get_type ();
  switch (mtype)
    {
    case MET_NORMAL:
    case MET_COMMON:
      delete metrics_lists->fetch (MET_COMMON);
      metrics_lists->store (MET_COMMON, new MetricList (mlist));
      remove_compare_metrics (metrics_lists->fetch (MET_COMMON));
      break;
      // ignoring the following cases (why?)
    case MET_SRCDIS:
    case MET_CALL:
    case MET_DATA:
    case MET_INDX:
    case MET_CALL_AGR:
    case MET_IO:
    case MET_HEAP:
      break;
    }

  if (cmp_mode != -1)
    {
      settings->set_compare_mode (cmp_mode);
      if (comparingExperiments ())
	add_compare_metrics (mlist);
    }

  switch (mtype)
    {
    case MET_NORMAL:
      delete metrics_lists->fetch (mtype);
      metrics_lists->store (mtype, mlist);
      // fall through to next case
    case MET_COMMON:
      metrics_lists->fetch (MET_SRCDIS)->set_metrics (mlist);
      metrics_lists->fetch (MET_CALL)->set_metrics (mlist);
      metrics_lists->fetch (MET_CALL_AGR)->set_metrics (mlist);
      remove_compare_metrics (metrics_lists->fetch (MET_CALL_AGR));
      metrics_lists->fetch (MET_DATA)->set_metrics (mlist);
      metrics_lists->fetch (MET_INDX)->set_metrics (mlist);
      metrics_lists->fetch (MET_IO)->set_metrics (mlist);
      metrics_lists->fetch (MET_HEAP)->set_metrics (mlist);
      break;
    case MET_CALL_AGR:
      delete metrics_lists->fetch (MET_CALL_AGR);
      metrics_lists->store (MET_CALL_AGR, mlist);
      remove_compare_metrics (mlist);
      break;
    case MET_SRCDIS:
    case MET_CALL:
    case MET_DATA:
    case MET_INDX:
    case MET_IO:
    case MET_HEAP:
      delete metrics_lists->fetch (mtype);
      metrics_lists->store (mtype, mlist);
      break;
    default:
      abort ();
    }
  reset_data (false);
}

void
DbeView::add_compare_metrics (MetricList *mlist)
{
  if (mlist == NULL || !comparingExperiments ())
    return;
  int sort_ref_index = mlist->get_sort_ref_index ();
  Vector<Metric*> *items = mlist->get_items ();
  Vector<Metric*> *newItems = new Vector<Metric*>();
  int mode = get_compare_mode ();
  int cmp_vbits = 0;
  if ((mode & CMP_DELTA) != 0)
    cmp_vbits = VAL_DELTA;
  else if ((mode & CMP_RATIO) != 0)
    cmp_vbits = VAL_RATIO;
  for (long i = 0, sz = items->size (); i < sz; i++)
    {
      Metric *mtr = items->get (i);
      if (sort_ref_index == i)
	mlist->set_sort_ref_index (newItems->size ());
      int vbits = mtr->get_visbits () & ~(VAL_DELTA | VAL_RATIO);
      mtr->set_raw_visbits (vbits);
      if (!mtr->comparable ())
	{
	  newItems->append (mtr);
	  continue;
	}
      if (mtr->get_expr_spec ())
	{
	  if (strcmp (mtr->get_expr_spec (), NTXT ("EXPGRID==1")) != 0)
	    {
	      if ((cmp_vbits & VAL_RATIO) != 0)
		// for ratios, make sure VAL_TIMEVAL is off and VAL_VALUE is on
		mtr->set_raw_visbits ((vbits | VAL_VALUE | cmp_vbits) & ~VAL_TIMEVAL);
	      else
		{
		  int ind = mlist->get_listorder (mtr->get_cmd (), mtr->get_subtype (), NTXT ("EXPGRID==1"));
		  if (ind >= 0)
		    // take VAL_VALUE and VAL_TIMEVAL from base experiment
		    mtr->set_raw_visbits (cmp_vbits
					  | (vbits & ~(VAL_VALUE | VAL_TIMEVAL))
					  | (mlist->get (ind)->get_visbits ()
					     & (VAL_VALUE | VAL_TIMEVAL)));
		  else
		    mtr->set_raw_visbits (cmp_vbits | vbits);
		}
	    }
	  newItems->append (mtr);
	  continue;
	}
      for (long i1 = 0, sz1 = dbeSession->expGroups->size (); i1 < sz1; i1++)
	{
	  Metric *m = get_compare_metric (mtr, i1 + 1);
	  switch (m->get_vtype ())
	    {
	    case VT_LABEL:
	    case VT_ADDRESS:
	    case VT_OFFSET:
	      m->set_raw_visbits (vbits);
	      break;
	    default:
	      if (i1 == 0)
		m->set_raw_visbits (vbits);
	      else if (cmp_vbits == VAL_RATIO
		       && ((vbits & (VAL_VALUE | VAL_TIMEVAL))
			   == (VAL_VALUE | VAL_TIMEVAL)))
		// make ratios for VAL_VALUE only
		m->set_raw_visbits ((vbits | VAL_VALUE | cmp_vbits) & ~VAL_TIMEVAL);
	      else
		m->set_raw_visbits (vbits | cmp_vbits);
	      break;
	    }
	  newItems->append (m);
	}
    }
  items->reset ();
  items->addAll (newItems);
  delete newItems;
  phaseIdx++;
  reset_data (false);
}

MetricList *
DbeView::get_compare_mlist (MetricList *met_list, int grInd)
{
  MetricList *mlist = new MetricList (met_list->get_type ());
  mlist->set_sort_ref_index (met_list->get_sort_ref_index ());
  mlist->set_sort_rev (met_list->get_sort_rev ());

  Vector<Metric*> *items_old = met_list->get_items ();
  for (int i = 0, sz = items_old->size (); i < sz; i++)
    {
      Metric *m = get_compare_metric (items_old->get (i), grInd + 1);
      mlist->append (m);
    }
  return mlist;
}

void
DbeView::remove_compare_metrics (MetricList *mlist)
{
  Vector<Metric*> *items = mlist->get_items ();
  Vector<Metric*> *items_old = items->copy ();
  items->reset ();
  int sort_index = mlist->get_sort_ref_index ();
  mlist->set_sort_ref_index (0);
  for (int i = 0, sz = items_old->size (); i < sz; i++)
    {
      Metric *m = items_old->fetch (i);
      if (m->get_expr_spec () == NULL)
	{
	  // this is a 'non-compare' metric; add it
	  items->append (m);
	  if (sort_index == i)
	    mlist->set_sort_ref_index (items->size () - 1);
	  continue;
	}
      // is the 'non-compare' version of the metric already in the list?
      int ind = mlist->get_listorder (m->get_cmd (), m->get_subtype ());
      if (ind == -1)
	{
	  // not in the list; add it
	  BaseMetric *bm = dbeSession->find_metric (m->get_type (), m->get_cmd (), NULL);
	  Metric *new_met = new Metric (bm, m->get_subtype ());
	  new_met->set_raw_visbits (m->get_visbits () & ~(CMP_DELTA | CMP_RATIO));
	  items->append (new_met);
	  if (sort_index == i)
	    mlist->set_sort_ref_index (items->size () - 1);
	}
      delete m;
    }
  delete items_old;
  reset_data (false);
}

// setMetrics -- set the metric list according to specification
//	The previous sort is preserved, if possible
//	Otherwise, the default sort setting is used
//	Returns NULL if OK, or an error string if not
//YXXX only MET_NORMAL appears to be used... code could be simplified
char *
DbeView::setMetrics (char *mspec, bool fromRcFile)
{
  char *ret;
  MetricType mtype = MET_NORMAL;
  // note that setting the default is done here, while all else is in MetricList
  if (mspec == NULL) abort ();
  if (strcasecmp (mspec, Command::DEFAULT_CMD) == 0)
    {
      mspec = settings->get_default_metrics ();
      fromRcFile = true;
    }
  MetricList *mlist = get_metric_list (mtype);
  mlist = new MetricList (mlist);
  ret = mlist->set_metrics (mspec, fromRcFile, derived_metrics);
  if (ret == NULL)
    {
      switch (mtype)
	{
	case MET_NORMAL:
	case MET_COMMON:
	  delete metrics_lists->fetch (MET_COMMON);
	  metrics_lists->store (MET_COMMON, new MetricList (mlist));
	  break;
	  // ignoring the following cases (why?)
	case MET_SRCDIS:
	case MET_CALL:
	case MET_DATA:
	case MET_INDX:
	case MET_CALL_AGR:
	case MET_IO:
	case MET_HEAP:
	  break;
	}
      add_compare_metrics (mlist);

      //YXXX looks like cut/paste code here, see reset_metric_list()
      switch (mtype)
	{
	case MET_NORMAL:
	  delete metrics_lists->fetch (mtype);
	  metrics_lists->store (mtype, mlist);
	  //YXXX is lack of break intentional?  If so, add comment...
	case MET_COMMON:
	  metrics_lists->fetch (MET_SRCDIS)->set_metrics (mlist);
	  metrics_lists->fetch (MET_CALL)->set_metrics (mlist);
	  metrics_lists->fetch (MET_CALL_AGR)->set_metrics (mlist);
	  remove_compare_metrics (metrics_lists->fetch (MET_CALL_AGR));
	  metrics_lists->fetch (MET_DATA)->set_metrics (mlist);
	  metrics_lists->fetch (MET_INDX)->set_metrics (mlist);
	  metrics_lists->fetch (MET_IO)->set_metrics (mlist);
	  metrics_lists->fetch (MET_HEAP)->set_metrics (mlist);
	  break;
	case MET_CALL_AGR:
	  delete metrics_lists->fetch (MET_CALL_AGR);
	  metrics_lists->store (MET_CALL_AGR, mlist);
	  remove_compare_metrics (mlist);
	  break;
	case MET_SRCDIS:
	case MET_CALL:
	case MET_DATA:
	case MET_INDX:
	case MET_IO:
	case MET_HEAP:
	  delete metrics_lists->fetch (mtype);
	  metrics_lists->store (mtype, mlist);
	  break;
	default:
	  abort ();
	}
      reset_data (false);
    }
  else
    delete mlist;
  return ret;
}


// Set Sort by name (er_print)
char *
DbeView::setSort (char * sort_list, MetricType mtype, bool fromRcFile)
{
  MetricList *mlist = NULL;

  // note that setting the default is done here, while all else is in MetricList
  if ((sort_list == NULL) || (strcmp (sort_list, Command::DEFAULT_CMD) == 0))
    {
      if (settings->str_dsort == NULL)
	settings->str_dsort = xstrdup (Command::DEFAULT_METRICS);
      sort_list = settings->get_default_sort ();
    }
  mlist = get_metric_list (mtype);

  if (mlist == NULL)
    abort ();

  // set the new sort
  char *ret = mlist->set_sort (sort_list, fromRcFile);
  if (ret != NULL)
    return ret;

  // now resort all cached data
  resortData (mtype);
  return NULL;
}

// Set sort from the visible index (Analyzer)
void
DbeView::setSort (int visindex, MetricType mtype, bool reverse)
{
  MetricList *mlist = get_metric_list (mtype);
  Vector<Metric*> *items = mlist->get_items ();
  if (visindex >= items->size ())
    return;
  mlist->set_sort (visindex, reverse);
  resortData (mtype);
  if (mtype == MET_NORMAL)
    {
      int idx_cc = -1;
      MetricList *mlist_cc = get_metric_list (MET_CALL);
      Vector<Metric*> *items_cc = mlist_cc->get_items ();
      for (int i = 0; i < items_cc->size (); i++)
	{
	  char * name_cc = items_cc->fetch (i)->get_username ();
	  char * name_normal = items->fetch (visindex)->get_username ();
	  if (0 == strncmp (name_cc, name_normal, strlen (name_cc)))
	    {
	      idx_cc = i;
	      break;
	    }
	}
      if (idx_cc != -1)
	{
	  mlist_cc->set_sort (idx_cc, reverse);
	  resortData (MET_CALL);
	  // Change a sort metric for MET_CALL_AGR
	  Metric *m = items_cc->fetch (idx_cc);
	  MetricList *cList = get_metric_list (MET_CALL_AGR);
	  Metric *m1 = cList->find_metric (m->get_cmd (), m->get_subtype ());
	  if (m1)
	    cList->set_sort_metric (m1->get_cmd (), m1->get_subtype (), reverse);
	}
    }
  if (mtype == MET_CALL)
    {
      int idx_norm = -1;
      MetricList *mlist_norm = get_metric_list (MET_NORMAL);
      Vector<Metric*> *items_norm = mlist_norm->get_items ();
      for (int i = 0; i < items_norm->size (); i++)
	{
	  char * name_norm = items_norm->fetch (i)->get_username ();
	  char * name_cc = items->fetch (visindex)->get_username ();
	  if (mlist_norm->get_sort_ref_index () == i
	      && 0 == strncmp (name_norm, name_cc, strlen (name_norm)))
	    {
	      idx_norm = i;
	      break;
	    }
	}
      if (idx_norm == -1)
	{
	  for (int i = 0; i < items_norm->size (); i++)
	    {
	      char * name_norm = items_norm->fetch (i)->get_username ();
	      char * name_cc = items->fetch (visindex)->get_username ();
	      if (0 == strncmp (name_norm, name_cc, strlen (name_norm)))
		{
		  idx_norm = i;
		  break;
		}
	    }
	}
      if (idx_norm != -1)
	{
	  mlist_norm->set_sort (idx_norm, reverse);
	  resortData (MET_NORMAL);
	}
      // Change a sort metric for MET_CALL_AGR
      Metric *m = items->fetch (visindex);
      MetricList *cList = get_metric_list (MET_CALL_AGR);
      Metric *m1 = cList->find_metric (m->get_cmd (), m->get_subtype ());
      if (m1)
	cList->set_sort_metric (m1->get_cmd (), m1->get_subtype (), reverse);
    }
}

void
DbeView::resortData (MetricType mtype)
{
  int idx;
  Hist_data *data;

  MetricList *mlist = get_metric_list (mtype);
  switch (mtype)
    {
    case MET_NORMAL:
      if (func_data != NULL)
	func_data->resort (mlist);
      if (line_data != NULL)
	line_data->resort (mlist);
      if (pc_data != NULL)
	pc_data->resort (mlist);
      break;
    case MET_CALL:
    case MET_CALL_AGR:
      if (fitem_data != NULL)
	fitem_data->resort (mlist);
      if (callers != NULL)
	  callers->resort (mlist);
      if (callees != NULL)
	callees->resort (mlist);
      break;
    case MET_DATA:
      if (dobj_data != NULL)
	dobj_data->resort (mlist);
      if (dlay_data != NULL)
	{
	  delete dlay_data;
	  dlay_data = NULL;
	}
      break;
    case MET_INDX:
      Vec_loop (Hist_data*, indx_data, idx, data)
      {
	if (data)
	  data->resort (mlist);
      }
      break;
    case MET_IO:
      if (iofile_data != NULL)
	iofile_data->resort (mlist);
      if (iovfd_data != NULL)
	iovfd_data->resort (mlist);
      if (iocs_data != NULL)
	  iocs_data->resort (mlist);
      break;
    case MET_HEAP:
      if (heapcs_data != NULL)
	heapcs_data->resort (mlist);
      break;
    case MET_COMMON:
    case MET_SRCDIS:
      break;
    }
}

// Get the sort metric name
char *
DbeView::getSort (MetricType mtype)
{
  MetricList *mlist = get_metric_list (mtype);
  return mlist->get_sort_name ();
}

// Get the sort command (to use for resetting)
char *
DbeView::getSortCmd (MetricType mtype)
{
  MetricList *mlist = get_metric_list (mtype);
  return mlist->get_sort_cmd ();
}

int
DbeView::get_sel_ind (Histable *selObj, int type, int subtype)
{
  Hist_data *data;
  switch (type)
    {
    case DSP_FUNCTION:
      data = func_data;
      break;
    case DSP_LINE:
      data = line_data;
      break;
    case DSP_PC:
      data = pc_data;
      break;
    case DSP_SOURCE:
    case DSP_SOURCE_V2:
      data = src_data;
      break;
    case DSP_DISASM:
    case DSP_DISASM_V2:
      data = dis_data;
      break;
    case DSP_DLAYOUT:
      data = dlay_data;
      break;
    case DSP_DATAOBJ:
      data = dobj_data;
      break;
    case DSP_IOACTIVITY:
      data = iofile_data;
      break;
    case DSP_IOVFD:
      data = iovfd_data;
      break;
    case DSP_IOCALLSTACK:
      data = iocs_data;
      break;
    case DSP_HEAPCALLSTACK:
      data = heapcs_data;
      break;
    case DSP_MEMOBJ:
    case DSP_INDXOBJ:
      data = get_indxobj_data (subtype);
      break;
    default:
      data = NULL;
      break;
    }
  if (data == NULL || data->get_status () != Hist_data::SUCCESS)
    return -1;
  Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items ();
  for (int i = 0, sz = hi_data->size (); i < sz; i++)
    {
      Hist_data::HistItem *hi = hi_data->fetch (i);
      if (hi->obj == selObj)
	return i;
    }
  return -1;
}

MetricList *
DbeView::get_metric_list (MetricType mtype, bool compare, int gr_num)
{
  MetricList *mlist;
  switch (mtype)
    {
    case MET_COMMON:// comparison mode, src & disasm views
      if (gr_num == 0)
	{// signifies same src file (or load obj) used by all groups
	  // show compare metrics in columns (not in separate source panels)
	  mlist = get_metric_list (MET_NORMAL);
	  break;
	}
      // once source panel per group; get metrics for this group
      mlist = get_metric_list (mtype);
      if (compare)
	{
	  mlist = get_compare_mlist (mlist, gr_num - 1);
	  int mode = get_compare_mode ();
	  if ((mode & (CMP_DELTA | CMP_RATIO)) != 0)
	    {
	      for (long i = 0, sz = mlist->size (); i < sz; i++)
		{
		  Metric *m = mlist->get (i);
		  char *expr_spec = m->get_expr_spec ();
		  if (expr_spec && (strcmp (expr_spec, NTXT ("EXPGRID==1")) != 0))
		    {
		      int vbits = m->get_visbits () & ~(VAL_DELTA | VAL_RATIO);
		      if ((mode & CMP_RATIO) != 0)
			vbits |= VAL_RATIO;
		      else if ((mode & CMP_DELTA) != 0)
			vbits |= VAL_DELTA;
		      m->set_raw_visbits (vbits);
		    }
		}
	    }
	}
      break;
    default:
      mlist = get_metric_list (mtype);
      break;
    }
  return mlist;
}

Hist_data *
DbeView::get_data (MetricList *mlist, Histable *selObj, int type, int subtype)
{
  Hist_data *data;
  switch (type)
    {
    case DSP_FUNCTION:
      delete func_data;
      mlist = new MetricList (mlist);
      func_data = get_hist_data (mlist, Histable::FUNCTION, subtype, Hist_data::ALL);
      return func_data;
    case DSP_LINE:
      delete line_data;
      mlist = new MetricList (mlist);
      line_data = get_hist_data (mlist, Histable::LINE, subtype, Hist_data::ALL);
      return line_data;
    case DSP_PC:
      delete pc_data;
      mlist = new MetricList (mlist);
      pc_data = get_hist_data (mlist, Histable::INSTR, subtype, Hist_data::ALL);
      return pc_data;
    case DSP_DATAOBJ:
      delete dobj_data;
      dobj_data = get_hist_data (mlist, Histable::DOBJECT, subtype,
				 Hist_data::ALL);
      break;
    case DSP_MEMOBJ:
      return get_hist_data (mlist, Histable::MEMOBJ, subtype, Hist_data::ALL);
    case DSP_INDXOBJ:
      data = get_hist_data (mlist, Histable::INDEXOBJ, subtype, Hist_data::ALL);
      indx_data->store (subtype, data);
      return data;
    case DSP_DLAYOUT:
      delete dlay_data;
      marks->reset ();
      data = get_hist_data (mlist, Histable::DOBJECT, subtype,
			    Hist_data::LAYOUT);
      // .. provides metric data for layout
      dlay_data = get_data_space ()->get_layout_data (data, marks,
						      get_thresh_dis ());
      return dlay_data;
    case DSP_CALLER:
      delete callers;
      callers = get_hist_data (mlist, Histable::FUNCTION, subtype,
			       Hist_data::CALLERS, selObj);
      return callers;
    case DSP_CALLEE:
      delete callees;
      callees = get_hist_data (mlist, Histable::FUNCTION, subtype,
			       Hist_data::CALLEES, selObj);
      return callees;
    case DSP_SELF:
      // Center Function item
      delete fitem_data;
      fitem_data = get_hist_data (mlist, Histable::FUNCTION, subtype,
				  Hist_data::SELF, selObj);
      return fitem_data;
    case DSP_SOURCE_V2:
    case DSP_DISASM_V2:
    case DSP_SOURCE:
    case DSP_DISASM:
      {
	// Source or disassembly
	if (selObj == NULL)
	  {
	    error_msg = status_str (DBEVIEW_NO_SEL_OBJ);
	    return NULL;
	  }
	Function *func = (Function *) selObj->convertto (Histable::FUNCTION);
	if (func == NULL)
	  {
	    error_msg = dbe_strdup (GTXT ("Not a real function; no source or disassembly available."));
	    return NULL;
	  }
	if (func->flags & FUNC_FLAG_SIMULATED)
	  {
	    error_msg = dbe_strdup (GTXT ("Not a real function; no source or disassembly available."));
	    return NULL;
	  }
	if (func->get_name () == NULL)
	  {
	    error_msg = dbe_strdup (GTXT ("Source location not recorded in experiment"));
	    return NULL;
	  }
	Module *module = func->module;
	if (module == NULL || module->get_name () == NULL)
	  {
	    error_msg = dbe_strdup (GTXT ("Object name not recorded in experiment"));
	    return NULL;
	  }
	marks->reset ();
	SourceFile *srcContext = (SourceFile *) selObj->convertto (Histable::SOURCEFILE);
	sel_binctx = func;

	if (func_data == NULL)
	  func_data = get_hist_data (mlist, Histable::FUNCTION, subtype, Hist_data::ALL);

	// for source and disassembly the name needs to be invisible,
	//	but that's handled in the module code
	if (type == DSP_SOURCE || type == DSP_SOURCE_V2)
	  {
	    marks2dsrc->reset ();
	    marks2dsrc_inc->reset ();
	    delete src_data;
	    data = src_data = module->get_data (this, mlist, Histable::LINE,
			      func_data->get_totals ()->value, srcContext, func,
			      marks, get_thresh_src (), get_src_compcom (),
			      get_src_visible (), get_hex_visible (),
			      false, false, marks2dsrc, marks2dsrc_inc);
	  }
	else
	  { /* type == DSP_DISASM */
	    marks2ddis->reset ();
	    marks2ddis_inc->reset ();
	    delete dis_data;
	    data = dis_data = module->get_data (this, mlist, Histable::INSTR,
			      func_data->get_totals ()->value, srcContext, func,
			      marks, get_thresh_dis (), get_dis_compcom (),
			      get_src_visible (), get_hex_visible (),
			      get_func_scope (), false, marks2ddis,
			      marks2ddis_inc);
	  }
	return data;
      }
    default:
      abort ();
    }
  return NULL;
}

Histable *
DbeView::get_compare_obj (Histable *obj)
{
  char *nm;
  switch (obj->get_type ())
    {
    case Histable::LINE:
      nm = obj->get_name ();
      if (nm == NULL)
	break;
      if (dbeSession->comp_dbelines == NULL)
	dbeSession->comp_dbelines = new HashMap<char*, DbeLine*>;
      return dbeSession->comp_dbelines->get (nm, (DbeLine*) obj);
    case Histable::SOURCEFILE:
      nm = obj->get_name ();
      if (nm == NULL)
	break;
      nm = get_basename (nm);
      if (dbeSession->comp_sources == NULL)
	dbeSession->comp_sources = new HashMap<char*, SourceFile*>;
      return dbeSession->comp_sources->get (nm, (SourceFile*) obj);
    default:
      return obj->get_compare_obj ();
    }
  return obj;
}

//
//   get_hist_data() creates a new Hist_data object;
//   it's caller's responsibility to delete it.
Hist_data *
DbeView::get_hist_data (MetricList *mlist_orig, Histable::Type type,
			int subtype, Hist_data::Mode mode, Histable *obj,
			Histable *context, Vector<Histable*> *sel_objs,
			PathTree::PtreeComputeOption flag)
{
  Vector<Histable*> *objs = NULL;
  if (obj != NULL)
    {
      objs = new Vector<Histable*>();
      objs->append (obj);
    }
  Hist_data *res = get_hist_data (mlist_orig, type, subtype, mode, objs, context, sel_objs, flag);
  delete objs;
  return res;
}

Hist_data *
DbeView::get_hist_data (MetricList *mlist_orig, Histable::Type type,
			int subtype, Hist_data::Mode mode,
			Vector<Histable*> *objs,
			Histable *context, Vector<Histable*> *sel_objs,
			PathTree::PtreeComputeOption flag)
{
  MetricList *mlist = new MetricList (mlist_orig);
  /*
   * mlist differs from mlist_orig in two ways:
   * - extra metrics have been added as needed to compute derived metrics
   * - extra metrics have been added as needed to compute time for HWC (time converted) metrics
   *     (We don't drop those extra metrics but we don't display they to user.)
   * - visibility bits have been added for compare mode (e.g., VAL_DELTA or VAL_RATIO)
   *     (We want to preserve those visbits.)
   */
  // loop over mlist to add missing dependencies for derived metrics
  for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++)
    {
      Metric *m = mlist->get_items ()->fetch (i);
      char *expr_spec = m->get_expr_spec ();
      if (expr_spec && (strcmp (expr_spec, NTXT ("EXPGRID==1")) != 0))
	{
	  int ind = mlist->get_listorder (m->get_cmd (), m->get_subtype (), NTXT ("EXPGRID==1"));
	  if (ind < 0)
	    {
	      BaseMetric *bm1 = dbeSession->find_metric (m->get_type (), m->get_cmd (), NTXT ("EXPGRID==1"));
	      Metric *m1 = new Metric (bm1, m->get_subtype ());
	      m1->set_dmetrics_visbits (VAL_VALUE);
	      mlist->append (m1);
	    }
	}
    }

  for (long i = 0, sz = mlist->get_items ()->size (); i < sz; i++)
    {
      Metric *m = mlist->get_items ()->fetch (i);
      if (m->get_type () == BaseMetric::DERIVED)
	{
	  Definition *def = m->get_definition ();
	  Vector<BaseMetric*> *dependencies = def->get_dependencies ();
	  long *map = def->get_map ();
	  for (long i1 = 0, sz1 = dependencies ? dependencies->size () : 0; i1 < sz1; i1++)
	    {
	      BaseMetric *bm = dependencies->fetch (i1);
	      int ind = mlist->get_listorder (bm->get_cmd (), m->get_subtype (), m->get_expr_spec ());
	      if (ind < 0)
		{
		  BaseMetric *bm1 = dbeSession->find_metric (bm->get_type (), bm->get_cmd (), m->get_expr_spec ());
		  assert (bm1 != NULL);
		  Metric *m1 = new Metric (bm1, m->get_subtype ());
		  m1->set_dmetrics_visbits (VAL_VALUE);
		  ind = mlist->size ();
		  mlist->append (m1);
		}
	      map[i1] = ind;
	    }
	}
      else if (m->get_type () == BaseMetric::HWCNTR)
	{
	  if (m->is_tvisible () && m->get_dependent_bm ())
	    {
	      int ii = mlist->get_listorder (m->get_dependent_bm ()->get_cmd (),
					m->get_subtype (), m->get_expr_spec ());
	      if (ii < 0)
		{
		  BaseMetric *bm1 = dbeSession->find_metric (m->get_type (),
					     m->get_dependent_bm ()->get_cmd (),
					     m->get_expr_spec ());
		  assert (bm1 != NULL);
		  Metric *m1 = new Metric (bm1, m->get_subtype ());
		  m1->set_dmetrics_visbits ((m->get_visbits ()
					     & ~VAL_VALUE) | VAL_TIMEVAL);
		  mlist->append (m1);
		}
	    }
	}
    }

  // compute Hist_data
  Hist_data *data;
  switch (type)
    {
    case Histable::INSTR:
    case Histable::LINE:
      data = ptree->compute_metrics (mlist, type, mode, objs, context, sel_objs);
      break;
    case Histable::FUNCTION:
    case Histable::MODULE:
    case Histable::LOADOBJECT:
      data = ptree->compute_metrics (mlist, type, mode, objs, NULL,
				     sel_objs, flag);
      break;
    case Histable::DOBJECT:
      data = dspace->compute_metrics (mlist, type, mode,
				      objs ? objs->fetch (0) : NULL);
      break;
    case Histable::MEMOBJ:
    case Histable::INDEXOBJ:
      data = indxspaces->get (subtype)->compute_metrics (mlist, type, mode,
							 objs, NULL);
      break;
    case Histable::IOACTFILE:
      if (objs == NULL)
	{
	  data = iofile_data = iospace->compute_metrics (mlist, type, mode,
							 NULL);
	  break;
	}
      else
	{
	  data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0));
	  break;
	}
    case Histable::IOACTVFD:
      if (objs == NULL)
	data = iovfd_data = iospace->compute_metrics (mlist, type, mode, NULL);
      else
	data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0));
      break;
    case Histable::IOCALLSTACK:
      if (objs == NULL)
	data = iocs_data = iospace->compute_metrics (mlist, type, mode, NULL);
      else
	data = iospace->compute_metrics (mlist, type, mode, objs->fetch (0));
      break;
    case Histable::HEAPCALLSTACK:
      if (objs == NULL)
	data = heapcs_data = heapspace->compute_metrics (mlist, type, mode, NULL);
      else
	data = heapspace->compute_metrics (mlist, type, mode, objs->fetch (0));
      break;
    default:
      data = NULL;
      break;
    }
  for (long i = mlist_orig->get_items ()->size (),
	  sz = mlist->get_items ()->size (); i < sz; i++)
    {
      Metric *m = mlist->get_items ()->get (i);
      m->set_dmetrics_visbits (VAL_HIDE_ALL | m->get_visbits ());
    }
  if (data)
    data->nmetrics = mlist_orig->size ();
  return data;
}

char *
DbeView::get_mobj_name (int subtype)
{
  MemorySpace *ms = getMemorySpace (subtype);
  if (ms == NULL)
    ms = addMemorySpace (subtype);
  return ms->getMemObjTypeName ();
}

MemorySpace *
DbeView::getMemorySpace (int subtype)
{
  for (long i = 0, sz = VecSize (memspaces); i < sz; i++)
    {
      MemorySpace *ms = memspaces->get (i);
      if (subtype == ms->getMemObjType ())
	return ms;
    }
  return NULL;
}

MemorySpace *
DbeView::addMemorySpace (int subtype)
{
  MemorySpace *ms = new MemorySpace (this, subtype);
  memspaces->append (ms);
  return ms;
}

Hist_data *
DbeView::get_indxobj_data (int subtype)
{
  if (subtype < 0 || subtype >= indx_data->size ())
    return NULL;
  return indx_data->fetch (subtype);
}

void
DbeView::set_indxobj_sel (int subtype, int sel_ind)
{
  Hist_data *data = get_indxobj_data (subtype);
  if (data == NULL)
    return;
  if (sel_ind >= 0 && sel_ind < data->size ())
    {
      Histable *obj = data->fetch (sel_ind)->obj;
      sel_idxobj->store (subtype, obj);
    }
}

Histable *
DbeView::get_indxobj_sel (int subtype)
{
  return sel_idxobj->fetch (subtype);
}

void
DbeView::addIndexSpace (int subtype)
{
  PathTree *is = new PathTree (this, subtype);
  indxspaces->store (subtype, is);
  indx_data->store (subtype, NULL);
  sel_idxobj->store (subtype, NULL);
  settings->indxobj_define (subtype, false);
}

Histable *
DbeView::get_sel_obj_io (uint64_t id, Histable::Type type)
{
  if (iospace == NULL)
    return NULL;
  Histable *obj = NULL;
  Hist_data *data = NULL;
  switch (type)
    {
    case Histable::IOACTFILE:
      data = iofile_data;
      break;
    case Histable::IOACTVFD:
      data = iovfd_data;
      break;
    case Histable::IOCALLSTACK:
      data = iocs_data;
      break;
    default:
      break;
    }
  if (data == NULL)
    return NULL;

  Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items ();
  int size = hi_data->size ();
  for (int i = 0; i < size; i++)
    {
      Hist_data::HistItem *hi = hi_data->fetch (i);
      if (hi->obj != NULL && (uint64_t) hi->obj->id == id)
	{
	  obj = hi->obj;
	  break;
	}
    }
  return obj;
}

Histable *
DbeView::get_sel_obj_heap (uint64_t id)
{
  if (heapspace == NULL || heapcs_data == NULL)
    return NULL;
  Histable *obj = NULL;
  Hist_data *data = heapcs_data;
  Vector<Hist_data::HistItem*> *hi_data = data->get_hist_items ();
  int size = hi_data->size ();
  for (int i = 0; i < size; i++)
    {
      Hist_data::HistItem *hi = hi_data->fetch (i);
      if ((hi->obj != NULL) && ((uint64_t) hi->obj->id) == id)
	{
	  obj = hi->obj;
	  break;
	}
    }
  return obj;
}

CStack_data *
DbeView::get_cstack_data (MetricList *mlist)
{
  return ptree->get_cstack_data (mlist);
}

Stats_data *
DbeView::get_stats_data (int index)
{
  DataView *packets = get_filtered_events (index, DATA_SAMPLE);
  if (packets == NULL)
    return NULL;
  return new Stats_data (packets);
}

Ovw_data *
DbeView::get_ovw_data (int index)
{
  DataView *packets = get_filtered_events (index, DATA_SAMPLE);
  Experiment* exp = dbeSession->get_exp (index);
  hrtime_t starttime = 0;
  if (exp != NULL)
    starttime = exp->getStartTime ();
  if (packets == NULL)
    return NULL;
  return new Ovw_data (packets, starttime);
}

char *
DbeView::set_filter (const char *filter_spec)
{
  if (dbe_strcmp (filter_spec, cur_filter_str) == 0)  // Nothing was changed
    return NULL;

  // if string is NULL, delete the filter
  if (filter_spec == NULL)
    {
      if (cur_filter_str)
	{
	  free (cur_filter_str);
	  cur_filter_str = NULL;
	}
      if (cur_filter_expr)
	{
	  delete cur_filter_expr;
	  cur_filter_expr = NULL;
	}
      noParFilter = false;
      purge_events ();
      reset_data (false);
      return NULL;
    }

  // process the filter
  Expression *expr = dbeSession->ql_parse (filter_spec);
  if (expr == NULL)
    return dbe_sprintf (GTXT ("Invalid filter specification `%s'\n"), filter_spec);

  if (dbe_strcmp (filter_spec, "1") == 0)
    noParFilter = false;
  else if (sel_obj != NULL)
    if (sel_obj->get_type () == Histable::LINE)
      if (expr->verifyObjectInExpr (sel_obj))
	noParFilter = true;

  // valid new filter
  if (cur_filter_str != NULL)
    {
      free (prev_filter_str);
      prev_filter_str = dbe_strdup (cur_filter_str);
    }
  free (cur_filter_str);
  cur_filter_str = dbe_strdup (filter_spec);
  delete cur_filter_expr;
  cur_filter_expr = expr;
  purge_events ();
  reset_data (false);
  return NULL;
}

FilterExp *
DbeView::get_FilterExp (Experiment *exp)
{
  if (cur_filter_expr == NULL)
    return NULL;
  Expression::Context *ctx = new Expression::Context (this, exp);
  return new FilterExp (cur_filter_expr, ctx, noParFilter);
}

char *
DbeView::get_filter ()
{
  return dbe_strdup (cur_filter_str);
}

FilterSet *
DbeView::get_filter_set (int n)
{
  fflush (stderr);
  if (n >= filters->size ())
    return NULL;
  return ( filters->fetch (n));
}

Vector<FilterNumeric*> *
DbeView::get_all_filters (int nexp)
{
  FilterSet *fs = get_filter_set (nexp);
  return fs ? fs->get_all_filters () : NULL;
}

FilterNumeric *
DbeView::get_FilterNumeric (int nexp, int idx)
{
  FilterSet *fs = get_filter_set (nexp);
  return fs ? fs->get_filter (idx) : NULL;
}

void
DbeView::backtrack_filter()
{
    if (prev_filter_str != NULL)
       set_filter(prev_filter_str);
    else set_filter("1"); // reset

}

void
DbeView::update_advanced_filter ()
{
  char *s = get_advanced_filter ();
  if (dbe_strcmp (s, cur_filter_str))
    {
      phaseIdx++;
      char *err_msg = set_filter (s);
      if (err_msg)
	{
#ifdef DEBUG
	  fprintf (stderr, NTXT ("ERROR: Advanced Filter: '%s'\n"), err_msg);
#endif
	}
    }
  free (s);
}

bool
DbeView::set_pattern (int n, Vector<char *> *pattern_str, bool *error)
{
  Vector<FilterNumeric*> *filts = get_all_filters (n);

  bool ret = false;
  *error = false;
  int imax = pattern_str->size ();
  if (imax > filts->size ())
    imax = filts->size ();
  for (int i = 0; i < imax; i++)
    {
      FilterNumeric *f = filts->fetch (i);
      char *s = pattern_str->fetch (i);
      if (s == NULL)
	continue;
      if (f->set_pattern (s, error))
	ret = true;
    }

  if (ret || cur_filter_expr)
    {
      update_advanced_filter ();
      filter_active = true;
    }
  return ret;
}

static void
append_experiments (StringBuilder *sb, int first, int last)
{
  if (first == -1)
    return;
  if (sb->length () != 0)
    sb->append (NTXT (" || "));
  sb->append ('(');
  if (first == last)
    {
      sb->append (NTXT ("EXPID=="));
      sb->append (first);
    }
  else
    {
      sb->append (NTXT ("EXPID>="));
      sb->append (first);
      sb->append (NTXT (" && EXPID<="));
      sb->append (last);
    }
  sb->append (')');
}

char *
DbeView::get_advanced_filter ()
{
  StringBuilder sb;
  bool wasFalse = false;
  int first = -1, last = -1;
  for (int n = 0, nexps = dbeSession->nexps (); n < nexps; n++)
    {
      FilterSet *fs = get_filter_set (n);
      char *s = fs->get_advanced_filter ();
      if (s)
	{
	  if (streq (s, NTXT ("1")))
	    {
	      last = n + 1;
	      if (first == -1)
		first = last;
	      continue;
	    }
	  append_experiments (&sb, first, last);
	  first = -1;
	  if (streq (s, NTXT ("0")))
	    {
	      wasFalse = true;
	      continue;
	    }
	  if (sb.length () != 0)
	    sb.append (NTXT (" || "));
	  sb.append (NTXT ("(EXPID=="));
	  sb.append (n + 1);
	  sb.append (NTXT (" && ("));
	  sb.append (s);
	  free (s);
	  sb.append (NTXT ("))"));
	}
      else
	{
	  last = n + 1;
	  if (first == -1)
	    first = last;
	}
    }
  if (first != 1)
    {
      append_experiments (&sb, first, last);
      first = -1;
    }
  if (sb.length () == 0)
    sb.append (wasFalse ? '0' : '1');
  else
    append_experiments (&sb, first, last);
  return sb.toString ();
}

bool
DbeView::set_pattern (int m, char *pattern)
{
  bool error = false;

  // Store original setting in case of error
  int nexps = dbeSession->nexps ();
  int orig_phaseIdx = phaseIdx;
  bool *orig_enable = new bool[nexps];
  char **orig_pattern = new char*[nexps];
  for (int i = 0; i < nexps; i++)
    {
      orig_pattern[i] = get_FilterNumeric (i, m)->get_pattern ();
      orig_enable[i] = get_exp_enable (i);
      set_exp_enable (i, false);
    }

  // Copy the pattern so that we could safely modify it
  char *buf = dbe_strdup (pattern);
  FilterNumeric *fexp = NULL;
  char *pb, *pe;
  pb = pe = buf;
  for (bool done = false; !done; pe++)
    {
      if (*pe == ':')
	{
	  // experiment filter;
	  *pe = '\0';
	  fexp = new FilterNumeric (NULL, NULL, NULL);
	  fexp->set_range (1, nexps, nexps);
	  fexp->set_pattern (pb, &error);
	  if (error)
	    break;
	  pb = pe + 1;
	}
      else if (*pe == '+' || *pe == '\0')
	{
	  // entity filter
	  if (*pe == '\0')
	    done = true;
	  else
	    *pe = '\0';
	  for (int i = 0; i < nexps; i++)
	    {
	      if (!fexp || fexp->is_selected (i + 1))
		{
		  FilterNumeric *f = get_FilterNumeric (i, m);
		  f->set_pattern (pb, &error);
		  if (error)
		    break;
		  set_exp_enable (i, true);
		}
	    }
	  if (error)
	    break;
	  delete fexp;
	  fexp = NULL;
	  pb = pe + 1;
	}
    }

  if (error)
    {
      for (int i = 0; i < nexps; i++)
	{
	  bool err;
	  set_exp_enable (i, orig_enable[i]);
	  FilterNumeric *f = get_FilterNumeric (i, m);
	  f->set_pattern (orig_pattern[i], &err);
	  free (orig_pattern[i]);
	}
      phaseIdx = orig_phaseIdx;
    }
  else
    {
      update_advanced_filter ();
      filter_active = true;
    }
  delete[] orig_enable;
  delete[] orig_pattern;
  delete fexp;
  free (buf);
  return !error;
}

void
DbeView::set_view_mode (VMode newmode)
{
  if (newmode != settings->get_view_mode ())
    {

      // For OpenMP, the expert mode path-tree is already present with the user mode
      // No need to increase the phaseIdx to trigger recomputation of path-tree
      // if we toggle between user and expert modes
      if (!(dbeSession->is_omp_available ()
	    && ((newmode == VMODE_EXPERT
		 && settings->get_view_mode () == VMODE_USER)
		|| (newmode == VMODE_USER
		    && settings->get_view_mode () == VMODE_EXPERT))))
	phaseIdx++; // For all other cases
      setNewViewMode ();
      settings->set_view_mode (newmode);
    }
}

Cmd_status
DbeView::set_view_mode (char *str, bool fromRC)
{
  VMode old = settings->get_view_mode ();
  Cmd_status ret = settings->set_view_mode (str, fromRC);
  if (old != settings->get_view_mode ())
    phaseIdx++;
  return ret;
}

Cmd_status
DbeView::set_en_desc (char *str, bool fromRC)
{
  // Tell the session
  Settings *s = dbeSession->get_settings ();
  s->set_en_desc (str, fromRC);

  // and tell our settings
  return settings->set_en_desc (str, fromRC);
}

// Get processor stats messages
char *
DbeView::get_processor_msg (int type)
{
  if (ptree == NULL)  // if no PathTree, no messages
    return NULL;

  StringBuilder sb;
  Emsg *m = (type == PSTAT_MSG) ? ptree->fetch_stats () : ptree->fetch_warnings ();
  for (; m != NULL; m = m->next)
    {
      char* newmsg = m->get_msg ();
      sb.append (newmsg);
      sb.append ("\n");
    }

  if (type == PSTAT_MSG)
    ptree->delete_stats ();
  else
    ptree->delete_warnings ();
  return (sb.length () > 0) ? sb.toString () : NULL;
}

void
DbeView::dump_nodes (FILE *outfile)
{
  FILE *f = (outfile == NULL ? stderr : outfile);
  ptree->print (f);
}

// Dump the clock profile events
void
DbeView::dump_profile (FILE *out_file)
{
  for (int idx = 0; idx < dbeSession->nexps (); idx++)
    {
      Experiment *exp = dbeSession->get_exp (idx);
      VMode view_mode = get_view_mode ();
      char * stateNames [/*LMS_NUM_STATES*/] = LMS_STATE_STRINGS;

      // Process clock profile date
      DataView *packets = get_filtered_events (idx, DATA_CLOCK);
      if (packets && packets->getSize () != 0)
	{
	  hrtime_t start = exp->getStartTime ();
	  fprintf (out_file,
		   GTXT ("\nTotal Clock Profiling Packets:  %d Experiment:  %s\n"),
		   (int) packets->getSize (), exp->get_expt_name ());
	  for (long i = 0; i < packets->getSize (); i++)
	    {
	      hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i);
	      hrtime_t ts = expr_ts - start;

	      // get the properties from the packet
	      uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i);
	      uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i);
	      int mstate = (int) packets->getIntValue (PROP_MSTATE, i);
	      int nticks = (int) packets->getIntValue (PROP_NTICK, i);

	      char *sname;
	      char buf[1024];
	      if (mstate >= 0 && mstate < LMS_NUM_STATES)
		  sname = stateNames[mstate];
	      else
		{
		  snprintf (buf, sizeof (buf), NTXT ("Unexpected mstate = %d"), mstate);
		  sname = buf;
		}

	      // get the stack   IGNORE HIDE
	      Vector<Histable*> *stack = getStackPCs (view_mode, packets, i);
	      int stack_size = stack->size ();

	      // print the packet header with the count of stack frames
	      fprintf (out_file,
		       GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"),
		       i, expr_ts, ts / NANOSEC, ts % NANOSEC,
		       expr_ts / NANOSEC, expr_ts % NANOSEC,
		       thrid, cpuid, stack_size);
	      fprintf (out_file,
		       GTXT ("    mstate = %d (%s), nticks = %d\n"),
		       mstate, sname, nticks);

	      // dump the callstack
	      for (int j = stack_size - 1; j >= 0; j--)
		{
		  Histable *frame = stack->fetch (j);
		  fprintf (out_file, GTXT ("          %s [0x%016llx]\n"), frame->get_name (), (long long) frame);
		}
	      fprintf (out_file, "\n");
	    }
	}
      else
	fprintf (out_file,
		 GTXT ("\nNo Clock Profiling Packets in Experiment:  %s\n"),
		 exp->get_expt_name ());
    }
}

// Dump the sync trace events
void
DbeView::dump_sync (FILE *out_file)
{
  for (int idx = 0; idx < dbeSession->nexps (); idx++)
    {
      Experiment *exp = dbeSession->get_exp (idx);
      VMode view_mode = get_view_mode ();

      // Process heap trace date
      DataView *packets = get_filtered_events (idx, DATA_SYNCH);
      if (packets && packets->getSize () != 0)
	{
	  hrtime_t start = exp->getStartTime ();
	  fprintf (out_file,
		   GTXT ("\nTotal Synctrace Packets:  %d Experiment:  %s\n"),
		   (int) packets->getSize (), exp->get_expt_name ());

	  for (long i = 0; i < packets->getSize (); i++)
	    {
	      hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i);
	      hrtime_t ts = expr_ts - start;

	      // get the properties from the packet
	      uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i);
	      uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i);
	      uint64_t syncobj = (uint64_t) packets->getLongValue (PROP_SOBJ, i);
	      hrtime_t syncrtime = (uint64_t) packets->getLongValue (PROP_SRQST, i);
	      hrtime_t syncdelay = expr_ts - syncrtime;

	      // get the stack   IGNORE HIDE
	      Vector<Histable*> *stack = getStackPCs (view_mode, packets, i);
	      int stack_size = stack->size ();

	      // print the packet header with the count of stack frames
	      fprintf (out_file,
		       GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"),
		       i, expr_ts, ts / NANOSEC, ts % NANOSEC,
		       expr_ts / NANOSEC, expr_ts % NANOSEC, thrid,
		       cpuid, stack_size);
	      fprintf (stderr,
		       GTXT ("       synchronization object @ 0x%016llx;  synchronization delay  %3lld.%09lld\n"),
		       (unsigned long long) syncobj, (long long) (syncdelay / NANOSEC), (long long) (syncdelay % NANOSEC));

	      // dump the callstack
	      for (int j = stack_size - 1; j >= 0; j--)
		{
		  Histable *frame = stack->fetch (j);
		  fprintf (out_file, GTXT ("          %s [0x%016llx]\n"), frame->get_name (), (long long) frame);
		}
	      fprintf (out_file, "\n");
	    }
	}
      else
	fprintf (out_file, GTXT ("\nNo Synctrace Packets in Experiment:  %s\n"),
		 exp->get_expt_name ());
    }
}

// Dump the IO trace events
void
DbeView::dump_iotrace (FILE *out_file)
{
  for (int idx = 0; idx < dbeSession->nexps (); idx++)
    {
      Experiment *exp = dbeSession->get_exp (idx);
      VMode view_mode = get_view_mode ();

      // Process IO trace date
      DataView *packets = get_filtered_events (idx, DATA_IOTRACE);
      if (packets && packets->getSize () != 0)
	{
	  hrtime_t start = exp->getStartTime ();
	  fprintf (out_file,
		   GTXT ("\nTotal IO trace Packets:  %d Experiment:  %s\n"),
		   (int) packets->getSize (), exp->get_expt_name ());
	  for (long i = 0; i < packets->getSize (); i++)
	    {
	      hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i);
	      hrtime_t ts = expr_ts - start;

	      // get the properties from the packet
	      uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i);
	      uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i);
	      IOTrace_type iotrtype = (IOTrace_type) packets->getIntValue (PROP_IOTYPE, i);
	      uint32_t iofd = (uint32_t) packets->getIntValue (PROP_IOFD, i);
	      uint64_t ionbyte = (uint64_t) packets->getIntValue (PROP_IONBYTE, i);
	      hrtime_t iorqst = (hrtime_t) packets->getLongValue (PROP_IORQST, i);
	      uint32_t ioofd = (uint32_t) packets->getIntValue (PROP_IOOFD, i);
	      FileSystem_type iofstype = (FileSystem_type) packets->getIntValue (PROP_CPUID, i);
	      int64_t iovfd = (int64_t) packets->getIntValue (PROP_IOVFD, i);

	      char *fName = NULL;
	      StringBuilder *sb = (StringBuilder*) packets->getObjValue (PROP_IOFNAME, i);
	      if (sb != NULL && sb->length () > 0)
		fName = sb->toString ();

	      // get the stack  IGNORE HIDE
	      Vector<Histable*> *stack = getStackPCs (view_mode, packets, i);
	      int stack_size = stack->size ();
	      const char *iotrname;
	      switch (iotrtype)
		{
		case READ_TRACE:
		  iotrname = "ReadTrace";
		  break;
		case WRITE_TRACE:
		  iotrname = "WriteTrace";
		  break;
		case OPEN_TRACE:
		  iotrname = "OpenTrace";
		  break;
		case CLOSE_TRACE:
		  iotrname = "CloseTrace";
		  break;
		case OTHERIO_TRACE:
		  iotrname = "OtherIOTrace";
		  break;
		case READ_TRACE_ERROR:
		  iotrname = "ReadTraceError";
		  break;
		case WRITE_TRACE_ERROR:
		  iotrname = "WriteTraceError";
		  break;
		case OPEN_TRACE_ERROR:
		  iotrname = "OpenTraceError";
		  break;
		case CLOSE_TRACE_ERROR:
		  iotrname = "CloseTraceError";
		  break;
		case OTHERIO_TRACE_ERROR:
		  iotrname = "OtherIOTraceError";
		  break;
		default:
		  iotrname = "UnknownIOTraceType";
		  break;
		}

	      // print the packet header with the count of stack frames
	      fprintf (out_file,
		       GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"),
		       i, expr_ts, ts / NANOSEC, ts % NANOSEC,
		       expr_ts / NANOSEC, expr_ts % NANOSEC,
		       thrid, cpuid, stack_size);
	      fprintf (out_file,
		       GTXT ("    %s: fd = %d, ofd = %d, vfd = %lld, fstype = %d, rqst =  %3lld.%09lld\n"),
		       iotrname, (int) iofd, (int) ioofd, (long long) iovfd,
		       (int) iofstype, (long long) (iorqst / NANOSEC),
		       (long long) (iorqst % NANOSEC));
	      fprintf (out_file, GTXT ("    filename = `%s', nbytes = %d\n"),
		       STR (fName), (int) ionbyte);
	      free (fName);

	      // dump the callstack
	      for (int j = stack_size - 1; j >= 0; j--)
		{
		  Histable *frame = stack->fetch (j);
		  fprintf (out_file, GTXT ("          %s [0x%016llx]\n"), frame->get_name (), (long long) frame);
		}
	      fprintf (out_file, "\n");
	    }
	}
      else
	fprintf (out_file, GTXT ("\nNo IO trace Packets in Experiment:  %s\n"),
		 exp->get_expt_name ());
    }
}

// Dump the HWC Profiling events
void
DbeView::dump_hwc (FILE *out_file)
{
  for (int idx = 0; idx < dbeSession->nexps (); idx++)
    {
      Experiment *exp = dbeSession->get_exp (idx);
      VMode view_mode = get_view_mode ();

      // Dump HWC profiling data
      DataView *packets = get_filtered_events (idx, DATA_HWC);
      if (packets && packets->getSize () != 0)
	{
	  hrtime_t start = exp->getStartTime ();
	  fprintf (out_file,
		   GTXT ("\nTotal HW Counter Profiling Packets:  %d Experiment:  %s\n"),
		   (int) packets->getSize (), exp->get_expt_name ());
	  for (long i = 0; i < packets->getSize (); i++)
	    {
	      const char * hwc_name;
	      hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i);
	      hrtime_t ts = expr_ts - start;
	      uint32_t tag = (uint32_t) packets->getIntValue (PROP_HWCTAG, i);
	      uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i);
	      uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i);

	      // This will work even with a different counter in every packet.
	      if (tag < 0 || tag >= MAX_HWCOUNT
		  || !exp->coll_params.hw_aux_name[tag])
		// if the packet has an invalid tag, use <invalid> as its name
		hwc_name = "<invalid>";
	      else
		hwc_name = exp->coll_params.hw_aux_name[tag];
	      int64_t mval = packets->getLongValue (PROP_HWCINT, i);
	      const char *err = HWCVAL_HAS_ERR (mval) ? " $$" : "";

	      // get the stack IGNORE HIDE
	      Vector<Histable*> *stack = getStackPCs (view_mode, packets, i);
	      int stack_size = stack->size ();

	      // print the packet header with the count of stack frames
	      fprintf (out_file,
		       GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n       count = %10lld (0x%016llx), tag = %d (%s)%s\n"),
		       (long) i, (long long) expr_ts,
		       (long long) (ts / NANOSEC), (long long) (ts % NANOSEC),
		       (long long) (expr_ts / NANOSEC), (long long) (expr_ts % NANOSEC),
		       (int) thrid, (int) cpuid, (int) stack_size,
		       (long long) (HWCVAL_CLR_ERR (mval)), (long long) mval,
		       (int) tag, hwc_name, err);

	      //  dump extended HWC packets values
	      uint64_t va = (uint64_t) packets->getLongValue (PROP_VADDR, i);
	      uint64_t pa = (uint64_t) packets->getLongValue (PROP_PADDR, i);
	      fprintf (out_file, GTXT ("       va = 0x%016llx, pa = 0x%016llx\n"),
		       (unsigned long long) va, (unsigned long long) pa);

	      // dump the callstack
	      for (int j = stack_size - 1; j >= 0; j--)
		{
		  Histable *frame = stack->fetch (j);
		  fprintf (out_file, GTXT ("          %s [0x%016llx]\n"), frame->get_name (), (long long) frame);
		}
	      fprintf (out_file, "\n");
	    }
	}
      else
	fprintf (out_file,
		 GTXT ("\nNo HWC Profiling Packets in Experiment:  %s\n"),
		 exp->get_expt_name ());
    }
}

// Dump the Heap events
void
DbeView::dump_heap (FILE *out_file)
{
  char *heapstrings[] = HEAPTYPE_STATE_USTRINGS;
  for (int idx = 0; idx < dbeSession->nexps (); idx++)
    {
      Experiment *exp = dbeSession->get_exp (idx);
      VMode view_mode = get_view_mode ();

      // Process heap trace date
      DataView *packets = get_filtered_events (idx, DATA_HEAP);
      if (packets && packets->getSize () != 0)
	{
	  hrtime_t start = exp->getStartTime ();
	  fprintf (out_file, GTXT ("\nTotal Heaptrace Packets:  %d Experiment:  %s\n"),
		   (int) packets->getSize (), exp->get_expt_name ());
	  for (long i = 0; i < packets->getSize (); i++)
	    {
	      hrtime_t expr_ts = (hrtime_t) packets->getLongValue (PROP_TSTAMP, i);
	      hrtime_t ts = expr_ts - start;

	      // get the properties from the packet
	      uint32_t thrid = (uint32_t) packets->getIntValue (PROP_THRID, i);
	      uint32_t cpuid = (uint32_t) packets->getIntValue (PROP_CPUID, i);
	      uint32_t heaptype = (uint32_t) packets->getIntValue (PROP_HTYPE, i);
	      uint64_t heapsize = (uint64_t) packets->getULongValue (PROP_HSIZE, i);
	      uint64_t heapvaddr = (uint64_t) packets->getULongValue (PROP_HVADDR, i);
	      uint64_t heapovaddr = (uint64_t) packets->getULongValue (PROP_HOVADDR, i);
	      if (heaptype == MUNMAP_TRACE)
		{
		  heapsize = (uint64_t) packets->getULongValue (PROP_HOVADDR, i);
		  heapovaddr = 0;
		}

	      // get the stack  IGNORE HIDE
	      Vector<Histable*> *stack = getStackPCs (view_mode, packets, i);
	      int stack_size = stack->size ();

	      // print the packet header with the count of stack frames
	      fprintf (out_file,
		       GTXT ("#%6ld: %lld, %3lld.%09lld (%4lld.%09lld) t = %d, cpu = %d, frames = %d\n"),
		       i, expr_ts, ts / NANOSEC, ts % NANOSEC,
		       expr_ts / NANOSEC, expr_ts % NANOSEC,
		       thrid, cpuid, stack_size);
	      char *typestr = heapstrings[heaptype];
	      fprintf (out_file,
		       GTXT ("    type = %d (%s), size = %llu (0x%llx), VADDR = 0x%016llx, OVADDR = 0x%016llx\n"),
		       (int) heaptype, typestr, (long long unsigned int) heapsize,
		       (long long unsigned int) heapsize,
		       (long long unsigned int) heapvaddr,
		       (long long unsigned int) heapovaddr);

	      // dump the callstack
	      for (int j = stack_size - 1; j >= 0; j--)
		{
		  Histable *frame = stack->fetch (j);
		  fprintf (out_file, GTXT ("          %s [0x%016llx]\n"), frame->get_name (), (long long) frame);
		}
	      fprintf (out_file, "\n");
	    }
	}
      else
	fprintf (out_file, GTXT ("\nNo Heaptrace Packets in Experiment:  %s\n"),
		 exp->get_expt_name ());
    }
}

// Dump the Java garbage collector events
void
DbeView::dump_gc_events (FILE *out_file)
{
  for (int idx = 0; idx < dbeSession->nexps (); idx++)
    {
      Experiment *exp = dbeSession->get_exp (idx);
      if (!exp->has_java)
	fprintf (out_file,
		 GTXT ("# No GC events in experiment %d, %s (PID %d, %s)\n"),
		 idx, exp->get_expt_name (), exp->getPID (), exp->utargname);
      else
	{
	  Vector<GCEvent*> *gce = exp->get_gcevents ();
	  GCEvent *this_event;
	  int index;
	  fprintf (out_file,
		   GTXT ("# %li events in experiment %d: %s (PID %d, %s)\n"),
		   gce->size (), idx,
		   exp->get_expt_name (), exp->getPID (), exp->utargname);
	  fprintf (out_file,
	       GTXT ("# exp:idx     GC_start,        GC_end,   GC_duration\n"));
	  Vec_loop (GCEvent*, gce, index, this_event)
	  {
	    hrtime_t start = this_event->start - exp->getStartTime ();
	    hrtime_t end = this_event->end - exp->getStartTime ();
	    hrtime_t delta = this_event->end - this_event->start;
	    fprintf (out_file,
		     "%5d:%d, %3lld.%09lld, %3lld.%09lld, %3lld.%09lld\n",
		     idx, index,
		     (long long) (start / NANOSEC), (long long) (start % NANOSEC),
		     (long long) (end / NANOSEC), (long long) (end % NANOSEC),
		     (long long) (delta / NANOSEC), (long long) (delta % NANOSEC));
	  }
	}
    }
}

void
DbeView::purge_events (int n)
{
  phaseIdx++;
  int lst;
  if (n == -1)
    lst = filters->size ();
  else
    lst = n > filters->size () ? filters->size () : n + 1;
  for (int i = n == -1 ? 0 : n; i < lst; i++)
    {
      Vector<DataView*> *expDataViewList = dataViews->fetch (i);
      if (expDataViewList)
	{
	  // clear out all the data_ids, but don't change the vector size
	  for (int data_id = 0; data_id < expDataViewList->size (); ++data_id)
	    {
	      delete expDataViewList->fetch (data_id);
	      expDataViewList->store (data_id, NULL);
	    }
	}
    }
  filter_active = false;
}


// LIBRARY_VISIBILITY
void
DbeView::resetAndConstructShowHideStacks ()
{
  for (int n = 0, nexps = dbeSession->nexps (); n < nexps; n++)
    {
      Experiment *exp = dbeSession->get_exp (n);
      if (exp != NULL)
	resetAndConstructShowHideStack (exp);
    }
}

// LIBRARY_VISIBILITY
void
DbeView::resetAndConstructShowHideStack (Experiment *exp)
{
  exp->resetShowHideStack ();
  /*  Vector<DataDescriptor*> *dDscrs = */ exp->getDataDescriptors ();

  DataDescriptor *dd;
  // Construct show hide stack only for objects which have call stacks
  // list below similar to path tree. What about HEAP_SZ? (DBFIXME)
  dd = exp->get_raw_events (DATA_CLOCK);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
  dd = exp->get_raw_events (DATA_SYNCH);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
  dd = exp->get_raw_events (DATA_IOTRACE);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
  dd = exp->get_raw_events (DATA_HWC);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
  dd = exp->get_raw_events (DATA_HEAP);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
  dd = exp->get_raw_events (DATA_RACE);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
  dd = exp->get_raw_events (DATA_DLCK);
  if (dd != NULL)
    constructShowHideStack (dd, exp);
}

// LIBRARY_VISIBILITY
void
DbeView::constructShowHideStack (DataDescriptor *dDscr, Experiment *exp)
{
  if (dDscr == NULL)
    return;
  int stack_prop = PROP_NONE;
  VMode view_mode = get_view_mode ();
  if (view_mode == VMODE_MACHINE)
    stack_prop = PROP_MSTACK;
  else if (view_mode == VMODE_EXPERT)
    stack_prop = PROP_XSTACK;
  else if (view_mode == VMODE_USER)
    stack_prop = PROP_USTACK;

  for (long j = 0, sz = dDscr->getSize (); j < sz; j++)
    {
      void *stackId = dDscr->getObjValue (stack_prop, j);
      Vector<Histable*> *stack = (Vector<Histable*>*)CallStack::getStackPCs (stackId);
      int stack_size = stack->size ();
      bool hide_on = false;
      LoadObject *hide_lo = NULL;
      Histable *last_addr = NULL;
      Histable *api_addr = NULL;
      DbeInstr *h_instr;

      Vector<Histable*> *hidepcs = new Vector<Histable*>;
      for (int i = stack_size - 1; i >= 0; i--)
	{
	  bool leaf = (i == 0);
	  Histable *cur_addr = stack->fetch (i);
	  Function *func = (Function*) cur_addr->convertto (Histable::FUNCTION);
	  if (func != NULL)
	    {
	      Module *mod = func->module;
	      LoadObject *lo = mod->loadobject;
	      int segx = lo->seg_idx;
	      if ((get_lo_expand (segx) == LIBEX_API) && (i != (stack_size - 1)))
		{
		  leaf = true;
		  api_addr = cur_addr;
		}
	      else if (get_lo_expand (segx) == LIBEX_HIDE)
		{
		  if (hide_on)
		    {
		      if (lo != hide_lo)
			{
			  // Changed hidden loadobject
			  if (last_addr != NULL)
			    {
			      h_instr = hide_lo->get_hide_instr ((DbeInstr*) last_addr);
			      hidepcs->append (h_instr);
			      last_addr = cur_addr;
			    }
			  hide_lo = lo;
			}
		    }
		  else
		    {
		      // Start hide
		      hide_on = true;
		      last_addr = cur_addr;
		      hide_lo = lo;
		    }
		  if (!leaf)
		    continue;
		}
	      else
		{
		  hide_on = false;
		  if (last_addr != NULL)
		    {
		      h_instr = hide_lo->get_hide_instr ((DbeInstr*) last_addr);
		      hidepcs->append (h_instr);
		      last_addr = NULL;
		    }
		}
	    }
	  if (last_addr != NULL && leaf) cur_addr = last_addr;
	  if (hide_on)
	    {
	      h_instr = hide_lo->get_hide_instr ((DbeInstr*) cur_addr);
	      hidepcs->append (h_instr);
	      if (api_addr != NULL)
		hidepcs->append (api_addr);
	    }
	  else
	    hidepcs->append (cur_addr);
	  if (leaf)
	    break;
	}
      for (int i = 0, k = hidepcs->size () - 1; i < k; ++i, --k)
	hidepcs->swap (i, k);

      CallStack *cstkSH = exp->callTreeShowHide ();
      CallStackNode *hstack = (CallStackNode *) cstkSH->add_stack (hidepcs);
      dDscr->setObjValue (PROP_HSTACK, j, hstack);
      CallStack::setHideStack (stackId, hstack);
      delete hidepcs;
      delete stack;
    }
}

DataView *
DbeView::get_filtered_events (int idx, int data_id)
{
  if (idx < 0 || idx >= dataViews->size ())
    return NULL;
  Vector<DataView*> *expDataViewList = dataViews->fetch (idx);
  if (!expDataViewList)
    return NULL; // Weird

  DataView *dview = expDataViewList->fetch (data_id);
  Experiment *exp = dbeSession->get_exp (idx);
  if (dview)
    {
      // if show-hide is on force a reconstruction of hide stacks
      // LIBRARY_VISIBILITY
      if (!showAll && (showHideChanged || newViewMode))
	{
	  DataDescriptor *dDscr = exp->get_raw_events (data_id);
	  constructShowHideStack (dDscr, exp);
	}
      return dview;
    }

  int orig_data_id = data_id;
  data_id = exp->base_data_id (data_id);
  if (orig_data_id != data_id)
    // orig_data_id is a derived DataView.  Get the master DataView:
    dview = expDataViewList->fetch (data_id);
  if (dview == NULL)
    {
      Expression *saved = cur_filter_expr;
      if (!adjust_filter (exp))
	return NULL;

      DataDescriptor *dDscr = exp->get_raw_events (data_id);
      if (!showAll && (showHideChanged || newViewMode))
	constructShowHideStack (dDscr, exp);

      Emsg *m = exp->fetch_warnings ();
      if (m != NULL)
	this->warning_msg = m->get_msg ();

      if (dDscr != NULL)
	{
	  FilterExp *filter = get_FilterExp (exp);
	  dview = dDscr->createView ();
	  dview->setFilter (filter);
	  if (dview->getSize () < dDscr->getSize ())
	    filter_active = true;
	}
      expDataViewList->store (data_id, dview);

      if (saved)
	{
	  delete cur_filter_expr;
	  cur_filter_expr = saved;
	}
    }
  if (orig_data_id != data_id)
    {
      // create the derived DataView:
      dview = exp->create_derived_data_view (orig_data_id, dview);
      expDataViewList->store (orig_data_id, dview);
    }
  return dview;
}

DataView *
DbeView::get_filtered_events (int idx, int data_id,
			      const int sortprops[], int sortprop_count)
{
  DataView *packets = get_filtered_events (idx, data_id);
  if (packets)
    packets->sort (sortprops, sortprop_count);
  return packets;
}

bool
DbeView::adjust_filter (Experiment *exp)
{
  if (cur_filter_expr)
    {
      Expression::Context ctx (this, exp);
      resetFilterHideMode ();
      Expression *fltr = cur_filter_expr->pEval (&ctx);
      if (fltr->complete ())
	{ // Filter is a constant
	  if (fltr->eval (NULL) == 0)
	    return false;
	  delete fltr;
	  fltr = NULL;
	}
      cur_filter_expr = fltr;
    }
  return true;
}

// Moved from Cacheable.cc:
char *
DbeView::status_str (DbeView_status status)
{
  switch (status)
    {
    case DBEVIEW_SUCCESS:
      return NULL;
    case DBEVIEW_NO_DATA:
      return dbe_strdup (GTXT ("Data not available for this filter selection"));
    case DBEVIEW_IO_ERROR:
      return dbe_strdup (GTXT ("Unable to open file"));
    case DBEVIEW_BAD_DATA:
      return dbe_strdup (GTXT ("Data corrupted"));
    case DBEVIEW_BAD_SYMBOL_DATA:
      return dbe_strdup (GTXT ("Functions/Modules information corrupted"));
    case DBEVIEW_NO_SEL_OBJ:
      return dbe_strdup (GTXT ("No selected object, bring up Functions Tab"));
    }
  return NULL;
}

Histable *
DbeView::set_sel_obj (Histable *obj)
{
  if (obj)
    {
      switch (obj->get_type ())
	{
	case Histable::INSTR:
	  lastSelInstr = (DbeInstr *) obj;
	  lastSelFunc = lastSelInstr->func;
	  this->sel_binctx = lastSelFunc;
	  break;
	case Histable::FUNCTION:
	  if (lastSelInstr && lastSelInstr->func != obj)
	    lastSelInstr = NULL;
	  lastSelFunc = (Function *) obj;
	  break;
	case Histable::LINE:
	  {
	    DbeLine *dbeLine = (DbeLine *) obj;
	    if (dbeLine->func)
	      {
		// remember previous DbeInstr and DbeFunc
		lastSelFunc = dbeLine->func;
		if (lastSelInstr && lastSelInstr->func != lastSelFunc)
		  lastSelInstr = NULL;
		this->sel_binctx = lastSelFunc;
	      }
	    else
	      this->sel_binctx = dbeLine->convertto (Histable::FUNCTION);
	    break;
	  }
	case Histable::MODULE:
	case Histable::LOADOBJECT:
	case Histable::EADDR:
	case Histable::MEMOBJ:
	case Histable::INDEXOBJ:
	case Histable::PAGE:
	case Histable::DOBJECT:
	case Histable::SOURCEFILE:
	case Histable::IOACTFILE:
	case Histable::IOACTVFD:
	case Histable::IOCALLSTACK:
	case Histable::HEAPCALLSTACK:
	case Histable::EXPERIMENT:
	case Histable::OTHER:
	  break;
	}
    }
  sel_obj = obj;
  Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d obj %s\n"),
	   __LINE__, obj ? obj->dump () : "NULL");
  Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d sel_obj %s\n"),
	   __LINE__, sel_obj ? sel_obj->dump () : "NULL");
  Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d lastSelFunc %s\n"),
	   __LINE__, lastSelFunc ? lastSelFunc->dump () : "NULL");
  Dprintf (DEBUG_DBE, NTXT ("### set_sel_obj: DbeView.cc:%d lastSelInstr %s\n"),
	   __LINE__, lastSelInstr ? lastSelInstr->dump () : "NULL");
  return sel_obj;
}

DbeInstr *
DbeView::convert_line_to_instr (DbeLine *dbeLine)
{
  Dprintf (DEBUG_DBE, "### convert_line_to_instr DbeView::%d dbeLine=%s\n", __LINE__, dbeLine->dump ());
  Function *func = convert_line_to_func (dbeLine);
  if (func)
    {
      Dprintf (DEBUG_DBE, "### convert_line_to_instr DbeView::%d func=%s\n", __LINE__, func->dump ());
      DbeInstr *dbeInstr = func->mapLineToPc (dbeLine);
      Dprintf (DEBUG_DBE && dbeInstr, "### convert_line_to_instr DbeView::%d dbeInstr=%s\n", __LINE__, dbeInstr->dump ());
      return dbeInstr;
    }
  Dprintf (DEBUG_DBE && lastSelInstr, "### convert_line_to_instr DbeView::%d lastSelInstr=%s\n", __LINE__, lastSelInstr->dump ());
  return lastSelInstr;
}

DbeInstr *
DbeView::convert_func_to_instr (Function *func)
{
  return (lastSelInstr && lastSelInstr->func == func) ?
	  lastSelInstr : (DbeInstr *) func->convertto (Histable::INSTR);
}

Function *
DbeView::convert_line_to_func (DbeLine *dbeLine)
{
  Function *func = dbeLine->func;
  if (func)
    return func;
  if (lastSelFunc != NULL)
    // Can be mapped to the same function ?
    for (DbeLine *dl = dbeLine->dbeline_base; dl; dl = dl->dbeline_func_next)
      if (dl->func == lastSelFunc)
	return lastSelFunc;

  PathTree *pathTree = NULL;
  Function *firstFunc = NULL;
  for (DbeLine *dl = dbeLine->dbeline_base; dl; dl = dl->dbeline_func_next)
    {
      // Find a first function with non-zero metrics
      if (dl->func)
	{
	  if (pathTree == NULL)
	    pathTree = get_path_tree ();
	  if (pathTree->get_func_nodeidx (dl->func))
	    return dl->func;
	  if (firstFunc == NULL)
	    firstFunc = dl->func;
	}
    }
  // Take a first function
  return firstFunc;
}

Histable *
DbeView::get_sel_obj (Histable::Type type)
{
  Histable *lastSelObj = sel_obj;
  Dprintf (DEBUG_DBE, NTXT ("### get_sel_obj: DbeView.cc:%d type=%d sel_obj %s\n"),
	   __LINE__, type, lastSelObj ? lastSelObj->dump () : "NULL");
  if (lastSelObj == NULL)
    return NULL;
  switch (type)
    {
    case Histable::INSTR:
      if (!showAll)
	{
	  // DBFIXME LIBRARY VISIBILITY
	  // hack to get to the hide mode object for PCs when filtering
	  // with a PC in timeline
	  if (lastSelObj->get_type () == Histable::INSTR)
	    {
	      Function *func = (Function*) (lastSelObj->convertto (Histable::FUNCTION));
	      LoadObject *lo = func->module->loadobject;
	      if (get_lo_expand (lo->seg_idx) == LIBEX_HIDE)
		return lo->get_hide_function ();
	    }
	}
      if (lastSelObj->get_type () == Histable::LINE)
	return convert_line_to_instr ((DbeLine*) lastSelObj);
      else if (lastSelObj->get_type () == Histable::FUNCTION)
	return convert_func_to_instr ((Function *) lastSelObj);
      return lastSelObj->convertto (type);
    case Histable::FUNCTION:
      if (lastSelObj->get_type () == Histable::LINE)
	{
	  Function *func = convert_line_to_func ((DbeLine*) lastSelObj);
	  if (func)
	    return func;
	  return NULL;
	}
      return lastSelObj->convertto (type);
    case Histable::LINE:
    default:
      return lastSelObj->convertto (type);
    }
}
