/* Copyright (C) 2021-2026 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 <errno.h>
#include <utime.h>
#include <alloca.h>
#include <dirent.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <set>

#include "util.h"
#include "CacheMap.h"
#include "DbeFile.h"
#include "DbeCacheMap.h"
#include "DefaultHandler.h"
#include "DefaultMap2D.h"
#include "Emsg.h"
#include "Elf.h"
#include "SAXParser.h"
#include "SAXParserFactory.h"
#include "StringBuilder.h"
#include "DbeSession.h"
#include "DbeThread.h"
#include "Application.h"
#include "CallStack.h"
#include "Experiment.h"
#include "Exp_Layout.h"
#include "DataStream.h"
#include "Expression.h"
#include "Function.h"
#include "HeapMap.h"
#include "LoadObject.h"
#include "Module.h"
#include "Ovw_data.h"
#include "PRBTree.h"
#include "Sample.h"
#include "SegMem.h"
#include "StringMap.h"
#include "UserLabel.h"
#include "Table.h"
#include "dbe_types.h"
#include "FileData.h"
#include "cc_libcollector.h"
#include "ExpGroup.h"

int nPush;
int nPop;
int pushCnt;
int popCnt;
int pushCnt3;
int popCnt3;

struct Experiment::UIDnode
{
  uint64_t uid;
  uint64_t val;
  UIDnode *next;
};

struct Experiment::RawFramePacket
{
  uint64_t uid;
  UIDnode *uidn;
  UIDnode *uidj;
  UIDnode *omp_uid;
  uint32_t omp_state;
};

static hrtime_t
parseTStamp (const char *s)
{
  hrtime_t ts = (hrtime_t) 0;
  ts = (hrtime_t) atoi (s) * NANOSEC;
  s = strchr (s, '.');
  if (s != NULL)
    ts += (hrtime_t) atoi (s + 1);
  return ts;
}

class Experiment::ExperimentFile
{
public:

  enum
  {
    EF_NOT_OPENED,
    EF_OPENED,
    EF_CLOSED,
    EF_FAILURE
  };

  ExperimentFile (Experiment *_exp, const char *_fname);
  ~ExperimentFile ();

  bool open (bool new_open = false);

  char *
  get_name ()
  {
    return fname;
  }

  inline int
  get_status ()
  {
    return ef_status;
  }

  char *fgets ();
  void close ();

  FILE *fh;

private:
  Experiment *exp;
  char *fname;
  off64_t offset;
  int bufsz, ef_status;
  char *buffer;
};

class Experiment::ExperimentHandler : public DefaultHandler
{
public:

  ExperimentHandler (Experiment *_exp);
  ~ExperimentHandler ();

  void
  startDocument () { }
  void endDocument ();
  void startElement (char *uri, char *localName, char *qName, Attributes *attrs);
  void endElement (char *uri, char *localName, char *qName);
  void characters (char *ch, int start, int length);

  void
  ignorableWhitespace (char*, int, int) { }
  void
  error (SAXParseException *e);

private:

  enum Element
  {
    EL_NONE,
    EL_EXPERIMENT,
    EL_COLLECTOR,
    EL_SETTING,
    EL_PROCESS,
    EL_SYSTEM,
    EL_EVENT,
    EL_PROFILE,
    EL_DATAPTR,
    EL_PROFDATA,
    EL_PROFPCKT,
    EL_FIELD,
    EL_CPU,
    EL_STATE,
    EL_FREQUENCY,
    EL_POWERM,
    EL_DTRACEFATAL
  };

  static int toInt (Attributes *attrs, const char *atr);
  static char*toStr (Attributes *attrs, const char *atr);
  void pushElem (Element);
  void popElem ();

  Experiment *exp;
  Element curElem;
  Vector<Element> *stack;
  Module *dynfuncModule;
  DataDescriptor *dDscr;
  PacketDescriptor *pDscr;
  PropDescr *propDscr;
  char *text;
  Cmsg_warn mkind;
  int mnum;
  int mec;
};


// HTableSize is the size of smemHTable and instHTable
// omazur: both HTableSize and the hash function haven't been tuned;
static const int HTableSize = 8192;

//-------------------------------------------------- Experiment file handler

Experiment::ExperimentFile::ExperimentFile (Experiment *_exp, const char *_fname)
{
  exp = _exp;
  fh = NULL;
  bufsz = 0;
  buffer = NULL;
  ef_status = EF_NOT_OPENED;
  offset = 0;
  fname = dbe_sprintf (NTXT ("%s/%s"), exp->expt_name, _fname);
}

Experiment::ExperimentFile::~ExperimentFile ()
{
  close ();
  free (buffer);
  free (fname);
}

bool
Experiment::ExperimentFile::open (bool new_open)
{
  if (fh == NULL)
    {
      fh = fopen64 (fname, NTXT ("r"));
      if (fh == NULL)
	{
	  ef_status = EF_FAILURE;
	  return false;
	}
      ef_status = EF_OPENED;
      if (new_open)
	offset = 0;
      if (offset != 0)
	fseeko64 (fh, offset, SEEK_SET);
    }
  return true;
}

char *
Experiment::ExperimentFile::fgets ()
{
  if (bufsz == 0)
    {
      bufsz = 1024;
      buffer = (char *) xmalloc (bufsz);
      buffer[bufsz - 1] = (char) 1; // sentinel
    }
  char *res = ::fgets (buffer, bufsz, fh);
  if (res == NULL)
    return NULL;
  while (buffer[bufsz - 1] == (char) 0)
    {
      int newsz = bufsz + 1024;
      char *newbuf = (char *) xmalloc (newsz);
      memcpy (newbuf, buffer, bufsz);
      free (buffer);
      buffer = newbuf;
      buffer[newsz - 1] = (char) 1; // sentinel
      // we don't care about fgets result here
      ::fgets (buffer + bufsz - 1, newsz - bufsz + 1, fh);
      bufsz = newsz;
    }
  return buffer;
}

void
Experiment::ExperimentFile::close ()
{
  if (fh)
    {
      offset = ftello64 (fh);
      fclose (fh);
      ef_status = EF_CLOSED;
      fh = NULL;
    }
}


//-------------------------------------------------- Experiment XML parser
int
Experiment::ExperimentHandler::toInt (Attributes *attrs, const char *atr)
{
  const char *str = attrs->getValue (atr);
  return str ? atoi (str) : 0;
}

char *
Experiment::ExperimentHandler::toStr (Attributes *attrs, const char *atr)
{
  const char *str = attrs->getValue (atr);
  return dbe_strdup (str ? str : NTXT (""));
}

Experiment::ExperimentHandler::ExperimentHandler (Experiment *_exp)
{
  exp = _exp;
  stack = new Vector<Element>;
  pushElem (EL_NONE);
  dynfuncModule = NULL;
  dDscr = NULL;
  pDscr = NULL;
  propDscr = NULL;
  text = NULL;
  mkind = CMSG_NONE;
  mnum = -1;
  mec = -1;
}

Experiment::ExperimentHandler::~ExperimentHandler ()
{
  delete stack;
  free (text);
}

void
Experiment::ExperimentHandler::endDocument ()
{
  { // SP_TAG_STATE should be used to describe states, but it isn't
    // let's do it here:
    DataDescriptor *dd = exp->getDataDescriptor (DATA_HEAP);
    if (dd != NULL)
      {
	PropDescr *prop = dd->getProp (PROP_HTYPE);
	if (prop != NULL)
	  {
	    char * stateNames [HEAPTYPE_LAST] = HEAPTYPE_STATE_STRINGS;
	    char * stateUNames[HEAPTYPE_LAST] = HEAPTYPE_STATE_USTRINGS;
	    for (int ii = 0; ii < HEAPTYPE_LAST; ii++)
	      prop->addState (ii, stateNames[ii], stateUNames[ii]);
	  }
      }
    dd = exp->getDataDescriptor (DATA_IOTRACE);
    if (dd != NULL)
      {
	PropDescr *prop = dd->getProp (PROP_IOTYPE);
	if (prop != NULL)
	  {
	    char * stateNames [IOTRACETYPE_LAST] = IOTRACETYPE_STATE_STRINGS;
	    char * stateUNames[IOTRACETYPE_LAST] = IOTRACETYPE_STATE_USTRINGS;
	    for (int ii = 0; ii < IOTRACETYPE_LAST; ii++)
	      prop->addState (ii, stateNames[ii], stateUNames[ii]);
	  }
      }
  }
}

void
Experiment::ExperimentHandler::pushElem (Element elem)
{
  curElem = elem;
  stack->append (curElem);
}

void
Experiment::ExperimentHandler::popElem ()
{
  curElem = stack->remove (stack->size () - 1);
}

void
Experiment::ExperimentHandler::startElement (char*, char*, char *qName, Attributes *attrs)
{
  DEBUG_CODE if (DEBUG_SAXPARSER) dump_startElement (qName, attrs);
  if (strcmp (qName, SP_TAG_EXPERIMENT) == 0)
    {
      pushElem (EL_EXPERIMENT);
      const char *str = attrs->getValue (NTXT ("version"));
      if (str != NULL)
	{
	  int major = atoi (str);
	  str = strchr (str, '.');
	  int minor = str ? atoi (str + 1) : 0;
	  exp->exp_maj_version = major;
	  exp->exp_min_version = minor;
	  if (major != SUNPERF_VERNUM || minor != SUNPERF_VERNUM_MINOR)
	    {
	      // not the current version, see if we support some earlier versions
	      if (major < 12)
		{
		  StringBuilder sb;
		  sb.sprintf (GTXT ("*** Error: experiment %s version %d.%d is not supported;\nuse the version of the tools that recorded the experiment to read it"),
			      exp->get_expt_name (), major, minor);
		  // exp->errorq->append( new Emsg(CMSG_FATAL, sb) );
		  exp->status = FAILURE;
		  exp->obsolete = 1;
		  throw new SAXException (sb.toString ());
		}
	    }
	}
    }
  else if (strcmp (qName, SP_TAG_COLLECTOR) == 0)
    pushElem (EL_COLLECTOR);
  else if (strcmp (qName, SP_TAG_SETTING) == 0)
    {
      int found = 0;
      pushElem (EL_SETTING);
      const char *str = attrs->getValue (SP_JCMD_LIMIT);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.limit = atoi (str);
	}
      str = attrs->getValue (SP_JCMD_BLKSZ);
      if (str != NULL)
	{
	  found = 1;
	  exp->blksz = strtol (str, NULL, 0);
	}
      str = attrs->getValue (SP_JCMD_STACKBASE);
      if (str)
	{
	  found = 1;
	  exp->stack_base = strtoull (str, NULL, 0);
	}
      str = attrs->getValue (SP_JCMD_HWC_DEFAULT);
      if (str != NULL)
	{
	  found = 1;
	  exp->hwc_default = true;
	}
      str = attrs->getValue (SP_JCMD_NOIDLE);
      if (str != NULL)
	{
	  found = 1;
	  exp->commentq->append (new Emsg (CMSG_COMMENT,
					   GTXT ("*** Note: experiment does not have events from idle CPUs")));
	}
      str = attrs->getValue (SP_JCMD_FAKETIME);
      if (str != NULL)
	{
	  found = 1;
	  exp->timelineavail = false;
	  exp->commentq->append (new Emsg (CMSG_COMMENT,
					   GTXT ("*** Note: experiment does not have timestamps; timeline unavailable")));
	}
      str = attrs->getValue (SP_JCMD_DELAYSTART);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.start_delay = xstrdup (str);
	}
      str = attrs->getValue (SP_JCMD_TERMINATE);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.terminate = xstrdup (str);
	}
      str = attrs->getValue (SP_JCMD_PAUSE_SIG);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.pause_sig = xstrdup (str);
	}
      str = attrs->getValue (SP_JCMD_SAMPLE_PERIOD);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.sample_periodic = 1;
	  exp->coll_params.sample_timer = atoi (str);
	}
      str = attrs->getValue (SP_JCMD_SAMPLE_SIG);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.sample_sig = str;
	}
      str = attrs->getValue (SP_JCMD_SRCHPATH);
      if (str != NULL)
	{
	  found = 1;
	  StringBuilder sb;
	  sb.sprintf (GTXT ("Search path: %s"), str);
	  exp->runlogq->append (new Emsg (CMSG_COMMENT, sb));
	  dbeSession->add_classpath ((char*) str);
	}
      str = attrs->getValue (SP_JCMD_LINETRACE);
      if (str != NULL)
	{
	  found = 1;
	  exp->coll_params.linetrace = xstrdup (str);
	}

      str = attrs->getValue (SP_JCMD_COLLENV);
      if (str != NULL)
	{
	  found = 1;
	  StringBuilder sb;
	  sb.sprintf (GTXT ("  Data collection environment variable: %s"), str);
	  exp->runlogq->append (new Emsg (CMSG_COMMENT, sb));
	}
      if (found == 0)
	{
	  int nattr = attrs->getLength ();
	  if (nattr != 0)
	    {
	      fprintf (stderr, "XXX Unexpected setting found; %d attributes:\n",
		       nattr);
	      for (int k = 0; k < nattr; k++)
		{
		  const char *qn = attrs->getQName (k);
		  const char *vl = attrs->getValue (k);
		  fprintf (stderr, "XXX      %s = %s\n", qn, vl);
		}
	    }
	}
      // END OF CODE FOR "setting"
    }
  else if (strcmp (qName, SP_TAG_SYSTEM) == 0)
    {
      pushElem (EL_SYSTEM);
      const char *str = attrs->getValue (NTXT ("hostname"));
      if (str != NULL)
	exp->hostname = xstrdup (str);
      str = attrs->getValue (NTXT ("os"));
      if (str != NULL)
	{
	  exp->os_version = xstrdup (str);
	  /* For Linux experiments expect sparse thread ID's */
	  if (strncmp (str, NTXT ("SunOS"), 5) != 0)
	    exp->sparse_threads = true;
	}
      str = attrs->getValue (NTXT ("arch"));
      if (str != NULL)
	{
	  if (strcmp (str, "i86pc") == 0 || strcmp (str, "i686") == 0
	      || strcmp (str, "x86_64") == 0)
	    exp->platform = Intel;
	  else if (strcmp (str, "aarch64") == 0)
	    exp->platform = Aarch64;
	  else if (strcmp (str, "riscv64") == 0)
	    exp->platform = RISCV;
	  else
	    exp->platform = Sparc;
	  exp->architecture = xstrdup (str);
	}
      str = attrs->getValue (NTXT ("bigendian"));
      if (str != NULL)
	{
	  exp->bigendian = *str == '1';
	  exp->need_swap_endian = DbeSession::is_bigendian () != exp->bigendian;
	}
      str = attrs->getValue (NTXT ("pagesz"));
      if (str != NULL)
	exp->page_size = atoi (str);
      str = attrs->getValue (NTXT ("npages"));
      if (str != NULL)
	exp->npages = atoi (str);
    }
  else if (strcmp (qName, SP_TAG_POWERM) == 0)
    pushElem (EL_POWERM);
  else if (strcmp (qName, SP_TAG_FREQUENCY) == 0)
    {
      pushElem (EL_FREQUENCY);
      const char *str = attrs->getValue (NTXT ("clk"));
      if (str != NULL)
	exp->set_clock (atoi (str));
      // check for frequency_scaling or turbo_mode recorded from libcollector under dbx
      str = attrs->getValue (NTXT ("frequency_scaling"));
      const char *str2 = attrs->getValue (NTXT ("turbo_mode"));
      if (str != NULL || str2 != NULL)
	exp->varclock = 1;
    }
  else if (strcmp (qName, SP_TAG_CPU) == 0)
    {
      pushElem (EL_CPU);
      exp->ncpus++;
      const char *str = attrs->getValue (NTXT ("clk"));
      if (str != NULL)
	{
	  int clk = atoi (str);
	  if (exp->maxclock == 0)
	    {
	      exp->minclock = clk;
	      exp->maxclock = clk;
	    }
	  else
	    {
	      if (clk < exp->minclock)
		exp->minclock = clk;
	      if (clk > exp->maxclock)
		exp->maxclock = clk;
	    }
	  exp->clock = clk;
	}
      // check for frequency_scaling or turbo_mode
      str = attrs->getValue (NTXT ("frequency_scaling"));
      const char *str2 = attrs->getValue (NTXT ("turbo_mode"));
      if (str != NULL || str2 != NULL)
	exp->varclock = 1;
    }
  else if (strcmp (qName, SP_TAG_PROCESS) == 0)
    {
      pushElem (EL_PROCESS);
      const char *str = attrs->getValue (NTXT ("wsize"));
      if (str != NULL)
	{
	  int wsz = atoi (str);
	  if (wsz == 32)
	    exp->wsize = W32;
	  else if (wsz == 64)
	    exp->wsize = W64;
	}
      str = attrs->getValue (NTXT ("pid"));
      if (str != NULL)
	exp->pid = atoi (str);
      str = attrs->getValue (NTXT ("ppid"));
      if (str != NULL)
	exp->ppid = atoi (str);
      str = attrs->getValue (NTXT ("pgrp"));
      if (str != NULL)
	exp->pgrp = atoi (str);
      str = attrs->getValue (NTXT ("sid"));
      if (str != NULL)
	exp->sid = atoi (str);
      str = attrs->getValue (NTXT ("cwd"));
      if (str != NULL)
	exp->ucwd = xstrdup (str);
      str = attrs->getValue (NTXT ("pagesz"));
      if (str != NULL)
	exp->page_size = atoi (str);
    }
  else if (strcmp (qName, SP_TAG_EVENT) == 0)
    { // Start code for event
      pushElem (EL_EVENT);
      hrtime_t ts = (hrtime_t) 0;
      const char *str = attrs->getValue (NTXT ("tstamp"));
      if (str != NULL)
	ts = parseTStamp (str);
      str = attrs->getValue (NTXT ("kind"));
      if (str != NULL)
	{
	  if (strcmp (str, SP_JCMD_RUN) == 0)
	    {
	      exp->broken = 0;
	      exp->exp_start_time = ts;
	      str = attrs->getValue (NTXT ("time"));
	      if (str != NULL)
		exp->start_sec = atoll (str);
	      str = attrs->getValue (NTXT ("pid"));
	      if (str != NULL)
		exp->pid = atoi (str);
	      str = attrs->getValue (NTXT ("ppid"));
	      if (str != NULL)
		exp->ppid = atoi (str);
	      str = attrs->getValue (NTXT ("pgrp"));
	      if (str != NULL)
		exp->pgrp = atoi (str);
	      str = attrs->getValue (NTXT ("sid"));
	      if (str != NULL)
		exp->sid = atoi (str);
	      exp->status = Experiment::INCOMPLETE;
	    }
	  else if (strcmp (str, SP_JCMD_ARCHIVE) == 0)
	    {
	      StringBuilder sb;
	      sb.sprintf (GTXT ("gprofng-archive run: XXXXXXX"));
	      exp->pprocq->append (new Emsg (CMSG_WARN, sb));
	    }
	  else if (strcmp (str, SP_JCMD_SAMPLE) == 0)
	    {
	      exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based
	      str = attrs->getValue (NTXT ("id"));
	      int id = str ? atoi (str) : -1;
	      char *label = dbe_strdup (attrs->getValue (NTXT ("label")));
	      exp->process_sample_cmd (NULL, ts, id, label);
	    }
	  else if (strcmp (str, SP_JCMD_EXIT) == 0)
	    {
	      // don't treat EXIT as an event w.r.t. last_event and non_paused_time
	      exp->status = Experiment::SUCCESS;
	    }
	  else if (strcmp (str, SP_JCMD_CERROR) == 0)
	    {
	      mkind = CMSG_ERROR;
	      str = attrs->getValue (NTXT ("id"));
	      if (str != NULL)
		{
		  mnum = atoi (str);
		}
	      str = attrs->getValue (NTXT ("ec"));
	      if (str != NULL)
		{
		  mec = atoi (str);
		}
	    }
	  else if (strcmp (str, SP_JCMD_CWARN) == 0)
	    {
	      mkind = CMSG_WARN;
	      str = attrs->getValue (NTXT ("id"));
	      if (str != NULL)
		mnum = atoi (str);
	    }
	  else if (strcmp (str, SP_JCMD_COMMENT) == 0)
	    {
	      mkind = CMSG_COMMENT;
	      str = attrs->getValue (NTXT ("id"));
	      if (str != NULL)
		mnum = atoi (str);
	      str = attrs->getValue (NTXT ("text"));
	      if (str != NULL)
		{
		  StringBuilder sb;
		  sb.sprintf (GTXT ("*** Note: %s"), str);
		  exp->commentq->append (new Emsg (CMSG_COMMENT, sb));
		}
	    }
	  else if (strcmp (str, SP_JCMD_DESC_START) == 0)
	    {
	      char *variant = toStr (attrs, NTXT ("variant"));
	      char *lineage = toStr (attrs, NTXT ("lineage"));
	      int follow = toInt (attrs, NTXT ("follow"));
	      char *msg = toStr (attrs, NTXT ("msg"));
	      exp->process_desc_start_cmd (NULL, ts, variant, lineage, follow, msg);
	    }
	  else if (strcmp (str, SP_JCMD_DESC_STARTED) == 0)
	    {
	      char *variant = toStr (attrs, NTXT ("variant"));
	      char *lineage = toStr (attrs, NTXT ("lineage"));
	      int follow = toInt (attrs, NTXT ("follow"));
	      char *msg = toStr (attrs, NTXT ("msg"));
	      exp->process_desc_started_cmd (NULL, ts, variant, lineage, follow, msg);
	    }
	  else if (strcmp (str, SP_JCMD_EXEC_START) == 0)
	    {
	      // if successful, acts like experiment termination - no "exit" entry will follow
	      exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based
	      char *variant = toStr (attrs, NTXT ("variant"));
	      char *lineage = toStr (attrs, NTXT ("lineage"));
	      int follow = toInt (attrs, NTXT ("follow"));
	      char *msg = toStr (attrs, NTXT ("msg"));
	      exp->process_desc_start_cmd (NULL, ts, variant, lineage, follow, msg);
	      exp->exec_started = true;
	    }
	  else if (strcmp (str, SP_JCMD_EXEC_ERROR) == 0)
	    {
	      exp->update_last_event (exp->exp_start_time + ts); // ts is 0-based
	      char *variant = toStr (attrs, NTXT ("variant"));
	      char *lineage = toStr (attrs, NTXT ("lineage"));
	      int follow = toInt (attrs, NTXT ("follow"));
	      char *msg = toStr (attrs, NTXT ("msg"));
	      exp->process_desc_started_cmd (NULL, ts, variant, lineage, follow, msg);
	      exp->exec_started = false;
	    }
	  else if (strcmp (str, SP_JCMD_JTHRSTART) == 0)
	    {
	      char *name = dbe_strdup (attrs->getValue (NTXT ("name")));
	      char *grpname = dbe_strdup (attrs->getValue (NTXT ("grpname")));
	      char *prntname = dbe_strdup (attrs->getValue (NTXT ("prntname")));
	      str = attrs->getValue (NTXT ("tid"));
	      uint64_t tid = str ? strtoull (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("jthr"));
	      Vaddr jthr = str ? strtoull (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("jenv"));
	      Vaddr jenv = str ? strtoull (str, NULL, 0) : 0;
	      exp->process_jthr_start_cmd (NULL, name, grpname, prntname, tid, jthr, jenv, ts);
	    }
	  else if (strcmp (str, SP_JCMD_JTHREND) == 0)
	    {
	      str = attrs->getValue (NTXT ("tid"));
	      uint64_t tid = str ? strtoull (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("jthr"));
	      Vaddr jthr = str ? strtoull (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("jenv"));
	      Vaddr jenv = str ? strtoull (str, NULL, 0) : 0;
	      exp->process_jthr_end_cmd (NULL, tid, jthr, jenv, ts);
	    }
	  else if (strcmp (str, SP_JCMD_GCEND) == 0)
	    {
	      if (exp->getDataDescriptor (DATA_GCEVENT) == NULL)
		exp->newDataDescriptor (DATA_GCEVENT);
	      exp->process_gc_end_cmd (ts);
	    }
	  else if (strcmp (str, SP_JCMD_GCSTART) == 0)
	    {
	      if (exp->getDataDescriptor (DATA_GCEVENT) == NULL)
		exp->newDataDescriptor (DATA_GCEVENT);
	      exp->process_gc_start_cmd (ts);
	    }
	  else if (strcmp (str, SP_JCMD_PAUSE) == 0)
	    {
	      if (exp->resume_ts != MAX_TIME)
		{
		  // data collection was active
		  hrtime_t delta = ts - exp->resume_ts;
		  exp->non_paused_time += delta;
		  exp->resume_ts = MAX_TIME; // collection is paused
		}
	      StringBuilder sb;
	      str = attrs->getValue (NTXT ("name"));
	      if (str == NULL)
		sb.sprintf (GTXT ("Pause: %ld.%09ld"), (long) (ts / NANOSEC),
			    (long) (ts % NANOSEC));
	      else
		sb.sprintf (GTXT ("Pause (%s): %ld.%09ld"), str,
			    (long) (ts / NANOSEC), (long) (ts % NANOSEC));
	      exp->runlogq->append (new Emsg (CMSG_COMMENT, sb));
	    }
	  else if (strcmp (str, SP_JCMD_RESUME) == 0)
	    {
	      if (exp->resume_ts == MAX_TIME)
		// data collection was paused
		exp->resume_ts = ts; // remember start time
	      StringBuilder sb;
	      sb.sprintf (GTXT ("Resume: %ld.%09ld"), (long) (ts / NANOSEC), (long) (ts % NANOSEC));
	      exp->runlogq->append (new Emsg (CMSG_COMMENT, sb));
	      if (exp->exp_start_time == ZERO_TIME)
		exp->exp_start_time = ts;
	    }
	  else if (strcmp (str, SP_JCMD_THREAD_PAUSE) == 0)
	    {
	      str = attrs->getValue (NTXT ("tid"));
	      uint64_t tid = str ? strtoull (str, NULL, 0) : 0;
	      StringBuilder sb;
	      sb.sprintf (GTXT ("Thread %llu pause: %ld.%09ld"), (unsigned long long) tid,
			  (long) (ts / NANOSEC), (long) (ts % NANOSEC));
	      exp->runlogq->append (new Emsg (CMSG_COMMENT, sb));
	    }
	  else if (strcmp (str, SP_JCMD_THREAD_RESUME) == 0)
	    {
	      str = attrs->getValue (NTXT ("tid"));
	      uint64_t tid = str ? strtoull (str, NULL, 0) : 0;
	      StringBuilder sb;
	      sb.sprintf (GTXT ("Thread %llu resume: %ld.%09ld"), (unsigned long long) tid,
			  (long) (ts / NANOSEC), (long) (ts % NANOSEC));
	      exp->runlogq->append (new Emsg (CMSG_COMMENT, sb));
	    }
	  else if (strcmp (str, NTXT ("map")) == 0)
	    {
	      ts += exp->exp_start_time;
	      str = attrs->getValue (NTXT ("vaddr"));
	      Vaddr vaddr = str ? strtoull (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("size"));
	      int msize = str ? atoi (str) : 0;
	      str = attrs->getValue (NTXT ("foffset"));
	      int64_t offset = str ? strtoll (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("modes"));
	      int64_t modes = str ? strtoll (str, NULL, 0) : 0;
	      str = attrs->getValue (NTXT ("chksum"));
	      int64_t chksum = 0;
	      if (str)
		chksum = Elf::normalize_checksum (strtoll (str, NULL, 0));
	      char *name = (char *) attrs->getValue (NTXT ("name"));
	      str = attrs->getValue (NTXT ("object"));
	      if (strcmp (str, NTXT ("segment")) == 0)
		{
		  if (strcmp (name, NTXT ("LinuxKernel")) == 0)
		    exp->process_Linux_kernel_cmd (ts);
		  else
		    exp->process_seg_map_cmd (NULL, ts, vaddr, msize, 0,
					      offset, modes, chksum, name);
		}
	      else if (strcmp (str, NTXT ("function")) == 0)
		{
		  exp->process_fn_load_cmd (dynfuncModule, name, vaddr, msize, ts);
		  dynfuncModule = NULL;
		}
	      else if (strcmp (str, NTXT ("dynfunc")) == 0)
		{
		  if (dynfuncModule == NULL)
		    {
		      dynfuncModule = dbeSession->createModule (exp->get_dynfunc_lo (DYNFUNC_SEGMENT), name);
		      dynfuncModule->flags |= MOD_FLAG_UNKNOWN;
		      dynfuncModule->set_file_name (dbe_strdup (dynfuncModule->getMainSrc ()->get_name ()));
		    }
		  (void) exp->create_dynfunc (dynfuncModule,
					      (char*) attrs->getValue (NTXT ("funcname")), vaddr, msize);
		}
	      else if (strcmp (str, NTXT ("jcm")) == 0)
		{
		  str = attrs->getValue (NTXT ("methodId"));
		  Vaddr mid = str ? strtoull (str, NULL, 0) : 0;
		  exp->process_jcm_load_cmd (NULL, mid, vaddr, msize, ts);
		}
	    }
	  else if (strcmp (str, NTXT ("unmap")) == 0)
	    {
	      ts += exp->exp_start_time;
	      str = attrs->getValue (NTXT ("vaddr"));
	      Vaddr vaddr = str ? strtoull (str, NULL, 0) : 0;
	      exp->process_seg_unmap_cmd (NULL, ts, vaddr);
	    }
	}
      // end of code for event
    }
  else if (strcmp (qName, SP_TAG_PROFILE) == 0)
    {
      pushElem (EL_PROFILE);
      const char *str = attrs->getValue (NTXT ("name"));
      if (str == NULL)
	return;
      if (strcmp (str, NTXT ("profile")) == 0)
	{
	  exp->coll_params.profile_mode = 1;
	  str = attrs->getValue (NTXT ("numstates"));
	  if (str != NULL)
	    exp->coll_params.lms_magic_id = atoi (str);
	  str = attrs->getValue (NTXT ("ptimer"));
	  if (str != NULL)
	    exp->coll_params.ptimer_usec = atoi (str); // microseconds

	  PropDescr *mstate_prop = NULL;
	  char * stateNames [/*LMS_NUM_STATES*/] = LMS_STATE_STRINGS;
	  char * stateUNames[/*LMS_NUM_STATES*/] = LMS_STATE_USTRINGS;
	  {
	    dDscr = exp->newDataDescriptor (DATA_CLOCK);
	    PropDescr *prop = new PropDescr (PROP_MSTATE, NTXT ("MSTATE"));
	    prop->uname = dbe_strdup (GTXT ("Thread state"));
	    prop->vtype = TYPE_UINT32;
	    // (states added below)
	    dDscr->addProperty (prop);
	    mstate_prop = prop;

	    prop = new PropDescr (PROP_NTICK, NTXT ("NTICK"));
	    prop->uname = dbe_strdup (GTXT ("Number of Profiling Ticks"));
	    prop->vtype = TYPE_UINT32;
	    dDscr->addProperty (prop);
	  }

	  switch (exp->coll_params.lms_magic_id)
	    {
	    case LMS_MAGIC_ID_SOLARIS:
	      exp->register_metric (Metric::CP_TOTAL);
	      exp->register_metric (Metric::CP_TOTAL_CPU);
	      exp->register_metric (Metric::CP_LMS_USER);
	      exp->register_metric (Metric::CP_LMS_SYSTEM);
	      exp->register_metric (Metric::CP_LMS_TRAP);
	      exp->register_metric (Metric::CP_LMS_DFAULT);
	      exp->register_metric (Metric::CP_LMS_TFAULT);
	      exp->register_metric (Metric::CP_LMS_KFAULT);
	      exp->register_metric (Metric::CP_LMS_STOPPED);
	      exp->register_metric (Metric::CP_LMS_WAIT_CPU);
	      exp->register_metric (Metric::CP_LMS_SLEEP);
	      exp->register_metric (Metric::CP_LMS_USER_LOCK);
	      for (int ii = 0; ii < LMS_NUM_SOLARIS_MSTATES; ii++)
		mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]);
	      break;
	    case LMS_MAGIC_ID_ERKERNEL_KERNEL:
	      exp->register_metric (Metric::CP_KERNEL_CPU);
	      {
		int ii = LMS_KERNEL_CPU;
		mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]);
	      }
	      break;
	    case LMS_MAGIC_ID_ERKERNEL_USER:
	      exp->register_metric (Metric::CP_TOTAL_CPU);
	      exp->register_metric (Metric::CP_LMS_USER);
	      exp->register_metric (Metric::CP_LMS_SYSTEM);
	      {
		int ii = LMS_KERNEL_CPU;
		mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]);
		ii = LMS_USER;
		mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]);
		ii = LMS_SYSTEM;
		mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]);
	      }
	      break;
	    case LMS_MAGIC_ID_LINUX:
	      exp->register_metric (Metric::CP_TOTAL_CPU);
	      {
		int ii = LMS_LINUX_CPU;
		mstate_prop->addState (ii, stateNames[ii], stateUNames[ii]);
	      }
	      break;
	    default:
	      // odd
	      break;
	    }
	}
      else if (strcmp (str, NTXT ("heaptrace")) == 0)
	{
	  exp->coll_params.heap_mode = 1;
	  exp->leaklistavail = true;
	  exp->heapdataavail = true;
	  exp->register_metric (Metric::HEAP_ALLOC_BYTES);
	  exp->register_metric (Metric::HEAP_ALLOC_CNT);
	  exp->register_metric (Metric::HEAP_LEAK_BYTES);
	  exp->register_metric (Metric::HEAP_LEAK_CNT);
	  dDscr = exp->newDataDescriptor (DATA_HEAP);
	}
      else if (strcmp (str, NTXT ("iotrace")) == 0)
	{
	  exp->coll_params.io_mode = 1;
	  exp->iodataavail = true;
	  exp->register_metric (Metric::IO_READ_TIME);
	  exp->register_metric (Metric::IO_READ_BYTES);
	  exp->register_metric (Metric::IO_READ_CNT);
	  exp->register_metric (Metric::IO_WRITE_TIME);
	  exp->register_metric (Metric::IO_WRITE_BYTES);
	  exp->register_metric (Metric::IO_WRITE_CNT);
	  exp->register_metric (Metric::IO_OTHER_TIME);
	  exp->register_metric (Metric::IO_OTHER_CNT);
	  exp->register_metric (Metric::IO_ERROR_TIME);
	  exp->register_metric (Metric::IO_ERROR_CNT);
	  dDscr = exp->newDataDescriptor (DATA_IOTRACE);
	}
      else if (strcmp (str, NTXT ("synctrace")) == 0)
	{
	  exp->coll_params.sync_mode = 1;
	  str = attrs->getValue (NTXT ("threshold"));
	  if (str != NULL)
	    exp->coll_params.sync_threshold = atoi (str);
	  str = attrs->getValue (NTXT ("scope"));
	  if (str != NULL)
	    exp->coll_params.sync_scope = atoi (str);
	  else  // Should only happen with old experiments; use the old default
	    exp->coll_params.sync_scope = SYNCSCOPE_NATIVE | SYNCSCOPE_JAVA;
	  exp->register_metric (Metric::SYNC_WAIT_TIME);
	  exp->register_metric (Metric::SYNC_WAIT_COUNT);
	  dDscr = exp->newDataDescriptor (DATA_SYNCH);
	}
      else if (strcmp (str, NTXT ("omptrace")) == 0)
	{
	  exp->coll_params.omp_mode = 1;
	  dDscr = exp->newDataDescriptor (DATA_OMP, DDFLAG_NOSHOW);
	}
      else if (strcmp (str, NTXT ("hwcounter")) == 0)
	{
	  str = attrs->getValue (NTXT ("cpuver"));
	  int cpuver = str ? atoi (str) : 0;
	  char *counter = dbe_strdup (attrs->getValue (NTXT ("hwcname")));
	  char *int_name = dbe_strdup (attrs->getValue (NTXT ("int_name"))); // may not be present
	  str = attrs->getValue (NTXT ("interval"));
	  int interval = str ? atoi (str) : 0;
	  str = attrs->getValue (NTXT ("tag"));
	  int tag = str ? atoi (str) : 0;
	  str = attrs->getValue (NTXT ("memop"));
	  int i_tpc = str ? atoi (str) : 0;
	  char *modstr = dbe_strdup (attrs->getValue (NTXT ("modstr")));
	  exp->process_hwcounter_cmd (NULL, cpuver, counter, int_name, interval, tag, i_tpc, modstr);
	  dDscr = exp->newDataDescriptor (DATA_HWC);
	}
      else if (strcmp (str, NTXT ("hwsimctr")) == 0)
	{
	  int cpuver = toInt (attrs, NTXT ("cpuver"));
	  char *hwcname = dbe_strdup (attrs->getValue (NTXT ("hwcname")));
	  char *int_name = dbe_strdup (attrs->getValue (NTXT ("int_name")));
	  char *metric = dbe_strdup (attrs->getValue (NTXT ("metric")));
	  int reg = toInt (attrs, NTXT ("reg_num"));
	  int interval = toInt (attrs, NTXT ("interval"));
	  int timecvt = toInt (attrs, NTXT ("timecvt"));
	  int i_tpc = toInt (attrs, NTXT ("memop"));
	  int tag = toInt (attrs, NTXT ("tag"));
	  exp->process_hwsimctr_cmd (NULL, cpuver, hwcname, int_name, metric, reg,
				     interval, timecvt, i_tpc, tag);
	  dDscr = exp->newDataDescriptor (DATA_HWC);
	}
      else if (strcmp (str, NTXT ("dversion")) == 0)
	exp->dversion = dbe_strdup (attrs->getValue (NTXT ("version")));
      else if (strcmp (str, NTXT ("jprofile")) == 0)
	{
	  exp->has_java = true;
	  str = attrs->getValue (NTXT ("jversion"));
	  if (str != NULL)
	    exp->jversion = xstrdup (str);
	}
      else if (strcmp (str, NTXT ("datarace")) == 0)
	{
	  exp->coll_params.race_mode = 1;
	  exp->racelistavail = true;
	  str = attrs->getValue (NTXT ("scheme"));
	  exp->coll_params.race_stack = str ? atoi (str) : 0;
	  exp->register_metric (Metric::RACCESS);
	  dDscr = exp->newDataDescriptor (DATA_RACE);
	}
      else if (strcmp (str, NTXT ("deadlock")) == 0)
	{
	  exp->coll_params.deadlock_mode = 1;
	  exp->deadlocklistavail = true;
	  exp->register_metric (Metric::DEADLOCKS);
	  dDscr = exp->newDataDescriptor (DATA_DLCK);
	}
    }
    /* XXX -- obsolete tag, but is still written to experiments */
  else if (strcmp (qName, SP_TAG_DATAPTR) == 0)
    {
      pushElem (EL_DATAPTR);
      return;
    }
  else if (strcmp (qName, SP_TAG_PROFDATA) == 0)
    {
      pushElem (EL_PROFDATA);
      // SS12 HWC experiments are not well structured
      const char *fname = attrs->getValue (NTXT ("fname"));
      if (fname && strcmp (fname, SP_HWCNTR_FILE) == 0)
	dDscr = exp->newDataDescriptor (DATA_HWC);
    }
  else if (strcmp (qName, SP_TAG_PROFPCKT) == 0)
    {
      pushElem (EL_PROFPCKT);
      const char *str = attrs->getValue (NTXT ("kind")); // see Pckt_type
      int kind = str ? atoi (str) : -1;
      if (kind < 0)
	return;
      if (exp->coll_params.omp_mode == 1)
	{
	  if (kind == OMP_PCKT)
	    dDscr = exp->newDataDescriptor (DATA_OMP, DDFLAG_NOSHOW);
	  else if (kind == OMP2_PCKT)
	    dDscr = exp->newDataDescriptor (DATA_OMP2, DDFLAG_NOSHOW);
	  else if (kind == OMP3_PCKT)
	    dDscr = exp->newDataDescriptor (DATA_OMP3, DDFLAG_NOSHOW);
	  else if (kind == OMP4_PCKT)
	    dDscr = exp->newDataDescriptor (DATA_OMP4, DDFLAG_NOSHOW);
	  else if (kind == OMP5_PCKT)
	    dDscr = exp->newDataDescriptor (DATA_OMP5, DDFLAG_NOSHOW);
	}
      pDscr = exp->newPacketDescriptor (kind, dDscr);
      return;
    }
  else if (strcmp (qName, SP_TAG_FIELD) == 0)
    {
      pushElem (EL_FIELD);
      if (pDscr != NULL)
	{
	  const char *name = attrs->getValue (NTXT ("name"));
	  if (name == NULL)
	    return;
	  int propID = dbeSession->registerPropertyName (name);
	  propDscr = new PropDescr (propID, name);
	  FieldDescr *fldDscr = new FieldDescr (propID, name);

	  const char *str = attrs->getValue (NTXT ("type"));
	  if (str)
	    {
	      if (strcmp (str, NTXT ("INT32")) == 0)
		fldDscr->vtype = TYPE_INT32;
	      else if (strcmp (str, NTXT ("UINT32")) == 0)
		fldDscr->vtype = TYPE_UINT32;
	      else if (strcmp (str, NTXT ("INT64")) == 0)
		fldDscr->vtype = TYPE_INT64;
	      else if (strcmp (str, NTXT ("UINT64")) == 0)
		fldDscr->vtype = TYPE_UINT64;
	      else if (strcmp (str, NTXT ("STRING")) == 0)
		fldDscr->vtype = TYPE_STRING;
	      else if (strcmp (str, NTXT ("DOUBLE")) == 0)
		fldDscr->vtype = TYPE_DOUBLE;
	      else if (strcmp (str, NTXT ("DATE")) == 0)
		{
		  fldDscr->vtype = TYPE_DATE;
		  const char *fmt = attrs->getValue (NTXT ("format"));
		  fldDscr->format = xstrdup (fmt ? fmt : "");
		}
	    }
	  propDscr->vtype = fldDscr->vtype;

	  // TYPE_DATE is converted to TYPE_UINT64 in propDscr
	  if (fldDscr->vtype == TYPE_DATE)
	    propDscr->vtype = TYPE_UINT64;

	  // Fix some types until they are fixed in libcollector
	  if (propID == PROP_VIRTPC || propID == PROP_PHYSPC)
	    {
	      if (fldDscr->vtype == TYPE_INT32)
		propDscr->vtype = TYPE_UINT32;
	      else if (fldDscr->vtype == TYPE_INT64)
		propDscr->vtype = TYPE_UINT64;
	    }

	  // The following props get mapped to 32-bit values in readPacket
	  if (propID == PROP_CPUID || propID == PROP_THRID
	      || propID == PROP_LWPID)
	    propDscr->vtype = TYPE_UINT32; // override experiment property

	  str = attrs->getValue (NTXT ("uname"));
	  if (str)
	    propDscr->uname = xstrdup (PTXT ((char*) str));
	  str = attrs->getValue (NTXT ("noshow"));
	  if (str && atoi (str) != 0)
	    propDscr->flags |= PRFLAG_NOSHOW;

	  if (dDscr == NULL)
	    {
	      StringBuilder sb;
	      sb.sprintf (GTXT ("*** Error: data parsing failed. Log file is corrupted."));
	      exp->warnq->append (new Emsg (CMSG_ERROR, sb));
	      throw new SAXException (sb.toString ());
	    }

	  dDscr->addProperty (propDscr);
	  str = attrs->getValue (NTXT ("offset"));
	  if (str)
	    fldDscr->offset = atoi (str);
	  pDscr->addField (fldDscr);
	}
    }
  else if (strcmp (qName, SP_TAG_STATE) == 0)
    {
      pushElem (EL_STATE);
      if (propDscr != NULL)
	{
	  const char *str = attrs->getValue (NTXT ("value"));
	  int value = str ? atoi (str) : -1;
	  str = attrs->getValue (NTXT ("name"));
	  const char *ustr = attrs->getValue (NTXT ("uname"));
	  propDscr->addState (value, str, ustr);
	}
    }
  else if (strcmp (qName, SP_TAG_DTRACEFATAL) == 0)
    pushElem (EL_DTRACEFATAL);
  else
    {
      StringBuilder sb;
      sb.sprintf (GTXT ("*** Warning: unrecognized element %s"), qName);
      exp->warnq->append (new Emsg (CMSG_WARN, sb));
      pushElem (EL_NONE);
    }
}

