/* Copyright (C) 2021-2024 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 <new>

#include "util.h"
#include "CacheMap.h"
#include "CallStack.h"
#include "DbeSession.h"
#include "DbeView.h"
#include "DbeLinkList.h"
#include "Experiment.h"
#include "Exp_Layout.h"
#include "Function.h"
#include "LoadObject.h"
#include "Module.h"

Descendants::Descendants ()
{
  count = 0;
  limit = sizeof (first_data) / sizeof (CallStackNode *);
  data = first_data;
}

Descendants::~Descendants ()
{
  if (data != first_data)
    free (data);
}

CallStackNode *
Descendants::find (Histable *hi, int *index)
{
  int cnt = count;
  int left = 0;
  for (int right = cnt - 1; left <= right;)
    {
      int ind = (left + right) / 2;
      CallStackNode *node = data[ind];
      Histable *instr = node->get_instr ();
      if (instr == hi)
	{
	  if (index)
	    *index = ind;
	  return node;
	}
      if (instr->id < hi->id)
	right = ind - 1;
      else
	left = ind + 1;
    }
  if (index)
    *index = left;
  return NULL;
}

void
Descendants::append (CallStackNode* item)
{
  if (count < limit)
    data[count++] = item;
  else
    insert (count, item);
}

void
Descendants::insert (int ind, CallStackNode* item)
{
  CallStackNode **old_data = data;
  int old_cnt = count;
  if (old_cnt + 1 >= limit)
    {
      int new_limit = (limit == 0) ? DELTA : limit * 2;
      CallStackNode **new_data = (CallStackNode **) malloc (new_limit * sizeof (CallStackNode *));
      for (int i = 0; i < ind; i++)
	new_data[i] = old_data[i];
      new_data[ind] = item;
      for (int i = ind; i < old_cnt; i++)
	new_data[i + 1] = old_data[i];
      limit = new_limit;
      data = new_data;
      if (old_data != first_data)
	free (old_data);
    }
  else
    {
      for (int i = ind; i < old_cnt; i++)
	old_data[i + 1] = old_data[i];
      old_data[ind] = item;
    }
  count++;
}

/*
 *    Private implementation of CallStack interface
 */

// When performing pipeline optimization on resolve_frame_info + add_stack
// cstk_ctx structure contains the state (or context) for one iteration to pass on
// from Phase 2 to Phase 3 (More details in Experiment.cc)
class CallStackP : public CallStack
{
public:
  CallStackP (Experiment *exp);

  virtual ~CallStackP ();

  virtual void add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp, cstk_ctx_chunk *cstCtxChunk);
  virtual void *add_stack (Vector<Histable*> *objs);
  virtual CallStackNode *get_node (int n);
  virtual void print (FILE *);

private:

  static const int CHUNKSZ = 16384;

  Experiment *experiment;
  CallStackNode *root;
  CallStackNode *jvm_node;
  int nodes;
  int nchunks;
  CallStackNode **chunks;
  Map<uint64_t, CallStackNode *> *cstackMap;
  DbeLock *cstackLock;

  CallStackNode *add_stack (long start, long end, Vector<Histable*> *objs, CallStackNode *myRoot);
  CallStackNode *new_Node (CallStackNode*, Histable*);
  CallStackNode *find_preg_stack (uint64_t);
  // objs are in the root..leaf order
  void *add_stack_d (Vector<Histable*> *objs);
  void add_stack_java (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, bool natpc_added, cstk_ctx_chunk *cstCtxChunk);
  void add_stack_java_epilogue (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, Vector<Histable*>* jpcs, bool natpc_added);

  // Adjust HW counter event to find better trigger PC, etc.
  DbeInstr *adjustEvent (DbeInstr *leafPC, DbeInstr * candPC,
			 Vaddr &eventEA, int abst_type);
  Vector<DbeInstr*> *natpcsP;
  Vector<Histable*> *jpcsP;
};

CallStackP::CallStackP (Experiment *exp)
{
  experiment = exp;
  nchunks = 0;
  chunks = NULL;
  nodes = 0;
  cstackMap = new CacheMap<uint64_t, CallStackNode *>;
  cstackLock = new DbeLock ();
  Function *total = dbeSession->get_Total_Function ();
  root = new_Node (0, total->find_dbeinstr (0, 0));
  jvm_node = NULL;
  natpcsP = NULL;
  jpcsP = NULL;
}

CallStackP::~CallStackP ()
{
  delete cstackLock;
  if (chunks)
    {
      for (int i = 0; i < nodes; i++)
	{
	  CallStackNode *node = get_node (i);
	  node->~CallStackNode ();
	}
      for (int i = 0; i < nchunks; i++)
	free (chunks[i]);
      free (chunks);
    }
  delete natpcsP;
  delete jpcsP;
  destroy_map (CallStackNode *, cstackMap);
}

CallStackNode *
CallStackP::new_Node (CallStackNode *anc, Histable *pcval)
{
  // cstackLock->aquireLock(); // Caller already locked it
  if (nodes >= nchunks * CHUNKSZ)
    {
      CallStackNode **old_chunks = chunks;
      nchunks++;

      // Reallocate Node chunk array
      chunks = (CallStackNode **) malloc (nchunks * sizeof (CallStackNode *));
      for (int i = 0; i < nchunks - 1; i++)
	chunks[i] = old_chunks[i];
      free (old_chunks);
      // Allocate new chunk for nodes.
      chunks[nchunks - 1] = (CallStackNode *) malloc (CHUNKSZ * sizeof (CallStackNode));
    }
  nodes++;
  CallStackNode *node = get_node (nodes - 1);
  new (node) CallStackNode (anc, pcval);
  // cstackLock->releaseLock();
  return node;
}