void
Experiment::ExperimentHandler::characters (char *ch, int start, int length)
{
  switch (curElem)
    {
    case EL_COLLECTOR:
      exp->cversion = dbe_strndup (ch + start, length);
      break;
    case EL_PROCESS:
      exp->process_arglist_cmd (NULL, dbe_strndup (ch + start, length));
      break;
    case EL_EVENT:
      free (text);
      text = dbe_strndup (ch + start, length);
      break;
    default:
      break;
    }
}

void
Experiment::ExperimentHandler::endElement (char*, char*, char*)
{
  if (curElem == EL_EVENT && mkind != CMSG_NONE && mnum >= 0)
    {
      char *str;
      if (mec > 0)
	str = dbe_sprintf ("%s -- %s", text != NULL ? text : "", strerror (mec));
      else
	str = dbe_sprintf ("%s", text != NULL ? text : "");
      Emsg *msg = new Emsg (mkind, mnum, str);
      if (mkind == CMSG_WARN)
	{
	  if (mnum != COL_WARN_FSTYPE
	      || dbeSession->check_ignore_fs_warn () == false)
	    exp->warnq->append (msg);
	  else
	    exp->commentq->append (msg);
	}
      else if (mkind == CMSG_ERROR || mkind == CMSG_FATAL)
	exp->errorq->append (msg);
      else if (mkind == CMSG_COMMENT)
	exp->commentq->append (msg);
      else
	delete msg;
      mkind = CMSG_NONE;
      mnum = -1;
      mec = -1;
    }
  else if (curElem == EL_PROFILE)
    dDscr = NULL;
  else if (curElem == EL_PROFPCKT)
    pDscr = NULL;
  else if (curElem == EL_FIELD)
    propDscr = NULL;
  free (text);
  text = NULL;
  popElem ();
}

void
Experiment::ExperimentHandler::error (SAXParseException *e)
{
  StringBuilder sb;
  sb.sprintf (GTXT ("%s at line %d, column %d"),
	      e->getMessage (), e->getLineNumber (), e->getColumnNumber ());
  char *msg = sb.toString ();
  SAXException *e1 = new SAXException (msg);
  free (msg);
  throw ( e1);
}

//-------------------------------------------------- Experiment

Experiment::Experiment ()
{
  groupId = 0;
  userExpId = expIdx = -1;
  founder_exp = NULL;
  baseFounder = NULL;
  children_exps = new Vector<Experiment*>;
  loadObjs = new Vector<LoadObject*>;
  loadObjMap = new StringMap<LoadObject*>(128, 128);
  sourcesMap = NULL;

  // Initialize configuration information.
  status = FAILURE;
  start_sec = 0;
  mtime = 0;
  hostname = NULL;
  username = NULL;
  architecture = NULL;
  os_version = NULL;
  uarglist = NULL;
  utargname = NULL;
  ucwd = NULL;
  cversion = NULL;
  dversion = NULL;
  jversion = NULL;
  exp_maj_version = 0;
  exp_min_version = 0;
  platform = Unknown;
  bigendian = DbeSession::is_bigendian();  // can be changed in log.xml reading
  wsize = Wnone;
  page_size = 4096;
  npages = 0;
  stack_base = 0xf0000000;
  broken = 1;
  obsolete = 0;
  hwc_bogus = 0;
  hwc_lost_int = 0;
  hwc_scanned = 0;
  hwc_default = false;
  invalid_packet = 0;

  // clear HWC event stats
  dsevents = 0;
  dsnoxhwcevents = 0;

  memset (&coll_params, 0, sizeof (coll_params));
  ncpus = 0;
  minclock = 0;
  maxclock = 0;
  clock = 0;
  varclock = 0;
  exec_started = false;
  timelineavail = true;
  leaklistavail = false;
  heapdataavail = false;
  iodataavail = false;
  dataspaceavail = false;
  ifreqavail = false;
  racelistavail = false;
  deadlocklistavail = false;
  ompavail = false;
  tiny_threshold = -1;
  pid = 0;
  ppid = 0;
  pgrp = 0;
  sid = 0;

  gc_duration = ZERO_TIME;
  exp_start_time = ZERO_TIME; // not known.  Wall-clock hrtime (not zero based)
  last_event = ZERO_TIME; // not known.  Wall-clock hrtime (not zero based)
  non_paused_time = 0; // 0 non-paused time (will sum as experiment is processed)
  resume_ts = 0; // by default, collection is "resumed" (not paused) from time=0
  need_swap_endian = false;
  exp_rel_start_time_set = false;
  exp_rel_start_time = ZERO_TIME;
  has_java = false;
  hex_field_width = 8;
  hw_cpuver = CPUVER_UNDEFINED;
  machinemodel = NULL;
  expt_name = NULL;
  arch_name = NULL;
  fndr_arch_name = NULL;
  dyntext_name = NULL;
  logFile = NULL;

  dataDscrs = new Vector<DataDescriptor*>;
  for (int i = 0; i < DATA_LAST; ++i)
    dataDscrs->append (NULL);

  pcktDscrs = new Vector<PacketDescriptor*>;
  blksz = PROFILE_BUFFER_CHUNK;
  jthreads = new Vector<JThread*>;
  jthreads_idx = new Vector<JThread*>;
  gcevents = new Vector<GCEvent*>;
  gcevent_last_used = (GCEvent *) NULL;
  heapUnmapEvents = new Vector<UnmapChunk*>;
  cstack = NULL;
  cstackShowHide = NULL;
  frmpckts = new Vector<RawFramePacket*>;
  typedef DefaultMap2D<uint32_t, hrtime_t, uint64_t> OmpMap0;
  mapPRid = new OmpMap0 (OmpMap0::Interval);
  typedef DefaultMap2D<uint32_t, hrtime_t, void*> OmpMap;
  mapPReg = new OmpMap (OmpMap::Interval);
  mapTask = new OmpMap (OmpMap::Interval);
  openMPdata = NULL;
  archiveMap = NULL;
  nnodes = 0;
  nchunks = 0;
  chunks = NULL;
  uidHTable = NULL;
  uidnodes = new Vector<UIDnode*>;
  mrecs = new Vector<MapRecord*>;
  samples = new Vector<Sample*>;
  sample_last_used = (Sample *) NULL;
  first_sample_label = (char*) NULL;
  fDataMap = NULL;
  vFdMap = NULL;
  resolveFrameInfo = true;
  discardTiny = false;
  init ();
}

Experiment::~Experiment ()
{
  fini ();
  free (coll_params.linetrace);
  for (int i = 0; i < MAX_HWCOUNT; i++)
    {
      free (coll_params.hw_aux_name[i]);
      free (coll_params.hw_username[i]);
    }
  free (hostname);
  free (username);
  free (architecture);
  free (os_version);
  free (uarglist);
  free (utargname);
  free (ucwd);
  free (cversion);
  free (dversion);
  free (jversion);
  delete logFile;
  free (expt_name);
  free (arch_name);
  free (fndr_arch_name);
  free (dyntext_name);
  delete jthreads_idx;
  delete cstack;
  delete cstackShowHide;
  delete mapPRid;
  delete mapPReg;
  delete mapTask;
  delete openMPdata;
  destroy_map (DbeFile *, archiveMap);
  delete[] uidHTable;
  delete uidnodes;
  delete mrecs;
  delete children_exps;
  delete loadObjs;
  delete loadObjMap;
  delete sourcesMap;
  free (first_sample_label);
  free (machinemodel);

  dataDscrs->destroy ();
  delete dataDscrs;
  pcktDscrs->destroy ();
  delete pcktDscrs;
  jthreads->destroy ();
  delete jthreads;
  gcevents->destroy ();
  delete gcevents;
  heapUnmapEvents->destroy ();
  delete heapUnmapEvents;
  frmpckts->destroy ();
  delete frmpckts;
  samples->destroy ();
  delete samples;
  delete fDataMap;
  delete vFdMap;

  for (long i = 0; i < nchunks; i++)
    delete[] chunks[i];
  delete[] chunks;
}

void
Experiment::init_cache ()
{
  if (smemHTable)
    return;
  smemHTable = new SegMem*[HTableSize];
  instHTable = new DbeInstr*[HTableSize];
  for (int i = 0; i < HTableSize; i++)
    {
      smemHTable[i] = NULL;
      instHTable[i] = NULL;
    }
  uidHTable = new UIDnode*[HTableSize];
  for (int i = 0; i < HTableSize; i++)
    uidHTable[i] = NULL;

  cstack = CallStack::getInstance (this);
  cstackShowHide = CallStack::getInstance (this);
}

void
Experiment::init ()
{
  userLabels = NULL;
  seg_items = new Vector<SegMem*>;
  maps = new PRBTree ();
  jmaps = NULL; // used by JAVA_CLASSES only
  jmidHTable = NULL;
  smemHTable = NULL;
  instHTable = NULL;
  min_thread = (uint64_t) - 1;
  max_thread = 0;
  thread_cnt = 0;
  min_lwp = (uint64_t) - 1;
  max_lwp = 0;
  lwp_cnt = 0;
  min_cpu = (uint64_t) - 1;
  max_cpu = 0;
  cpu_cnt = 0;

  commentq = new Emsgqueue (NTXT ("commentq"));
  runlogq = new Emsgqueue (NTXT ("runlogq"));
  errorq = new Emsgqueue (NTXT ("errorq"));
  warnq = new Emsgqueue (NTXT ("warnq"));
  notesq = new Emsgqueue (NTXT ("notesq"));
  pprocq = new Emsgqueue (NTXT ("pprocq"));
  ifreqq = NULL;

  metrics = new Vector<BaseMetric*>;
  tagObjs = new Vector<Vector<Histable*>*>;
  tagObjs->store (PROP_THRID, new Vector<Histable*>);
  tagObjs->store (PROP_LWPID, new Vector<Histable*>);
  tagObjs->store (PROP_CPUID, new Vector<Histable*>);
  tagObjs->store (PROP_EXPID, new Vector<Histable*>);
  sparse_threads = false;
}

void
Experiment::fini ()
{
  seg_items->destroy ();
  delete seg_items;
  delete maps;
  delete jmaps;
  delete[] smemHTable;
  delete[] instHTable;
  delete jmidHTable;
  delete commentq;
  delete runlogq;
  delete errorq;
  delete warnq;
  delete notesq;
  delete pprocq;
  if (ifreqq != NULL)
    {
      delete ifreqq;
      ifreqq = NULL;
    }

  int index;
  BaseMetric *mtr;
  Vec_loop (BaseMetric*, metrics, index, mtr)
  {
    dbeSession->drop_metric (mtr);
  }
  delete metrics;
  tagObjs->fetch (PROP_THRID)->destroy ();
  tagObjs->fetch (PROP_LWPID)->destroy ();
  tagObjs->fetch (PROP_CPUID)->destroy ();
  tagObjs->fetch (PROP_EXPID)->destroy ();
  tagObjs->destroy ();
  delete tagObjs;
}

// These are the data files which can be read in parallel
// for multiple sub-experiments.
// Postpone calling resolve_frame_info()
void
Experiment::read_experiment_data (bool read_ahead)
{

  read_frameinfo_file ();
  if (read_ahead)
    {
      resolveFrameInfo = false;
      (void) get_profile_events ();
      resolveFrameInfo = true;
    }
}

Experiment::Exp_status
Experiment::open_epilogue ()
{

  // set up mapping for tagObj(PROP_EXPID)
  (void) mapTagValue (PROP_EXPID, userExpId);

  post_process ();
  if (last_event != ZERO_TIME)
    { // if last_event is known
      StringBuilder sb;
      hrtime_t ts = last_event - exp_start_time;
      sb.sprintf (GTXT ("Experiment Ended: %ld.%09ld\nData Collection Duration: %ld.%09ld"),
		  (long) (ts / NANOSEC), (long) (ts % NANOSEC),
		  (long) (non_paused_time / NANOSEC),
		  (long) (non_paused_time % NANOSEC));
      runlogq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // Check for incomplete experiment, and inform the user
  if (status == INCOMPLETE)
    {
      if (exec_started == true)
	// experiment ended with the exec, not abnormally
	status = SUCCESS;
      else
	{
	  char * cmnt = GTXT ("*** Note: experiment was not closed");
	  commentq->append (new Emsg (CMSG_COMMENT, cmnt));
	  // runlogq->append(new Emsg(CMSG_COMMENT, cmnt));
	}
    }
  // write a descriptive header for the experiment
  write_header ();
  return status;
}

Experiment::Exp_status
Experiment::open (char *path)
{

  // Find experiment directory
  if (find_expdir (path) != SUCCESS)
    // message will have been queued and status set
    return status;

  // Get creation time for experiment
  dbe_stat_t st;
  if (dbe_stat (path, &st) == 0)
    mtime = st.st_mtime;

  // Read the warnings file
  read_warn_file ();

  // Open the log file
  read_log_file ();
  if (status == SUCCESS && last_event // last event is initialized
      && (last_event - exp_start_time) / 1000000 < tiny_threshold)
    {
      // Process "tiny_threshold" (SP_ANALYZER_DISCARD_TINY_EXPERIMENTS)
      // At this point, we've only processed log.xml.
      // Note: if an experiment terminated abnormally, last_event will not yet
      //   represent events from clock profiling and other metrics.
      //   Other events will often have timestamps after the last log.xml entry.
      discardTiny = true;
      return status;
    }
  if (status == FAILURE)
    {
      if (logFile->get_status () == ExperimentFile::EF_FAILURE)
	{
	  Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log file in experiment cannot be read"));
	  errorq->append (m);
	}
      else if (fetch_errors () == NULL)
	{
	  if (broken == 1)
	    {
	      Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log does not show target starting"));
	      errorq->append (m);
	    }
	  else
	    {
	      Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: log file in experiment could not be parsed"));
	      errorq->append (m);
	    }
	}
      return status;
    }
  init_cache ();
  if (varclock != 0)
    {
      StringBuilder sb;
      sb.sprintf (
		  GTXT ("*** Warning: system has variable clock frequency, which may cause variable execution times and inaccurate conversions of cycle counts into time."));
      warnq->append (new Emsg (CMSG_WARN, sb));
    }

  // Read the notes file
  read_notes_file ();
  read_labels_file ();
  read_archives ();

  // The log file shows experiment started
  read_java_classes_file ();

  read_map_file ();

  // Dyntext file has to be processed after loadobjects file
  // as we need to be able to map (vaddr,ts) to dynamic functions.
  read_dyntext_file ();

  // Read the overview file and create samples.
  // Profiling data hasn't been read yet so we may have
  // events after the last recorded sample.
  // We'll create a fake sample to cover all those
  // events later.
  read_overview_file ();

  // Check if instruction frequency data is available
  read_ifreq_file ();

  // Check if OMP data is available
  read_omp_file ();

  return status;
}

/* XXX -- update() is a no-op now, but may be needed for auto-update */
Experiment::Exp_status
Experiment::update ()
{
  return status;
}

void
Experiment::append (LoadObject *lo)
{
  loadObjs->append (lo);
  char *obj_name = lo->get_pathname ();
  char *bname = get_basename (obj_name);
  loadObjMap->put (obj_name, lo);
  loadObjMap->put (bname, lo);
  if (lo->flags & SEG_FLAG_EXE)
    loadObjMap->put (COMP_EXE_NAME, lo);
}

void
Experiment::read_notes_file ()
{
  Emsg *m;

  // Open log file:
  char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE);
  FILE *f = fopen (fname, NTXT ("r"));
  free (fname);
  if (f == NULL)
    return;
  if (!dbeSession->is_interactive ())
    {
      m = new Emsg (CMSG_COMMENT, NTXT ("Notes:"));
      notesq->append (m);
    }

  while (1)
    {
      char str[MAXPATHLEN];
      char *e = fgets (str, ((int) sizeof (str)) - 1, f);
      if (e == NULL)
	{
	  if (!dbeSession->is_interactive ())
	    {
	      m = new Emsg (CMSG_COMMENT,
			    "============================================================");
	      notesq->append (m);
	    }
	  break;
	}
      size_t i = strlen (str);
      if (i > 0 && str[i - 1] == '\n')
	// remove trailing nl
	str[i - 1] = 0;
      m = new Emsg (CMSG_COMMENT, str);
      notesq->append (m);
    }
  (void) fclose (f);
}

int
Experiment::save_notes (char* text, bool handle_file)
{
  if (handle_file)
    {
      FILE *fnotes;
      char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE);
      fnotes = fopen (fname, NTXT ("w"));
      free (fname);
      if (fnotes != NULL)
	{
	  (void) fprintf (fnotes, NTXT ("%s"), text);
	  fclose (fnotes);
	}
      else
	return 1; // Cannot write file
    }
  notesq->clear ();
  Emsg *m = new Emsg (CMSG_COMMENT, text);
  notesq->append (m);

  return 0;
}

int
Experiment::delete_notes (bool handle_file)
{
  if (handle_file)
    {
      char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_NOTES_FILE);
      if (unlink (fname) != 0)
	{
	  free (fname);
	  return 1; // Cannot delete file
	}
      free (fname);
    }
  notesq->clear ();
  return 0;
}

int
Experiment::read_warn_file ()
{
  int local_status = SUCCESS;

  ExperimentFile *warnFile = new ExperimentFile (this, SP_WARN_FILE);
  if (warnFile == NULL)
    return FAILURE;
  if (!warnFile->open ())
    {
      delete warnFile;
      return FAILURE;
    }
  SAXParserFactory *factory = SAXParserFactory::newInstance ();
  SAXParser *saxParser = factory->newSAXParser ();
  DefaultHandler *dh = new ExperimentHandler (this);
  try
    {
      saxParser->parse ((File*) warnFile->fh, dh);
    }
  catch (SAXException *e)
    {
      // Fatal error in the parser
      StringBuilder sb;
      sb.sprintf (NTXT ("%s: %s"), SP_WARN_FILE, e->getMessage ());
      char *str = sb.toString ();
      Emsg *m = new Emsg (CMSG_FATAL, str);
      errorq->append (m);
      local_status = FAILURE;
      delete e;
    }
  delete warnFile;
  delete dh;
  delete saxParser;
  delete factory;

  return local_status;
}

int
Experiment::read_log_file ()
{
  if (logFile == NULL)
    logFile = new ExperimentFile (this, SP_LOG_FILE);
  if (!logFile->open ())
    {
      status = FAILURE;
      return status;
    }

  SAXParserFactory *factory = SAXParserFactory::newInstance ();
  SAXParser *saxParser = factory->newSAXParser ();
  DefaultHandler *dh = new ExperimentHandler (this);
  try
    {
      saxParser->parse ((File*) logFile->fh, dh);
    }
  catch (SAXException *e)
    {
      // Fatal error in the parser
      StringBuilder sb;
      if (obsolete == 1)
	sb.sprintf (NTXT ("%s"), e->getMessage ());
      else
	sb.sprintf (NTXT ("%s: %s"), SP_LOG_FILE, e->getMessage ());
      char *str = sb.toString ();
      Emsg *m = new Emsg (CMSG_FATAL, str);
      errorq->append (m);
      status = FAILURE;
      delete e;
    }
  logFile->close ();
  dbeSession->register_metric (GTXT ("IPC"), GTXT ("Instructions Per Cycle"),
			       NTXT ("insts/cycles"));
  dbeSession->register_metric (GTXT ("CPI"), GTXT ("Cycles Per Instruction"),
			       NTXT ("cycles/insts"));
  dbeSession->register_metric (GTXT ("K_IPC"),
			       GTXT ("Kernel Instructions Per Cycle"),
			       NTXT ("K_insts/K_cycles"));
  dbeSession->register_metric (GTXT ("K_CPI"),
			       GTXT ("Kernel Cycles Per Instruction"),
			       NTXT ("K_cycles/K_insts"));

  delete dh;
  delete saxParser;
  delete factory;

  return status;
}

////////////////////////////////////////////////////////////////////////////////
//  class Experiment::ExperimentLabelsHandler
//

class Experiment::ExperimentLabelsHandler : public DefaultHandler
{
public:

  ExperimentLabelsHandler (Experiment *_exp)
  {
    exp = _exp;
  }

  ~ExperimentLabelsHandler () { };
  void startDocument () { }
  void endDocument () { }
  void endElement (char * /*uri*/, char * /*localName*/, char * /*qName*/) { }
  void characters (char * /*ch*/, int /*start*/, int /*length*/) { }
  void ignorableWhitespace (char*, int, int) { }
  void error (SAXParseException * /*e*/) { }

  void startElement (char *uri, char *localName, char *qName, Attributes *attrs);

private:

  inline const char *
  s2s (const char *s)
  {
    return s ? s : "NULL";
  }

  Experiment *exp;
};

void
Experiment::ExperimentLabelsHandler::startElement (char*, char*, char *qName,
						   Attributes *attrs)
{
  DEBUG_CODE if (DEBUG_SAXPARSER) dump_startElement (qName, attrs);
  if (qName == NULL || strcmp (qName, NTXT ("id")) != 0)
    return;
  char *name = NULL, *all_times = NULL, *comment = NULL, *hostName = NULL;
  long startSec = 0;
  //    long tm_zone = 0;
  hrtime_t startHrtime = (hrtime_t) 0;
  long long lbl_ts = 0;
  int relative = 0;
  timeval start_tv;
  start_tv.tv_usec = start_tv.tv_sec = 0;
  for (int i = 0, sz = attrs ? attrs->getLength () : 0; i < sz; i++)
    {
      const char *qn = attrs->getQName (i);
      const char *vl = attrs->getValue (i);
      if (strcmp (qn, NTXT ("name")) == 0)
	name = dbe_xml2str (vl);
      else if (strcmp (qn, NTXT ("cmd")) == 0)
	all_times = dbe_xml2str (vl);
      else if (strcmp (qn, NTXT ("comment")) == 0)
	comment = dbe_xml2str (vl);
      else if (strcmp (qn, NTXT ("relative")) == 0)
	relative = atoi (vl);
      else if (strcmp (qn, NTXT ("hostname")) == 0)
	hostName = dbe_xml2str (vl);
      else if (strcmp (qn, NTXT ("time")) == 0)
	startSec = atol (vl);
      else if (strcmp (qn, NTXT ("tstamp")) == 0)
	startHrtime = parseTStamp (vl);
      else if (strcmp (qn, NTXT ("lbl_ts")) == 0)
	{
	  if (*vl == '-')
	    lbl_ts = -parseTStamp (vl + 1);
	  else
	    lbl_ts = parseTStamp (vl);
	}
    }
  if (name == NULL || hostName == NULL || (all_times == NULL && comment == NULL))
    {
      free (name);
      free (hostName);
      free (all_times);
      free (comment);
      return;
    }
  UserLabel *lbl = new UserLabel (name);
  lbl->comment = comment;
  lbl->hostname = hostName;
  lbl->start_sec = startSec;
  lbl->start_hrtime = startHrtime;
  exp->userLabels->append (lbl);
  if (all_times)
    {
      lbl->all_times = all_times;
      lbl->start_tv = start_tv;
      lbl->relative = relative;
      if (relative == UserLabel::REL_TIME)
	lbl->atime = lbl_ts;
      else
	{ // relative == UserLabel::CUR_TIME
	  long long delta = 0;
	  if (exp->hostname && strcmp (lbl->hostname, exp->hostname) == 0)
	    delta = lbl_ts + (lbl->start_hrtime - exp->exp_start_time);
	  else
	    for (int i = 0; i < exp->userLabels->size (); i++)
	      {
		UserLabel *firstLbl = exp->userLabels->fetch (i);
		if (strcmp (lbl->hostname, firstLbl->hostname) == 0)
		  {
		    delta = lbl_ts + (lbl->start_hrtime - firstLbl->start_hrtime) +
			    ((long long) (firstLbl->start_sec - exp->start_sec)) * NANOSEC;
		    break;
		  }
	      }
	  lbl->atime = delta > 0 ? delta : 0;
	}
    }
}

static int
sortUserLabels (const void *a, const void *b)
{
  UserLabel *l1 = *((UserLabel **) a);
  UserLabel *l2 = *((UserLabel **) b);
  int v = dbe_strcmp (l1->name, l2->name);
  if (v != 0)
    return v;
  if (l1->atime < l2->atime)
    return -1;
  else if (l1->atime > l2->atime)
    return 1;
  if (l1->id < l2->id)
    return -1;
  else if (l1->id > l2->id)
    return 1;
  return 0;
}

static char *
append_string (char *s, char *str)
{
  if (s == NULL)
    return dbe_strdup (str);
  char *new_s = dbe_sprintf (NTXT ("%s %s"), s, str);
  free (s);
  return new_s;
}

void
Experiment::read_labels_file ()
{
  ExperimentFile *fp = new ExperimentFile (this, SP_LABELS_FILE);
  if (!fp->open ())
    {
      delete fp;
      return;
    }
  userLabels = new Vector<UserLabel*>();
  SAXParserFactory *factory = SAXParserFactory::newInstance ();
  SAXParser *saxParser = factory->newSAXParser ();
  DefaultHandler *dh = new ExperimentLabelsHandler (this);
  try
    {
      saxParser->parse ((File*) fp->fh, dh);
    }
  catch (SAXException *e)
    {
      // Fatal error in the parser
      StringBuilder sb;
      sb.sprintf (NTXT ("%s: %s"), SP_LABELS_FILE, e->getMessage ());
      char *str = sb.toString ();
      Emsg *m = new Emsg (CMSG_FATAL, str);
      errorq->append (m);
      delete e;
    }
  fp->close ();
  delete fp;
  delete dh;
  delete saxParser;
  delete factory;

  userLabels->sort (sortUserLabels);
  UserLabel::dump ("After sortUserLabels:", userLabels);
  UserLabel *ulbl = NULL;
  for (int i = 0, sz = userLabels->size (); i < sz; i++)
    {
      UserLabel *lbl = userLabels->fetch (i);
      if (ulbl == NULL)
	ulbl = new UserLabel (lbl->name);
      else if (dbe_strcmp (lbl->name, ulbl->name) != 0)
	{ // new Label
	  ulbl->register_user_label (groupId);
	  if (ulbl->expr == NULL)
	    delete ulbl;
	  ulbl = new UserLabel (lbl->name);
	}
      if (lbl->all_times)
	{
	  if (strncmp (lbl->all_times, NTXT ("start"), 5) == 0)
	    {
	      if (!ulbl->start_f)
		{
		  ulbl->start_f = true;
		  ulbl->timeStart = lbl->atime;
		}
	    }
	  else
	    { // stop
	      if (!ulbl->start_f)
		continue;
	      ulbl->all_times = append_string (ulbl->all_times, lbl->all_times);
	      ulbl->stop_f = true;
	      ulbl->timeStop = lbl->atime;
	      ulbl->gen_expr ();
	    }
	}
      if (lbl->comment != NULL)
	ulbl->comment = append_string (ulbl->comment, lbl->comment);
    }
  if (ulbl)
    {
      ulbl->register_user_label (groupId);
      if (ulbl->expr == NULL)
	delete ulbl;
    }
  Destroy (userLabels);
}

void
Experiment::read_archives ()
{
  if (founder_exp)
    return;
  char *allocated_str = NULL;
  char *nm = get_arch_name ();
  DIR *exp_dir = opendir (nm);
  if (exp_dir == NULL)
    {
      if (founder_exp == NULL)
	{
	  // Check if the user uses a subexperiment only
	  nm = dbe_sprintf (NTXT ("%s/../%s"), expt_name, SP_ARCHIVES_DIR);
	  exp_dir = opendir (nm);
	  if (exp_dir == NULL)
	    {
	      free (nm);
	      return;
	    }
	  allocated_str = nm;
	}
      else
	return;
    }

  StringBuilder sb;
  sb.append (nm);
  sb.append ('/');
  int dlen = sb.length ();
  free (allocated_str);
  archiveMap = new StringMap<DbeFile *>();

  struct dirent *entry = NULL;
  while ((entry = readdir (exp_dir)) != NULL)
    {
      char *dname = entry->d_name;
      if (dname[0] == '.'
	  && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0')))
	// skip links to ./ or ../
	continue;
      sb.setLength (dlen);
      sb.append (dname);
      char *fnm = sb.toString ();
      DbeFile *df = new DbeFile (fnm);
      df->set_location (fnm);
      df->filetype |= DbeFile::F_FILE;
      df->inArchive = true;
      df->experiment = this;
      archiveMap->put (dname, df);
      free (fnm);
    }
  closedir (exp_dir);
}

static char *
gen_file_name (const char *packet_name, const char *src_name)
{
  char *fnm, *bname = get_basename (packet_name);
  if (bname == packet_name)
    fnm = dbe_strdup (src_name);
  else
    fnm = dbe_sprintf ("%.*s%s", (int) (bname - packet_name),
		       packet_name, src_name);

  // convert "java.lang.Object/Integer.java" => "java/lang/Object/Integer.java"
  bname = get_basename (fnm);
  for (char *s = fnm; s < bname; s++)
    if (*s == '.')
      *s = '/';
  return fnm;
}

static char *
get_jlass_name (const char *nm)
{
  // Convert "Ljava/lang/Object;" => "java/lang/Object.class"
  if (*nm == 'L')
    {
      size_t len = strlen (nm);
      if (nm[len - 1] == ';')
	return dbe_sprintf ("%.*s.class", (int) (len - 2), nm + 1);
    }
  return dbe_strdup (nm);
}

static char *
get_jmodule_name (const char *nm)
{
  // convert "Ljava/lang/Object;" => "java.lang.Object"
  if (*nm == 'L')
    {
      size_t len = strlen (nm);
      if (nm[len - 1] == ';')
	{
	  char *mname = dbe_sprintf (NTXT ("%.*s"), (int) (len - 2), nm + 1);
	  for (char *s = mname; *s; s++)
	    if (*s == '/')
	      *s = '.';
	  return mname;
	}
    }
  return dbe_strdup (nm);
}

LoadObject *
Experiment::get_j_lo (const char *className, const char *fileName)
{
  char *class_name = get_jlass_name (className);
  Dprintf (DUMP_JCLASS_READER,
	"Experiment::get_j_lo: className='%s' class_name='%s' fileName='%s'\n",
	   STR (className), STR (class_name), STR (fileName));
  LoadObject *lo = loadObjMap->get (class_name);
  if (lo == NULL)
    {
      lo = createLoadObject (class_name, fileName);
      lo->type = LoadObject::SEG_TEXT;
      lo->mtime = (time_t) 0;
      lo->size = 0;
      lo->set_platform (Java, wsize);
      lo->dbeFile->filetype |= DbeFile::F_FILE | DbeFile::F_JAVACLASS;
      append (lo);
      Dprintf (DUMP_JCLASS_READER,
	       "Experiment::get_j_lo: creates '%s' location='%s'\n",
	       STR (lo->get_name ()), STR (lo->dbeFile->get_location (false)));
    }
  free (class_name);
  return lo;
}

Module *
Experiment::get_jclass (const char *className, const char *fileName)
{
  LoadObject *lo = get_j_lo (className, NULL);
  char *mod_name = get_jmodule_name (className);
  Module *mod = lo->find_module (mod_name);
  if (mod == NULL)
    {
      mod = dbeSession->createClassFile (mod_name);
      mod->loadobject = lo;
      if (strcmp (fileName, NTXT ("<Unknown>")) != 0)
	mod->set_file_name (gen_file_name (lo->get_pathname (), fileName));
      else
	mod->set_file_name (dbe_strdup (fileName));
      lo->append_module (mod);
      mod_name = NULL;
    }
  else if (mod->file_name && (strcmp (mod->file_name, "<Unknown>") == 0)
	   && strcmp (fileName, "<Unknown>") != 0)
    mod->set_file_name (gen_file_name (lo->get_pathname (), fileName));
  Dprintf (DUMP_JCLASS_READER,
	"Experiment::get_jclass: class_name='%s' mod_name='%s' fileName='%s'\n",
	   mod->loadobject->get_pathname (), mod->get_name (), mod->file_name);
  free (mod_name);
  return mod;
}

#define ARCH_STRLEN(s) ( ( strlen(s) + 4 ) & ~0x3 )

int
Experiment::read_java_classes_file ()
{
  char *data_file_name = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_JCLASSES_FILE);
  Data_window *dwin = new Data_window (data_file_name);
  free (data_file_name);
  if (dwin->not_opened ())
    {
      delete dwin;
      return INCOMPLETE;
    }
  dwin->need_swap_endian = need_swap_endian;
  jmaps = new PRBTree ();
  jmidHTable = new DbeCacheMap<unsigned long long, JMethod>;

  hrtime_t cur_loaded = 0;
  Module *cur_mod = NULL;
  for (int64_t offset = 0;;)
    {
      CM_Packet *cpkt = (CM_Packet*) dwin->bind (offset, sizeof (CM_Packet));
      if (cpkt == NULL)
	break;
      uint16_t v16 = (uint16_t) cpkt->tsize;
      size_t cpktsize = dwin->decode (v16);
      cpkt = (CM_Packet*) dwin->bind (offset, cpktsize);
      if ((cpkt == NULL) || (cpktsize == 0))
	{
	  char *buf = dbe_sprintf (GTXT ("archive file malformed %s"),
				   arch_name);
	  errorq->append (new Emsg (CMSG_ERROR, buf));
	  free (buf);
	  break;
	}
      v16 = (uint16_t) cpkt->type;
      v16 = dwin->decode (v16);
      switch (v16)
	{
	case ARCH_JCLASS:
	  {
	    ARCH_jclass *ajcl = (ARCH_jclass*) cpkt;
	    uint64_t class_id = dwin->decode (ajcl->class_id);
	    char *className = ((char*) ajcl) + sizeof (*ajcl);
	    char *fileName = className + ARCH_STRLEN (className);
	    Dprintf (DUMP_JCLASS_READER,
		     "read_java_classes_file: ARCH_JCLASS(Ox%x)"
		     "class_id=Ox%llx className='%s' fileName='%s' \n",
		     (int) v16, (long long) class_id, className, fileName);
	    cur_mod = NULL;
	    if (*className == 'L')
	      { // Old libcollector generated '[' (one array dimension).
		cur_mod = get_jclass (className, fileName);
		cur_loaded = dwin->decode (ajcl->tstamp);
		jmaps->insert (class_id, cur_loaded, cur_mod);
	      }
	    break;
	  }
	case ARCH_JCLASS_LOCATION:
	  {
	    ARCH_jclass_location *ajcl = (ARCH_jclass_location *) cpkt;
	    uint64_t class_id = dwin->decode (ajcl->class_id);
	    char *className = ((char*) ajcl) + sizeof (*ajcl);
	    char *fileName = className + ARCH_STRLEN (className);
	    Dprintf (DUMP_JCLASS_READER,
		     "read_java_classes_file: ARCH_JCLASS_LOCATION(Ox%x)"
		     "class_id=Ox%llx className='%s' fileName='%s' \n",
		     (int) v16, (long long) class_id, className, fileName);
	    get_j_lo (className, fileName);
	    break;
	  }
	case ARCH_JMETHOD:
	  {
	    if (cur_mod == NULL)
	      break;
	    ARCH_jmethod *ajmt = (ARCH_jmethod*) cpkt;
	    uint64_t method_id = dwin->decode (ajmt->method_id);
	    char *s_name = ((char*) ajmt) + sizeof (*ajmt);
	    char *s_signature = s_name + ARCH_STRLEN (s_name);
	    char *fullname = dbe_sprintf ("%s.%s", cur_mod->get_name (), s_name);
	    Dprintf (DUMP_JCLASS_READER,
		     "read_java_classes_file: ARCH_JMETHOD(Ox%x) "
		     "method_id=Ox%llx name='%s' signature='%s' fullname='%s'\n",
		     (int) v16, (long long) method_id, s_name,
		     s_signature, fullname);
	    JMethod *jmthd = cur_mod->find_jmethod (fullname, s_signature);
	    if (jmthd == NULL)
	      {
		jmthd = dbeSession->createJMethod ();
		jmthd->size = (unsigned) - 1; // unknown until later (maybe)
		jmthd->module = cur_mod;
		jmthd->set_signature (s_signature);
		jmthd->set_name (fullname);
		cur_mod->functions->append (jmthd);
		cur_mod->loadobject->functions->append (jmthd);
		Dprintf (DUMP_JCLASS_READER,
		    "read_java_classes_file: ARCH_JMETHOD CREATE fullname=%s\n",
			 fullname);
	      }
	    jmaps->insert (method_id, cur_loaded, jmthd);
	    free (fullname);
	    break;
	  }
	default:
	  Dprintf (DUMP_JCLASS_READER,
		   "read_java_classes_file: type=Ox%x (%d) cpktsize=%d\n",
		   (int) v16, (int) v16, (int) cpktsize);
	  break; // ignore unknown packets
	}
      offset += cpktsize;
    }
  delete dwin;
  return SUCCESS;
}

void
Experiment::read_map_file ()
{
  ExperimentFile *mapFile = new ExperimentFile (this, SP_MAP_FILE);
  if (!mapFile->open ())
    {
      delete mapFile;
      return;
    }

  SAXParserFactory *factory = SAXParserFactory::newInstance ();
  SAXParser *saxParser = factory->newSAXParser ();
  DefaultHandler *dh = new ExperimentHandler (this);
  try
    {
      saxParser->parse ((File*) mapFile->fh, dh);
    }
  catch (SAXException *e)
    {
      // Fatal error in the parser
      StringBuilder sb;
      sb.sprintf (NTXT ("%s: %s"), SP_MAP_FILE, e->getMessage ());
      char *str = sb.toString ();
      Emsg *m = new Emsg (CMSG_FATAL, str);
      errorq->append (m);
      status = FAILURE;
      free (str);
      delete e;
    }
  delete mapFile;
  delete dh;
  delete saxParser;
  delete factory;

  for (int i = 0, sz = mrecs ? mrecs->size () : 0; i < sz; i++)
    {
      MapRecord *mrec = mrecs->fetch (i);
      SegMem *smem, *sm_lo, *sm_hi;
      switch (mrec->kind)
	{
	case MapRecord::LOAD:
	  smem = new SegMem;
	  smem->base = mrec->base;
	  smem->size = mrec->size;
	  smem->load_time = mrec->ts;
	  smem->unload_time = MAX_TIME;
	  smem->obj = mrec->obj;
	  smem->set_file_offset (mrec->foff);
	  seg_items->append (smem); // add to the master list

	  // Check if the new segment overlaps other active segments
	  sm_lo = (SegMem*) maps->locate (smem->base, smem->load_time);
	  if (sm_lo && sm_lo->base + sm_lo->size > smem->base)
	    {
	      // check to see if it is a duplicate record: same address and size, and
	      if ((smem->base == sm_lo->base) && (smem->size == sm_lo->size))
		{
		  // addresses and sizes match, check name
		  if (strstr (smem->obj->get_name (), sm_lo->obj->get_name ()) != NULL
		      || strstr (sm_lo->obj->get_name (), smem->obj->get_name ()) != NULL)
		    // this is a duplicate; just move on the the next map record
		    continue;
		  fprintf (stderr,
			   GTXT ("*** Warning: Segment `%s' loaded with same address, size as `%s' [0x%llx-0x%llx]\n"),
			   smem->obj->get_name (), sm_lo->obj->get_name (),
			   sm_lo->base, sm_lo->base + sm_lo->size);
		}

	      // Not a duplicate; implicitly unload the old one
	      //     Note: implicit unloading causes high <Unknown>
	      //           when such overlapping is bogus
	      StringBuilder sb;
	      sb.sprintf (GTXT ("*** Warning: Segment %s [0x%llx-0x%llx] overlaps %s [0x%llx-0x%llx], which has been implicitly unloaded"),
			  smem->obj->get_name (), smem->base, smem->base + smem->size,
			  sm_lo->obj->get_name (), sm_lo->base, sm_lo->base + sm_lo->size);
	      warnq->append (new Emsg (CMSG_WARN, sb));
	    }

	  // now look for other segments with which this might overlap
	  sm_hi = (SegMem*) maps->locate_up (smem->base, smem->load_time);
	  while (sm_hi && sm_hi->base < smem->base + smem->size)
	    {

	      // Note: implicit unloading causes high <Unknown> when such overlapping is bogus
	      // maps->remove( sm_hi->base, smem->load_time );
	      StringBuilder sb;
	      sb.sprintf (GTXT ("*** Warning: Segment %s [0x%llx-0x%llx] overlaps %s [0x%llx-0x%llx], which has been implicitly unloaded"),
			  smem->obj->get_name (), smem->base,
			  smem->base + smem->size, sm_hi->obj->get_name (),
			  sm_hi->base, sm_hi->base + sm_hi->size);
	      warnq->append (new Emsg (CMSG_WARN, sb));
	      sm_hi = (SegMem*) maps->locate_up (sm_hi->base + sm_hi->size,
						 smem->load_time);
	    }

	  maps->insert (smem->base, smem->load_time, smem);
	  break;
	case MapRecord::UNLOAD:
	  smem = (SegMem*) maps->locate (mrec->base, mrec->ts);
	  if (smem && smem->base == mrec->base)
	    {
	      smem->unload_time = mrec->ts;
	      maps->remove (mrec->base, mrec->ts);
	    }
	  break;
	}
    }
  mrecs->destroy ();

  // See if there are comments or warnings for a load object;
  // if so, queue them to Experiment
  for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++)
    {
      LoadObject *lo = loadObjs->get (i);
      for (Emsg *m = lo->fetch_warnings (); m; m = m->next)
	warnq->append (m->get_warn (), m->get_msg ());
      for (Emsg *m = lo->fetch_comments (); m; m = m->next)
	commentq->append (m->get_warn (), m->get_msg ());
    }
}

void
Experiment::read_frameinfo_file ()
{
  init_cache ();
  char *base_name = get_basename (expt_name);
  char *msg = dbe_sprintf (GTXT ("Loading CallStack Data: %s"), base_name);
  read_data_file ("data." SP_FRINFO_FILE, msg);
  free (msg);
  frmpckts->sort (frUidCmp);
  uidnodes->sort (uidNodeCmp);
}