CallStackNode *
CallStackP::find_preg_stack (uint64_t prid)
{
  DataView *dview = experiment->getOpenMPdata ();
  dview->sort (PROP_CPRID);
  Datum tval;
  tval.setUINT64 (prid);
  long idx = dview->getIdxByVals (&tval, DataView::REL_EQ);
  if (idx < 0)
    return root;
  CallStackNode *node = (CallStackNode*) dview->getObjValue (PROP_USTACK, idx);
  if (node != NULL)
    return node;
  uint64_t pprid = dview->getLongValue (PROP_PPRID, idx);
  if (pprid == prid)
    return root;
  void *nat_stack = dview->getObjValue (PROP_MSTACK, idx);
  Vector<Histable*> *pcs = getStackPCs (nat_stack);

  // Find the bottom frame
  int btm;
  bool inOMP = false;
  DbeInstr *instr;
  Histable *hist;
  for (btm = 0; btm < pcs->size (); btm++)
    {
      hist = pcs->fetch (btm);
      if (hist->get_type () == Histable::INSTR)
	instr = (DbeInstr *) hist;
      else    // DBELINE
	instr = (DbeInstr *) hist->convertto (Histable::INSTR);
      LoadObject *lo = instr->func->module->loadobject;
      if (!inOMP)
	{
	  if (lo->flags & SEG_FLAG_OMP)
	    inOMP = true;
	}
      else if (!(lo->flags & SEG_FLAG_OMP))
	break;
    }

  // Find the top frame
  dview->sort (PROP_CPRID);
  int top;
  tval.setUINT64 (pprid);
  long pidx = dview->getIdxByVals (&tval, DataView::REL_EQ);
  if (pidx < 0)     // No parent. Process the entire nat_stack
    top = pcs->size () - 1;
  else
    {
      uint32_t thrid = (uint32_t) dview->getIntValue (PROP_THRID, idx);
      uint32_t pthrid = (uint32_t) dview->getIntValue (PROP_THRID, pidx);
      if (thrid != pthrid)
	{
	  // Parent is on a different stack.
	  // Process the entire nat_stack. Skip libthread.
	  for (top = pcs->size () - 1; top >= 0; top--)
	    {
	      hist = pcs->fetch (top);
	      if (hist->get_type () == Histable::INSTR)
		instr = (DbeInstr *) hist;
	      else // DBELINE
		instr = (DbeInstr *) hist->convertto (Histable::INSTR);
	      if (instr->func->module->loadobject->flags & SEG_FLAG_OMP)
		break;
	    }
	  if (top < 0)  // None found. May be incomplete call stack (x86)
	    top = pcs->size () - 1;
	}
      else
	{
	  // Parent is on the same stack. Find match.
	  top = pcs->size () - 1;
	  void *pnat_stack = dview->getObjValue (PROP_MSTACK, pidx);
	  Vector<Histable*> *ppcs = getStackPCs (pnat_stack);
	  for (int ptop = ppcs->size () - 1; top >= 0 && ptop >= 0;
		  top--, ptop--)
	    {
	      if (pcs->fetch (top) != ppcs->fetch (ptop))
		break;
	    }
	  delete ppcs;
	}
    }

  // Process the found range
  Vector<Histable*> *upcs = new Vector<Histable*>(128);
  for (int i = btm; i <= top; ++i)
    {
      hist = (DbeInstr*) pcs->fetch (i);
      if (hist->get_type () == Histable::INSTR)
	instr = (DbeInstr *) hist;
      else // DBELINE
	instr = (DbeInstr *) hist->convertto (Histable::INSTR);

      if (instr->func->module->loadobject->flags & SEG_FLAG_OMP)
	// Skip all frames from libmtsk
	continue;
      upcs->append (instr);
    }
  delete pcs;
  node = find_preg_stack (pprid);
  while (node != root)
    {
      upcs->append (node->instr);
      node = node->ancestor;
    }
  node = (CallStackNode *) add_stack (upcs);
  dview->setObjValue (PROP_USTACK, idx, node);
  delete upcs;
  return node;
}

#define JNI_MARKER -3

// This is one iteration if the third stage of
// resolve_frame_info + add_stack pipeline. Works on building the java
// stacks
void
CallStackP::add_stack_java (DataDescriptor *dDscr, long idx, FramePacket *frp,
			    hrtime_t tstamp, uint32_t thrid,
			    Vector<DbeInstr*>* natpcs, bool natpc_added,
			    cstk_ctx_chunk *cstCtxChunk)
{
  Vector<Histable*> *jpcs = NULL;
  cstk_ctx *cstctx = NULL;
  if (cstCtxChunk != NULL)
    {
      cstctx = cstCtxChunk->cstCtxAr[idx % CSTCTX_CHUNK_SZ];
      jpcs = cstctx->jpcs;
      jpcs->reset ();
    }
  if (jpcs == NULL)
    {
      // this is when we are not doing the pipeline optimization
      // Temporary array for resolved addresses
      // [leaf_pc .. root_pc] == [0..stack_size-1]
      // Leave room for a possible "truncated" frame
      if (jpcsP == NULL)
	jpcsP = new Vector<Histable*>;
      jpcs = jpcsP;
      jpcs->reset ();
    }

  //
  // Construct the user stack
  //
  // Construct Java user stack
  int jstack_size = frp->stackSize (true);
  if (jstack_size)
    {
      // jpcs = new Vector<Histable*>( jstack_size );
      if (frp->isTruncatedStack (true))
	{
	  Function *truncf = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc);
	  jpcs->append (truncf->find_dbeinstr (0, 0));
	}

      int nind = natpcs->size () - 1; // first native frame
      for (int jind = jstack_size - 1; jind >= 0; jind--)
	{
	  bool jleaf = (jind == 0); // is current java frame a leaf?
	  Vaddr mid = frp->getMthdFromStack (jind);
	  int bci = frp->getBciFromStack (jind);
	  DbeInstr *cur_instr = experiment->map_jmid_to_PC (mid, bci, tstamp);
	  jpcs->append (cur_instr);
	  if (bci == JNI_MARKER)
	    {
	      JMethod *j_method = (JMethod*) cur_instr->func;
	      // Find matching native function on the native stack
	      bool found = false;
	      for (; nind >= 0; nind--)
		{
		  DbeInstr *nat_addr = natpcs->fetch (nind);
		  if (0 == nat_addr)
		    continue;
		  Function *nat_func = nat_addr->func;
		  if (!found && j_method->jni_match (nat_func))
		    found = true;
		  if (found)
		    {
		      // XXX omazur: the following will skip JNI native method
		      // implemented in JVM itself.
		      // If we are back in JVM switch to processing Java
		      // frames if there are any.
		      if ((nat_func->module->loadobject->flags & SEG_FLAG_JVM) && !jleaf)
			break;
		      jpcs->append (nat_addr);
		    }
		}
	    }
	}
    }
  add_stack_java_epilogue (dDscr, idx, frp, tstamp, thrid, natpcs, jpcs, natpc_added);
}

// This is one iteration if the fourth stage of
// resolve_frame_info + add_stack pipeline.
// It adds the native and java stacks to the stackmap

void
CallStackP::add_stack_java_epilogue (DataDescriptor *dDscr, long idx, FramePacket *frp, hrtime_t tstamp, uint32_t thrid, Vector<DbeInstr*>* natpcs, Vector<Histable*> *jpcs, bool natpc_added)
{
  CallStackNode *node = NULL;
  if (!natpc_added)
    {
      node = (CallStackNode *) add_stack ((Vector<Histable*>*)natpcs);
      dDscr->setObjValue (PROP_MSTACK, idx, node);
      dDscr->setObjValue (PROP_XSTACK, idx, node);
      dDscr->setObjValue (PROP_USTACK, idx, node);
    }

  int jstack_size = frp->stackSize (true);
  if (jstack_size)
    {
      if (jpcs != NULL)
	node = (CallStackNode *) add_stack_d (jpcs);
      if (node == NULL)
	node = (CallStackNode*) dDscr->getObjValue (PROP_USTACK, idx);
      dDscr->setObjValue (PROP_USTACK, idx, node);
      Function *func = (Function*) node->instr->convertto (Histable::FUNCTION);
      if (func != dbeSession->get_JUnknown_Function ())
	dDscr->setObjValue (PROP_XSTACK, idx, node);
    }

  JThread *jthread = experiment->map_pckt_to_Jthread (thrid, tstamp);
  if (jthread == JTHREAD_NONE && jstack_size != 0 && node != NULL)
    {
      Function *func = (Function*) node->instr->convertto (Histable::FUNCTION);
      if (func != dbeSession->get_JUnknown_Function ())
	jthread = JTHREAD_DEFAULT;
    }
  dDscr->setObjValue (PROP_JTHREAD, idx, jthread);
  if (jthread == JTHREAD_NONE || (jthread != JTHREAD_DEFAULT && jthread->is_system ()))
    {
      if (jvm_node == NULL)
	{
	  Function *jvm = dbeSession->get_jvm_Function ();
	  if (jvm)
	    {
	      jvm_node = new_Node (root, jvm->find_dbeinstr (0, 0));
	      CommonPacket::jvm_overhead = jvm_node;
	    }
	}
      dDscr->setObjValue (PROP_USTACK, idx, jvm_node);
    }
}