void
Experiment::read_omp_preg ()
{
  // Parallel region descriptions
  DataDescriptor *pregDdscr = getDataDescriptor (DATA_OMP4);
  if (pregDdscr == NULL)
    return;
  DataView *pregData = pregDdscr->createView ();
  pregData->sort (PROP_CPRID); // omptrace PROP_CPRID

  // OpenMP enter parreg events
  DataDescriptor *dDscr = getDataDescriptor (DATA_OMP2);
  if (dDscr == NULL || dDscr->getSize () == 0)
    {
      delete pregData;
      return;
    }

  char *idxname = NTXT ("OMP_preg");
  delete dbeSession->indxobj_define (idxname, GTXT ("OpenMP Parallel Region"),
				     NTXT ("CPRID"), NULL, NULL);
  int idxtype = dbeSession->findIndexSpaceByName (idxname);
  if (idxtype < 0)
    {
      delete pregData;
      return;
    }
  ompavail = true;

  // Pre-create parallel region with id == 0
  Histable *preg0 = dbeSession->createIndexObject (idxtype, (int64_t) 0);
  preg0->set_name (dbe_strdup (GTXT ("Implicit OpenMP Parallel Region")));

  // Take care of the progress bar
  char *msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"),
  get_basename (expt_name));
  theApplication->set_progress (0, msg);
  free (msg);
  long deltaReport = 1000;
  long nextReport = 0;
  long errors_found = 0;
  Vector<Histable*> pregs;

  long size = dDscr->getSize ();
  for (long i = 0; i < size; ++i)
    {
      if (i == nextReport)
	{
	  int percent = (int) (i * 100 / size);
	  if (percent > 0)
	    theApplication->set_progress (percent, NULL);
	  nextReport += deltaReport;
	}

      uint32_t thrid = dDscr->getIntValue (PROP_THRID, i);
      hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i);
      uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); // omptrace CPRID
      mapPRid->put (thrid, tstamp, cprid);

      pregs.reset ();
      /*
       * We will use 2 pointers to make sure there is no loop.
       * First pointer "curpreg" goes to the next element,
       * second pointer "curpreg_loop_control" goes to the next->next element.
       * If these pointers have the same value - there is a loop.
       */
      uint64_t curpreg_loop_control = cprid;
      Datum tval_loop_control;
      if (curpreg_loop_control != 0)
	{
	  tval_loop_control.setUINT64 (curpreg_loop_control);
	  long idx = pregData->getIdxByVals (&tval_loop_control, DataView::REL_EQ);
	  if (idx < 0)
	    curpreg_loop_control = 0;
	  else
	    curpreg_loop_control = pregData->getLongValue (PROP_PPRID, idx);
	}
      for (uint64_t curpreg = cprid; curpreg != 0;)
	{
	  Histable *val = NULL;
	  Datum tval;
	  tval.setUINT64 (curpreg);
	  long idx = pregData->getIdxByVals (&tval, DataView::REL_EQ);
	  if (idx < 0)
	    break;
	  /*
	   * Check if there is a loop
	   */
	  if (0 != curpreg_loop_control)
	    {
	      if (curpreg == curpreg_loop_control)
		{
		  errors_found++;
		  if (1 == errors_found)
		    {
		      Emsg *m = new Emsg (CMSG_WARN, GTXT ("*** Warning: circular links in OMP regions; data may not be correct."));
		      warnq->append (m);
		    }
		  break;
		}
	    }
	  uint64_t pragmapc = pregData->getLongValue (PROP_PRPC, idx);
	  DbeInstr *instr = map_Vaddr_to_PC (pragmapc, tstamp);
	  if (instr == NULL)
	    {
	      break;
	    }
	  val = instr;
	  DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE);
	  if (dbeline->lineno > 0)
	    {
	      if (instr->func->usrfunc)
		dbeline = dbeline->sourceFile->find_dbeline
			(instr->func->usrfunc, dbeline->lineno);
	      dbeline->set_flag (DbeLine::OMPPRAGMA);
	      val = dbeline;
	    }
	  val = dbeSession->createIndexObject (idxtype, val);
	  pregs.append (val);

	  curpreg = pregData->getLongValue (PROP_PPRID, idx);
	  /*
	   * Update curpreg_loop_control
	   */
	  if (0 != curpreg_loop_control)
	    {
	      tval_loop_control.setUINT64 (curpreg_loop_control);
	      idx = pregData->getIdxByVals
		      (&tval_loop_control, DataView::REL_EQ);
	      if (idx < 0)
		curpreg_loop_control = 0;
	      else
		{
		  curpreg_loop_control = pregData->getLongValue
			  (PROP_PPRID, idx);
		  tval_loop_control.setUINT64 (curpreg_loop_control);
		  idx = pregData->getIdxByVals
			  (&tval_loop_control, DataView::REL_EQ);
		  if (idx < 0)
		    curpreg_loop_control = 0;
		  else
		    curpreg_loop_control = pregData->getLongValue
			    (PROP_PPRID, idx);
		}
	    }
	}
      pregs.append (preg0);
      void *prstack = cstack->add_stack (&pregs);
      mapPReg->put (thrid, tstamp, prstack);
    }
  theApplication->set_progress (0, NTXT (""));
  delete pregData;
}

void
Experiment::read_omp_task ()
{
  // Task description
  DataDescriptor *taskDataDdscr = getDataDescriptor (DATA_OMP5);
  if (taskDataDdscr == NULL)
    return;

  //7035272: previously, DataView was global; now it's local...is this OK?
  DataView *taskData = taskDataDdscr->createView ();
  taskData->sort (PROP_TSKID); // omptrace PROP_TSKID

  // OpenMP enter task events
  DataDescriptor *dDscr = getDataDescriptor (DATA_OMP3);
  if (dDscr == NULL || dDscr->getSize () == 0)
    {
      delete taskData;
      return;
    }

  char *idxname = NTXT ("OMP_task");
  // delete a possible error message. Ugly.
  delete dbeSession->indxobj_define (idxname, GTXT ("OpenMP Task"), NTXT ("TSKID"), NULL, NULL);
  int idxtype = dbeSession->findIndexSpaceByName (idxname);
  if (idxtype < 0)
    {
      delete taskData;
      return;
    }
  ompavail = true;

  // Pre-create task with id == 0
  Histable *task0 = dbeSession->createIndexObject (idxtype, (int64_t) 0);
  task0->set_name (dbe_strdup (GTXT ("OpenMP Task from Implicit Parallel Region")));

  // Take care of the progress bar
  char *msg = dbe_sprintf (GTXT ("Processing OpenMP Task Data: %s"), get_basename (expt_name));
  theApplication->set_progress (0, msg);
  free (msg);
  long deltaReport = 1000;
  long nextReport = 0;

  Vector<Histable*> tasks;
  long size = dDscr->getSize ();
  long errors_found = 0;
  for (long i = 0; i < size; ++i)
    {
      if (i == nextReport)
	{
	  int percent = (int) (i * 100 / size);
	  if (percent > 0)
	    theApplication->set_progress (percent, NULL);
	  nextReport += deltaReport;
	}

      uint32_t thrid = dDscr->getIntValue (PROP_THRID, i);
      hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i);
      uint64_t tskid = dDscr->getLongValue (PROP_TSKID, i); //omptrace TSKID
      tasks.reset ();
      /*
       * We will use 2 pointers to make sure there is no loop.
       * First pointer "curtsk" goes to the next element,
       * second pointer "curtsk_loop_control" goes to the next->next element.
       * If these pointers have the same value - there is a loop.
       */
      uint64_t curtsk_loop_control = tskid;
      Datum tval_loop_control;
      if (curtsk_loop_control != 0)
	{
	  tval_loop_control.setUINT64 (curtsk_loop_control);
	  long idx = taskData->getIdxByVals (&tval_loop_control, DataView::REL_EQ);
	  if (idx < 0)
	    curtsk_loop_control = 0;
	  else
	    curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, idx);
	}
      for (uint64_t curtsk = tskid; curtsk != 0;)
	{
	  Histable *val = NULL;

	  Datum tval;
	  tval.setUINT64 (curtsk);
	  long idx = taskData->getIdxByVals (&tval, DataView::REL_EQ);
	  if (idx < 0)
	    break;
	  /*
	   * Check if there is a loop
	   */
	  if (0 != curtsk_loop_control)
	    {
	      if (curtsk == curtsk_loop_control)
		{
		  errors_found++;
		  if (1 == errors_found)
		    {
		      Emsg *m = new Emsg (CMSG_WARN, GTXT ("*** Warning: circular links in OMP tasks; data may not be correct."));
		      warnq->append (m);
		    }
		  break;
		}
	    }
	  uint64_t pragmapc = taskData->getLongValue (PROP_PRPC, idx);
	  DbeInstr *instr = map_Vaddr_to_PC (pragmapc, tstamp);
	  if (instr == NULL)
	    break;
	  val = instr;
	  DbeLine *dbeline = (DbeLine*) instr->convertto (Histable::LINE);
	  if (dbeline->lineno > 0)
	    {
	      if (instr->func->usrfunc)
		dbeline = dbeline->sourceFile->find_dbeline
			(instr->func->usrfunc, dbeline->lineno);
	      dbeline->set_flag (DbeLine::OMPPRAGMA);
	      val = dbeline;
	    }
	  val = dbeSession->createIndexObject (idxtype, val);
	  tasks.append (val);

	  curtsk = taskData->getLongValue (PROP_PTSKID, idx);
	  /*
	   * Update curtsk_loop_control
	   */
	  if (0 != curtsk_loop_control)
	    {
	      tval_loop_control.setUINT64 (curtsk_loop_control);
	      idx = taskData->getIdxByVals (&tval_loop_control, DataView::REL_EQ);
	      if (idx < 0)
		curtsk_loop_control = 0;
	      else
		{
		  curtsk_loop_control = taskData->getLongValue (PROP_PTSKID, idx);
		  tval_loop_control.setUINT64 (curtsk_loop_control);
		  idx = taskData->getIdxByVals (&tval_loop_control,
						DataView::REL_EQ);
		  if (idx < 0)
		    curtsk_loop_control = 0;
		  else
		    curtsk_loop_control = taskData->getLongValue (PROP_PTSKID,
								  idx);
		}
	    }
	}
      tasks.append (task0);
      void *tskstack = cstack->add_stack (&tasks);
      mapTask->put (thrid, tstamp, tskstack);
    }
  theApplication->set_progress (0, NTXT (""));
  delete taskData;
}

void
Experiment::read_omp_file ()
{
  // DATA_OMP2 table is common between OpenMP 2.5 and 3.0 profiling
  DataDescriptor *dDscr = getDataDescriptor (DATA_OMP2);
  if (dDscr == NULL)
    return;
  if (dDscr->getSize () == 0)
    {
      char *base_name = get_basename (expt_name);
      char *msg = dbe_sprintf (GTXT ("Loading OpenMP Data: %s"), base_name);
      read_data_file (SP_OMPTRACE_FILE, msg);
      free (msg);

      // OpenMP fork events
      dDscr = getDataDescriptor (DATA_OMP);
      long sz = dDscr->getSize ();
      if (sz > 0)
	{
	  // progress bar
	  msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"),
			     base_name);
	  theApplication->set_progress (0, msg);
	  free (msg);
	  long deltaReport = 5000;
	  long nextReport = 0;
	  for (int i = 0; i < sz; ++i)
	    {
	      if (i == nextReport)
		{
		  int percent = (int) (i * 100 / sz);
		  if (percent > 0)
		    theApplication->set_progress (percent, NULL);
		  nextReport += deltaReport;
		}
	      uint32_t thrid = dDscr->getIntValue (PROP_THRID, i);
	      hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i);
	      uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); //omptrace
	      mapPRid->put (thrid, tstamp, cprid);
	    }
	  theApplication->set_progress (0, NTXT (""));

	  ompavail = true;
	  openMPdata = dDscr->createView ();
	  openMPdata->sort (PROP_CPRID); // omptrace PROP_CPRID

	  // thread enters parreg events
	  dDscr = getDataDescriptor (DATA_OMP2);
	  sz = dDscr->getSize ();

	  // progress bar
	  msg = dbe_sprintf (GTXT ("Processing OpenMP Parallel Region Data: %s"),
			     base_name);
	  theApplication->set_progress (0, msg);
	  free (msg);
	  deltaReport = 5000;
	  nextReport = 0;

	  for (int i = 0; i < sz; ++i)
	    {
	      if (i == nextReport)
		{
		  int percent = (int) (i * 100 / sz);
		  if (percent > 0)
		    theApplication->set_progress (percent, NULL);
		  nextReport += deltaReport;
		}
	      uint32_t thrid = dDscr->getIntValue (PROP_THRID, i);
	      hrtime_t tstamp = dDscr->getLongValue (PROP_TSTAMP, i);
	      uint64_t cprid = dDscr->getLongValue (PROP_CPRID, i); //omptrace
	      mapPRid->put (thrid, tstamp, cprid);
	    }
	  theApplication->set_progress (0, NTXT (""));
	}
      else
	{
	  read_omp_preg ();
	  read_omp_task ();
	}
      if (ompavail && coll_params.profile_mode)
	{
	  dbeSession->status_ompavail = 1;
	  register_metric (Metric::OMP_WORK);
	  register_metric (Metric::OMP_WAIT);
	  register_metric (Metric::OMP_OVHD);
	  if (coll_params.lms_magic_id == LMS_MAGIC_ID_SOLARIS)
	    register_metric (Metric::OMP_MASTER_THREAD);
	}
    }
}

void
Experiment::read_ifreq_file ()
{
  char *fname = dbe_sprintf (NTXT ("%s/%s"), expt_name, SP_IFREQ_FILE);
  FILE *f = fopen (fname, NTXT ("r"));
  free (fname);
  if (f == NULL)
    {
      ifreqavail = false;
      return;
    }
  ifreqavail = true;
  ifreqq = new Emsgqueue (NTXT ("ifreqq"));

  while (1)
    {
      Emsg *m;
      char str[MAXPATHLEN];
      char *e = fgets (str, ((int) sizeof (str)) - 1, f);
      if (e == NULL)
	{
	  // end the list from the experiment
	  m = new Emsg (CMSG_COMMENT,
			GTXT ("============================================================"));
	  ifreqq->append (m);
	  break;
	}
      // get the string
      size_t i = strlen (str);
      if (i > 0 && str[i - 1] == '\n')
	// remove trailing nl
	str[i - 1] = 0;
      // and append it
      m = new Emsg (CMSG_COMMENT, str);
      ifreqq->append (m);
    }
  (void) fclose (f);
}

Experiment *
Experiment::getBaseFounder ()
{
  if (baseFounder)
    return baseFounder;
  Experiment *founder = this;
  Experiment *parent = founder->founder_exp;
  while (parent)
    {
      founder = parent;
      parent = founder->founder_exp;
    }
  baseFounder = founder;
  return baseFounder;
}

hrtime_t
Experiment::getRelativeStartTime ()
{
  if (exp_rel_start_time_set)
    return exp_rel_start_time;
  Experiment *founder = getBaseFounder ();
  hrtime_t child_start = this->getStartTime ();
  hrtime_t founder_start = founder->getStartTime ();
  exp_rel_start_time = child_start - founder_start;
  if (child_start == 0 && founder_start)
    exp_rel_start_time = 0;     // when descendents have incomplete log.xml
  exp_rel_start_time_set = true;
  return exp_rel_start_time;
}

DataDescriptor *
Experiment::get_raw_events (int data_id)
{
  DataDescriptor *dDscr;
  switch (data_id)
    {
    case DATA_CLOCK:
      dDscr = get_profile_events ();
      break;
    case DATA_SYNCH:
      dDscr = get_sync_events ();
      break;
    case DATA_HWC:
      dDscr = get_hwc_events ();
      break;
    case DATA_HEAP:
      dDscr = get_heap_events ();
      break;
    case DATA_HEAPSZ:
      dDscr = get_heapsz_events ();
      break;
    case DATA_IOTRACE:
      dDscr = get_iotrace_events ();
      break;
    case DATA_RACE:
      dDscr = get_race_events ();
      break;
    case DATA_DLCK:
      dDscr = get_deadlock_events ();
      break;
    case DATA_SAMPLE:
      dDscr = get_sample_events ();
      break;
    case DATA_GCEVENT:
      dDscr = get_gc_events ();
      break;
    default:
      dDscr = NULL;
      break;
    }
  return dDscr;
}

int
Experiment::base_data_id (int data_id)
{
  switch (data_id)
    {
    case DATA_HEAPSZ:
      return DATA_HEAP; // DATA_HEAPSZ DataView is based on DATA_HEAP's DataView
    default:
      break;
    }
  return data_id;
}

DataView *
Experiment::create_derived_data_view (int data_id, DataView *dview)
{
  // dview contains filtered packets
  switch (data_id)
    {
    case DATA_HEAPSZ:
      return create_heapsz_data_view (dview);
    default:
      break;
    }
  return NULL;
}

DataDescriptor *
Experiment::get_profile_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_CLOCK);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () == 0)
    {
      char *base_name = get_basename (expt_name);
      char *msg = dbe_sprintf (GTXT ("Loading Profile Data: %s"), base_name);
      read_data_file (SP_PROFILE_FILE, msg);
      free (msg);
      add_evt_time_to_profile_events (dDscr);
      resolve_frame_info (dDscr);
    }
  else if (!dDscr->isResolveFrInfoDone ())
    resolve_frame_info (dDscr);
  return dDscr;
}

void
Experiment::add_evt_time_to_profile_events (DataDescriptor *dDscr)
{
  if (coll_params.lms_magic_id != LMS_MAGIC_ID_SOLARIS)
    return;

  DataView *dview = dDscr->createView ();
  dview->sort (PROP_THRID, PROP_TSTAMP);

  // add PROP_EVT_TIME
  PropDescr* tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME");
  tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration"));
  tmp_propDscr->vtype = TYPE_INT64;
  dDscr->addProperty (tmp_propDscr);

  long sz = dview->getSize ();
  long long ptimer_usec = get_params ()->ptimer_usec;
  for (long i = 0; i < sz; i++)
    {
      int next_sample;
      int jj;
      {
	hrtime_t this_tstamp = dview->getLongValue (PROP_TSTAMP, i);
	long this_thrid = dview->getLongValue (PROP_THRID, i);
	for (jj = i + 1; jj < sz; jj++)
	  {
	    hrtime_t tmp_tstamp = dview->getLongValue (PROP_TSTAMP, jj);
	    if (tmp_tstamp != this_tstamp)
	      break;
	    long tmp_thrid = dview->getLongValue (PROP_THRID, jj);
	    if (tmp_thrid != this_thrid)
	      break;
	  }
	next_sample = jj;
      }

      long nticks = 0;
      for (jj = i; jj < next_sample; jj++)
	nticks += dview->getLongValue (PROP_NTICK, jj);
      if (nticks <= 1)
	continue; // no duration

      nticks--;
      hrtime_t duration = ptimer_usec * 1000LL * nticks; // nanoseconds
      for (jj = i; jj < next_sample; jj++)
	dview->setValue (PROP_EVT_TIME, jj, duration);
      i = jj - 1;
    }
  delete dview;
}

DataDescriptor *
Experiment::get_sync_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_SYNCH);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () > 0)
    return dDscr;

  // fetch data
  {
    char *base_name = get_basename (expt_name);
    char *msg = dbe_sprintf (GTXT ("Loading Synctrace Data: %s"), base_name);
    read_data_file (SP_SYNCTRACE_FILE, msg);
    free (msg);
    resolve_frame_info (dDscr);
  }

  // check for PROP_EVT_TIME
  PropDescr *tmp_propDscr = dDscr->getProp (PROP_EVT_TIME);
  if (tmp_propDscr)
    return dDscr;

  // add PROP_EVT_TIME
  tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME");
  tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration"));
  tmp_propDscr->vtype = TYPE_INT64;
  dDscr->addProperty (tmp_propDscr);

  long sz = dDscr->getSize ();
  for (long i = 0; i < sz; i++)
    {
      uint64_t event_duration = dDscr->getLongValue (PROP_TSTAMP, i);
      event_duration -= dDscr->getLongValue (PROP_SRQST, i);
      dDscr->setValue (PROP_EVT_TIME, i, event_duration);
    }
  return dDscr;
}

DataDescriptor *
Experiment::get_hwc_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_HWC);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () == 0)
    {
      char *base_name = get_basename (expt_name);
      char *msg = dbe_sprintf (GTXT ("Loading HW Profile Data: %s"), base_name);

      // clear HWC event stats
      dsevents = 0;
      dsnoxhwcevents = 0;
      read_data_file (SP_HWCNTR_FILE, msg);
      free (msg);
      resolve_frame_info (dDscr);

      // describe the HW counters in PropDescr
      PropDescr *prop = dDscr->getProp (PROP_HWCTAG);
      if (prop)
	{
	  Collection_params *cparam = get_params ();
	  if (cparam->hw_mode != 0)
	    for (int aux = 0; aux < MAX_HWCOUNT; aux++)
	      if (cparam->hw_aux_name[aux])
		{
		  const char* cmdname = cparam->hw_aux_name[aux];
		  const char* uname = cparam->hw_username[aux];
		  prop->addState (aux, cmdname, uname);
		}
	}
      else
	assert (0);

      double dserrrate = 100.0 * ((double) dsnoxhwcevents) / ((double) dsevents);
      if ((dsevents > 0) && (dserrrate > 10.0))
	{
	  // warn the user that rate is high
	  StringBuilder sb;
	  if (dbeSession->check_ignore_no_xhwcprof ())
	    sb.sprintf (
			GTXT ("Warning: experiment %s has %.1f%%%% (%lld of %lld) dataspace events that were accepted\n  without verification; data may be incorrect or misleading\n  recompile with -xhwcprof and rerecord to get better data\n"),
			base_name, dserrrate, (long long) dsnoxhwcevents,
			(long long) dsevents);
	  else
	    sb.sprintf (
			GTXT ("Warning: experiment %s has %.1f%%%% (%lld of %lld) dataspace events that could not be verified\n  recompile with -xhwcprof and rerecord to get better data\n"),
			base_name, dserrrate, (long long) dsnoxhwcevents,
			(long long) dsevents);
	  errorq->append (new Emsg (CMSG_WARN, sb));
	}

      // see if we've scanned the data
      if (hwc_scanned == 0)
	{
	  // no, scan the packets to see how many are bogus, or represent lost interrupts
	  long hwc_cnt = 0;

	  // loop over the packets, counting the bad ones
	  if (hwc_bogus != 0 || hwc_lost_int != 0)
	    {
	      // hwc counter data had bogus packets and/or packets reflecting lost interrupts
	      double bogus_rate = 100. * (double) hwc_bogus / (double) hwc_cnt;
	      if (bogus_rate > 5.)
		{
		  StringBuilder sb;
		  sb.sprintf (
			      GTXT ("WARNING: Too many invalid HW counter profile events (%ld/%ld = %3.2f%%) in experiment %d (`%s'); data may be unreliable"),
			      (long) hwc_bogus, (long) hwc_cnt, bogus_rate,
			      (int) userExpId, base_name);
		  Emsg *m = new Emsg (CMSG_WARN, sb);
		  warnq->append (m);
		}
	      hwc_scanned = 1;
	    }
	}
    }
  return dDscr;
}

DataDescriptor *
Experiment::get_iotrace_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_IOTRACE);
  if (dDscr == NULL)
    return NULL;

  if (dDscr->getSize () > 0)
    return dDscr;

  char *base_name = get_basename (expt_name);
  char *msg = dbe_sprintf (GTXT ("Loading IO Trace Data: %s"), base_name);
  read_data_file (SP_IOTRACE_FILE, msg);
  free (msg);

  if (dDscr->getSize () == 0)
    return dDscr;
  resolve_frame_info (dDscr);

  // check for PROP_EVT_TIME
  PropDescr *tmp_propDscr = dDscr->getProp (PROP_EVT_TIME);
  if (tmp_propDscr)
    return dDscr;

  // add PROP_EVT_TIME
  tmp_propDscr = new PropDescr (PROP_EVT_TIME, "EVT_TIME");
  tmp_propDscr->uname = dbe_strdup (GTXT ("Event duration"));
  tmp_propDscr->vtype = TYPE_INT64;
  dDscr->addProperty (tmp_propDscr);

  // add PROP_IOVFD
  tmp_propDscr = new PropDescr (PROP_IOVFD, "IOVFD");
  tmp_propDscr->uname = dbe_strdup (GTXT ("Virtual File Descriptor"));
  tmp_propDscr->vtype = TYPE_INT64;
  dDscr->addProperty (tmp_propDscr);

  delete fDataMap;
  fDataMap = new DefaultMap<int64_t, FileData*>;

  delete vFdMap;
  vFdMap = new DefaultMap<int, int64_t>;

  static int64_t virtualFd = 0;

  FileData *fData;
  virtualFd += 10;
  fData = fDataMap->get (VIRTUAL_FD_STDIN);
  if (fData == NULL)
    {
      fData = new FileData (STDIN_FILENAME);
      fData->setVirtualFd (VIRTUAL_FD_STDIN);
      fData->id = VIRTUAL_FD_STDIN;
      fData->setFileDes (STDIN_FD);
      fDataMap->put (VIRTUAL_FD_STDIN, fData);
      vFdMap->put (STDIN_FD, VIRTUAL_FD_STDIN);
    }

  fData = fDataMap->get (VIRTUAL_FD_STDOUT);
  if (fData == NULL)
    {
      fData = new FileData (STDOUT_FILENAME);
      fData->setVirtualFd (VIRTUAL_FD_STDOUT);
      fData->id = VIRTUAL_FD_STDOUT;
      fData->setFileDes (STDOUT_FD);
      fDataMap->put (VIRTUAL_FD_STDOUT, fData);
      vFdMap->put (STDOUT_FD, VIRTUAL_FD_STDOUT);
    }

  fData = fDataMap->get (VIRTUAL_FD_STDERR);
  if (fData == NULL)
    {
      fData = new FileData (STDERR_FILENAME);
      fData->setVirtualFd (VIRTUAL_FD_STDERR);
      fData->id = VIRTUAL_FD_STDERR;
      fData->setFileDes (STDERR_FD);
      fDataMap->put (VIRTUAL_FD_STDERR, fData);
      vFdMap->put (STDERR_FD, VIRTUAL_FD_STDERR);
    }

  fData = fDataMap->get (VIRTUAL_FD_OTHERIO);
  if (fData == NULL)
    {
      fData = new FileData (OTHERIO_FILENAME);
      fData->setVirtualFd (VIRTUAL_FD_OTHERIO);
      fData->id = VIRTUAL_FD_OTHERIO;
      fData->setFileDes (OTHERIO_FD);
      fDataMap->put (VIRTUAL_FD_OTHERIO, fData);
    }

  DataView *dview = dDscr->createView ();
  dview->sort (PROP_TSTAMP);
  long sz = dview->getSize ();
  for (long i = 0; i < sz; i++)
    {
      hrtime_t event_duration = dview->getLongValue (PROP_TSTAMP, i);
      hrtime_t event_start = dview->getLongValue (PROP_IORQST, i);
      if (event_start > 0)
	event_duration -= event_start;
      else
	event_duration = 0;
      dview->setValue (PROP_EVT_TIME, i, event_duration);

      int32_t fd = -1;
      int64_t vFd = VIRTUAL_FD_NONE;
      char *fName = NULL;
      int32_t origFd = -1;
      StringBuilder *sb = NULL;
      FileData *fDataOrig = NULL;
      FileSystem_type fsType;

      IOTrace_type ioType = (IOTrace_type) dview->getIntValue (PROP_IOTYPE, i);
      switch (ioType)
	{
	case READ_TRACE:
	case WRITE_TRACE:
	case READ_TRACE_ERROR:
	case WRITE_TRACE_ERROR:
	  fd = dview->getIntValue (PROP_IOFD, i);
	  vFd = vFdMap->get (fd);
	  if (vFd == 0 || vFd == VIRTUAL_FD_NONE
	      || (fData = fDataMap->get (vFd)) == NULL)
	    {
	      fData = new FileData (UNKNOWNFD_FILENAME);
	      fData->setVirtualFd (virtualFd);
	      fData->setFsType ("N/A");
	      fData->setFileDes (fd);
	      fDataMap->put (virtualFd, fData);
	      vFdMap->put (fd, virtualFd);
	      vFd = virtualFd;
	      virtualFd++;
	    }
	  dview->setValue (PROP_IOVFD, i, vFd);
	  break;
	case OPEN_TRACE:
	  fName = NULL;
	  sb = (StringBuilder*) dview->getObjValue (PROP_IOFNAME, i);
	  if (sb != NULL && sb->length () > 0)
	    fName = sb->toString ();
	  fd = dview->getIntValue (PROP_IOFD, i);
	  origFd = dview->getIntValue (PROP_IOOFD, i);
	  fsType = (FileSystem_type) dview->getIntValue (PROP_IOFSTYPE, i);

	  if (fName != NULL)
	    {
	      fData = new FileData (fName);
	      fDataMap->put (virtualFd, fData);
	      vFdMap->put (fd, virtualFd);
	      fData->setFileDes (fd);
	      fData->setFsType (fsType);
	      fData->setVirtualFd (virtualFd);
	      vFd = virtualFd;
	      virtualFd++;
	    }
	  else if (origFd > 0)
	    {
	      vFd = vFdMap->get (origFd);
	      if (vFd == 0 || vFd == VIRTUAL_FD_NONE)
		{
		  Dprintf (DEBUG_IO,
			   "*** Error I/O tracing: (open) cannot get the virtual file descriptor, fd=%d  origFd=%d\n",
			   fd, origFd);
		  continue;
		}
	      else if ((fDataOrig = fDataMap->get (vFd)) == NULL)
		{
		  Dprintf (DEBUG_IO,
			   "*** Error IO tracing: (open) cannot get original FileData object, fd=%d  origFd=%d\n",
			   fd, origFd);
		  continue;
		}
	      else
		{
		  fName = fDataOrig->getFileName ();
		  fData = new FileData (fName);
		  fData->setFileDes (fd);
		  fData->setFsType (fDataOrig->getFsType ());
		  fData->setVirtualFd (virtualFd);
		  fDataMap->put (virtualFd, fData);
		  vFdMap->put (fd, virtualFd);
		  vFd = virtualFd;
		  virtualFd++;
		}
	    }
	  else if (fd >= 0)
	    {
	      vFd = vFdMap->get (fd);
	      if (vFd == 0 || vFd == VIRTUAL_FD_NONE
		  || (fData = fDataMap->get (vFd)) == NULL)
		{
		  fData = new FileData (UNKNOWNFD_FILENAME);
		  fData->setVirtualFd (virtualFd);
		  fData->setFsType ("N/A");
		  fData->setFileDes (fd);
		  fDataMap->put (virtualFd, fData);
		  vFdMap->put (fd, virtualFd);
		  vFd = virtualFd;
		  virtualFd++;
		}
	    }
	  else
	    {
	      Dprintf (DEBUG_IO,
		       NTXT ("*** Error IO tracing: (open) unknown open IO type, fd=%d  origFd=%d\n"), fd, origFd);
	      continue;
	    }

	  dview->setValue (PROP_IOVFD, i, vFd);
	  break;

	case OPEN_TRACE_ERROR:
	  fName = NULL;

	  sb = (StringBuilder*) dview->getObjValue (PROP_IOFNAME, i);
	  if (sb != NULL && sb->length () > 0)
	    fName = sb->toString ();
	  fd = dview->getIntValue (PROP_IOFD, i);
	  origFd = dview->getIntValue (PROP_IOOFD, i);
	  fsType = (FileSystem_type) dview->getIntValue (PROP_IOFSTYPE, i);

	  if (fName != NULL)
	    {
	      fData = new FileData (fName);
	      fDataMap->put (virtualFd, fData);
	      fData->setFileDes (fd);
	      fData->setFsType (fsType);
	      fData->setVirtualFd (virtualFd);
	      vFd = virtualFd;
	      virtualFd++;
	    }
	  else if (origFd > 0)
	    {
	      vFd = vFdMap->get (origFd);
	      if (vFd == 0 || vFd == VIRTUAL_FD_NONE)
		{
		  Dprintf (DEBUG_IO,
			   "*** Error IO tracing: (open error) cannot get the virtual file descriptor, fd=%d  origFd=%d\n",
			   fd, origFd);
		  continue;
		}
	      else if ((fDataOrig = fDataMap->get (vFd)) == NULL)
		{
		  Dprintf (DEBUG_IO,
			   "*** Error IO tracing: (open error) cannot get original FileData object, fd=%d  origFd=%d\n",
			   fd, origFd);
		  continue;
		}
	      else
		{
		  fName = fDataOrig->getFileName ();
		  fData = new FileData (fName);
		  fData->setFileDes (fd);
		  fData->setFsType (fDataOrig->getFsType ());
		  fData->setVirtualFd (virtualFd);
		  fDataMap->put (virtualFd, fData);
		  vFd = virtualFd;
		  virtualFd++;
		}
	    }

	  dview->setValue (PROP_IOVFD, i, vFd);
	  break;

	case CLOSE_TRACE:
	case CLOSE_TRACE_ERROR:
	  fd = dview->getIntValue (PROP_IOFD, i);
	  vFd = vFdMap->get (fd);
	  if (vFd == 0 || vFd == VIRTUAL_FD_NONE)
	    {
	      Dprintf (DEBUG_IO,
		       "*** Error IO tracing: (close) cannot get the virtual file descriptor, fd=%d\n",
		       fd);
	      continue;
	    }
	  fData = fDataMap->get (vFd);
	  if (fData == NULL)
	    {
	      Dprintf (DEBUG_IO,
		       "*** Error IO tracing: (close) cannot get the FileData object, fd=%d\n",
		       fd);
	      continue;
	    }

	  vFdMap->put (fd, VIRTUAL_FD_NONE);
	  dview->setValue (PROP_IOVFD, i, vFd);
	  break;

	case OTHERIO_TRACE:
	case OTHERIO_TRACE_ERROR:
	  vFd = VIRTUAL_FD_OTHERIO;
	  fData = fDataMap->get (vFd);
	  if (fData == NULL)
	    {
	      Dprintf (DEBUG_IO,
		       "*** Error IO tracing: (other IO) cannot get the FileData object\n");
	      continue;
	    }

	  dview->setValue (PROP_IOVFD, i, vFd);
	  break;
	case IOTRACETYPE_LAST:
	  break;
	}
    }

  delete dview;

  return dDscr;
}

DataDescriptor *
Experiment::get_heap_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_HEAP);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () > 0)
    return dDscr;

  char *base_name = get_basename (expt_name);
  char *msg = dbe_sprintf (GTXT ("Loading Heap Trace Data: %s"), base_name);
  read_data_file (SP_HEAPTRACE_FILE, msg);
  free (msg);

  if (dDscr->getSize () == 0)
    return dDscr;
  resolve_frame_info (dDscr);

  // Match FREE to MALLOC
  PropDescr *prop = new PropDescr (PROP_HLEAKED, NTXT ("HLEAKED"));
  prop->uname = dbe_strdup (GTXT ("Bytes Leaked"));
  prop->vtype = TYPE_UINT64;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_HMEM_USAGE, NTXT ("HMEM_USAGE"));
  prop->uname = dbe_strdup (GTXT ("Heap Memory Usage"));
  prop->vtype = TYPE_UINT64;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_HFREED, NTXT ("HFREED"));
  prop->uname = dbe_strdup (GTXT ("Bytes Freed"));
  prop->vtype = TYPE_UINT64;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_HCUR_ALLOCS, NTXT ("HCUR_ALLOCS"));
  prop->uname = dbe_strdup (GTXT ("Net Bytes Allocated"));
  prop->vtype = TYPE_INT64;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_HCUR_LEAKS, NTXT ("HCUR_LEAKS"));
  prop->uname = dbe_strdup (GTXT ("Net Bytes Leaked"));
  prop->vtype = TYPE_UINT64;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_HCUR_NET_ALLOC, NTXT ("HCUR_NET_ALLOC"));
  prop->vtype = TYPE_INT64;
  prop->flags = DDFLAG_NOSHOW;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_DDSCR_LNK, NTXT ("DDSCR_LNK"));
  prop->vtype = TYPE_UINT64;
  prop->flags = DDFLAG_NOSHOW;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_VOIDP_OBJ, NTXT ("VOIDP_OBJ"));
  prop->vtype = TYPE_OBJ;
  prop->flags = DDFLAG_NOSHOW;
  dDscr->addProperty (prop);

  prop = new PropDescr (PROP_TSTAMP2, NTXT ("TSTAMP2"));
  prop->uname = dbe_strdup (GTXT ("End Timestamp (nanoseconds)"));
  prop->vtype = TYPE_UINT64;
  prop->flags = DDFLAG_NOSHOW;
  dDscr->addProperty (prop);

  DataView *dview = dDscr->createView ();
  dview->sort (PROP_TSTAMP);

  // Keep track of memory usage
  Size memoryUsage = 0;

  HeapMap *heapmap = new HeapMap ();
  long sz = dview->getSize ();
  for (long i = 0; i < sz; i++)
    {

      Heap_type mtype = (Heap_type) dview->getIntValue (PROP_HTYPE, i);
      Vaddr vaddr = dview->getULongValue (PROP_HVADDR, i);
      Vaddr ovaddr = dview->getULongValue (PROP_HOVADDR, i);
      Size hsize = dview->getULongValue (PROP_HSIZE, i);
      hrtime_t tstamp = dview->getLongValue (PROP_TSTAMP, i);

      switch (mtype)
	{
	case MALLOC_TRACE:
	  dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME);
	  if (vaddr)
	    {
	      dview->setValue (PROP_HLEAKED, i, hsize);
	      heapmap->allocate (vaddr, i + 1);

	      // Increase heap size
	      memoryUsage += hsize;
	      dview->setValue (PROP_HMEM_USAGE, i, memoryUsage);
	    }
	  break;

	case FREE_TRACE:
	  if (vaddr)
	    {
	      long idx = heapmap->deallocate (vaddr) - 1;
	      if (idx >= 0)
		{
		  // Decrease heap size
		  Size leaked = dview->getLongValue (PROP_HLEAKED, idx);
		  memoryUsage -= leaked;
		  dview->setValue (PROP_HMEM_USAGE, i, memoryUsage);

		  Size alloc = dview->getLongValue (PROP_HSIZE, idx);
		  // update allocation
		  dview->setValue (PROP_HLEAKED, idx, (uint64_t) 0);
		  dview->setValue (PROP_TSTAMP2, idx, tstamp);
		  dview->setValue (PROP_DDSCR_LNK, idx, dview->getIdByIdx (i) + 1);
		  // update this event
		  dview->setValue (PROP_HFREED, i, alloc);
		}
	    }
	  break;

	case REALLOC_TRACE:
	  dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME);
	  if (ovaddr)
	    {
	      long idx = heapmap->deallocate (ovaddr) - 1;
	      if (idx >= 0)
		{
		  // Decrease heap size
		  Size leaked = dview->getLongValue (PROP_HLEAKED, idx);
		  memoryUsage -= leaked;
		  dview->setValue (PROP_HMEM_USAGE, i, memoryUsage);

		  Size alloc = dview->getLongValue (PROP_HSIZE, idx);
		  // update allocation
		  dview->setValue (PROP_HLEAKED, idx, (uint64_t) 0);
		  dview->setValue (PROP_TSTAMP2, idx, tstamp);
		  dview->setValue (PROP_DDSCR_LNK, idx, dview->getIdByIdx (i) + 1);
		  // update this event
		  dview->setValue (PROP_HFREED, i, alloc);
		}
	    }
	  if (vaddr)
	    {
	      dview->setValue (PROP_HLEAKED, i, hsize);
	      heapmap->allocate (vaddr, i + 1);

	      // Increase heap size
	      memoryUsage += hsize;
	      dview->setValue (PROP_HMEM_USAGE, i, memoryUsage);
	    }
	  break;
	case MMAP_TRACE:
	case MUNMAP_TRACE:
	  // Adjust the size to be multiple of page_size
	  //hsize = (( hsize - 1 ) / page_size + 1 ) * page_size;
	  if (vaddr)
	    {
	      UnmapChunk *list;
	      if (mtype == MMAP_TRACE)
		{
		  dview->setValue (PROP_TSTAMP2, i, (uint64_t) MAX_TIME);
		  dview->setValue (PROP_HLEAKED, i, hsize);
		  list = heapmap->mmap (vaddr, hsize, i);

		  // Increase heap size
		  memoryUsage += hsize;
		  dview->setValue (PROP_HMEM_USAGE, i, memoryUsage);
		}
	      else
		{ // MUNMAP_TRACE
		  list = heapmap->munmap (vaddr, hsize);

		  // Set allocation size to zero
		  // Note: We're currently reusing PROP_HSIZE to mean allocation size
		  // If we ever need to save the original HSIZE, we'll need to
		  // create a new PROP_* to represent event allocation size
		  //
		  //	For now, tuck the original size away as HOVADDR
		  dview->setValue (PROP_HOVADDR, i, (uint64_t) hsize);
		  dview->setValue (PROP_HSIZE, i, (uint64_t) 0);
		}
	      Size total_freed = 0;
	      while (list)
		{
		  long idx = list->val;
		  total_freed += list->size;
		  Size leaked = dview->getLongValue (PROP_HLEAKED, idx);

		  // Decrease heap size
		  memoryUsage -= list->size;
		  dview->setValue (PROP_HMEM_USAGE, i, memoryUsage);

		  Size leak_update = leaked - list->size;
		  // update allocation
		  dview->setValue (PROP_HLEAKED, idx, leak_update);
		  // update allocation's list of frees
		  {
		    UnmapChunk *copy = new UnmapChunk;
		    heapUnmapEvents->append (copy);
		    copy->val = dview->getIdByIdx (i);
		    copy->size = list->size;
		    copy->next = (UnmapChunk *) dview->getObjValue (PROP_VOIDP_OBJ, idx);
		    dview->setObjValue (PROP_VOIDP_OBJ, idx, copy);
		  }
		  if (leak_update <= 0)
		    if (leak_update == 0)
		      dview->setValue (PROP_TSTAMP2, idx, tstamp);
		  UnmapChunk *t = list;
		  list = list->next;
		  delete t;
		}
	      // update this event
	      if (total_freed)
		// only need to write value if it is non-zero
		dview->setValue (PROP_HFREED, i, total_freed);
	    }
	  break;
	  // ignoring HEAPTYPE_LAST, which will never be recorded
	case HEAPTYPE_LAST:
	  break;
	}
    }
  delete heapmap;
  delete dview;

  return dDscr;
}

DataDescriptor *
Experiment::get_heapsz_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_HEAPSZ);
  if (dDscr)
    return dDscr;
  dDscr = get_heap_events (); // derived from DATA_HEAP
  if (dDscr == NULL)
    return NULL;
  dDscr = newDataDescriptor (DATA_HEAPSZ, 0, dDscr);
  return dDscr;
}

static void
update_heapsz_packet (std::set<long> &pkt_id_set, DataView *dview,
		      long alloc_pkt_id, int64_t net_alloc, uint64_t leaks)
{
  // pkt_id_set: set is updated to include packet
  // alloc_pkt_id: data descriptor id (NOT dview idx)
  // net_alloc: adjustment to net allocation for this packet (note: signed value)
  // leaks: leak bytes to attribute to alloc_pkt_id
  std::pair < std::set<long>::iterator, bool> ret;
  ret = pkt_id_set.insert (alloc_pkt_id); // add to set
  bool new_to_set = ret.second; // was not in set
  if (!new_to_set)
    {
      // Has been seen before, update values
      net_alloc += dview->getDataDescriptorValue (PROP_HCUR_NET_ALLOC, alloc_pkt_id);
      if (leaks)
	{
	  uint64_t old = dview->getDataDescriptorValue (PROP_HCUR_LEAKS, alloc_pkt_id);
	  if (old != 0)
	    leaks = old;
	}
    }
  dview->setDataDescriptorValue (PROP_HCUR_NET_ALLOC, alloc_pkt_id, net_alloc);
  dview->setDataDescriptorValue (PROP_HCUR_LEAKS, alloc_pkt_id, leaks);
}

DataView *
Experiment::create_heapsz_data_view (DataView *heap_dview)
{
  // heap_dview has DATA_HEAP _filtered_ packets.
  // This creates, populates, and returns DATA_HEAPSZ DataView
  DataDescriptor *dDscr = get_heapsz_events ();
  if (dDscr == NULL)
    return NULL;
  std::set<long> pkt_id_set;
  DataView *dview = heap_dview;
  long sz = dview->getSize ();
  for (long i = 0; i < sz; i++)
    {
      int64_t hsize = (int64_t) dview->getULongValue (PROP_HSIZE, i);
      uint64_t leaks = dview->getULongValue (PROP_HLEAKED, i);
      long alloc_pkt_id = dview->getIdByIdx (i);
      update_heapsz_packet (pkt_id_set, dview, alloc_pkt_id, hsize, leaks);

      // linked free
      UnmapChunk *mmap_frees = (UnmapChunk *) dview->getObjValue (PROP_VOIDP_OBJ, i); // mmap metadata
      if (mmap_frees)
	{
	  // mmap: all frees associated with this packet
	  while (mmap_frees)
	    {
	      long free_pkt_id = mmap_frees->val;
	      int64_t free_sz = mmap_frees->size;
	      update_heapsz_packet (pkt_id_set, dview, free_pkt_id, -free_sz, 0);
	      mmap_frees = mmap_frees->next;
	    }
	}
      else
	{
	  // malloc: check for associated free
	  long free_pkt_id = dview->getLongValue (PROP_DDSCR_LNK, i) - 1;
	  if (free_pkt_id >= 0)
	    update_heapsz_packet (pkt_id_set, dview, free_pkt_id, -hsize, 0);
	}
    }

  // create a new DataView based on the filtered-in and associated free events
  std::set<long>::iterator it;
  DataView *heapsz_dview = dDscr->createExtManagedView ();
  for (it = pkt_id_set.begin (); it != pkt_id_set.end (); ++it)
    {
      long ddscr_pkt_id = *it;
      heapsz_dview->appendDataDescriptorId (ddscr_pkt_id);
    }
  compute_heapsz_data_view (heapsz_dview);
  return heapsz_dview;
}

void
Experiment::compute_heapsz_data_view (DataView *heapsz_dview)
{
  DataView *dview = heapsz_dview;

  // Keep track of memory usage
  int64_t currentAllocs = 0;
  Size currentLeaks = 0;
  dview->sort (PROP_TSTAMP);
  long sz = dview->getSize ();
  for (long i = 0; i < sz; i++)
    {
      int64_t net_alloc = dview->getLongValue (PROP_HCUR_NET_ALLOC, i);
      currentAllocs += net_alloc;
      dview->setValue (PROP_HCUR_ALLOCS, i, currentAllocs);

      Size leaks = dview->getULongValue (PROP_HCUR_LEAKS, i);
      currentLeaks += leaks;
      dview->setValue (PROP_HCUR_LEAKS, i, currentLeaks);
    }
}

void
Experiment::DBG_memuse (Sample * s)
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_HEAP);
  if (dDscr == NULL || dDscr->getSize () == 0)
    return;

  DataView *dview = dDscr->createView ();
  dview->sort (PROP_TSTAMP);
  hrtime_t ts1 = s->get_start_time ();
  hrtime_t ts2 = s->get_end_time ();

  HeapMap *heapmap = new HeapMap ();
  long sz = dview->getSize ();
  Size maxSize = 0;
  Size curSize = 0;
  hrtime_t maxTime = 0;
  for (long i = 0; i < sz; i++)
    {
      hrtime_t tstamp = dview->getLongValue (PROP_TSTAMP, i);
      if (tstamp < ts1)
	continue;
      if (tstamp >= ts2)
	break;

      Heap_type mtype = (Heap_type) dview->getIntValue (PROP_HTYPE, i);
      Vaddr vaddr = dview->getULongValue (PROP_HVADDR, i);
      Vaddr ovaddr = dview->getULongValue (PROP_HOVADDR, i);
      switch (mtype)
	{
	case REALLOC_TRACE:
	  break;
	case MALLOC_TRACE:
	  ovaddr = 0;
	  break;
	case FREE_TRACE:
	  ovaddr = vaddr;
	  vaddr = 0;
	  break;
	default:
	  vaddr = 0;
	  ovaddr = 0;
	  break;
	}
      if (ovaddr)
	{
	  long idx = heapmap->deallocate (ovaddr) - 1;
	  if (idx >= 0)
	    curSize -= dview->getULongValue (PROP_HSIZE, idx);
	}
      if (vaddr)
	{
	  heapmap->allocate (vaddr, i + 1);
	  curSize += dview->getULongValue (PROP_HSIZE, i);
	  if (curSize > maxSize)
	    {
	      maxSize = curSize;
	      maxTime = tstamp;
	    }
	}
    }
  printf ("SAMPLE=%s (id=%d) MEMUSE=%lld TSTAMP=%lld\n", s->get_start_label (),
	  s->get_number (), maxSize, maxTime - getStartTime ());
  delete dview;
  delete heapmap;
}

void
Experiment::DBG_memuse (const char *sname)
{
  for (int i = 0; i < samples->size (); ++i)
    {
      Sample *sample = samples->fetch (i);
      if (streq (sname, sample->get_start_label ()))
	{
	  DBG_memuse (sample);
	  break;
	}
    }
}

DataDescriptor *
Experiment::get_race_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_RACE);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () == 0)
    {
      char *base_name = get_basename (expt_name);
      char *msg = dbe_sprintf (GTXT ("Loading Race Data: %s"), base_name);
      read_data_file (SP_RACETRACE_FILE, msg);
      free (msg);
      resolve_frame_info (dDscr);
    }
  return dDscr;
}

DataDescriptor *
Experiment::get_deadlock_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_DLCK);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () == 0)
    {
      char *base_name = get_basename (expt_name);
      char *msg = dbe_sprintf (GTXT ("Loading Deadlocks Data: %s"), base_name);
      read_data_file (SP_DEADLOCK_FILE, msg);
      free (msg);
      resolve_frame_info (dDscr);
    }
  return dDscr;
}

DataDescriptor *
Experiment::get_sample_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_SAMPLE);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () > 0)
    return dDscr;

  // read_overview_file(); //YXXX do this here at some point instead of:
  PropDescr *tmp_propDscr;
  tmp_propDscr = new PropDescr (PROP_SMPLOBJ, NTXT ("SMPLOBJ"));
  tmp_propDscr->uname = NULL;
  tmp_propDscr->vtype = TYPE_OBJ;
  dDscr->addProperty (tmp_propDscr);

  tmp_propDscr = new PropDescr (PROP_TSTAMP, NTXT ("TSTAMP"));
  tmp_propDscr->uname = dbe_strdup ("High resolution timestamp");
  tmp_propDscr->vtype = TYPE_UINT64;
  dDscr->addProperty (tmp_propDscr);

  tmp_propDscr = new PropDescr (PROP_SAMPLE, NTXT ("SAMPLE"));
  tmp_propDscr->uname = dbe_strdup ("Sample number");
  tmp_propDscr->vtype = TYPE_UINT64;
  dDscr->addProperty (tmp_propDscr);

  tmp_propDscr = new PropDescr (PROP_EVT_TIME, NTXT ("EVT_TIME"));
  tmp_propDscr->uname = dbe_strdup ("Event duration");
  tmp_propDscr->vtype = TYPE_UINT64;
  dDscr->addProperty (tmp_propDscr);

  long ssize = samples->size ();
  for (long ii = 0; ii < ssize; ii++)
    {
      Sample * sample = samples->fetch (ii);
      long recn = dDscr->addRecord ();
      hrtime_t sduration = sample->get_end_time () - sample->get_start_time ();
      dDscr->setObjValue (PROP_SMPLOBJ, recn, sample);
      dDscr->setValue (PROP_SAMPLE, recn, sample->get_number ());
      dDscr->setValue (PROP_TSTAMP, recn, sample->get_end_time ());
      dDscr->setValue (PROP_EVT_TIME, recn, sduration);
    }
  return dDscr;
}

DataDescriptor *
Experiment::get_gc_events ()
{
  DataDescriptor *dDscr = getDataDescriptor (DATA_GCEVENT);
  if (dDscr == NULL)
    return NULL;
  if (dDscr->getSize () > 0)
    return dDscr;

  // read_overview_file(); //YXXX do this here at some point instead of:
  PropDescr *tmp_propDscr;
  tmp_propDscr = new PropDescr (PROP_GCEVENTOBJ, NTXT ("GCEVENTOBJ"));
  tmp_propDscr->uname = NULL;
  tmp_propDscr->vtype = TYPE_OBJ;
  dDscr->addProperty (tmp_propDscr);

  tmp_propDscr = new PropDescr (PROP_TSTAMP, NTXT ("TSTAMP"));
  tmp_propDscr->uname = dbe_strdup ("High resolution timestamp");
  tmp_propDscr->vtype = TYPE_UINT64;
  dDscr->addProperty (tmp_propDscr);

  tmp_propDscr = new PropDescr (PROP_GCEVENT, NTXT ("GCEVENT"));
  tmp_propDscr->uname = dbe_strdup ("GCEvent number");
  tmp_propDscr->vtype = TYPE_UINT64;
  dDscr->addProperty (tmp_propDscr);

  tmp_propDscr = new PropDescr (PROP_EVT_TIME, NTXT ("EVT_TIME"));
  tmp_propDscr->uname = dbe_strdup ("Event duration");
  tmp_propDscr->vtype = TYPE_UINT64;
  dDscr->addProperty (tmp_propDscr);

  long ssize = gcevents->size ();
  for (long ii = 0; ii < ssize; ii++)
    {
      GCEvent * gcevent = gcevents->fetch (ii);
      long recn = dDscr->addRecord ();
      hrtime_t sduration = gcevent->end - gcevent->start;
      dDscr->setObjValue (PROP_GCEVENTOBJ, recn, gcevent);
      dDscr->setValue (PROP_GCEVENT, recn, gcevent->id);
      dDscr->setValue (PROP_TSTAMP, recn, gcevent->end);
      dDscr->setValue (PROP_EVT_TIME, recn, sduration);
    }
  return dDscr;
}

void
Experiment::update_last_event (hrtime_t ts/*wall_ts*/)
{
  if (last_event == ZERO_TIME)
    {
      // not yet initialized
      last_event = ts;
    }
  if (last_event - exp_start_time < ts - exp_start_time)
    // compare deltas to avoid hrtime_t wrap
    last_event = ts;
}