// This is one iteration of the 2nd stage of
// resolve_frame_info + add_stack() pipeline. Builds the stack for a given framepacket.
// When pipeline optimization is turnd off, cstctxchunk passed is NULL
void
CallStackP::add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp,
		       cstk_ctx_chunk* cstCtxChunk)
{
  Vector<DbeInstr*> *natpcs = NULL;
  cstk_ctx *cstctx = NULL;
  int stack_size = frp->stackSize ();
  if (cstCtxChunk != NULL)
    {
      cstctx = cstCtxChunk->cstCtxAr[idx % CSTCTX_CHUNK_SZ];
      natpcs = cstctx->natpcs;
      natpcs->reset ();
    }
  if (natpcs == NULL)
    {
      // this is when we are not doing the pipeline optimization
      // Temporary array for resolved addresses
      // [leaf_pc .. root_pc] == [0..stack_size-1]
      // Leave room for a possible "truncated" frame
      if (natpcsP == NULL)
	natpcsP = new Vector<DbeInstr*>;
      natpcs = natpcsP;
      natpcs->reset ();
    }

  bool leaf = true;
  hrtime_t tstamp = (hrtime_t) dDscr->getLongValue (PROP_TSTAMP, idx);
  uint32_t thrid = (uint32_t) dDscr->getIntValue (PROP_THRID, idx);

  enum
  {
    NONE,
    CHECK_O7,
    USE_O7,
    SKIP_O7
  } state = NONE;

  Vaddr o7_to_skip = 0;
  for (int index = 0; index < stack_size; index++)
    {
      if (frp->isLeafMark (index))
	{
	  state = CHECK_O7;
	  continue;
	}

      if (state == SKIP_O7)
	{
	  // remember this bad o7 value since OMP might not recognize it
	  o7_to_skip = frp->getFromStack (index);
	  state = NONE;
	  continue;
	}

      Vaddr va = frp->getFromStack (index);
      DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp);
#if ARCH(Intel)// TBR? FIXUP_XXX_SPARC_LINUX: switch should be on experiment ARCH, not dbe ARCH
      // We need to adjust return addresses on intel
      // in order to attribute inclusive metrics to
      // proper call instructions.
      if (experiment->exp_maj_version <= 9)
	if (!leaf && cur_instr->addr != 0)
	  cur_instr = cur_instr->func->find_dbeinstr (0, cur_instr->addr - 1);
#endif

      // Skip PC's from PLT, update leaf and state accordingly
      if ((cur_instr->func->flags & FUNC_FLAG_PLT)
	   && (leaf || state == CHECK_O7))
	{
	  if (state == CHECK_O7)
	    state = USE_O7;
	  leaf = false;
	  continue;
	}
      if (state == CHECK_O7)
	{
	  state = USE_O7;
	  uint64_t saddr = cur_instr->func->save_addr;
	  if (cur_instr->func->isOutlineFunction)
	    // outline functions assume 'save' instruction
	    // Note: they accidentally have saddr == FUNC_ROOT
	    state = SKIP_O7;
	  else if (saddr == FUNC_ROOT)
	    {
	      // If a function is statically determined as a root
	      // but dynamically appears not, don't discard o7.
	      // One such case is __misalign_trap_handler on sparcv9.
	      if (stack_size == 3)
		state = SKIP_O7;
	    }
	  else if (saddr != FUNC_NO_SAVE && cur_instr->addr > saddr)
	    state = SKIP_O7;
	}
      else if (state == USE_O7)
	{
	  state = NONE;
	  if (cur_instr->flags & PCInvlFlag)
	    continue;
	}
      if (leaf)
	{
	  Vaddr evpc = (Vaddr) dDscr->getLongValue (PROP_VIRTPC, idx);
	  if (evpc != 0
	      && !(index > 0 && frp->isLeafMark (index - 1)
		   && evpc == (Vaddr) (-1)))
	    {
	      /* contains hwcprof info */
	      cur_instr->func->module->read_hwcprof_info ();

	      // complete ABS validation of candidate eventPC/eventEA
	      // and correction/adjustment of collected callstack leaf PC
	      DbeInstr *candPC = experiment->map_Vaddr_to_PC (evpc, tstamp);
	      Vaddr vaddr = (Vaddr) dDscr->getLongValue (PROP_VADDR, idx);
	      Vaddr tmp_vaddr = vaddr;
	      int abst_type;
	      uint32_t tag = dDscr->getIntValue (PROP_HWCTAG, idx);
	      if (tag < 0 || tag >= MAX_HWCOUNT)
		abst_type = ABST_NOPC;
	      else
		abst_type = experiment->coll_params.hw_tpc[tag];

	      // We need to adjust addresses for ABST_EXACT_PEBS_PLUS1
	      // (Nehalem/SandyBridge PEBS identifies PC+1, not PC)
	      if (abst_type == ABST_EXACT_PEBS_PLUS1 && candPC->addr != 0)
		candPC = candPC->func->find_dbeinstr (0, candPC->func->find_previous_addr (candPC->addr));

	      cur_instr = adjustEvent (cur_instr, candPC, tmp_vaddr, abst_type);
	      if (vaddr != tmp_vaddr)
		{
		  if (tmp_vaddr < ABS_CODE_RANGE)
		    {
		      /* post processing backtrack failed */
		      dDscr->setValue (PROP_VADDR, idx, tmp_vaddr);
		      dDscr->setValue (PROP_PADDR, idx, ABS_NULL);
		      /* hwcp->eventVPC =  xxxxx leave eventPC alone,
		       *   or can we set it to leafpc? */
		      dDscr->setValue (PROP_PHYSPC, idx, ABS_NULL);
		    }
		  else
		    {
		      /* internal error: why would post-processing modify vaddr? */
		      dDscr->setValue (PROP_PADDR, idx, (Vaddr) (-1));
		      dDscr->setValue (PROP_PHYSPC, idx, (Vaddr) (-1));
		    }
		}
	    }
	}
      natpcs->append (cur_instr);
      leaf = false;

      // A hack to deceive the user into believing that outlined code
      // is called from the base function
      DbeInstr *drvd = cur_instr->func->derivedNode;
      if (drvd != NULL)
	natpcs->append (drvd);
    }
  if (frp->isTruncatedStack ())
    {
      Function *truncf = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc);
      natpcs->append (truncf->find_dbeinstr (0, 0));
    }
  else if (frp->isFailedUnwindStack ())
    {
      Function *funwf = dbeSession->getSpecialFunction (DbeSession::FailedUnwindFunc);
      natpcs->append (funwf->find_dbeinstr (0, 0));
    }

  CallStackNode *node = (CallStackNode*) add_stack ((Vector<Histable*>*)natpcs);
  dDscr->setObjValue (PROP_MSTACK, idx, node);
  dDscr->setObjValue (PROP_XSTACK, idx, node);
  dDscr->setObjValue (PROP_USTACK, idx, node);

  // OpenMP 3.0 stacks
  stack_size = frp->ompstack->size ();
  if (stack_size > 0 || frp->omp_state == OMP_IDLE_STATE)
    {
      Function *func;
      Vector<Histable*> *omppcs = new Vector<Histable*>(stack_size);
      Vector<Histable*> *ompxpcs = new Vector<Histable*>(stack_size);
      switch (frp->omp_state)
	{
	case OMP_IDLE_STATE:
	case OMP_RDUC_STATE:
	case OMP_IBAR_STATE:
	case OMP_EBAR_STATE:
	case OMP_LKWT_STATE:
	case OMP_CTWT_STATE:
	case OMP_ODWT_STATE:
	case OMP_ATWT_STATE:
	  {
	    func = dbeSession->get_OMP_Function (frp->omp_state);
	    DbeInstr *instr = func->find_dbeinstr (0, 0);
	    omppcs->append (instr);
	    ompxpcs->append (instr);
	    break;
	  }
	}
      Vector<Vaddr> *stck = frp->ompstack;
      leaf = true;
      for (int index = 0; index < stack_size; index++)
	{
	  if (stck->fetch (index) == SP_LEAF_CHECK_MARKER)
	    {
	      state = CHECK_O7;
	      continue;
	    }
	  if (state == SKIP_O7)
	    {
	      state = NONE;
	      continue;
	    }

	  // The OMP stack might not have enough information to know to discard a bad o7.
	  // So just remember what the native stack skipped.
	  if (o7_to_skip == stck->fetch (index))
	    {
	      state = NONE;
	      continue;
	    }
	  Vaddr va = stck->fetch (index);
	  DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp);

	  // Skip PC's from PLT, update leaf and state accordingly
	  if ((cur_instr->func->flags & FUNC_FLAG_PLT) &&
	      (leaf || state == CHECK_O7))
	    {
	      if (state == CHECK_O7)
		state = USE_O7;
	      leaf = false;
	      continue;
	    }
	  if (state == CHECK_O7)
	    {
	      state = USE_O7;
	      uint64_t saddr = cur_instr->func->save_addr;
	      if (cur_instr->func->isOutlineFunction)
		// outline functions assume 'save' instruction
		// Note: they accidentally have saddr == FUNC_ROOT
		state = SKIP_O7;
	      else if (saddr == FUNC_ROOT)
		{
		  // If a function is statically determined as a root
		  // but dynamically appears not, don't discard o7.
		  // One such case is __misalign_trap_handler on sparcv9.
		  if (stack_size == 3)
		    state = SKIP_O7;
		}
	      else if (saddr != FUNC_NO_SAVE && cur_instr->addr > saddr)
		state = SKIP_O7;
	    }
	  else if (state == USE_O7)
	    {
	      state = NONE;
	      if (cur_instr->flags & PCInvlFlag)
		continue;
	    }

	  DbeLine *dbeline = (DbeLine*) cur_instr->convertto (Histable::LINE);
	  if (cur_instr->func->usrfunc)
	    {
	      dbeline = dbeline->sourceFile->find_dbeline (cur_instr->func->usrfunc, dbeline->lineno);
	      omppcs->append (dbeline);
	    }
	  else if (dbeline->lineno > 0)
	    omppcs->append (dbeline);
	  else
	    omppcs->append (cur_instr);
	  if (dbeline->is_set (DbeLine::OMPPRAGMA) &&
	      frp->omp_state == OMP_WORK_STATE)
	    dDscr->setValue (PROP_OMPSTATE, idx, OMP_OVHD_STATE);
	  ompxpcs->append (cur_instr);
	  leaf = false;
	}
      if (frp->omptruncated == SP_TRUNC_STACK_MARKER)
	{
	  func = dbeSession->getSpecialFunction (DbeSession::TruncatedStackFunc);
	  DbeInstr *instr = func->find_dbeinstr (0, 0);
	  omppcs->append (instr);
	  ompxpcs->append (instr);
	}
      else if (frp->omptruncated == SP_FAILED_UNWIND_MARKER)
	{
	  func = dbeSession->getSpecialFunction (DbeSession::FailedUnwindFunc);
	  DbeInstr *instr = func->find_dbeinstr (0, 0);
	  omppcs->append (instr);
	  ompxpcs->append (instr);
	}

      // User model call stack
      node = (CallStackNode*) add_stack (omppcs);
      dDscr->setObjValue (PROP_USTACK, idx, node);
      delete omppcs;

      // Expert call stack
      node = (CallStackNode*) add_stack (ompxpcs);
      dDscr->setObjValue (PROP_XSTACK, idx, node);
      delete ompxpcs;
      dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT);
      return;
    }

  // OpenMP 2.5 stacks
  if (frp->omp_cprid || frp->omp_state)
    {
      DataView *dview = experiment->getOpenMPdata ();
      if (dview == NULL)
	{
	  // It appears we may get OMP_SERL_STATE from a passive libmtsk
	  dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT);
	  return;
	}
      if (dview->getDataDescriptor () == dDscr)
	{
	  // Don't process the user stack for OpenMP fork events yet
	  dDscr->setObjValue (PROP_USTACK, idx, (void*) NULL);
	  dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT);
	  return;
	}
      Vector<Histable*> *omppcs = new Vector<Histable*>(stack_size);

      // Construct OMP user stack
      // Find the bottom frame
      int btm = 0;
      switch (frp->omp_state)
	{
	case OMP_IDLE_STATE:
	  {
	    Function *func = dbeSession->get_OMP_Function (frp->omp_state);
	    omppcs->append (func->find_dbeinstr (0, 0));
	    // XXX: workaround for inconsistency between OMP_IDLE_STATE
	    // and omp_cprid != 0
	    frp->omp_cprid = 0;
	    btm = natpcs->size ();
	    break;
	  }
	case OMP_RDUC_STATE:
	case OMP_IBAR_STATE:
	case OMP_EBAR_STATE:
	case OMP_LKWT_STATE:
	case OMP_CTWT_STATE:
	case OMP_ODWT_STATE:
	case OMP_ATWT_STATE:
	  {
	    Function *func = dbeSession->get_OMP_Function (frp->omp_state);
	    omppcs->append (func->find_dbeinstr (0, 0));
	    bool inOMP = false;
	    for (btm = 0; btm < natpcs->size (); btm++)
	      {
		LoadObject *lo = natpcs->fetch (btm)->func->module->loadobject;
		if (!inOMP)
		  {
		    if (lo->flags & SEG_FLAG_OMP)
		      inOMP = true;
		  }
		else if (!(lo->flags & SEG_FLAG_OMP))
		  break;
	      }
	    break;
	  }
	case OMP_NO_STATE:
	case OMP_WORK_STATE:
	case OMP_SERL_STATE:
	default:
	  break;
	}

      // Find the top frame
      int top = -1;
      switch (frp->omp_state)
	{
	case OMP_IDLE_STATE:
	  break;
	default:
	  {
	    dview->sort (PROP_CPRID);
	    Datum tval;
	    tval.setUINT64 (frp->omp_cprid);
	    long pidx = dview->getIdxByVals (&tval, DataView::REL_EQ);
	    if (pidx < 0)   // No parent. Process the entire nat_stack
	      top = natpcs->size () - 1;
	    else
	      {
		uint32_t pthrid = (uint32_t) dview->getIntValue (PROP_THRID, pidx);
		if (thrid != pthrid)
		  {
		    // Parent is on a different stack.
		    // Process the entire nat_stack. Skip libthread.
		    for (top = natpcs->size () - 1; top >= 0; top--)
		      {
			DbeInstr *instr = natpcs->fetch (top);
			if (instr->func->module->loadobject->flags & SEG_FLAG_OMP)
			  break;
		      }
		    if (top < 0) // None found. May be incomplete call stack
		      top = natpcs->size () - 1;
		  }
		else
		  {
		    // Parent is on the same stack. Find match.
		    top = natpcs->size () - 1;
		    void *pnat_stack = dview->getObjValue (PROP_MSTACK, pidx);
		    Vector<Histable*> *ppcs = getStackPCs (pnat_stack);
		    for (int ptop = ppcs->size () - 1; top >= 0 && ptop >= 0;
			    top--, ptop--)
		      {
			if (natpcs->fetch (top) != ppcs->fetch (ptop))
			  break;
		      }
		    delete ppcs;
		  }
	      }
	    // If no frames are found for Barrier/Reduction save at least one
	    if ((frp->omp_state == OMP_RDUC_STATE
		 || frp->omp_state == OMP_IBAR_STATE
		 || frp->omp_state == OMP_EBAR_STATE)
		&& top < btm && btm < natpcs->size ())
	      top = btm;
	  }
	}
      for (int i = btm; i <= top; ++i)
	{
	  DbeInstr *instr = natpcs->fetch (i);
	  if (instr->func->module->loadobject->flags & SEG_FLAG_OMP)
	    continue; // Skip all frames from libmtsk
	  omppcs->append (instr);
	}
      node = find_preg_stack (frp->omp_cprid);
      while (node != root)
	{
	  omppcs->append (node->instr);
	  node = node->ancestor;
	}
      node = (CallStackNode *) add_stack (omppcs);
      dDscr->setObjValue (PROP_USTACK, idx, node);
      delete omppcs;
      dDscr->setObjValue (PROP_JTHREAD, idx, JTHREAD_DEFAULT);
      return;
    }

  // Construct Java user stack
  add_stack_java (dDscr, idx, frp, tstamp, thrid, natpcs, true, NULL);
}