void
Experiment::write_header ()
{
  StringBuilder sb;

  // write commentary to the experiment, describing the parameters
  if (dbeSession->ipc_mode || dbeSession->rdt_mode)
    {
      // In GUI: print start time at the beginning
      time_t t = (time_t) start_sec;
      char *start_time = ctime (&t);
      if (start_time != NULL)
	{
	  sb.setLength (0);
	  sb.sprintf (GTXT ("Experiment started %s"), start_time);
	  commentq->append (new Emsg (CMSG_COMMENT, sb));
	}
    }
  // write message with target arglist
  if (uarglist != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("\nTarget command (%s): '%s'"),
		  (wsize == W32 ? "32-bit" : "64-bit"), uarglist);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  sb.setLength (0);
  sb.sprintf (GTXT ("Process pid %d, ppid %d, pgrp %d, sid %d"),
	      pid, ppid, pgrp, sid);
  commentq->append (new Emsg (CMSG_COMMENT, sb));

  // add comment for user name, if set
  if (username != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("User: `%s'"), username);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // add comment for current working directory
  if (ucwd != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("Current working directory: %s"), ucwd);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // add comment for collector version string
  if (cversion != NULL)
    {
      char *wstring;
      switch (wsize)
	{
	case Wnone:
	  wstring = NTXT ("?");
	  break;
	case W32:
	  wstring = GTXT ("32-bit");
	  break;
	case W64:
	  wstring = GTXT ("64-bit");
	  break;
	default:
	  wstring = NTXT ("??");
	  break;
	}
      sb.setLength (0);
      sb.sprintf (GTXT ("Collector version: `%s'; experiment version %d.%d (%s)"),
		  cversion, exp_maj_version, exp_min_version, wstring);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // add comment for driver version string (er_kernel)
  if (dversion != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("Kernel driver version: `%s'"), dversion);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  if (jversion != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("JVM version: `%s'"), jversion);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // add comment for hostname, parameters
  if (hostname == NULL)
    hostname = dbe_strdup (GTXT ("unknown"));
  if (os_version == NULL)
    os_version = dbe_strdup (GTXT ("unknown"));
  if (architecture == NULL)
    architecture = dbe_strdup (GTXT ("unknown"));
  sb.setLength (0);
  sb.sprintf (GTXT ("Host `%s', OS `%s', page size %d, architecture `%s'"),
	      hostname, os_version, page_size, architecture);
  commentq->append (new Emsg (CMSG_COMMENT, sb));

  sb.setLength (0);
  if (maxclock != minclock)
    {
      clock = maxclock;
      sb.sprintf (
		  GTXT ("  %d CPUs, with clocks ranging from %d to %d MHz.; max of %d MHz. assumed"),
		  ncpus, minclock, maxclock, clock);
    }
  else
    sb.sprintf (GTXT ("  %d CPU%s, clock speed %d MHz."),
		ncpus, (ncpus == 1 ? NTXT ("") : "s"), clock);
  commentq->append (new Emsg (CMSG_COMMENT, sb));

  // add comment for machine memory size
  if (page_size > 0 && npages > 0)
    {
      long long memsize = ((long long) npages * page_size) / (1024 * 1024);
      sb.setLength (0);
      sb.sprintf (GTXT ("  Memory: %d pages @  %d = %lld MB."),
		  npages, page_size, memsize);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // add comment for machine memory size
  if (machinemodel != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Machine model: %s"), machinemodel);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }

  // add comment for start time
  time_t t = (time_t) start_sec;
  char *p = ctime (&t);
  sb.setLength (0);
  if (p != NULL)
    sb.sprintf (GTXT ("Experiment started %s"), p);
  else
    sb.sprintf (GTXT ("\nExperiment start not recorded"));
  write_coll_params ();
  commentq->append (new Emsg (CMSG_COMMENT, sb));
  commentq->appendqueue (runlogq);
  runlogq->mark_clear ();
}

void
Experiment::write_coll_params ()
{
  StringBuilder sb;

  // now write the various collection parameters as comments
  sb.setLength (0);
  sb.append (GTXT ("Data collection parameters:"));
  commentq->append (new Emsg (CMSG_COMMENT, sb));
  if (coll_params.profile_mode == 1)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Clock-profiling, interval = %d microsecs."),
		  (int) (coll_params.ptimer_usec));
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.sync_mode == 1)
    {
      sb.setLength (0);
      char *scope_str = NTXT ("");
      switch (coll_params.sync_scope)
	{
	case 0:
	  scope_str = GTXT ("Native- and Java-APIs");
	  break;
	case SYNCSCOPE_JAVA:
	  scope_str = GTXT ("JAVA-APIs");
	  break;
	case SYNCSCOPE_NATIVE:
	  scope_str = GTXT ("Native-APIs");
	  break;
	case SYNCSCOPE_JAVA | SYNCSCOPE_NATIVE:
	  scope_str = GTXT ("Native- and Java-APIs");
	  break;
	}
      if (coll_params.sync_threshold < 0)
	sb.sprintf (GTXT ("  Synchronization tracing, threshold = %d microsecs. (calibrated); %s"),
		    -coll_params.sync_threshold, scope_str);
      else
	sb.sprintf (GTXT ("  Synchronization tracing, threshold = %d microsecs.; %s"),
		    coll_params.sync_threshold, scope_str);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.heap_mode == 1)
    {
      sb.setLength (0);
      sb.append (GTXT ("  Heap tracing"));
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.io_mode == 1)
    {
      sb.setLength (0);
      sb.append (GTXT ("  IO tracing"));
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.race_mode == 1)
    {
      sb.setLength (0);
      char *race_stack_name;
      switch (coll_params.race_stack)
	{
	case 0:
	  race_stack_name = GTXT ("dual-stack");
	  break;
	case 1:
	  race_stack_name = GTXT ("single-stack");
	  break;
	case 2:
	  race_stack_name = GTXT ("leaf");
	  break;
	default:
	  abort ();
	}
      sb.sprintf (GTXT ("  Datarace detection, %s"), race_stack_name);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.deadlock_mode == 1)
    {
      sb.setLength (0);
      sb.append (GTXT ("  Deadlock detection"));
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.hw_mode == 1)
    {
      sb.setLength (0);
      if (hwc_default == true)
	sb.append (GTXT ("  HW counter-profiling (default); counters:"));
      else
	sb.append (GTXT ("  HW counter-profiling; counters:"));
      commentq->append (new Emsg (CMSG_COMMENT, sb));
      for (int i = 0; i < MAX_HWCOUNT; i++)
	{
	  if (!coll_params.hw_aux_name[i])
	    continue;
	  sb.setLength (0);
	  sb.sprintf (GTXT ("    %s, tag %d, interval %d, memop %d"),
		      coll_params.hw_aux_name[i], i,
		      coll_params.hw_interval[i], coll_params.hw_tpc[i]);
	  commentq->append (new Emsg (CMSG_COMMENT, sb));
	}
    }
  if (coll_params.sample_periodic == 1)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Periodic sampling, %d secs."),
		  coll_params.sample_timer);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.limit != 0)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Experiment size limit, %d"),
		  coll_params.limit);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.linetrace != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Follow descendant processes from: %s"),
		  coll_params.linetrace);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.pause_sig != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Pause signal %s"), coll_params.pause_sig);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.sample_sig != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Sample signal %s"), coll_params.sample_sig);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.start_delay != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Data collection delay start %s seconds"), coll_params.start_delay);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  if (coll_params.terminate != NULL)
    {
      sb.setLength (0);
      sb.sprintf (GTXT ("  Data collection termination after %s seconds"), coll_params.terminate);
      commentq->append (new Emsg (CMSG_COMMENT, sb));
    }
  // add a blank line after data description
  commentq->append (new Emsg (CMSG_COMMENT, NTXT ("")));
}


/*
 *    Raw packet processing
 */
static int
check_mstate (char *ptr, PacketDescriptor *pDscr, int arg)
{
  switch (arg)
    {
    case PROP_UCPU:
    case PROP_SCPU:
    case PROP_TRAP:
    case PROP_TFLT:
    case PROP_DFLT:
    case PROP_KFLT:
    case PROP_ULCK:
    case PROP_TSLP:
    case PROP_WCPU:
    case PROP_TSTP:
      break;
    default:
      return 0;
    }
  Vector<FieldDescr*> *fields = pDscr->getFields ();
  for (int i = 0, sz = fields->size (); i < sz; i++)
    {
      FieldDescr *fDscr = fields->fetch (i);
      if (fDscr->propID == arg)
	return *((int*) (ptr + fDscr->offset));
    }
  return 0;
}

#define PACKET_ALIGNMENT 4

uint64_t
Experiment::readPacket (Data_window *dwin, Data_window::Span *span)
{
  Common_packet *rcp = (Common_packet *) dwin->bind (span,
						    sizeof (CommonHead_packet));
  uint16_t v16;
  uint64_t size = 0;
  if (rcp)
    {
      if ((((long) rcp) % PACKET_ALIGNMENT) != 0)
	{
	  invalid_packet++;
	  size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK;
	  return size;
	}
      v16 = (uint16_t) rcp->tsize;
      size = dwin->decode (v16);
      if (size == 0)
	{
	  size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK;
	  return size;
	}
      rcp = (Common_packet *) dwin->bind (span, size);
    }
  if (rcp == NULL)
    return 0;

  if ((((long) rcp) % PACKET_ALIGNMENT) != 0)
    {
      invalid_packet++;
      size = PROFILE_BUFFER_CHUNK - span->offset % PROFILE_BUFFER_CHUNK;
      return size;
    }
  v16 = (uint16_t) rcp->type;
  uint32_t rcptype = dwin->decode (v16);
  if (rcptype == EMPTY_PCKT)
    return size;
  if (rcptype == FRAME_PCKT)
    {
      RawFramePacket *fp = new RawFramePacket;
      fp->uid = dwin->decode (((Frame_packet*) rcp)->uid);
      fp->uidn = NULL;
      fp->uidj = NULL;
      fp->omp_uid = NULL;
      fp->omp_state = 0;
      char *ptr = (char*) rcp + dwin->decode (((Frame_packet*) rcp)->hsize);
      if ((((long) ptr) % PACKET_ALIGNMENT) != 0)
	{
	  invalid_packet++;
	  delete fp;
	  return size;
	}
      v16 = (uint16_t) ((Frame_packet*) rcp)->tsize;
      char *end = (char*) rcp + dwin->decode (v16);
      for (; ptr < end;)
	{
	  Common_info *cinfo = (Common_info*) ptr;
	  uint32_t hsize = dwin->decode (cinfo->hsize);
	  if (hsize == 0 || ptr + hsize > end)
	    break;
	  int kind = dwin->decode (cinfo->kind);
	  bool compressed = false;
	  if (kind & COMPRESSED_INFO)
	    {
	      compressed = true;
	      kind &= ~COMPRESSED_INFO;
	    }
	  switch (kind)
	    {
	    case STACK_INFO:
	      {
		char *stack = ptr + sizeof (Stack_info);
		size_t stack_size = hsize - sizeof (Stack_info);
		uint64_t uidn = dwin->decode (((Stack_info*) cinfo)->uid);
		if (stack_size <= 0)
		  {
		    fp->uidn = get_uid_node (uidn);
		    break;
		  }
		uint64_t link_uid = (uint64_t) 0;
		if (compressed)
		  {
		    stack_size -= sizeof (uint64_t);
		    unsigned char *s = (unsigned char*) (stack + stack_size);
		    int shift = 0;
		    for (size_t i = 0; i<sizeof (link_uid); i++)
		      {
			link_uid |= (uint64_t) * s++ << shift;
			shift += 8;
		      }
		  }
		if (wsize == W32)
		  fp->uidn = add_uid (dwin, uidn,
				      (int) (stack_size / sizeof (uint32_t)),
				      (uint32_t*) stack, link_uid);
		else
		  fp->uidn = add_uid (dwin, uidn,
				      (int) (stack_size / sizeof (uint64_t)),
				      (uint64_t*) stack, link_uid);
		break;
	      }
	    case JAVA_INFO:
	      {
		char *stack = ptr + sizeof (Java_info);
		size_t stack_size = hsize - sizeof (Java_info);
		uint64_t uidj = dwin->decode (((Java_info*) cinfo)->uid);
		if (stack_size <= 0)
		  {
		    fp->uidj = get_uid_node (uidj);
		    break;
		  }

		uint64_t link_uid = (uint64_t) 0;
		if (compressed)
		  {
		    stack_size -= sizeof (uint64_t);
		    unsigned char *s = (unsigned char*) (stack + stack_size);
		    int shift = 0;
		    for (size_t i = 0; i<sizeof (link_uid); i++)
		      {
			link_uid |= (uint64_t) * s++ << shift;
			shift += 8;
		      }
		  }
		if (wsize == W32)
		  fp->uidj = add_uid (dwin, uidj,
				      (int) (stack_size / sizeof (uint32_t)),
				      (uint32_t*) stack, link_uid);
		else
		  {
		    // bug 6909545: garbage in 64-bit JAVA_INFO
		    char *nstack = (char*) xmalloc (stack_size);
		    char *dst = nstack;
		    char *srcmax = stack + stack_size - sizeof (uint64_t);
		    for (char *src = stack; src <= srcmax;)
		      {
			int64_t val = dwin->decode (*(int32_t*) src);
			*(uint64_t*) dst = dwin->decode (val);
			src += sizeof (uint64_t);
			dst += sizeof (uint64_t);
			if (src > srcmax)
			  {
			    fprintf (stderr, "er_print: Experiment::readPacket: Error in data: src=%llx greater than %llx\n",
				     (long long) src, (long long) srcmax);
			    break;
			  }
			*(uint64_t*) dst = *(uint64_t*) src;
			src += sizeof (uint64_t);
			dst += sizeof (uint64_t);
		      }
		    fp->uidj = add_uid (dwin, uidj,
					(int) (stack_size / sizeof (uint64_t)),
					(uint64_t*) nstack, link_uid);
		    free (nstack);
		  }
		break;
	      }
	    case OMP_INFO:
	      fp->omp_state = dwin->decode (((OMP_info*) ptr)->omp_state);
	      break;
	    case OMP2_INFO:
	      {
		uint64_t omp_uid = dwin->decode (((OMP2_info*) ptr)->uid);
		fp->omp_uid = get_uid_node (omp_uid);
		fp->omp_state = dwin->decode (((OMP2_info*) ptr)->omp_state);
		break;
	      }
	    default:
	      break;
	    }
	  ptr += hsize;
	}
      frmpckts->append (fp);
      return size;
    }
  else if (rcptype == UID_PCKT)
    {
      Uid_packet *uidp = (Uid_packet*) rcp;
      uint64_t uid = dwin->decode (uidp->uid);
      char *arr_bytes = (char*) (uidp + 1);
      v16 = (uint16_t) rcp->tsize;
      size_t arr_length = dwin->decode (v16) - sizeof (Uid_packet);
      if (arr_length <= 0)
	return size;
      uint64_t link_uid = (uint64_t) 0;
      if (dwin->decode (uidp->flags) & COMPRESSED_INFO)
	{
	  arr_length -= sizeof (uint64_t);
	  unsigned char *s = (unsigned char*) (arr_bytes + arr_length);
	  int shift = 0;
	  for (size_t i = 0; i<sizeof (link_uid); i++)
	    {
	      link_uid |= (uint64_t) * s++ << shift;
	      shift += 8;
	    }
	}
      if (wsize == W32)
	add_uid (dwin, uid, (int) (arr_length / sizeof (uint32_t)),
		 (uint32_t*) arr_bytes, link_uid);
      else
	add_uid (dwin, uid, (int) (arr_length / sizeof (uint64_t)),
		 (uint64_t*) arr_bytes, link_uid);
      return size;
    }

  PacketDescriptor *pcktDescr = getPacketDescriptor (rcptype);
  if (pcktDescr == NULL)
    return size;
  DataDescriptor *dataDescr = pcktDescr->getDataDescriptor ();
  if (dataDescr == NULL)
    return size;

  /* omazur: TBR START -- old experiment */
  if (rcptype == PROF_PCKT)
    {
      // For backward compatibility with older SS12 experiments
      int numstates = get_params ()->lms_magic_id; // ugly, for old experiments
      if (numstates > LMS_NUM_SOLARIS_MSTATES)
	numstates = LMS_NUM_SOLARIS_MSTATES;
      for (int i = 0; i < numstates; i++)
	if (check_mstate ((char*) rcp, pcktDescr, PROP_UCPU + i))
	  readPacket (dwin, (char*) rcp, pcktDescr, dataDescr, PROP_UCPU + i,
		      size);
    }
  else
    readPacket (dwin, (char*) rcp, pcktDescr, dataDescr, 0, size);
  return size;
}

static uint32_t get_v32(char *p)
{
  uint32_t v;
  memcpy (&v, p, sizeof(uint32_t));
  return v;
}

static uint64_t get_v64(char *p)
{
  uint64_t v;
  memcpy (&v, p, sizeof(uint64_t));
  return v;
}

void
Experiment::readPacket (Data_window *dwin, char *ptr, PacketDescriptor *pDscr,
			DataDescriptor *dDscr, int arg, uint64_t pktsz)
{
  long recn = dDscr->addRecord ();
  Vector<FieldDescr*> *fields = pDscr->getFields ();
  uint32_t v32;
  uint64_t v64;
  int sz = fields->size ();
  for (int i = 0; i < sz; i++)
    {
      FieldDescr *field = fields->fetch (i);
      if (field->propID == arg)
	{
	  v32 = get_v32(ptr + field->offset);
	  dDscr->setValue (PROP_NTICK, recn, dwin->decode (v32));
	  dDscr->setValue (PROP_MSTATE, recn, (uint32_t) (field->propID - PROP_UCPU));
	}
      if (field->propID == PROP_THRID || field->propID == PROP_LWPID
	  || field->propID == PROP_CPUID)
	{
	  uint64_t tmp64 = 0;
	  switch (field->vtype)
	    {
	    case TYPE_INT32:
	    case TYPE_UINT32:
	      v32 = get_v32 (ptr + field->offset);
	      tmp64 = dwin->decode (v32);
	      break;
	    case TYPE_INT64:
	    case TYPE_UINT64:
	      v64 = get_v64 (ptr + field->offset);
	      tmp64 = dwin->decode (v64);
	      break;
	    case TYPE_STRING:
	    case TYPE_DOUBLE:
	    case TYPE_OBJ:
	    case TYPE_DATE:
	    case TYPE_BOOL:
	    case TYPE_ENUM:
	    case TYPE_LAST:
	    case TYPE_NONE:
	      break;
	    }
	  uint32_t tag = mapTagValue ((Prop_type) field->propID, tmp64);
	  dDscr->setValue (field->propID, recn, tag);
	}
      else
	{
	  switch (field->vtype)
	    {
	    case TYPE_INT32:
	    case TYPE_UINT32:
	      v32 = get_v32 (ptr + field->offset);
	      dDscr->setValue (field->propID, recn, dwin->decode (v32));
	      break;
	    case TYPE_INT64:
	    case TYPE_UINT64:
	      v64 = get_v64 (ptr + field->offset);
	      dDscr->setValue (field->propID, recn, dwin->decode (v64));
	      break;
	    case TYPE_STRING:
	      {
		int len = (int) (pktsz - field->offset);
		if ((len > 0) && (ptr[field->offset] != 0))
		  {
		    StringBuilder *sb = new StringBuilder ();
		    sb->append (ptr + field->offset, 0, len);
		    dDscr->setObjValue (field->propID, recn, sb);
		  }
		break;
	      }
	      // ignoring the following cases (why?)
	    case TYPE_DOUBLE:
	    case TYPE_OBJ:
	    case TYPE_DATE:
	    case TYPE_BOOL:
	    case TYPE_ENUM:
	    case TYPE_LAST:
	    case TYPE_NONE:
	      break;
	    }
	}
    }
}

#define PROG_BYTE 102400 // update progress bar every PROG_BYTE bytes

void
Experiment::read_data_file (const char *fname, const char *msg)
{
  Data_window::Span span;
  off64_t total_len, remain_len;
  char *progress_bar_msg;
  int progress_bar_percent = -1;

  char *data_file_name = dbe_sprintf (NTXT ("%s/%s"), expt_name, fname);
  Data_window *dwin = new Data_window (data_file_name);
  // Here we can call stat(data_file_name) to get file size,
  // and call a function to reallocate vectors for clock profiling data
  free (data_file_name);
  if (dwin->not_opened ())
    {
      delete dwin;
      return;
    }
  dwin->need_swap_endian = need_swap_endian;

  span.offset = 0;
  span.length = dwin->get_fsize ();
  total_len = remain_len = span.length;
  progress_bar_msg = dbe_sprintf (NTXT ("%s %s"), NTXT ("  "), msg);
  invalid_packet = 0;
  for (;;)
    {
      uint64_t pcktsz = readPacket (dwin, &span);
      if (pcktsz == 0)
	break;
      // Update progress bar
      if ((span.length <= remain_len) && (remain_len > 0))
	{
	  int percent = (int) (100 * (total_len - remain_len) / total_len);
	  if (percent > progress_bar_percent)
	    {
	      progress_bar_percent += 10;
	      theApplication->set_progress (percent, progress_bar_msg);
	    }
	  remain_len -= PROG_BYTE;
	}
      span.length -= pcktsz;
      span.offset += pcktsz;
    }
  delete dwin;

  if (invalid_packet)
    {
      StringBuilder sb;
      sb.sprintf (GTXT ("WARNING: There are %d invalid packet(s) in the %s file"),
		  invalid_packet, fname);
      Emsg *m = new Emsg (CMSG_WARN, sb);
      warnq->append (m);
    }

  theApplication->set_progress (0, NTXT (""));
  free (progress_bar_msg);
}

int
Experiment::read_overview_file ()
{
  char *data_file_name = dbe_sprintf ("%s/%s", expt_name, SP_OVERVIEW_FILE);
  Data_window *dwin = new Data_window (data_file_name);
  free (data_file_name);
  if (dwin->not_opened ())
    {
      delete dwin;
      return 0;
    }
  dwin->need_swap_endian = need_swap_endian;
  newDataDescriptor (DATA_SAMPLE);

  Data_window::Span span;
  span.offset = 0;
  span.length = dwin->get_fsize ();

  PrUsage *data = NULL, *data_prev = NULL;
  Sample *sample;
  int sample_number = 1;

  int64_t prDataSize;
  if (wsize == W32)
    prDataSize = PrUsage::bind32Size ();
  else
    prDataSize = PrUsage::bind64Size ();

  while (span.length > 0)
    {
      data_prev = data;
      data = new PrUsage ();

      void *dw = dwin->bind (&span, prDataSize);
      if ((dw == NULL) || (prDataSize > span.length))
	{
	  Emsg *m = new Emsg (CMSG_ERROR, GTXT ("Warning: overview data file can't be read"));
	  warnq->append (m);
	  status = FAILURE;
	  delete dwin;
	  return status;
	}

      if (wsize == W32)
	data->bind32 (dw, need_swap_endian);
      else
	data->bind64 (dw, need_swap_endian);
      span.length -= prDataSize;
      span.offset += prDataSize;

      // Skip the first packet
      if (data_prev == NULL)
	continue;
      if (sample_number > samples->size ())
	{ // inconsistent log/overview
	  sample = new Sample (sample_number);
	  char * label = GTXT ("<unknown>");
	  sample->start_label = dbe_strdup (label);
	  sample->end_label = dbe_strdup (label);
	  samples->append (sample);
	}
      else
	sample = samples->fetch (sample_number - 1);
      sample_number++;
      sample->start_time = data_prev->pr_tstamp + 1;
      sample->end_time = data->pr_tstamp;
      sample->prusage = data_prev;

      data_prev->pr_rtime = data->pr_rtime - data_prev->pr_rtime;
      data_prev->pr_utime = data->pr_utime - data_prev->pr_utime;
      data_prev->pr_stime = data->pr_stime - data_prev->pr_stime;
      data_prev->pr_ttime = data->pr_ttime - data_prev->pr_ttime;
      data_prev->pr_tftime = data->pr_tftime - data_prev->pr_tftime;
      data_prev->pr_dftime = data->pr_dftime - data_prev->pr_dftime;
      data_prev->pr_kftime = data->pr_kftime - data_prev->pr_kftime;
      data_prev->pr_ltime = data->pr_ltime - data_prev->pr_ltime;
      data_prev->pr_slptime = data->pr_slptime - data_prev->pr_slptime;
      data_prev->pr_wtime = data->pr_wtime - data_prev->pr_wtime;
      data_prev->pr_stoptime = data->pr_stoptime - data_prev->pr_stoptime;
      data_prev->pr_minf = data->pr_minf - data_prev->pr_minf;
      data_prev->pr_majf = data->pr_majf - data_prev->pr_majf;
      data_prev->pr_nswap = data->pr_nswap - data_prev->pr_nswap;
      data_prev->pr_inblk = data->pr_inblk - data_prev->pr_inblk;
      data_prev->pr_oublk = data->pr_oublk - data_prev->pr_oublk;
      data_prev->pr_msnd = data->pr_msnd - data_prev->pr_msnd;
      data_prev->pr_mrcv = data->pr_mrcv - data_prev->pr_mrcv;
      data_prev->pr_sigs = data->pr_sigs - data_prev->pr_sigs;
      data_prev->pr_vctx = data->pr_vctx - data_prev->pr_vctx;
      data_prev->pr_ictx = data->pr_ictx - data_prev->pr_ictx;
      data_prev->pr_sysc = data->pr_sysc - data_prev->pr_sysc;
      data_prev->pr_ioch = data->pr_ioch - data_prev->pr_ioch;
      sample->get_usage (); // force validation
    }

  for (long smpNum = samples->size (); smpNum >= sample_number; smpNum--)
    {
      // overview file was truncated
      sample = samples->remove (smpNum - 1);
      delete sample;
    }

  if (data)
    {
      // Update last_event so that getEndTime() covers
      // all loadobjects, too.
      update_last_event (data->pr_tstamp);
      delete data;
    }
  delete dwin;
  return SUCCESS;
}

int
Experiment::uidNodeCmp (const void *a, const void *b)
{
  UIDnode *nd1 = *(UIDnode**) a;
  UIDnode *nd2 = *(UIDnode**) b;
  if (nd1->uid == nd2->uid)
    return 0;
  return nd1->uid < nd2->uid ? -1 : 1;
}

static uint64_t
funcAddr (uint32_t val)
{
  if (val == (uint32_t) SP_LEAF_CHECK_MARKER)
    return (uint64_t) SP_LEAF_CHECK_MARKER;
  if (val == (uint32_t) SP_TRUNC_STACK_MARKER)
    return (uint64_t) SP_TRUNC_STACK_MARKER;
  if (val == (uint32_t) SP_FAILED_UNWIND_MARKER)
    return (uint64_t) SP_FAILED_UNWIND_MARKER;
  return val;
}

Experiment::UIDnode *
Experiment::add_uid (Data_window *dwin, uint64_t uid, int size,
		     uint32_t *array, uint64_t link_uid)
{
  if (uid == (uint64_t) 0)
    return NULL;
  uint64_t val = funcAddr (dwin->decode (array[0]));
  UIDnode *node = NULL;
  UIDnode *res = get_uid_node (uid, val);
  UIDnode *next = res;
  for (int i = 0; i < size; i++)
    {
      val = funcAddr (dwin->decode (array[i]));
      if (next == NULL)
	{
	  next = get_uid_node ((uint64_t) 0, val);
	  if (node != NULL)
	    node->next = next;
	}
      node = next;
      next = node->next;
      if (node->val == 0)
	node->val = val;
      else if (node->val != val)   // Algorithmic error (should never happen)
	node->val = (uint64_t) SP_LEAF_CHECK_MARKER;
    }
  if (next == NULL && link_uid != (uint64_t) 0 && node != NULL)
    node->next = get_uid_node (link_uid);
  return res;
}

Experiment::UIDnode *
Experiment::add_uid (Data_window *dwin, uint64_t uid, int size, uint64_t *array, uint64_t link_uid)
{
  if (uid == (uint64_t) 0)
    return NULL;
  UIDnode *node = NULL;
  uint64_t val = dwin->decode (array[0]);
  UIDnode *res = get_uid_node (uid, val);
  UIDnode *next = res;
  for (int i = 0; i < size; i++)
    {
      val = dwin->decode (array[i]);
      if (next == NULL)
	{
	  next = get_uid_node ((uint64_t) 0, val);
	  if (node != NULL)
	    node->next = next;
	}
      node = next;
      next = node->next;
      if (node->val == (uint64_t) 0)
	node->val = val;
      else if (node->val != val)   // Algorithmic error (should never happen)
	node->val = (uint64_t) - 1;
    }
  if (next == NULL && link_uid != (uint64_t) 0 && node != NULL)
    node->next = get_uid_node (link_uid);
  return res;
}

Experiment::UIDnode *
Experiment::new_uid_node (uint64_t uid, uint64_t val)
{
#define NCHUNKSTEP 1024
  if (nnodes >= nchunks * CHUNKSZ)
    {
      // Reallocate Node chunk array
      UIDnode** old_chunks = chunks;
      chunks = new UIDnode*[nchunks + NCHUNKSTEP];
      if (old_chunks)
	memcpy (chunks, old_chunks, nchunks * sizeof (UIDnode*));
      nchunks += NCHUNKSTEP;
      delete[] old_chunks;
      // Clean future pointers
      memset (&chunks[nchunks - NCHUNKSTEP], 0, NCHUNKSTEP * sizeof (UIDnode*));
    }

  if (NULL == chunks[nnodes / CHUNKSZ])   // Allocate new chunk for nodes.
    chunks[nnodes / CHUNKSZ] = new UIDnode[CHUNKSZ];
  UIDnode *node = &chunks[nnodes / CHUNKSZ][nnodes % CHUNKSZ];
  node->uid = uid;
  node->val = val;
  node->next = NULL;
  nnodes++;
  return node;
}

Experiment::UIDnode *
Experiment::get_uid_node (uint64_t uid, uint64_t val)
{
  int hash = (((int) uid) >> 4) & (HTableSize - 1);
  if (uid != (uint64_t) 0)
    {
      UIDnode *node = uidHTable[hash];
      if (node && node->uid == uid)
	return node;
    }
  UIDnode *node = new_uid_node (uid, val);
  if (uid != (uint64_t) 0)
    {
      uidHTable[hash] = node;
      uidnodes->append (node);
    }
  return node;
}

Experiment::UIDnode *
Experiment::get_uid_node (uint64_t uid)
{
  if (uid == (uint64_t) 0)
    return NULL;
  int hash = (((int) uid) >> 4) & (HTableSize - 1);
  UIDnode *node = uidHTable[hash];
  if (node && node->uid == uid)
    return node;
  node = new_uid_node (uid, (uint64_t) 0);
  node->next = node;
  return node;
}

Experiment::UIDnode *
Experiment::find_uid_node (uint64_t uid)
{
  int hash = (((int) uid) >> 4) & (HTableSize - 1);
  UIDnode *node = uidHTable[hash];
  if (node && node->uid == uid)
    return node;
  int lt = 0;
  int rt = uidnodes->size () - 1;
  while (lt <= rt)
    {
      int md = (lt + rt) / 2;
      node = uidnodes->fetch (md);
      if (node->uid < uid)
	lt = md + 1;
      else if (node->uid > uid)
	rt = md - 1;
      else
	{
	  uidHTable[hash] = node;
	  return node;
	}
    }
  return NULL;
}

int
Experiment::frUidCmp (const void *a, const void *b)
{
  RawFramePacket *fp1 = *(RawFramePacket**) a;
  RawFramePacket *fp2 = *(RawFramePacket**) b;
  if (fp1->uid == fp2->uid)
    return 0;
  return fp1->uid < fp2->uid ? -1 : 1;
}