// adjustment of leafPC/eventVA for XHWC packets with candidate eventPC
//  Called from CallStack during initial processing of the events
DbeInstr *
CallStackP::adjustEvent (DbeInstr *leafPC, DbeInstr *candPC, Vaddr &eventVA,
			 int abst_type)
{
  // increment counter of dataspace events
  experiment->dsevents++;
  bool isPrecise;
  if (abst_type == ABST_EXACT_PEBS_PLUS1)
    isPrecise = true;
  else if (abst_type == ABST_EXACT)
    isPrecise = true;
  else
    isPrecise = false;

  if (isPrecise)
    /* precise backtracking */
    /* assume within 1 instruction of leaf (this could be checked here) */
    // no change to eventVA or candPC
    return candPC;

  Function *func = leafPC->func;
  unsigned int bt_entries = func->module->bTargets.size ();
  DbeInstr *bestPC = NULL;

  // bt == branch target (potential destination of a branch
  if (bt_entries == 0)
    { // no XHWCprof info for this module
      // increment counter
      experiment->dsnoxhwcevents++;

      // see if event is to be processed anyway
      if (!dbeSession->check_ignore_no_xhwcprof ())
	{
	  // Don't ignore error
	  // XXX -- set error code in event VA -- replace with other mechanism
	  if (eventVA > ABS_CODE_RANGE)
	    eventVA = ABS_NULL;
	  eventVA |= ABS_NO_CTI_INFO; // => effective address can't be validated
	  bestPC = leafPC; // => no PC correction possible
	}
      else
	bestPC = candPC; // assume the event valid
    }
  else
    {
      // we have the info to verify the backtracking
      target_info_t *bt;
      int bt_entry = bt_entries;
      uint64_t leafPC_offset = func->img_offset + leafPC->addr;
      uint64_t candPC_offset = candPC->func->img_offset + candPC->addr;
      do
	{
	  bt_entry--;
	  bt = func->module->bTargets.fetch (bt_entry);
	  /* bts seem to be sorted by offset, smallest to largest */
	}
      while (bt_entry > 0 && bt->offset > leafPC_offset);
      /* if bt_entry == 0, all items have been checked */

      if (bt->offset > leafPC_offset)
	{ /* XXXX isn't is possible that all bt's are after leafPC_offset? */
	  bestPC = leafPC; // actual event PC can't be determined
	  if (eventVA > ABS_CODE_RANGE)
	    eventVA = ABS_NULL;
	  eventVA |= ABS_INFO_FAILED; // effective address can't be validated
	}
      else if (bt->offset > candPC_offset)
	{
	  // use synthetic PC corresponding to bTarget
	  bestPC = func->find_dbeinstr (PCTrgtFlag, bt->offset - func->img_offset);
	  if (eventVA > ABS_CODE_RANGE)
	    eventVA = ABS_NULL;
	  eventVA |= ABS_CTI_TARGET; // effective  address can't be validated
	}
      else
	bestPC = candPC;    // accept provided virtual address as valid
    }
  return bestPC;
}

void *
CallStackP::add_stack_d (Vector<Histable*> *objs)
{
  // objs: root..leaf
  // Reverse objs
  for (int i = 0, j = objs->size () - 1; i < j; ++i, --j)
    objs->swap (i, j);
  return add_stack (objs);
}

CallStackNode::CallStackNode (CallStackNode *_ancestor, Histable *_instr)
{
  ancestor = _ancestor;
  instr = _instr;
  alt_node = NULL;
}

CallStackNode::~CallStackNode () { }

bool
CallStackNode::compare (long start, long end, Vector<Histable*> *objs, CallStackNode *mRoot)
{
  CallStackNode *p = this;
  for (long i = start; i < end; i++, p = p->get_ancestor ())
    if (p == NULL || p->get_instr () != objs->get (i))
      return false;
  return p == mRoot;
}

void
CallStackNode::dump ()
{
  const char *s = "";
  int sz = 0;
  for (CallStackNode *p = this; p; p = p->get_ancestor ())
    {
      fprintf (stderr, NTXT ("%.*s 0x%08llx id=0x%08llx %s\n"), sz, s,
	       (long long) p, (long long) p->get_instr ()->id,
	       STR (p->get_instr ()->get_name ()));
      s = "-";
      sz += 1;
    }
}

long total_calls_add_stack, total_stacks, total_nodes, call_stack_size[201];