Experiment::RawFramePacket *
Experiment::find_frame_packet (uint64_t uid)
{
  int lt = 0;
  int rt = frmpckts->size () - 1;
  while (lt <= rt)
    {
      int md = (lt + rt) / 2;
      RawFramePacket *fp = frmpckts->fetch (md);
      if (fp->uid < uid)
	lt = md + 1;
      else if (fp->uid > uid)
	rt = md - 1;
      else
	return fp;
    }

  return NULL;
}

#define FRINFO_CACHEOPT_SIZE_LIMIT  4000000
#define FRINFO_PIPELINE_SIZE_LIMIT  500000
#define FRINFO_PIPELINE_NUM_STAGES  3

// Pipelined execution of resolve_frame_info() and add_stack().
// Since this is the largest time consuming part of loading an experiment (especially
// so for large java experiments) - executing this part as a 3 stage pipeline can
// give significant performance gain - and this concept can be aggressively applied
// to enhance the gain further in future. The three stages are:
// Phase 1:  resolve_frame_info()
// Phase 2:  first part of add_stack() where the native stack is built
// Phase 3:  second part og add_stack() where the java stack is built
// Phase 4:   insert the native and java stacks into the stack map
// The main thread operates in the first Phase and the other stages are
// operated by a ssplib sequential queue - The threads working on the queues run concurrently
// with each other and with the main thread. But within a particular queue, jobs are
// executed sequentially


// This is the second phase of the pipeline of resolve_frame_info and add_stack
//  It works on a chunk of iterations (size CSTCTX_CHUNK_SZ) and invokes add_stack()
// for each one of them

void
Experiment::resolve_frame_info (DataDescriptor *dDscr)
{
  if (!resolveFrameInfo)
    return;
  if (NULL == cstack)
    return;
  dDscr->setResolveFrInfoDone ();

  // Check for TSTAMP
  int propID = dbeSession->getPropIdByName (NTXT ("TSTAMP"));
  Data *dataTStamp = dDscr->getData (propID);
  if (dataTStamp == NULL)
    return;

  propID = dbeSession->getPropIdByName (NTXT ("FRINFO"));
  Data *dataFrinfo = dDscr->getData (propID);

  propID = dbeSession->getPropIdByName (NTXT ("THRID"));
  Data *dataThrId = dDscr->getData (propID);

  // We can get frame info either by FRINFO or by [THRID,STKIDX]
  if (dataFrinfo == NULL)
    return;

  char *propName = NTXT ("MSTACK");
  propID = dbeSession->getPropIdByName (propName);
  PropDescr *prMStack = new PropDescr (propID, propName);
  prMStack->uname = dbe_strdup (GTXT ("Machine Call Stack"));
  prMStack->vtype = TYPE_OBJ;
  dDscr->addProperty (prMStack);

  propName = NTXT ("USTACK");
  propID = dbeSession->getPropIdByName (propName);
  PropDescr *prUStack = new PropDescr (propID, propName);
  prUStack->uname = dbe_strdup (GTXT ("User Call Stack"));
  prUStack->vtype = TYPE_OBJ;
  dDscr->addProperty (prUStack);

  propName = NTXT ("XSTACK");
  propID = dbeSession->getPropIdByName (propName);
  PropDescr *prXStack = new PropDescr (propID, propName);
  prXStack->uname = dbe_strdup (GTXT ("Expert Call Stack"));
  prXStack->vtype = TYPE_OBJ;
  dDscr->addProperty (prXStack);

  propName = NTXT ("HSTACK");
  propID = dbeSession->getPropIdByName (propName);
  PropDescr *prHStack = new PropDescr (propID, propName);
  prHStack->uname = dbe_strdup (GTXT ("ShowHide Call Stack"));
  prHStack->vtype = TYPE_OBJ;
  dDscr->addProperty (prHStack);

  if (has_java)
    {
      propName = NTXT ("JTHREAD");
      propID = dbeSession->getPropIdByName (propName);
      PropDescr *prJThread = new PropDescr (propID, propName);
      prJThread->uname = dbe_strdup (GTXT ("Java Thread"));
      prJThread->vtype = TYPE_OBJ;
      dDscr->addProperty (prJThread);
    }

  if (ompavail)
    {
      PropDescr *prop = new PropDescr (PROP_OMPSTATE, NTXT ("OMPSTATE"));
      prop->uname = dbe_strdup (GTXT ("OpenMP state"));
      prop->vtype = TYPE_UINT32;
      char * stateNames [OMP_LAST_STATE] = OMP_THR_STATE_STRINGS;
      char * stateUNames[OMP_LAST_STATE] = OMP_THR_STATE_USTRINGS;
      for (int ii = 0; ii < OMP_LAST_STATE; ii++)
	prop->addState (ii, stateNames[ii], stateUNames[ii]);
      dDscr->addProperty (prop);

      // add PROP_CPRID to profiling data (not same as omptrace's PROP_CPRID)
      prop = dDscr->getProp (PROP_CPRID);
      if (prop)
	{
	  VType_type type = prop->vtype;
	  assert (type == TYPE_OBJ); //see 7040526
	}
      prop = new PropDescr (PROP_CPRID, NTXT ("CPRID")); //profiling PROP_CPRID
      prop->uname = dbe_strdup (GTXT ("OpenMP parallel region"));
      prop->vtype = TYPE_OBJ;
      dDscr->addProperty (prop);

      // add PROP_TSKID to profiling data (not same as omptrace's PROP_TSKID)
      prop = dDscr->getProp (PROP_TSKID);
      if (prop)
	{
	  VType_type type = prop->vtype;
	  assert (type == TYPE_OBJ); //see 7040526
	}
      prop = new PropDescr (PROP_TSKID, NTXT ("TSKID")); //profiling PROP_TSKID
      prop->uname = dbe_strdup (GTXT ("OpenMP task"));
      prop->vtype = TYPE_OBJ;
      dDscr->addProperty (prop);
    }
  char *progress_bar_msg = dbe_sprintf (NTXT ("%s %s: %s"), NTXT ("  "),
					GTXT ("Processing CallStack Data"),
					get_basename (expt_name));
  int progress_bar_percent = -1;
  long deltaReport = 5000;
  long nextReport = 0;

  long size = dDscr->getSize ();
  //    bool resolve_frinfo_pipelined = size > FRINFO_PIPELINE_SIZE_LIMIT && !ompavail;
  bool resolve_frinfo_pipelined = false;

  Map<uint64_t, uint64_t> *nodeCache = NULL;
  Map<uint64_t, uint64_t> *frameInfoCache = NULL;
  if (size > FRINFO_CACHEOPT_SIZE_LIMIT && dversion == NULL)
    {
      frameInfoCache = new CacheMap<uint64_t, uint64_t>;
      nodeCache = new CacheMap<uint64_t, uint64_t>;
    }

  pushCnt = popCnt = pushCnt3 = popCnt3 = 0;
  nPush = nPop = 0;

  FramePacket *fp = NULL;
  //    DbeThreadPool * threadPool = new DbeThreadPool(5);
  fp = new FramePacket;
  fp->stack = new Vector<Vaddr>;
  fp->jstack = new Vector<Vaddr>;
  fp->ompstack = new Vector<Vaddr>;
  fp->omp_state = 0;
  fp->mpi_state = 0;

  // piggyback on post-processing to calculate exp->last_event
  const hrtime_t _exp_start_time = getStartTime (); // wall clock time
  hrtime_t exp_duration = getLastEvent () == ZERO_TIME ? 0
	  : getLastEvent () - _exp_start_time; // zero-based

  int missed_fi = 0;
  int total_fi = 0;

  for (long i = 0; i < size; i++)
    {
      if (i == nextReport)
	{
	  int percent = (int) (i * 100 / size);
	  if (percent > progress_bar_percent)
	    {
	      progress_bar_percent += 10;
	      theApplication->set_progress (percent, progress_bar_msg);
	    }
	  nextReport += deltaReport;
	}

      uint32_t thrid = (uint32_t) dataThrId->fetchInt (i);
      hrtime_t tstamp = (hrtime_t) dataTStamp->fetchLong (i);

      // piggyback on post-processing to calculate exp->last_event
      {
	hrtime_t relative_timestamp = tstamp - _exp_start_time;
	if (exp_duration < relative_timestamp)
	  exp_duration = relative_timestamp;
      }
      uint64_t frinfo = (uint64_t) dataFrinfo->fetchLong (i);

      RawFramePacket *rfp = NULL;
      if (frinfo)
	{
	  // CacheMap does not work with NULL key
	  if (frameInfoCache != NULL)
	    rfp = (RawFramePacket *) frameInfoCache->get (frinfo);
	}
      if (rfp == 0)
	{
	  rfp = find_frame_packet (frinfo);
	  if (rfp != 0)
	    {
	      if (frameInfoCache != NULL)
		frameInfoCache->put (frinfo, (uint64_t) rfp);
	    }
	  else
	    missed_fi++;
	  total_fi++;
	}

      // Process OpenMP properties
      if (ompavail)
	{
	  fp->omp_state = rfp ? rfp->omp_state : 0;
	  dDscr->setValue (PROP_OMPSTATE, i, fp->omp_state);

	  fp->omp_cprid = mapPRid->get (thrid, tstamp, mapPRid->REL_EQLE);
	  void *omp_preg = mapPReg->get (thrid, tstamp, mapPReg->REL_EQLE);
	  if (!omp_preg)
	    {
	      char *idxname = NTXT ("OMP_preg");
	      int idxtype = dbeSession->findIndexSpaceByName (idxname);
	      if (idxtype != -1)
		{
		  Histable *preg0 = dbeSession->findObjectById (Histable::INDEXOBJ, idxtype, (int64_t) 0);
		  if (preg0)
		    {
		      Vector<Histable*> pregs;
		      pregs.append (preg0);
		      omp_preg = cstack->add_stack (&pregs);
		      mapPReg->put (thrid, tstamp, omp_preg);
		    }
		}
	    }
	  dDscr->setObjValue (PROP_CPRID, i, omp_preg); //profiling PROP_CPRID
	  void *omp_task = mapTask->get (thrid, tstamp, mapTask->REL_EQLE);
	  if (!omp_task)
	    {
	      char *idxname = NTXT ("OMP_task");
	      int idxtype = dbeSession->findIndexSpaceByName (idxname);
	      if (idxtype != -1)
		{
		  Histable *task0 = dbeSession->findObjectById (Histable::INDEXOBJ, idxtype, (int64_t) 0);
		  if (task0)
		    {
		      Vector<Histable*> tasks;
		      tasks.append (task0);
		      omp_task = cstack->add_stack (&tasks);
		      mapTask->put (thrid, tstamp, omp_task);
		    }
		}
	    }
	  dDscr->setObjValue (PROP_TSKID, i, omp_task); //profiling PROP_TSKID
	}
      else
	{
	  fp->omp_state = 0;
	  fp->omp_cprid = 0;
	}

      // Construct the native stack
      fp->stack->reset ();
      Vaddr leafpc = dDscr->getULongValue (PROP_LEAFPC, i);
      if (leafpc)
	fp->stack->append (leafpc);
      UIDnode *node = rfp ? rfp->uidn : NULL;
      while (node)
	{
	  if (node->next == node)
	    // this node contains link_uid
	    node = find_uid_node (node->uid);
	  else
	    {
	      fp->stack->append (node->val);
	      node = node->next;
	    }
	}
      fp->truncated = 0;
      int last = fp->stack->size () - 1;
      if (last >= 0)
	{
	  switch (fp->stack->fetch (last))
	    {
	    case SP_TRUNC_STACK_MARKER:
	      fp->truncated = (Vaddr) SP_TRUNC_STACK_MARKER;
	      fp->stack->remove (last);
	      break;
	    case SP_FAILED_UNWIND_MARKER:
	      fp->truncated = (Vaddr) SP_FAILED_UNWIND_MARKER;
	      fp->stack->remove (last);
	      break;
	    }
	}

      // Construct the Java stack
      fp->jstack->reset ();
      node = rfp ? rfp->uidj : NULL;
      while (node)
	{
	  if (node->next == node)
	    {
	      // this node contains link_uid
	      UIDnode *n = NULL;
	      if (node->uid)
		{
		  // CacheMap does not work with NULL key
		  if (nodeCache != NULL)
		    n = (UIDnode *) nodeCache->get (node->uid);
		}
	      if (n == NULL)
		{
		  n = find_uid_node (node->uid);
		  if (n != NULL)
		    {
		      if (nodeCache != NULL)
			nodeCache->put (node->uid, (uint64_t) n);
		    }
		}
	      node = n;
	    }
	  else
	    {
	      fp->jstack->append (node->val);
	      node = node->next;
	    }
	}
      fp->jtruncated = false;
      last = fp->jstack->size () - 1;
      if (last >= 1 && fp->jstack->fetch (last) == SP_TRUNC_STACK_MARKER)
	{
	  fp->jtruncated = true;
	  fp->jstack->remove (last);
	  fp->jstack->remove (last - 1);
	}

      // Construct the OpenMP stack
      if (ompavail)
	{
	  fp->ompstack->reset ();
	  if (rfp && rfp->omp_uid)
	    {
	      if (leafpc)
		fp->ompstack->append (leafpc);
	      node = rfp->omp_uid;
	      while (node)
		{
		  if (node->next == node)
		    // this node contains link_uid
		    node = find_uid_node (node->uid);
		  else
		    {
		      fp->ompstack->append (node->val);
		      node = node->next;
		    }
		}
	      fp->omptruncated = false;
	      last = fp->ompstack->size () - 1;
	      if (last >= 0)
		{
		  switch (fp->ompstack->fetch (last))
		    {
		    case SP_TRUNC_STACK_MARKER:
		      fp->omptruncated = (Vaddr) SP_TRUNC_STACK_MARKER;
		      fp->ompstack->remove (last);
		      break;
		    case SP_FAILED_UNWIND_MARKER:
		      fp->omptruncated = (Vaddr) SP_FAILED_UNWIND_MARKER;
		      fp->ompstack->remove (last);
		      break;
		    }
		}
	    }
	}
      cstack->add_stack (dDscr, i, fp, NULL);
    }

  // piggyback on post-processing to calculate exp->last_event
  {
    hrtime_t exp_end_time = _exp_start_time + exp_duration;
    update_last_event (exp_end_time);
  }

  if (missed_fi > 0)
    {
      StringBuilder sb;
      sb.sprintf (
		  GTXT ("*** Warning: %d frameinfo packets are missing from total of %d when resolving %s."),
		  missed_fi, total_fi, dDscr->getName ());
      warnq->append (new Emsg (CMSG_WARN, sb));
    }

  //    threadPool->wait_group();
  //    delete threadPool;
  theApplication->set_progress (0, NTXT (""));
  free (progress_bar_msg);
  if (!resolve_frinfo_pipelined && fp != NULL)
    {
      delete fp->ompstack;
      delete fp->jstack;
      delete fp->stack;
      delete fp;
    }
  delete frameInfoCache;
  frameInfoCache = NULL;
  delete nodeCache;
  nodeCache = NULL;
}

void
Experiment::post_process ()
{
  // update non_paused_time after final update to "last_event"
  if (resume_ts != MAX_TIME && last_event)
    {
      hrtime_t ts = last_event - exp_start_time;
      hrtime_t delta = ts - resume_ts;
      non_paused_time += delta;
      resume_ts = MAX_TIME; // collection is paused
    }

  // GC: prune events outside of experiment duration, calculate GC duration, update indices
  int index;
  GCEvent * gcevent;
  gc_duration = ZERO_TIME;
  if (gcevents != NULL)
    {
      // delete events that finish before exp_start_time or start after last_event
      for (int ii = 0; ii < gcevents->size ();)
	{
	  gcevent = gcevents->fetch (ii);
	  if (gcevent->end - exp_start_time < 0
	      || last_event - gcevent->start < 0)
	    delete gcevents->remove (ii);
	  else
	    ii++;
	}
    }
  Vec_loop (GCEvent*, gcevents, index, gcevent)
  {
    gcevent->id = index + 1; // renumber to account for any deleted events
    if (gcevent->start - exp_start_time < 0 || gcevent->start == ZERO_TIME)
      // truncate events that start before experiment start
      gcevent->start = exp_start_time;
    if (last_event - gcevent->end < 0)
      // truncate events that end after experiment end
      gcevent->end = last_event;
    gc_duration += gcevent->end - gcevent->start;
  }
}

Experiment::Exp_status
Experiment::find_expdir (char *path)
{
  // This function checks that the experiment directory
  // is of the proper form, and accessible
  dbe_stat_t sbuf;

  // Save the name
  expt_name = dbe_strdup (path);

  // Check that the name ends in .er
  size_t i = strlen (path);
  if (i > 0 && path[i - 1] == '/')
    path[--i] = '\0';

  if (i < 4 || strcmp (&path[i - 3], NTXT (".er")) != 0)
    {
      Emsg *m = new Emsg (CMSG_FATAL,
			  GTXT ("*** Error: not a valid experiment name"));
      errorq->append (m);
      status = FAILURE;
      return FAILURE;
    }

  // Check if new directory structure (i.e., no pointer file)
  if (dbe_stat (path, &sbuf))
    {
      Emsg *m = new Emsg (CMSG_FATAL, GTXT ("*** Error: experiment not found"));
      errorq->append (m);
      status = FAILURE;
      return FAILURE;
    }
  if (S_ISDIR (sbuf.st_mode) == 0)
    {
      // ignore pointer-file experiments
      Emsg *m = new Emsg (CMSG_FATAL,
			  GTXT ("*** Error: experiment was recorded with an earlier version, and can not be read"));
      errorq->append (m);
      obsolete = 1;
      status = FAILURE;
      return FAILURE;
    }
  return SUCCESS;
}

void
Experiment::purge ()
{
  // This routine will purge all of the caches of releasable storage.
  for (int i = 0; i < dataDscrs->size (); ++i)
    {
      DataDescriptor *dataDscr = dataDscrs->fetch (i);
      if (dataDscr == NULL)
	continue;
      dataDscr->reset ();
    }
  delete cstack;
  delete cstackShowHide;
  cstack = CallStack::getInstance (this);
  cstackShowHide = CallStack::getInstance (this);
}

void
Experiment::resetShowHideStack ()
{
  delete cstackShowHide;
  cstackShowHide = CallStack::getInstance (this);
}

#define GET_INT_VAL(v, s, len) \
    for (v = len = 0; isdigit(*s); s++, len++) { v = v * 10 + (*s -'0'); }

static int
dir_name_cmp (const void *a, const void *b)
{
  char *s1 = *((char **) a);
  char *s2 = *((char **) b);
  while (*s1)
    {
      if (isdigit (*s1) && isdigit (*s2))
	{
	  int v1, v2, len1, len2;
	  GET_INT_VAL (v1, s1, len1);
	  GET_INT_VAL (v2, s2, len2);
	  if (v1 != v2)
	    return v1 - v2;
	  if (len1 != len2)
	    return len2 - len1;
	  continue;
	}
      if (*s1 != *s2)
	break;
      s1++;
      s2++;
    }
  return *s1 - *s2;
}

Vector<char*> *
Experiment::get_descendants_names ()
{
  char *dir_name = get_expt_name ();
  if (dir_name == NULL)
    return NULL;
  DIR *exp_dir = opendir (dir_name);
  if (exp_dir == NULL)
    return NULL;
  Vector<char*> *exp_names = new Vector<char*>();
  for (struct dirent *entry = readdir (exp_dir); entry;
	  entry = readdir (exp_dir))
    {
      if (entry->d_name[0] == '_' || strncmp (entry->d_name, "M_r", 3) == 0)
	{
	  char *dpath = dbe_sprintf (NTXT ("%s/%s"), dir_name, entry->d_name);
	  dbe_stat_t sbuf;
	  if (dbe_stat (dpath, &sbuf) == 0 && S_ISDIR (sbuf.st_mode))
	    exp_names->append (dpath);
	  else
	    free (dpath);
	}
    }
  closedir (exp_dir);
  if (exp_names->size () == 0)
    {
      delete exp_names;
      return NULL;
    }
  exp_names->sort (dir_name_cmp);
  return exp_names;
}

bool
Experiment::create_dir (char *dname)
{
  if (mkdir (dname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
    {
      return true;
    }
  dbe_stat_t sbuf;
  if (dbe_stat (dname, &sbuf) != 0 || S_ISDIR (sbuf.st_mode) == 0)
    {
      char *buf = dbe_sprintf (GTXT ("Unable to create directory `%s'\n"),
			       dname);
      errorq->append (new Emsg (CMSG_ERROR, buf));
      free (buf);
      return false;
    }
  return true;
}

char *
Experiment::get_arch_name ()
{
  if (arch_name == NULL)
    {
      // Determine the master experiment directory.
      // omazur: should do it in a less hacky way. XXXX
      char *ptr = strstr_r (expt_name, DESCENDANT_EXPT_KEY);
      ptr = ptr ? ptr + 3 : expt_name + strlen (expt_name);
      arch_name = dbe_sprintf (NTXT ("%.*s/%s"), (int) (ptr - expt_name),
			       expt_name, SP_ARCHIVES_DIR);
    }
  return arch_name;
}

char *
Experiment::get_fndr_arch_name ()
{
  if (fndr_arch_name == NULL)
    // Determine the founder experiment directory.
    fndr_arch_name = dbe_strdup (get_arch_name ());
  return fndr_arch_name;
}

enum
{
  HASH_NAME_LEN     = 11    // (64 / 6 + 1) = 11
};

static char *
get_hash_string (char buf[HASH_NAME_LEN + 1], uint64_t hash)
{
  static const char *har =
	  "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
  for (int i = 0; i < HASH_NAME_LEN; i++)
    {
      buf[i] = har[hash & 0x3f];
      hash = hash >> 6;
    }
  buf[HASH_NAME_LEN] = 0;
  return buf;
}

char *
Experiment::getNameInArchive (const char *fname, bool archiveFile)
{
  char *aname = get_archived_name (fname, archiveFile);
  char *ret = dbe_sprintf (NTXT ("%s/%s"), get_arch_name (), aname);
  free (aname);
  return ret;
}

#define MAX_ARCHIVE_FILENAME_LEN    (256 - HASH_NAME_LEN - 2)

char *
Experiment::get_archived_name (const char *fname, bool archiveFile)
{
  char *bname = get_basename (fname);

  // dirname_hash:
  char dirnameHash[HASH_NAME_LEN + 1];
  // Treat "a.out" and "./a.out" equally
  uint64_t hash = bname != fname ? crc64 (fname, bname - fname)
				 : crc64 (NTXT ("./"), 2);
  get_hash_string (dirnameHash, hash);

  char *ret;
  long bname_len = dbe_sstrlen (bname);
  if (bname_len > MAX_ARCHIVE_FILENAME_LEN)
    {
      char basenameHash[HASH_NAME_LEN + 1];
      hash = crc64 (bname, bname_len);
      get_hash_string (basenameHash, hash);
      ret = dbe_sprintf ("%.*s%c%s_%s",
			 MAX_ARCHIVE_FILENAME_LEN - HASH_NAME_LEN - 1,
			 bname, archiveFile ? '.' : '_',
			 dirnameHash, basenameHash);
    }
  else
    ret = dbe_sprintf ("%s%c%s", bname, archiveFile ? '.' : '_', dirnameHash);
  return ret;
}

char *
Experiment::checkFileInArchive (const char *fname, bool archiveFile)
{
  if (archiveMap)
    {
      char *aname = get_archived_name (fname, archiveFile);
      DbeFile *df = archiveMap->get (aname);
      free (aname);
      if (df)
	return xstrdup (df->get_location ());
      return NULL;
    }
  if (founder_exp)
    return founder_exp->checkFileInArchive (fname, archiveFile);
  return NULL;
}


// Comparing SegMem

static int
SegMemCmp (const void *a, const void *b)
{
  SegMem *item1 = *((SegMem **) a);
  SegMem *item2 = *((SegMem **) b);
  return item1->unload_time > item2->unload_time ? 1 :
	 item1->unload_time == item2->unload_time ? 0 : -1;
}

SegMem*
Experiment::update_ts_in_maps (Vaddr addr, hrtime_t ts)
{
  Vector<void *> *segMems = maps->values ();
  if (segMems && !segMems->is_sorted ())
    {
      Dprintf (DEBUG_MAPS, NTXT ("update_ts_in_maps: segMems.size=%lld\n"), (long long) segMems->size ());
      segMems->sort (SegMemCmp);
    }
  for (int i = 0, sz = segMems ? segMems->size () : 0; i < sz; i++)
    {
      SegMem *sm = (SegMem *) segMems->fetch (i);
      if (ts < sm->unload_time)
	{
	  for (; i < sz; i++)
	    {
	      sm = (SegMem *) segMems->fetch (i);
	      if ((addr >= sm->base) && (addr < sm->base + sm->size))
		{
		  Dprintf (DEBUG_MAPS,
			   "update_ts_in_maps: old:%u.%09u -> %u.%09u addr=0x%08llx size=%lld\n",
			   (unsigned) (sm->load_time / NANOSEC),
			   (unsigned) (sm->load_time % NANOSEC),
			   (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
			   (unsigned long long) sm->base, (long long) sm->size);
		  maps->remove (sm->base, sm->load_time);
		  sm->load_time = ts;
		  maps->insert (sm->base, ts, sm);
		  return sm;
		}
	    }
	}
    }
  Dprintf (DEBUG_MAPS, "update_ts_in_maps: NOT FOUND %u.%09u addr=0x%08llx\n",
	   (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
	   (unsigned long long) addr);
  return NULL;
}

DbeInstr*
Experiment::map_Vaddr_to_PC (Vaddr addr, hrtime_t ts)
{
  // Look up in the hash table first
  int hash = (((int) addr) >> 8) & (HTableSize - 1);
  SegMem *si = smemHTable[hash];
  if (si == NULL || addr < si->base || addr >= si->base + si->size
      || ts < si->load_time || ts >= si->unload_time)
    {
      // Not in the hash table
      si = (SegMem*) maps->locate (addr, ts);
      if (si == NULL || addr < si->base || addr >= si->base + si->size
	  || ts < si->load_time || ts >= si->unload_time)
	{
	  si = update_ts_in_maps (addr, ts);
	  if (si == NULL)
	    return dbeSession->get_Unknown_Function ()->find_dbeinstr (PCInvlFlag, addr);
	}
      smemHTable[hash] = si;
    }

  // Calculate the file offset of 'addr'
  uint64_t f_offset = si->get_file_offset () + (addr - si->base);

  DbeInstr *instr;
  if (si->obj->get_type () == Histable::LOADOBJECT)
    {
      LoadObject *lo = (LoadObject*) si->obj;
      lo->sync_read_stabs ();
      instr = lo->find_dbeinstr (f_offset);
    }
  else
    {
      int hash2 = ((((int) addr) & 0xFFFC00) | (((int) f_offset) >> 2))
	      & (HTableSize - 1);
      instr = instHTable[hash2];
      if (instr == NULL || instr->func != si->obj || instr->addr != f_offset)
	{
	  // Not in the hash table
	  Function *fp = (Function *) si->obj;
	  instr = fp->find_dbeinstr (0, f_offset);
	  instHTable[hash2] = instr;
	}
    }
  if (!instr->func->isUsed)
    {
      instr->func->isUsed = true;
      instr->func->module->isUsed = true;
      instr->func->module->loadobject->isUsed = true;
    }
  return instr;
}

Sample *
Experiment::map_event_to_Sample (hrtime_t ts)
{
  Sample *sample;
  int index;

  // Check if the last used sample is the right one,
  // if not then find it.
  if (sample_last_used && ts >= sample_last_used->start_time
      && ts <= sample_last_used->end_time)
    return sample_last_used;

  Vec_loop (Sample*, samples, index, sample)
  {
    if ((ts >= sample->start_time) &&
	(ts <= sample->end_time))
      {
	sample_last_used = sample;
	return sample;
      }
  }
  return (Sample*) NULL;
}

GCEvent *
Experiment::map_event_to_GCEvent (hrtime_t ts)
{
  GCEvent *gcevent;
  int index;

  // Check if the last used sample is the right one,
  // if not then find it.
  if (gcevent_last_used && ts >= gcevent_last_used->start
      && ts <= gcevent_last_used->end)
    return gcevent_last_used;
  Vec_loop (GCEvent*, gcevents, index, gcevent)
  {
    if ((ts >= gcevent->start) &&
	(ts <= gcevent->end))
      {
	gcevent_last_used = gcevent;
	return gcevent;
      }
  }
  return (GCEvent*) NULL;
}

DbeInstr*
Experiment::map_jmid_to_PC (Vaddr mid, int bci, hrtime_t ts)
{
  if (mid == 0 || jmaps == NULL)
    // special case: no Java stack was recorded, bci - error code
    return dbeSession->get_JUnknown_Function ()->find_dbeinstr (0, bci);

  JMethod *jmthd = jmidHTable->get (mid);
  if (jmthd == NULL)
    {
      jmthd = (JMethod *) jmaps->locate_exact_match (mid, ts);
      if (jmthd)
	jmidHTable->put (mid, jmthd);
    }
  if (jmthd == NULL || jmthd->get_type () != Histable::FUNCTION)
    return dbeSession->get_JUnknown_Function ()->find_dbeinstr (0, (uint64_t) mid);
  return jmthd->find_dbeinstr (0, bci);
}

Emsg *
Experiment::fetch_comments ()
{
  return commentq->fetch ();
}

Emsg *
Experiment::fetch_runlogq ()
{
  return runlogq->fetch ();
}

Emsg *
Experiment::fetch_errors ()
{
  return errorq->fetch ();
}

Emsg *
Experiment::fetch_warnings ()
{
  return warnq->fetch ();
}

Emsg *
Experiment::fetch_notes ()
{
  return notesq->fetch ();
}

Emsg *
Experiment::fetch_ifreq ()
{
  return ifreqq->fetch ();
}

Emsg *
Experiment::fetch_pprocq ()
{
  return pprocq->fetch ();
}

int
Experiment::read_dyntext_file ()
{
  dyntext_name = dbe_sprintf ("%s/%s", expt_name, SP_DYNTEXT_FILE);
  Data_window *dwin = new Data_window (dyntext_name);
  if (dwin->not_opened ())
    {
      delete dwin;
      return 1;
    }
  dwin->need_swap_endian = need_swap_endian;

  Function *fp = NULL;
  char *progress_msg = NULL; // Message for the progress bar
  for (int64_t offset = 0;;)
    {
      DT_common *cpckt = (DT_common *) dwin->bind (offset, sizeof (DT_common));
      if (cpckt == NULL)
	break;
      size_t cpcktsize = dwin->decode (cpckt->size);
      cpckt = (DT_common *) dwin->bind (offset, cpcktsize);
      if (cpckt == NULL)
	break;
      switch (dwin->decode (cpckt->type))
	{
	case DT_HEADER:
	  {
	    DT_header *hdr = (DT_header*) cpckt;
	    hrtime_t ts = dwin->decode (hdr->time) + exp_start_time;
	    SegMem *si = (SegMem*) maps->locate (dwin->decode (hdr->vaddr), ts);
	    fp = si ? (Function *) si->obj : NULL;
	    if (fp && (fp->get_type () != Histable::FUNCTION
		       || !(fp->flags & FUNC_FLAG_DYNAMIC)))
	      fp = NULL;
	    break;
	  }
	case DT_CODE:
	  if (fp)
	    {
	      fp->img_fname = dyntext_name;
	      fp->img_offset = offset + sizeof (DT_common);
	      if ((platform != Intel) && (platform != Amd64))
		{ //ARCH(SPARC)
		  // Find out 'save' instruction address for SPARC
		  char *ptr = ((char*) cpckt) + sizeof (DT_common);
		  size_t img_size = cpcktsize - sizeof (DT_common);
		  for (size_t i = 0; i < img_size; i += 4)
		    if (ptr[i] == (char) 0x9d && ptr[i + 1] == (char) 0xe3)
		      {
			fp->save_addr = i;
			break;
		      }
		}
	    }
	  break;
	case DT_SRCFILE:
	  if (fp)
	    {
	      char *srcname = dbe_strndup (((char*) cpckt) + sizeof (DT_common),
					   cpcktsize - sizeof (DT_common));
	      LoadObject *ds = fp->module ? fp->module->loadobject : NULL;
	      assert (ds != NULL);
	      Module *mod = dbeSession->createModule (ds, NULL);
	      mod->set_file_name (srcname);
	      //}
	      if (fp->module)
		{
		  // It's most likely (unknown). Remove fp from it.
		  long idx = fp->module->functions->find (fp);
		  if (idx >= 0)
		    fp->module->functions->remove (idx);
		}
	      fp->module = mod;
	      mod->functions->append (fp);
	    }
	  break;
	case DT_LTABLE:
	  if (fp)
	    {
	      DT_lineno *ltab = (DT_lineno*) ((char*) cpckt + sizeof (DT_common));
	      size_t sz = (cpcktsize - sizeof (DT_common)) / sizeof (DT_lineno);
	      if (sz <= 0)
		break;
	      // Take care of the progress bar
	      static int percent = 0;
	      static long deltaReport = sz / 100; // 1000;
	      static long nextReport = 0;
	      static long progress_count = 0;
	      fp->pushSrcFile (fp->getDefSrc (), 0);
	      for (size_t i = 0; i < sz; i++)
		{
		  int lineno = dwin->decode (ltab[i].lineno);
		  if (fp->usrfunc != NULL)
		    {
		      // Update progress bar
		      if (dbeSession->is_interactive ())
			{
			  if (progress_count == nextReport)
			    {
			      if (percent < 99)
				{
				  percent++;
				  if (NULL == progress_msg)
				    {
				      progress_msg = dbe_sprintf (GTXT ("Processing Dynamic Text: %s"),
								  get_basename (expt_name));
				    }
				  theApplication->set_progress (percent, progress_msg);
				  nextReport += deltaReport;
				}
			    }
			  progress_count++;
			}
		      DbeLine *dbeline = fp->usrfunc->mapPCtoLine (lineno, NULL);
		      lineno = dbeline != NULL ? dbeline->lineno : -1;
		    }
		  fp->add_PC_info (dwin->decode (ltab[i].offset), lineno);
		}
	      fp->popSrcFile ();
	    }
	  break;
	default:
	  // skip unknown records
	  break;
	}
      offset += cpcktsize;
    }
  free (progress_msg);
  delete dwin;
  return 0;
}

uint32_t
Experiment::mapTagValue (Prop_type prop, uint64_t value)
{
  Vector<Histable*> *objs = tagObjs->fetch (prop);
  int lt = 0;
  int rt = objs->size () - 1;
  while (lt <= rt)
    {
      int md = (lt + rt) / 2;
      Other *obj = (Other*) objs->fetch (md);
      if (obj->value64 < value)
	lt = md + 1;
      else if (obj->value64 > value)
	rt = md - 1;
      else
	return obj->tag;
    }

  uint32_t tag;
  if (sparse_threads && (prop == PROP_THRID || prop == PROP_LWPID))
    tag = objs->size () + 1; // "+ 1" related to 7038295
  else
    tag = (int) value; // truncation; See 6788767

  Other *obj = new Other ();
  obj->value64 = value;
  obj->tag = tag;
  if (lt == objs->size ())
    objs->append (obj);
  else
    objs->insert (lt, obj);

  // Update min and max tags
  if (prop == PROP_LWPID)
    {
      if ((uint64_t) tag < min_lwp)
	min_lwp = (uint64_t) tag;
      if ((uint64_t) tag > max_lwp)
	max_lwp = (uint64_t) tag;
      lwp_cnt++;
    }
  else if (prop == PROP_THRID)
    {
      if ((uint64_t) tag < min_thread)
	min_thread = (uint64_t) tag;
      if ((uint64_t) tag > max_thread)
	max_thread = (uint64_t) tag;
      thread_cnt++;
    }
  else if (prop == PROP_CPUID)
    {
      // On Solaris 8, we don't get CPU id -- don't change
      if (value != (uint64_t) - 1)
	{//YXXX is this related only to solaris 8?
	  if ((uint64_t) tag < min_cpu)
	    min_cpu = (uint64_t) tag;
	  if ((uint64_t) tag > max_cpu)
	    max_cpu = (uint64_t) tag;
	}
      cpu_cnt++;
    }
  return obj->tag;
}

Vector<Histable*> *
Experiment::getTagObjs (Prop_type prop)
{
  return tagObjs->fetch (prop);
}

Histable *
Experiment::getTagObj (Prop_type prop, uint32_t tag)
{
  Vector<Histable*> *objs = tagObjs->fetch (prop);
  if (objs == NULL)
    return NULL;
  for (int i = 0; i < objs->size (); i++)
    {
      Other *obj = (Other*) objs->fetch (i);
      if (obj->tag == tag)
	return obj;
    }
  return NULL;
}

JThread *
Experiment::map_pckt_to_Jthread (uint32_t tid, hrtime_t tstamp)
{
  if (!has_java)
    return JTHREAD_DEFAULT;
  int lt = 0;
  int rt = jthreads_idx->size () - 1;
  while (lt <= rt)
    {
      int md = (lt + rt) / 2;
      JThread *jthread = jthreads_idx->fetch (md);
      if (jthread->tid < tid)
	lt = md + 1;
      else if (jthread->tid > tid)
	rt = md - 1;
      else
	{
	  for (; jthread; jthread = jthread->next)
	    if (tstamp >= jthread->start && tstamp < jthread->end)
	      return jthread;
	  break;
	}
    }

  return JTHREAD_NONE;
}

JThread*
Experiment::get_jthread (uint32_t tid)
{
  if (!has_java)
    return JTHREAD_DEFAULT;
  int lt = 0;
  int rt = jthreads_idx->size () - 1;
  while (lt <= rt)
    {
      int md = (lt + rt) / 2;
      JThread *jthread = jthreads_idx->fetch (md);
      if (jthread->tid < tid)
	lt = md + 1;
      else if (jthread->tid > tid)
	rt = md - 1;
      else
	{
	  JThread *jthread_first = jthread;
	  while ((jthread = jthread->next) != NULL)
	    if (!jthread->is_system () &&
		jthread->jthr_id < jthread_first->jthr_id)
	      jthread_first = jthread;
	  return jthread_first;
	}
    }

  return JTHREAD_NONE;
}

// SS12 experiment
DataDescriptor *
Experiment::newDataDescriptor (int data_id, int flags,
			       DataDescriptor *master_dDscr)
{
  DataDescriptor *dataDscr = NULL;
  if (data_id >= 0 && data_id < dataDscrs->size ())
    {
      dataDscr = dataDscrs->fetch (data_id);
      if (dataDscr != NULL)
	return dataDscr;
    }

  assert (data_id >= 0 && data_id < DATA_LAST);
  const char *nm = get_prof_data_type_name (data_id);
  const char *uname = get_prof_data_type_uname (data_id);

  if (master_dDscr)
    dataDscr = new DataDescriptor (data_id, nm, uname, master_dDscr);
  else
    dataDscr = new DataDescriptor (data_id, nm, uname, flags);
  dataDscrs->store (data_id, dataDscr);
  return dataDscr;
}

Vector<DataDescriptor*> *
Experiment::getDataDescriptors ()
{
  Vector<DataDescriptor*> *result = new Vector<DataDescriptor*>;
  for (int i = 0; i < dataDscrs->size (); ++i)
    {
      DataDescriptor *dd;
      dd = get_raw_events (i); // force data fetch
      if (dd != NULL)
	result->append (dd);
    }
  return result;
}

DataDescriptor *
Experiment::getDataDescriptor (int data_id)
{
  if (data_id < 0 || data_id >= dataDscrs->size ())
    return NULL;
  return dataDscrs->fetch (data_id);
}

PacketDescriptor *
Experiment::newPacketDescriptor (int kind, DataDescriptor *dDscr)
{
  PacketDescriptor *pDscr = new PacketDescriptor (dDscr);
  pcktDscrs->store (kind, pDscr);
  return pDscr;
}

PacketDescriptor *
Experiment::getPacketDescriptor (int kind)
{
  if (kind < 0 || kind >= pcktDscrs->size ())
    return NULL;
  return pcktDscrs->fetch (kind);
}

void
Experiment::set_clock (int clk)
{
  if (clk > 0)
    {
      if (maxclock < clk)
	{
	  maxclock = clk;
	  clock = maxclock;
	}
      if (minclock == 0 || minclock > clk)
	minclock = clk;
    }
}

bool
JThread::is_system ()
{
  if (group_name == NULL)
    return false;
  return strcmp (group_name, NTXT ("system")) == 0;
}

void
Experiment::dump_stacks (FILE *outfile)
{
  cstack->print (outfile);
}

void
Experiment::dump_map (FILE *outfile)
{
  int index;
  SegMem *s;
  fprintf (outfile, GTXT ("Experiment %s\n"), get_expt_name ());
  fprintf (outfile, GTXT ("Address         Size (hex)              Load time     Unload time    Checksum  Name\n"));
  Vec_loop (SegMem*, seg_items, index, s)
  {
    timestruc_t load;
    timestruc_t unload;
    hr2timestruc (&load, (s->load_time - exp_start_time));
    if (load.tv_nsec < 0)
      {
	load.tv_sec--;
	load.tv_nsec += NANOSEC;
      }
    if (s->unload_time == MAX_TIME)
      {
	unload.tv_sec = 0;
	unload.tv_nsec = 0;
      }
    else
      hr2timestruc (&unload, (s->unload_time - exp_start_time));
    if (load.tv_nsec < 0)
      {
	load.tv_sec--;
	load.tv_nsec += NANOSEC;
      }
    fprintf (outfile,
	     "0x%08llx  %8lld (0x%08llx) %5lld.%09lld %5lld.%09lld  \"%s\"\n",
	     (long long) s->base, (long long) s->size, (long long) s->size,
	     (long long) load.tv_sec, (long long) load.tv_nsec,
	     (long long) unload.tv_sec, (long long) unload.tv_nsec,
	     s->obj->get_name ());
  }
  fprintf (outfile, NTXT ("\n"));
}

/**
 * Copy file to archive
 * @param name
 * @param aname
 * @param hide_msg
 * @return 0 - success, 1 - error
 */
int
Experiment::copy_file_to_archive (const char *name, const char *aname, int hide_msg)
{
  errno = 0;
  int fd_w = ::open64 (aname, O_WRONLY | O_CREAT | O_EXCL, 0644);
  if (fd_w == -1)
    {
      if (errno == EEXIST)
	return 0;
      fprintf (stderr, GTXT ("gprofng-archive: unable to copy `%s': %s\n"),
	       name, STR (strerror (errno)));
      return 1;
    }

  if (dbe_stat_file (name, NULL) != 0)
    {
      fprintf (stderr, GTXT ("gprofng-archive: cannot access file `%s': %s\n"),
	       name, STR (strerror (errno)));
      close (fd_w);
      return 1;
    }

  int fd_r = ::open64 (name, O_RDONLY);
  if (fd_r == -1)
    {
      fprintf (stderr, GTXT ("gprofng-archive: unable to open `%s': %s\n"),
	       name, strerror (errno));
      close (fd_w);
      unlink (aname);
      return 1;
    }

  if (!hide_msg)
    fprintf (stderr, GTXT ("Copying `%s'  to `%s'\n"), name, aname);
  bool do_unlink = false;
  for (;;)
    {
      unsigned char buf[65536];
      int n, n1;
      n = (int) read (fd_r, (void *) buf, sizeof (buf));
      if (n <= 0)
	break;
      n1 = (int) write (fd_w, buf, n);
      if (n != n1)
	{
	  fprintf (stderr, GTXT ("gprofng-archive: unable to write %d bytes to `%s': %s\n"),
		   n, aname, STR (strerror (errno)));
	  do_unlink = true;
	  break;
	}
    }
  close (fd_w);

  dbe_stat_t s_buf;
  if (fstat64 (fd_r, &s_buf) == 0)
    {
      struct utimbuf u_buf;
      u_buf.actime = s_buf.st_atime;
      u_buf.modtime = s_buf.st_mtime;
      utime (aname, &u_buf);
    }
  close (fd_r);
  if (do_unlink)
    {
      if (!hide_msg)
	fprintf (stderr, GTXT ("gprofng-archive: remove %s\n"), aname);
      unlink (aname);
      return 1;
    }
  return 0;
}

/**
 * Copy file to common archive
 * Algorithm:
 * Calculate checksum
 * Generate file name to be created in common archive
 * Check if it is not in common archive yet
 * Copy file to the common archive directory if it is not there yet
 * Create symbolic link: "aname" -> "caname", where "caname" is the name in common archive
 * @param name - original file name
 * @param aname - file name in experiment archive
 * @param common_archive - common archive directory
 * @return 0 - success, 1 - error
 */
int
Experiment::copy_file_to_common_archive (const char *name, const char *aname,
					 int hide_msg,
					 const char *common_archive,
					 int relative_path)
{
  if (!name || !aname || !common_archive)
    {
      if (!name)
	fprintf (stderr, GTXT ("gprofng-archive: Internal error: file name is NULL\n"));
      if (!aname)
	fprintf (stderr, GTXT ("gprofng-archive: Internal error: file name in archive is NULL\n"));
      if (!common_archive)
	fprintf (stderr, GTXT ("gprofng-archive: Internal error: path to common archive is NULL\n"));
      return 1;
    }
  // Check if file is already archived
  if (dbe_stat (aname, NULL) == 0)
    return 0; // File is already archived
  // Generate full path to common archive directory
  char *cad = NULL;
  char *abs_aname = NULL;
  if ((common_archive[0] != '/') || (aname[0] != '/'))
    {
      long size = pathconf (NTXT ("."), _PC_PATH_MAX);
      if (size < 0)
	{
	  fprintf (stderr, GTXT ("gprofng-archive: Fatal error: pathconf(\".\", _PC_PATH_MAX) failed\n"));
	  return 1;
	}
      char *buf = (char *) xmalloc ((size_t) size);
      char *ptr = getcwd (buf, (size_t) size);
      if (ptr == NULL)
	{
	  fprintf (stderr, GTXT ("gprofng-archive: Fatal error: cannot determine current directory\n"));
	  free (buf);
	  return 1;
	}
      if (common_archive[0] != '/')
	cad = dbe_sprintf (NTXT ("%s/%s"), ptr, common_archive);
      else
	cad = dbe_strdup (common_archive);
      if (aname[0] != '/')
	abs_aname = dbe_sprintf (NTXT ("%s/%s"), ptr, aname);
      else
	abs_aname = dbe_strdup (aname);
      free (buf);
    }
  else
    {
      cad = dbe_strdup (common_archive);
      abs_aname = dbe_strdup (aname);
    }
  // Calculate checksum
  char * errmsg = NULL;
  uint32_t crcval = get_cksum (name, &errmsg);
  if (0 == crcval)
    { // error
      free (cad);
      free (abs_aname);
      if (NULL != errmsg)
	{
	  fprintf (stderr, GTXT ("gprofng-archive: Fatal error: %s\n"), errmsg);
	  free (errmsg);
	  return 1;
	}
      fprintf (stderr,
	       GTXT ("gprofng-archive: Fatal error: get_cksum(%s) returned %d\n"),
	       name, crcval);
      return 1;
    }
  // Generate file name to be created in common archive
  char *fname = get_basename (name);
  char *abs_caname = dbe_sprintf (NTXT ("%s/%u_%s"), cad, crcval, fname);
  if (abs_caname == NULL)
    {
      free (cad);
      free (abs_aname);
      fprintf (stderr,
	       GTXT ("gprofng-archive: Fatal error: unable to allocate memory\n"));
      return 1;
    }
  // Check if full name is not too long
  long len = dbe_sstrlen (abs_caname);
  long max = pathconf (cad, _PC_PATH_MAX);
  if ((max < 0) || (len <= 0))
    { // unknown error
      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: pathconf(%s, _PC_PATH_MAX) failed\n"),
	       cad);
      free (abs_caname);
      free (cad);
      free (abs_aname);
      return 1;
    }
  if (len >= max)
    {
      // Try to truncate the name
      if ((len - max) <= dbe_sstrlen (fname))
	{
	  // Yes, we can do it
	  abs_caname[max - 1] = 0;
	  if (!hide_msg)
	    fprintf (stderr, GTXT ("gprofng-archive: file path is too long - truncated:%s\n"),
		     abs_caname);
	}
    }
  // Check if file name is not too long
  char *cafname = get_basename (abs_caname);
  len = dbe_sstrlen (cafname);
  max = pathconf (cad, _PC_NAME_MAX);
  if ((max < 0) || (len <= 0))
    { // unknown error
      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: pathconf(%s, _PC_NAME_MAX) failed\n"),
	       cad);
      free (abs_caname);
      free (cad);
      free (abs_aname);
      return 1;
    }
  if (len >= max)
    {
      // Try to truncate the name
      if ((len - max) <= dbe_sstrlen (fname))
	{
	  // Yes, we can do it
	  cafname[max - 1] = 0;
	  if (!hide_msg)
	    fprintf (stderr, GTXT ("gprofng-archive: file name is too long - truncated:%s\n"),
		     abs_caname);
	}
    }
  // Copy file to the common archive directory if it is not there yet
  int res = 0;
  if (dbe_stat_file (abs_caname, NULL) != 0)
    {
      // Use temporary file to avoid synchronization problems
      char *t = dbe_sprintf ("%s/archive_%llx", cad, (unsigned long long) gethrtime());
      free (cad);
      // Copy file to temporary file
      res = copy_file_to_archive (name, t, hide_msg); // hide messages
      if (res != 0)
	{
	  fprintf (stderr, GTXT ("gprofng-archive: Fatal error: cannot copy file %s to temporary file: %s\n"),
		   name, t);
	  unlink (t);
	  free (t);
	  free (abs_caname);
	  free (abs_aname);
	  return 1;
	}
      // Set read-only permissions
      dbe_stat_t statbuf;
      if (0 == dbe_stat_file (name, &statbuf))
	{
	  mode_t mask = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
	  mode_t mode = statbuf.st_mode & mask;
	  chmod (t, mode);
	}
      // Try to rename temporary file "t" to "abs_caname"
      // res = link(t, abs_caname); // link() fails on some f/s - use rename()
      res = rename (t, abs_caname);
      if (res != 0)
	{
	  if (errno != EEXIST)
	    {
	      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: rename(%s, %s) returned error: %d\n"),
		       t, abs_caname, res);
	      unlink (t);
	      free (t);
	      free (abs_caname);
	      free (abs_aname);
	      return 1;
	    }
	  // File "abs_caname" is already there - continue
	}
      unlink (t);
      free (t);
    }
  else
    free (cad);
  char *lname = NULL;
  if (relative_path)
    {
      if (common_archive[0] != '/' && aname[0] != '/')
	{
	  // compare one relative path to another and find common beginning
	  char *rel_caname = dbe_sprintf ("%s/%s", common_archive, cafname);
	  if (rel_caname == NULL)
	    {
	      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: unable to allocate memory\n"));
	      return 1;
	    }
	  lname = get_relative_link (rel_caname, aname);
	  free (rel_caname);
	}
      else
	{
	  if (abs_aname == NULL)
	    {
	      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: unable to allocate memory\n"));
	      return 1;
	    }
	  lname = get_relative_link (abs_caname, abs_aname);
	}
    }
  else  // absolute path
    lname = dbe_strdup (abs_caname);
  free (abs_aname);
  if (lname == NULL)
    {
      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: unable to allocate memory\n"));
      return 1;
    }
  // Create symbolic link: aname -> lname
  if (dbe_stat_file (abs_caname, NULL) == 0)
    {
      res = symlink (lname, aname);
      if (res != 0)
	{
	  fprintf (stderr, GTXT ("gprofng-archive: Fatal error: symlink(%s, %s) returned error: %d (errno=%s)\n"),
		   lname, aname, res, strerror (errno));
	  free (abs_caname);
	  free (lname);
	  return 1;
	}
      if (!hide_msg)
	fprintf (stderr, GTXT ("Created symbolic link %s to file in common archive: %s\n"),
		 aname, lname);
    }
  else
    {
      fprintf (stderr, GTXT ("gprofng-archive: Internal error: file does not exist in common archive: %s\n"),
	       abs_caname);
      res = 1;
    }
  free (abs_caname);
  free (lname);
  return res;
}

/**
 * Copy file to archive
 * @param name
 * @param aname
 * @param hide_msg
 * @param common_archive
 * @return 0 - success
 */
int
Experiment::copy_file (const char *name, const char *aname, int hide_msg,
		       const char *common_archive, int relative_path)
{
  if (common_archive)
    {
      if (0 == copy_file_to_common_archive (name, aname, hide_msg,
					    common_archive, relative_path))
	return 0;
      // Error. For now - fatal error. Message is already printed.
      fprintf (stderr, GTXT ("gprofng-archive: Fatal error: cannot copy file %s to common archive %s\n"),
	       name, common_archive);
      return 1;
    }
  return (copy_file_to_archive (name, aname, hide_msg));
}

LoadObject *
Experiment::createLoadObject (const char *path, uint64_t chksum)
{
  LoadObject *lo = dbeSession->createLoadObject (path, chksum);
  if (lo->firstExp == NULL)
    lo->firstExp = this;
  return lo;
}

LoadObject *
Experiment::createLoadObject (const char *path, const char *runTimePath)
{
  DbeFile *df = findFileInArchive (path, runTimePath);
  if (df && (df->get_stat () == NULL))
    df = NULL; // No access to file
  LoadObject *lo = dbeSession->createLoadObject (path, runTimePath, df);
  if (df && (lo->dbeFile->get_location (false) == NULL))
    {
      lo->dbeFile->set_location (df->get_location ());
      lo->dbeFile->inArchive = df->inArchive;
      lo->dbeFile->sbuf = df->sbuf;
      lo->dbeFile->experiment = df->experiment;
      lo->firstExp = df->experiment;
    }
  if (lo->firstExp == NULL)
    {
      lo->firstExp = this;
      lo->dbeFile->experiment = this;
    }
  return lo;
}

SourceFile *
Experiment::get_source (const char *path)
{
  if (founder_exp && (founder_exp != this))
    return founder_exp->get_source (path);
  if (sourcesMap == NULL)
    sourcesMap = new StringMap<SourceFile*>(1024, 1024);
  if (strncmp (path, NTXT ("./"), 2) == 0)
    path += 2;
  SourceFile *sf = sourcesMap->get (path);
  if (sf)
    return sf;
  char *fnm = checkFileInArchive (path, false);
  if (fnm)
    {
      sf = new SourceFile (path);
      dbeSession->append (sf);
      DbeFile *df = sf->dbeFile;
      df->set_location (fnm);
      df->inArchive = true;
      df->check_access (fnm); // init 'sbuf'
      df->sbuf.st_mtime = 0; // Don't check timestamps
      free (fnm);
    }
  else
    sf = dbeSession->createSourceFile (path);
  sourcesMap->put (path, sf);
  return sf;
}

Vector<Histable*> *
Experiment::get_comparable_objs ()
{
  update_comparable_objs ();
  if (comparable_objs || dbeSession->expGroups->size () <= 1)
    return comparable_objs;
  comparable_objs = new Vector<Histable*>(dbeSession->expGroups->size ());
  for (long i = 0, sz = dbeSession->expGroups->size (); i < sz; i++)
    {
      ExpGroup *gr = dbeSession->expGroups->get (i);
      if (groupId == gr->groupId)
	{
	  comparable_objs->append (this);
	  continue;
	}
      Histable *h = NULL;
      for (long i1 = 0, sz1 = gr->exps ? gr->exps->size () : 0; i1 < sz1; i1++)
	{
	  Experiment *exp = gr->exps->get (i1);
	  if ((exp->comparable_objs == NULL) && (dbe_strcmp (utargname, exp->utargname) == 0))
	    {
	      exp->phaseCompareIdx = phaseCompareIdx;
	      h = exp;
	      h->comparable_objs = comparable_objs;
	      break;
	    }
	}
      comparable_objs->append (h);
    }
  dump_comparable_objs ();
  return comparable_objs;
}

DbeFile *
Experiment::findFileInArchive (const char *fname)
{
  if (archiveMap)
    {
      char *aname = get_archived_name (fname);
      DbeFile *df = archiveMap->get (aname);
      free (aname);
      return df;
    }
  if (founder_exp)
    return founder_exp->findFileInArchive (fname);
  return NULL;
}

DbeFile *
Experiment::findFileInArchive (const char *className, const char *runTimePath)
{
  DbeFile *df = NULL;
  if (runTimePath)
    {
      const char *fnm = NULL;
      if (strncmp (runTimePath, NTXT ("zip:"), 4) == 0)
	fnm = runTimePath + 4;
      else if (strncmp (runTimePath, NTXT ("jar:file:"), 9) == 0)
	fnm = runTimePath + 9;
      if (fnm)
	{
	  const char *s = strchr (fnm, '!');
	  if (s)
	    {
	      char *s1 = dbe_strndup (fnm, s - fnm);
	      df = findFileInArchive (s1);
	      free (s1);
	    }
	  else
	    df = findFileInArchive (fnm);
	  if (df)
	    df->filetype |= DbeFile::F_JAR_FILE;
	}
      else if (strncmp (runTimePath, NTXT ("file:"), 5) == 0)
	{
	  fnm = runTimePath + 5;
	  df = findFileInArchive (fnm);
	}
      else
	df = findFileInArchive (runTimePath);
    }
  if (df == NULL)
    df = findFileInArchive (className);
  return df;
}