void *
CallStackP::add_stack (Vector<Histable*> *objs)
{
  // objs: leaf..root
  uint64_t hash = objs->size ();
  for (long i = objs->size () - 1; i >= 0; --i)
    hash ^= (unsigned long long) objs->get (i);

  uint64_t key = hash ? hash : 1;
  CallStackNode *node = cstackMap->get (key);
#ifdef DEBUG
  if (DUMP_CALL_STACK)
    {
      total_calls_add_stack++;
      call_stack_size[objs->size () > 200 ? 200 : objs->size ()]++;
      Dprintf (DUMP_CALL_STACK,
	       "add_stack: %lld size=%lld  key=0x%08llx cashNode=0x%08llx\n",
	       (long long) total_calls_add_stack, (long long) objs->size (),
	       (long long) key, (long long) node);
      for (long i = 0, sz = VecSize (objs); i < sz; i++)
	Dprintf (DUMP_CALL_STACK, "  add_stack: %.*s 0x%08llx id=0x%08llx %s\n",
		 (int) i, NTXT (" "), (long long) objs->get (i),
		 (long long) objs->get (i)->id, STR (objs->get (i)->get_name ()));
    }
#endif
  if (node && node->compare (0, objs->size (), objs, root))
    {
      Dprintf (DUMP_CALL_STACK, NTXT ("STACK FOUND: key=0x%08llx 0x%08llx id=0x%08llx %s\n"),
	       (long long) key, (long long) node,
	       (long long) node->get_instr ()->id,
	       STR (node->get_instr ()->get_name ()));
      return node;
    }
  node = root;
  for (long i = objs->size () - 1; i >= 0; i--)
    {
      Histable *instr = objs->get (i);
      int old_count = node->count;
      int left;
      CallStackNode *nd = node->find (instr, &left);
      if (nd)
	{
	  node = nd;
	  continue;
	}
      cstackLock->aquireLock (); // Use one lock for all nodes
      // node->aquireLock();
      if (old_count != node->count)
	{
	  nd = node->find (instr, &left);
	  if (nd)
	    { // the other thread has created this node
	      cstackLock->releaseLock ();
	      // node->releaseLock();
	      node = nd;
	      continue;
	    }
	}
      // New Call Stack
      total_stacks++;
      nd = node;
      CallStackNode *first = NULL;
      do
	{
	  CallStackNode *anc = node;
	  total_nodes++;
	  node = new_Node (anc, objs->get (i));
	  if (first)
	    anc->append (node);
	  else
	    first = node;
	}
      while (i-- > 0);
      nd->insert (left, first);
      cstackLock->releaseLock ();
      // nd->releaseLock();
      break;
    }
  cstackMap->put (key, node);
  if (DUMP_CALL_STACK)
    node->dump ();
  return node;
}

CallStackNode *
CallStackP::get_node (int n)
{
  if (n < nodes)
    return &chunks[n / CHUNKSZ][n % CHUNKSZ];
  return NULL;
}

/*
 *  Debugging methods
 */
void
CallStackP::print (FILE *fd)
{
  FILE *f = (fd == NULL ? stderr : fd);
  fprintf (f, GTXT ("CallStack: nodes = %d\n\n"), nodes);
  int maxdepth = 0;
  int maxwidth = 0;
  const char *t;
  char *n;
  for (int i = 0; i < nodes; i++)
    {
      CallStackNode *node = &chunks[i / CHUNKSZ][i % CHUNKSZ];
      Histable *instr = node->instr;
      if (instr->get_type () == Histable::LINE)
	{
	  t = "L";
	  n = ((DbeLine *) instr)->func->get_name ();
	}
      else if (instr->get_type () == Histable::INSTR)
	{
	  t = "I";
	  n = ((DbeInstr *) instr)->func->get_name ();
	}
      else
	{
	  t = "O";
	  n = instr->get_name ();
	}
      long long addr = (long long) instr->get_addr ();
      fprintf (f, GTXT ("node: 0x%016llx anc: 0x%016llx -- 0x%016llX:  %s %s\n"),
	       (unsigned long long) node, (unsigned long long) node->ancestor,
	       addr, t, n);
    }
  fprintf (f, GTXT ("md = %d, mw = %d\n"), maxdepth, maxwidth);
}

/*
 *  Static CallStack methods
 */
CallStack *
CallStack::getInstance (Experiment *exp)
{
  return new CallStackP (exp);
}

int
CallStack::stackSize (void *stack)
{
  CallStackNode *node = (CallStackNode *) stack;
  int sz = 0;
  for (; node; node = node->ancestor)
    sz++;
  return sz - 1; // don't count the root node
}

Histable *
CallStack::getStackPC (void *stack, int n)
{
  CallStackNode *node = (CallStackNode *) stack;
  while (n-- && node)
    node = node->ancestor;
  if (node == NULL)
    return dbeSession->get_Unknown_Function ()->find_dbeinstr (PCInvlFlag, 0);
  return node->instr;
}

Vector<Histable*> *
CallStack::getStackPCs (void *stack, bool get_hide_stack)
{
  Vector<Histable*> *res = new Vector<Histable*>;
  CallStackNode *node = (CallStackNode *) stack;
  if (get_hide_stack && node->alt_node != NULL)
    node = node->alt_node;
  while (node && node->ancestor)
    { // skip the root node
      res->append (node->instr);
      node = node->ancestor;
    }
  return res;
}

int
CallStack::compare (void *stack1, void *stack2)
{
  // Quick comparision
  if (stack1 == stack2)
    return 0;

  CallStackNode *node1 = (CallStackNode *) stack1;
  CallStackNode *node2 = (CallStackNode *) stack2;
  while (node1 != NULL && node2 != NULL)
    {
      //to keep the result const on different platforms
      //we use instr->id instead of instr
      if (node1->instr->id < node2->instr->id)
	return -1;
      else if (node1->instr->id > node2->instr->id)
	return 1;
      node1 = node1->ancestor;
      node2 = node2->ancestor;
    }
  if (node1 == NULL && node2 != NULL)
    return -1;
  else if (node1 != NULL && node2 == NULL)
    return 1;
  else
    return 0;
}

// LIBRARY VISIBILITY

void
CallStack::setHideStack (void *stack, void *hideStack)
{
  CallStackNode *hNode = (CallStackNode *) stack;
  hNode->alt_node = (CallStackNode *) hideStack;
}
