/* 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 <unistd.h>
#include <ar.h>
#include <ctype.h>
#include <sys/param.h>

#include "util.h"
#include "DbeSession.h"
#include "Experiment.h"
#include "DataObject.h"
#include "Function.h"
#include "DbeView.h"
#include "MetricList.h"
#include "Module.h"
#include "ClassFile.h"
#include "LoadObject.h"
#include "Disasm.h"
#include "CompCom.h"
#include "Dwarf.h"
#include "DbeFile.h"
#include "PathTree.h"
#include "Elf.h"

Module::Module ()
{
  lang_code = Sp_lang_unknown;
  flags = 0;
  status = AE_NOTREAD;
  openSourceFlag = AE_NOTREAD;
  hexVisible = false;
  disPath = NULL;
  stabsPath = NULL;
  stabsTmp = NULL;
  disName = NULL;
  stabsName = NULL;
  indexStabsLink = NULL;
  file_name = NULL;
  functions = new Vector<Function*>;
  loadobject = NULL;
  dot_o_file = NULL;
  main_source = dbeSession->get_Unknown_Source ();
  srcContext = main_source;
  includes = new Vector<SourceFile*>;
  includes->append (main_source);
  curr_inc = NULL;
  fragmented = 0;
  hwcprof = 0;
  hdrOffset = 0;
  hasDwarf = false;
  hasStabs = false;
  readStabs = false;
  comComs = NULL;
  infoList = NULL;
  datatypes = NULL;
  objStabs = NULL;
  disasm = NULL;
  comp_flags = NULL;
  comp_dir = NULL;
  linkerStabName = NULL;
  disMTime = (time_t) 0;
  stabsMTime = (time_t) 0;
  real_timestamp = 0;
  curr_timestamp = 0;
  src_items = NULL;
  dis_items = NULL;
  data_items = NULL;
  cur_dbev = NULL;
  maximum = NULL;
  maximum_inc = NULL;
  empty = NULL;
  inlinedSubr = NULL;
}

Module::~Module ()
{
  removeStabsTmp ();
  delete includes;
  if (comComs != NULL)
    {
      comComs->destroy ();
      delete comComs;
    }
  free (comp_flags);
  free (comp_dir);
  free (linkerStabName);
  free (disPath);
  free (stabsPath);
  free (disName);
  free (stabsName);
  delete functions;
  free (file_name);
  if (indexStabsLink)
    // Remove a link to the current module
    indexStabsLink->indexStabsLink = NULL;

  if (dot_o_file)
    {
      delete dot_o_file->dbeFile;
      delete dot_o_file;
    }
  delete src_items;
  delete dis_items;
  delete disasm;
  free (inlinedSubr);
  if (lang_code != Sp_lang_java)
    delete dbeFile;

}

Stabs *
Module::openDebugInfo ()
{
  setFile ();
  objStabs = loadobject->openDebugInfo ();
  return objStabs;
}

void
Module::removeStabsTmp ()
{
  // Remove temporary *.o (got from *.a) after reading Stabs
  if (stabsTmp != NULL)
    {
      unlink (stabsTmp);
      free (stabsTmp);
      stabsTmp = NULL;
    }
}

int64_t
Module::get_size ()
{
  Function *fp;
  int index;
  int64_t result = 0;
  Vec_loop (Function*, functions, index, fp)
  {
    result += fp->size;
  }
  return result;
}

bool
Module::is_fortran ()
{
  return Stabs::is_fortran (lang_code);
}

SourceFile *
Module::findSource (const char *fname, bool create)
{
  SourceFile *sf = NULL;
  if (loadobject && loadobject->firstExp)
    sf = loadobject->firstExp->get_source (fname);
  if (sf == NULL)
    sf = dbeSession->createSourceFile (fname);
  for (int i = 0, sz = includes ? includes->size () : 0; i < sz; i++)
    {
      SourceFile *sf1 = includes->fetch (i);
      if (sf == sf1)
	return sf;
    }
  if (create)
    {
      if (includes == NULL)
	includes = new Vector<SourceFile*>;
      includes->append (sf);
      return sf;
    }
  return NULL;
}

SourceFile *
Module::setIncludeFile (char *includeFile)
{
  curr_inc = NULL;
  if (includeFile)
    curr_inc = findSource (includeFile, true);
  return curr_inc;
}

char *
Module::anno_str (char *fnm)
{
  char timebuf1[26], timebuf2[26];
  const time_t real_time = (time_t) (unsigned int) real_timestamp;
  const time_t curr_time = (time_t) (unsigned int) curr_timestamp;

  switch (status)
    {
    case AE_OK:
    case AE_NOTREAD:
      return NULL;
    case AE_NOSRC:
      return dbe_sprintf (GTXT ("Source file `%s' not readable"),
			  fnm ? fnm : file_name);
    case AE_NOOBJ:
      if (lang_code == Sp_lang_java)
	{
	  Emsg *emsg = get_error ();
	  if (emsg)
	    {
	      char *s = dbe_strdup (emsg->get_msg ());
	      remove_msg (emsg);
	      return s;
	    }
	  return dbe_sprintf (GTXT ("Object file `%s.class' not readable"),
			      name);
	}
      return dbe_sprintf (GTXT ("Object file `%s' not readable"), get_name ());
    case AE_NOLOBJ:
      if (lang_code == Sp_lang_java)
	return dbe_sprintf (GTXT ("Object file `%s' not readable"),
			    dbeFile ? dbeFile->get_name () : name);
      return dbe_sprintf (GTXT ("Object file `%s' not readable"), loadobject->get_pathname ());
    case AE_NOSTABS:
      return dbe_sprintf (GTXT ("Error reading line-number information in object `%s'; source annotation not available"),
			  stabsPath ? stabsPath : NTXT (""));
    case AE_NOSYMTAB:
      return dbe_sprintf (GTXT ("Error reading symbol table in object `%s'; disassembly annotation not available"),
			  disPath ? disPath : NTXT (""));
    case AE_TIMESRC:
      return dbe_sprintf (GTXT ("Warning! Source file `%s' is newer than the experiment data"),
			  main_source->dbeFile->getResolvedPath ());
    case AE_TIMEDIS:
      return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"),
			  disName ? disName : NTXT (""));
    case AE_TIMESTABS:
      return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"),
			  stabsName ? stabsName : NTXT (""));
    case AE_TIMESTABS_DIFF:
      snprintf (timebuf1, sizeof (timebuf1), NTXT ("%s"), ctime (&curr_time));
      snprintf (timebuf2, sizeof (timebuf2), NTXT ("%s"), ctime (&real_time));
      timebuf1[24] = timebuf2[24] = '\0';
      return dbe_sprintf (GTXT ("Warning! Object file `%s' is not the same one that was linked into executable.\n"
				"\tObject file: `%s'\n\tcompiled on: %s\n"
				"\tExecutable contains object file compiled on: %s"),
			  getResolvedObjectPath (), getResolvedObjectPath (),
			  timebuf1, timebuf2);
    case AE_OTHER:
    default:
      return dbe_strdup (GTXT ("Annotation computation error"));
    }
}//anno_str

LoadObject *
Module::createLoadObject (const char *lo_name)
{
  LoadObject *lo = new LoadObject (lo_name);
  lo->dbeFile->filetype |= DbeFile::F_DOT_O;
  return lo;
}

static bool
tsIsNewer (time_t t1, time_t t2)
{
  return t1 != 0 && t2 != 0 && t1 < t2;
}

Module::Anno_Errors
Module::checkTimeStamp (bool chkDis)
{
  /* Check the linked and the real object timestamps due to bug #4796329 */
  if (real_timestamp && curr_timestamp && real_timestamp != curr_timestamp)
    return AE_TIMESTABS_DIFF;

  time_t srctime = main_source->getMTime ();
  for (int index = 0; index < dbeSession->nexps (); index++)
    {
      time_t mtime = dbeSession->get_exp (index)->get_mtime ();
      if (tsIsNewer (mtime, srctime))
	return AE_TIMESRC;
      if (tsIsNewer (mtime, stabsMTime))
	return AE_TIMESTABS;
      if (chkDis && tsIsNewer (mtime, disMTime))
	return AE_TIMEDIS;
    }
  return AE_OK;
}//checkTimeStamp

static size_t
get_ar_size (char *s, size_t len)
{
  size_t sz = 0;
  for (size_t i = 0; i < len; i++)
    {
      if (s[i] < '0' || s[i] > '9')
	break;
      sz = sz * 10 + (s[i] - '0');
    }
  return sz;
}

static void
dump_hdr_field (char *nm, char *s, size_t len)
{
  Dprintf (DEBUG_READ_AR, NTXT ("  %s "), nm);
  for (size_t i = 0; i < len; i++)
    Dprintf (DEBUG_READ_AR, "%c", isprint (s[i]) ? s[i] : '?');
  Dprintf (DEBUG_READ_AR, NTXT ("  "));
  for (size_t i = 0; i < len; i++)
    Dprintf (DEBUG_READ_AR, NTXT (" %d"), s[i]);
  Dprintf (DEBUG_READ_AR, NTXT (" \n"));
}

static void
dump_ar_hdr (int lineNum, struct ar_hdr *hdr)
{
  if (DEBUG_READ_AR)
    {
      Dprintf (DEBUG_READ_AR, NTXT ("Module::read_ar %d\n"), lineNum);
      dump_hdr_field (NTXT ("ar_name"), hdr->ar_name, sizeof (hdr->ar_name));
      dump_hdr_field (NTXT ("ar_date"), hdr->ar_date, sizeof (hdr->ar_date));
      dump_hdr_field (NTXT ("ar_uid"), hdr->ar_uid, sizeof (hdr->ar_uid));
      dump_hdr_field (NTXT ("ar_gid"), hdr->ar_gid, sizeof (hdr->ar_gid));
      dump_hdr_field (NTXT ("ar_mode"), hdr->ar_mode, sizeof (hdr->ar_mode));
      dump_hdr_field (NTXT ("ar_size"), hdr->ar_size, sizeof (hdr->ar_size));
      dump_hdr_field (NTXT ("ar_fmag"), hdr->ar_fmag, sizeof (hdr->ar_fmag));
    }
}

bool
Module::read_ar (int ar, int obj, char *obj_base)
{
  struct ar_hdr hdr; // Archive header
  char magic[SARMAG]; // Magic string from archive
  Dprintf (DEBUG_READ_AR, "Module::read_ar %d %p %s %s \n", __LINE__,
	   this, STR (obj_base), STR (get_name ()));
  // Check the magic string
  if ((read_from_file (ar, magic, SARMAG) != SARMAG)
       || strncmp (magic, ARMAG, SARMAG))
    return false;

  // Read and skip the first file in the archive (index file to ld)
  if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr))
    return false;
  DEBUG_CODE dump_ar_hdr (__LINE__, &hdr);
  if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), SEEK_CUR)
	     == -1)
    return false;

  // Read the string file where it keeps long file names (if exist)
  if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr))
    return false;
  DEBUG_CODE dump_ar_hdr (__LINE__, &hdr);
  char *longnames = NULL; // Area with names longer than ~13
  size_t longnames_size = 0;
  if (!strncmp (hdr.ar_name, NTXT ("//"), 2))
    {
      longnames_size = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size));
      longnames = (char *) xmalloc (longnames_size + 1);
      int64_t cnt = read_from_file (ar, longnames, longnames_size);
      if (cnt != (int64_t) longnames_size)
	{
	  free (longnames);
	  return false;
	}
      longnames[longnames_size] = 0;
    }
  else
    // back out, no long file names
    lseek (ar, -(sizeof (hdr)), SEEK_CUR);

  // Search the ar for the object file name
  char ar_buf[sizeof (hdr.ar_name) + 1];
  ar_buf[sizeof (hdr.ar_name)] = 0;
  while (1)
    {
      if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr))
	break;
      DEBUG_CODE dump_ar_hdr (__LINE__, &hdr);
      char *ar_name;
      if (hdr.ar_name[0] != '/')
	{ // Name is in the header
	  for (size_t i = 0; i < sizeof (hdr.ar_name); i++)
	    {
	      if (hdr.ar_name[i] == '/')
		{
		  ar_buf[i] = 0;
		  break;
		}
	      ar_buf[i] = hdr.ar_name[i];
	    }
	  ar_name = ar_buf;
	}
      else if (hdr.ar_name[1] == ' ')
	{ // Name is blank
	  ar_buf[0] = 0;
	  ar_name = ar_buf;
	}
      else
	{ // Name is in the string table
	  if (longnames == NULL)
	    break;
	  size_t offset = get_ar_size (hdr.ar_name + 1,
				       sizeof (hdr.ar_name) - 1);
	  if (offset >= longnames_size)
	    break;
	  for (size_t i = offset; i < longnames_size; i++)
	    {
	      if (longnames[i] == '/')
		{
		  longnames[i] = 0;
		  break;
		}
	    }
	  ar_name = longnames + offset;
	}
      Dprintf (DEBUG_READ_AR, "Module::read_ar %d ar_name=%s\n", __LINE__,
	       ar_name);

      if (streq (ar_name, obj_base))
	{ // create object file
	  free (longnames);
	  for (size_t objsize = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size));
		  objsize > 0;)
	    {
	      char buf[MAXPATHLEN];
	      size_t n = objsize < sizeof (buf) ? objsize : sizeof (buf);
	      int64_t cnt = read_from_file (ar, buf, n);
	      if (cnt != (int64_t) n)
		return false;
	      cnt = write (obj, buf, n);
	      if (cnt != (int64_t) n)
		return false;
	      objsize -= n;
	    }
	  return true;
	}
      if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)),
		 SEEK_CUR) == -1)
	break;
    }
  free (longnames);
  return false;
}

static char *
get_obj_name_from_lib (char *nm)
{
  char *base = strrchr (nm, '(');
  if (base)
    {
      size_t last = strlen (base) - 1;
      if (base[last] == ')')
	return base;
    }
  return NULL;
}

bool
Module::setFile ()
{
  if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0)
    return true;
  if ((loadobject->dbeFile->filetype & DbeFile::F_FICTION) != 0)
    return false;
  if ((flags & MOD_FLAG_UNKNOWN) != 0)
    return true;

  if (lang_code == Sp_lang_java)
    {
      if (dbeFile->get_need_refind ())
	{
	  char *fnm = dbeFile->get_location ();
	  stabsPath = dbe_strdup (fnm);
	  stabsName = dbe_strdup (fnm);
	  disPath = dbe_strdup (fnm);
	  disName = dbe_strdup (fnm);
	  stabsMTime = dbeFile->sbuf.st_mtime;
	}
      return dbeFile->get_location () != NULL;
    }

  if (dbeFile == NULL)
    {
      char *objname = get_obj_name_from_lib (name);
      if (objname)
	{
	  // in the format of libpath(obj)
	  objname = dbe_strdup (objname + 1);
	  size_t last = strlen (objname) - 1;
	  objname[last] = '\0';
	}
      dbeFile = new DbeFile (objname ? objname : name);
      free (objname);
      dbeFile->filetype |= DbeFile::F_DOT_O;
    }
  if (dbeFile->get_need_refind ())
    {
      disMTime = (time_t) 0;
      stabsMTime = (time_t) 0;
      free (disName);
      free (stabsName);
      disName = NULL;
      stabsName = NULL;

      // Find the Executable/Shared-Object file of module
      char *path = loadobject->dbeFile->get_location ();
      if (path)
	{
	  disPath = xstrdup (path);
	  disName = xstrdup (path);
	  disMTime = loadobject->dbeFile->sbuf.st_mtime;
	}

      char *objname = get_obj_name_from_lib (name);
      if (objname)
	{
	  // in the format of libpath(obj)
	  char *namebuf = dbe_strdup (name);
	  char *base = namebuf + (objname - name);
	  *base = '\0';
	  base++;
	  size_t last = strlen (base) - 1;
	  base[last] = '\0';
	  stabsTmp = dbeSession->get_tmp_file_name (base, false);
	  dbeSession->tmp_files->append (xstrdup (stabsTmp));

	  DbeFile *dbf = dbeSession->getDbeFile (namebuf,
					DbeFile::F_DOT_A_LIB | DbeFile::F_FILE);
	  path = dbf->get_location ();
	  int ar = -1, obj = -1;
	  if (path != NULL)
	    {
	      ar = open64 (path, O_RDONLY | O_LARGEFILE);
	      if (ar != -1)
		obj = open64 (stabsTmp, O_CREAT | O_WRONLY | O_LARGEFILE, 0600);
	    }
	  if (ar != -1 && obj != -1 && read_ar (ar, obj, base))
	    {
	      dbeFile->set_location (stabsTmp);
	      dbeFile->check_access (stabsTmp); // init 'sbuf'
	      dbeFile->sbuf.st_mtime = 0; // Don't check timestamps
	      dbeFile->container = dbf;
	      stabsPath = xstrdup (stabsTmp);
	      stabsName = xstrdup (path);
	      stabsMTime = dbeFile->sbuf.st_mtime;
	    }
	  else
	    {
	      removeStabsTmp ();
	      objname = NULL;
	    }
	  if (ar != -1)
	    close (ar);
	  if (obj != -1)
	    close (obj);
	  free (namebuf);
	}
      if (objname == NULL)
	{
	  path = dbeFile->get_location ();
	  if (path != NULL)
	    {
	      stabsPath = xstrdup (path);
	      stabsName = xstrdup (path);
	      stabsMTime = hasDwarf ? 0 : dbeFile->sbuf.st_mtime;
	    }
	}

      // First, try to access the symbol table of the module itself
      // If failed, access the symbol table of the executable
      if (stabsPath == NULL)
	{
	  if (disPath == NULL)
	    return false;
	  stabsPath = xstrdup (disPath);
	  stabsName = xstrdup (disName);
	  stabsMTime = disMTime;
	}
      else if (disPath == NULL)
	{
	  disPath = xstrdup (stabsPath);
	  disName = xstrdup (stabsName);
	  disMTime = stabsMTime;
	}
    }
  return stabsPath != NULL;
}

// openStabs -- open mappings from PCs to source lines
bool
Module::openStabs (bool all)
{
  if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0
      || (flags & MOD_FLAG_UNKNOWN) != 0)
    return true;
  if (loadobject->platform == Java)
    {
      setIncludeFile (NULL);
      readFile ();
      return ( status == AE_OK);
    }
  if (readStabs)
    return true;

  // Read Stabs info.
  int64_t Inode = main_source->getInode ();
  char *fname = strrchr (file_name, (int) '/');
  char *mname = strrchr (main_source->get_name (), (int) '/');
  if (fname && mname && !streq (fname, mname))
    {
      SourceFile *sf = findSource (file_name, false);
      if (sf != NULL)
	Inode = sf->getInode ();
    }

  comComs = new Vector<ComC*>;
  Stabs *stabs = openDebugInfo ();
  if (stabs == NULL)
    return false;
  int st = stabs->read_stabs (Inode, this, comComs, true);
  if (!hasDwarf && hasStabs && !streq (stabsPath, disPath))
    {
      // Read stabs from .o file
      if (dot_o_file == NULL)
	{
	  if (dbeFile->get_location ())
	    {
	      dot_o_file = createLoadObject (dbeFile->get_name ());
	      dot_o_file->dbeFile->set_location (dbeFile->get_location ());
	      dot_o_file->dbeFile->sbuf = dbeFile->sbuf;
	      dot_o_file->dbeFile->container = dbeFile->container;
	    }
	}
      if (dot_o_file
	  && dot_o_file->sync_read_stabs () == LoadObject::ARCHIVE_SUCCESS)
	{
	  Stabs *stabs_o = dot_o_file->objStabs;
	  if (stabs_o)
	    {
	      st = stabs_o->read_stabs (Inode, this,
					comComs->size () > 0 ? NULL : comComs);
	      Elf *elf_o = stabs_o->openElf (false);
	      if (elf_o->dwarf)
		stabs->read_dwarf_from_dot_o (this);
	    }
	}
    }
  if (all)
    read_hwcprof_info ();

  readStabs = true;
  return st == Stabs::DBGD_ERR_NONE;
}

char *
Module::get_disasm (uint64_t inst_address, uint64_t end_address,
		   uint64_t start_address, uint64_t address, int64_t &inst_size)
{
  return disasm->get_disasm (inst_address, end_address, start_address,
			     address, inst_size);
}

void
Module::read_stabs (bool all)
{
  if (openSourceFlag == AE_NOTREAD)
    {
      openSourceFlag = AE_OK;
      if (lang_code == Sp_lang_java)
	{
	  char *clpath = file_name;
	  if (clpath == NULL || strcmp (clpath, "<Unknown>") == 0)
	    clpath = ClassFile::get_java_file_name (name, false);
	  main_source = findSource (clpath, true);
	  main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE;
	  if (clpath != file_name)
	    free (clpath);
	}
      else
	main_source = findSource (file_name, true);
      if (setFile ())
	openStabs (all);
    }
}

bool
Module::openDisPC ()
{
  if (disasm == NULL)
    {
      if (!(loadobject->flags & SEG_FLAG_DYNAMIC) && loadobject->platform != Java)
	{
	  // Read Stabs & Symbol tables
	  if (openDebugInfo () == NULL)
	    return false;
	  if (!objStabs->read_symbols (functions))
	    return false;
	}
      disasm = new Disasm (loadobject->platform, objStabs);
    }
  return true;
}

static SourceFile *cmpSrcContext; // Use only for func_cmp

static int
func_cmp (const void *a, const void *b)
{
  Function *fp1 = *((Function **) a);
  Function *fp2 = *((Function **) b);
  return fp1->func_cmp (fp2, cmpSrcContext);
}

bool
Module::computeMetrics (DbeView *dbev, Function *func, MetricList *metrics,
			Histable::Type type, bool src_metric,
			bool func_scope, SourceFile *source)
{
  name_idx = metrics->get_listorder (NTXT ("name"), Metric::STATIC);
  if (name_idx < 0)
    {
      metrics->print_metric_list (stderr,
				  GTXT ("Fatal: no name metric in Module::computeMetrics mlist:\n"),
				  1);
      abort ();
    }

  // Now find the metrics for size and address, if present
  size_index = metrics->get_listorder (NTXT ("size"), Metric::STATIC);
  addr_index = metrics->get_listorder (NTXT ("address"), Metric::STATIC);

  // free the old cached data for both src and disassembly
  //   If it's disassembly with visible source metrics, we use both
  if (dis_items)
    {
      delete dis_items;
      dis_items = NULL;
    }
  if (src_items)
    {
      delete src_items;
      src_items = NULL;
    }

  // ask the DbeView to generate new data to be cached
  if (src_metric || type == Histable::LINE)
    {
      Histable *obj = (func_scope) ? (Histable*) func : (Histable*)this;
      if (lang_code == Sp_lang_java)
	obj = func_scope ? (Histable *) func :
	      (source && source->get_type () == Histable::SOURCEFILE ?
	       (Histable *) source : (Histable *) this);
      src_items = dbev->get_hist_data (metrics, Histable::LINE, 0,
				       Hist_data::MODL, obj, source);
    }
  if (type == Histable::INSTR)
    dis_items = dbev->get_hist_data (metrics, Histable::INSTR, 0,
			       Hist_data::MODL,
			       func_scope ? (Histable*) func : (Histable*) this,
			       source);

  Hist_data *cur_hist_data;
  if (type == Histable::INSTR)
    cur_hist_data = dis_items;
  else
    cur_hist_data = src_items;

  Vector<Metric*> *items = cur_hist_data->get_metric_list ()->get_items ();
  long sz = items->size ();
  empty = new TValue[sz];
  memset (empty, 0, sizeof (TValue) * sz);
  for (long i = 0; i < sz; i++)
    empty[i].tag = items->get (i)->get_vtype ();
  return true;
}

// Method to get annotated source or disassembly for the module
//	or a function within it
Hist_data *
Module::get_data (DbeView *dbev, MetricList *mlist, Histable::Type type,
		  TValue *ftotal, SourceFile *srcFile, Function *func,
		  Vector<int> *marks, int threshold, int vis_bits,
		  int src_visible, bool hex_vis, bool func_scope,
		  bool /*src_only*/, Vector<int_pair_t> *marks2d,
		  Vector<int_pair_t> *marks2d_inc)
{
  cur_dbev = dbev;
  srcContext = srcFile ? srcFile : main_source;
  read_stabs ();
  status = AE_OK;
  dbev->warning_msg = NULL;
  dbev->error_msg = NULL;
  if (type == Histable::LINE)
    {
      if (!srcContext->readSource ())
	{
	  status = AE_NOSRC;
	  dbev->error_msg = anno_str (srcContext->get_name ());
	  return NULL;
	}
      if (!computeMetrics (dbev, func, mlist, type, false, func_scope, srcContext))
	{
	  status = AE_OTHER;
	  dbev->error_msg = anno_str ();
	  return NULL;
	}
      status = checkTimeStamp (false);
    }
  else
    { // Histable::INSTR
      Anno_Errors src_status = AE_OK;
      if (!srcContext->readSource ())
	{
	  src_status = AE_NOSRC;
	  dbev->error_msg = anno_str (srcContext->get_name ());
	}
      if (!setFile ())
	status = AE_NOLOBJ;
      else
	{
	  if (!openStabs ())
	    src_status = AE_NOSTABS;
	  if (!openDisPC ())
	    status = AE_NOSYMTAB;
	}
      if (status != AE_OK)
	{
	  dbev->error_msg = anno_str ();
	  return NULL;
	}
      if (src_status != AE_OK && func != NULL)
	{
	  if (loadobject->platform == Java && (func->flags & FUNC_FLAG_NATIVE) != 0)
	    {
	      append_msg (CMSG_ERROR,
			  GTXT ("`%s' is a native method; byte code not available\n"),
			  func->get_name ());
	      status = AE_NOOBJ;
	      dbev->error_msg = anno_str ();
	      return NULL;
	    }
	  func_scope = true;
	}
      // get the disassembly-line metric data
      if (!computeMetrics (dbev, func, mlist, type,
			   (src_visible & SRC_METRIC) != 0,
			   func_scope, srcContext))
	{
	  status = AE_OTHER;
	  dbev->error_msg = anno_str ();
	  return NULL;
	}
      status = checkTimeStamp (true);
    }
  total = ftotal;

  // initialize line number
  init_line ();

  // initialize data -- get duplicate metric list for the line texts
  // pick up the metric list from the computed data
  MetricList *nmlist = NULL;
  if (type == Histable::INSTR)
    {
      mlist = dis_items->get_metric_list ();
      nmlist = new MetricList (mlist);
      data_items = new Hist_data (nmlist, Histable::INSTR, Hist_data::MODL);
      data_items->set_status (dis_items->get_status ());
      set_dis_data (func, vis_bits, dbev->get_cmpline_visible (),
		    src_visible, hex_vis, func_scope,
		    dbev->get_funcline_visible ());
    }
  else
    {
      mlist = src_items->get_metric_list ();
      nmlist = new MetricList (mlist);
      data_items = new Hist_data (nmlist, Histable::LINE, Hist_data::MODL);
      data_items->set_status (src_items->get_status ());
      set_src_data (func_scope ? func : NULL, vis_bits,
		    dbev->get_cmpline_visible (),
		    dbev->get_funcline_visible ());
    }
  data_items->compute_minmax ();

  Metric *mitem;
  int index;
  Hist_data::HistItem *max_item;
  TValue *value;
  Hist_data::HistItem *max_item_inc;
  TValue *value_inc;
  double dthreshold = threshold / 100.0;

  int sz = data_items->get_metric_list ()->get_items ()->size ();
  maximum = new TValue[sz];
  maximum_inc = new TValue[sz];
  memset (maximum, 0, sizeof (TValue) * sz);
  memset (maximum_inc, 0, sizeof (TValue) * sz);
  max_item = data_items->get_maximums ();
  max_item_inc = data_items->get_maximums_inc ();

  Vec_loop (Metric*, data_items->get_metric_list ()->get_items (), index, mitem)
  {
    maximum_inc[index].tag = maximum[index].tag = mitem->get_vtype ();

    if (mitem->get_subtype () == Metric::STATIC)
      continue;
    if (!mitem->is_visible () && !mitem->is_tvisible ()
	&& !mitem->is_pvisible ())
      continue;

    value = &max_item->value[index];
    value_inc = &max_item_inc->value[index];

    double dthresh;
    if (mitem->is_zeroThreshold () == true)
      dthresh = 0;
    else
      dthresh = dthreshold;
    switch (value->tag)
      {
      case VT_INT:
	maximum[index].i = (int) (dthresh * (double) value->i);
	maximum_inc[index].i = (int) (dthresh * (double) value_inc->i);
	break;
      case VT_DOUBLE:
	maximum[index].d = dthresh * value->d;
	maximum_inc[index].d = dthresh * value_inc->d;
	break;
      case VT_LLONG:
	maximum[index].ll = (unsigned long long) (dthresh * (double) value->ll);
	maximum_inc[index].ll = (unsigned long long)
		(dthresh * (double) value_inc->ll);
	break;
      case VT_ULLONG:
	maximum[index].ull = (unsigned long long)
		(dthresh * (double) value->ull);
	maximum_inc[index].ull = (unsigned long long)
		(dthresh * (double) value_inc->ull);
	break;
      default:
	// not needed for non-numerical metrics
	break;
      }
  }

  // mark all high values
  for (int index1 = 0; index1 < data_items->size (); index1++)
    {
      Hist_data::HistItem *hi = data_items->fetch (index1);
      int index2;
      Vec_loop (Metric*, nmlist->get_items (), index2, mitem)
      {
	bool mark = false;
	if (mitem->get_subtype () == Metric::STATIC)
	  continue;
	if (!mitem->is_visible () && !mitem->is_tvisible ()
	    && !mitem->is_pvisible ())
	  continue;

	switch (hi->value[index2].tag)
	  {
	  case VT_DOUBLE:
	    if (nmlist->get_type () == MET_SRCDIS
		&& data_items->get_callsite_mark ()->get (hi->obj))
	      {
		if (hi->value[index2].d > maximum_inc[index2].d)
		  mark = true;
		break;
	      }
	    if (hi->value[index2].d > maximum[index2].d)
	      mark = true;
	    break;
	  case VT_INT:
	    if (nmlist->get_type () == MET_SRCDIS
		&& data_items->get_callsite_mark ()->get (hi->obj))
	      {
		if (hi->value[index2].i > maximum_inc[index2].i)
		  mark = true;
		break;
	      }
	    if (hi->value[index2].i > maximum[index2].i)
	      mark = true;
	    break;
	  case VT_LLONG:
	    if (nmlist->get_type () == MET_SRCDIS
		&& data_items->get_callsite_mark ()->get (hi->obj))
	      {
		if (hi->value[index2].ll > maximum_inc[index2].ll)
		  mark = true;
		break;
	      }
	    if (hi->value[index2].ll > maximum[index2].ll)
	      mark = true;
	    break;
	  case VT_ULLONG:
	    if (nmlist->get_type () == MET_SRCDIS
		&& data_items->get_callsite_mark ()->get (hi->obj))
	      {
		if (hi->value[index2].ull > maximum_inc[index2].ull)
		  mark = true;
		break;
	      }
	    if (hi->value[index2].ull > maximum[index2].ull)
	      mark = true;
	    break;
	    // ignoring the following cases (why?)
	  case VT_SHORT:
	  case VT_FLOAT:
	  case VT_HRTIME:
	  case VT_LABEL:
	  case VT_ADDRESS:
	  case VT_OFFSET:
	    break;
	  }
	if (mark)
	  {
	    marks->append (index1);
	    break;
	  }
      }
    }

  // mark all high values to marks2d
  if (marks2d != NULL && marks2d_inc != NULL)
    {
      for (int index1 = 0; index1 < data_items->size (); index1++)
	{
	  Hist_data::HistItem *hi = data_items->fetch (index1);
	  int index2;
	  Vec_loop (Metric*, nmlist->get_items (), index2, mitem)
	  {
	    Metric::SubType subType = mitem->get_subtype ();
	    if (subType == Metric::STATIC)
	      continue;
	    if (!mitem->is_visible () && !mitem->is_tvisible ()
		&& !mitem->is_pvisible ())
	      continue;
	    switch (hi->value[index2].tag)
	      {
	      case VT_DOUBLE:
		if (nmlist->get_type () == MET_SRCDIS
		    && data_items->get_callsite_mark ()->get (hi->obj))
		  {
		    if (hi->value[index2].d > maximum_inc[index2].d)
		      {
			int_pair_t pair = {index1, index2};
			marks2d_inc->append (pair);
		      }
		    break;
		  }
		if (hi->value[index2].d > maximum[index2].d)
		  {
		    int_pair_t pair = {index1, index2};
		    marks2d->append (pair);
		  }
		break;
	      case VT_INT:
		if (nmlist->get_type () == MET_SRCDIS
		    && data_items->get_callsite_mark ()->get (hi->obj))
		  {
		    if (hi->value[index2].i > maximum_inc[index2].i)
		      {
			int_pair_t pair = {index1, index2};
			marks2d_inc->append (pair);
		      }
		    break;
		  }
		if (hi->value[index2].i > maximum[index2].i)
		  {
		    int_pair_t pair = {index1, index2};
		    marks2d->append (pair);
		  }
		break;
	      case VT_LLONG:
		if (nmlist->get_type () == MET_SRCDIS
		    && data_items->get_callsite_mark ()->get (hi->obj))
		  {
		    if (hi->value[index2].ll > maximum_inc[index2].ll)
		      {
			int_pair_t pair = {index1, index2};
			marks2d_inc->append (pair);
		      }
		    break;
		  }
		if (hi->value[index2].ll > maximum[index2].ll)
		  {
		    int_pair_t pair = {index1, index2};
		    marks2d->append (pair);
		  }
		break;
	      case VT_ULLONG:
		if (nmlist->get_type () == MET_SRCDIS
		    && data_items->get_callsite_mark ()->get (hi->obj))
		  {
		    if (hi->value[index2].ull > maximum_inc[index2].ull)
		      {
			int_pair_t pair = {index1, index2};
			marks2d_inc->append (pair);
		      }
		    break;
		  }
		if (hi->value[index2].ull > maximum[index2].ull)
		  {
		    int_pair_t pair = {index1, index2};
		    marks2d->append (pair);
		  }
		break;
	      case VT_SHORT:
	      case VT_FLOAT:
	      case VT_HRTIME:
	      case VT_LABEL:
	      case VT_ADDRESS:
	      case VT_OFFSET:
		break;
	      }
	  }
	}
    }

  // free memory used by Computing & Printing metrics
  delete[] maximum;
  delete[] maximum_inc;
  delete[] empty;
  maximum = NULL;
  maximum_inc = NULL;
  empty = NULL;
  dbev->warning_msg = anno_str ();
  return data_items;
}

Vector<uint64_t> *
Module::getAddrs (Function *func)
{
  uint64_t start_address = func->img_offset;
  uint64_t end_address = start_address + func->size;
  int64_t inst_size = 0;

  // initialize "disasm" if necessary
  if (!openDisPC ())
    return NULL;

  Vector<uint64_t> *addrs = new Vector<uint64_t>;
  for (uint64_t inst_address = start_address; inst_address < end_address;)
    {
      char *s = disasm->get_disasm (inst_address, end_address, start_address,
				    func->img_offset, inst_size);
      free (s);
      addrs->append (inst_address - start_address);
      inst_address += inst_size;
      if (inst_size == 0)
	break;
    }
  return addrs;
}

void
Module::init_line ()
{
  // initialize the compiler commentary data
  cindex = 0;
  if (comComs != NULL && comComs->size () > 0)
    cline = comComs->fetch (cindex)->line;
  else
    cline = -1;

  sindex = 0;
  if (src_items && src_items->size () > 0)
    sline = ((DbeLine*) src_items->fetch (0)->obj)->lineno;
  else
    sline = -1;

  dindex = 0;
  mindex = 0;
  mline = -1;
  if (dis_items && dis_items->size () > 0)
    {
      daddr = (DbeInstr*) dis_items->fetch (0)->obj;

      // After sorting all HistItems with PCLineFlag appear
      // at the end of the list. Find the first one.
      for (mindex = dis_items->size () - 1; mindex >= 0; mindex--)
	{
	  Hist_data::HistItem *item = dis_items->fetch (mindex);
	  if (!(((DbeInstr*) item->obj)->flags & PCLineFlag))
	    break;
	  mline = (unsigned) (((DbeInstr*) item->obj)->addr);
	}
      mindex++;
    }
  else
    daddr = NULL;
}

void
Module::set_src_data (Function *func, int vis_bits, int cmpline_visible,
		      int funcline_visible)
{
  Function *curr_func = NULL;

  // start at the top of the file, and loop over all lines in the file (source context)
  for (curline = 1; curline <= srcContext->getLineCount (); curline++)
    {
      // Before writing the line, see if there's compiler commentary to insert
      if (cline == curline)
	set_ComCom (vis_bits);

      // Find out if we need to print zero metrics with the line
      DbeLine *dbeline = srcContext->find_dbeline (NULL, curline);
      Anno_Types type = AT_SRC_ONLY;
      if (dbeline->dbeline_func_next)
	{
	  if (func)
	    for (DbeLine *dl = dbeline->dbeline_func_next; dl; dl = dl->dbeline_func_next)
	      {
		if (dl->func == func)
		  {
		    type = AT_SRC;
		    break;
		  }
	      }
	  else
	    type = AT_SRC;
	}

      if (funcline_visible)
	{ // show red lines
	  // is there a function index line to insert?
	  Function *func_next = NULL;
	  for (DbeLine *dl = dbeline; dl; dl = dl->dbeline_func_next)
	    {
	      Function *f = dl->func;
	      if (f && f->line_first == curline
		  && f->getDefSrc () == srcContext)
		{
		  if (lang_code == Sp_lang_java
		      && (f->flags & FUNC_FLAG_DYNAMIC))
		    continue;
		  if (cur_dbev && cur_dbev->get_path_tree ()->get_func_nodeidx (f))
		    {
		      func_next = f;
		      break;
		    }
		  else if (func_next == NULL)
		    func_next = f;
		}
	    }
	  if (func_next && curr_func != func_next)
	    {
	      curr_func = func_next;
	      char *func_name = curr_func->get_name ();
	      if (is_fortran () && streq (func_name, NTXT ("MAIN_")))
		func_name = curr_func->get_match_name ();
	      Hist_data::HistItem *item =
		      src_items->new_hist_item (curr_func, AT_FUNC, empty);
	      item->value[name_idx].l = dbe_sprintf (GTXT ("<Function: %s>"),
						     func_name);
	      data_items->append_hist_item (item);
	    }
	} // end of red line
      set_src (type, dbeline); // add the source line
    } //  end of loop over source lines

  // See if compiler flags are set; if so, append them
  if (cmpline_visible && comp_flags)
    {
      Hist_data::HistItem *item = src_items->new_hist_item (NULL, AT_EMPTY,
							    empty);
      item->value[name_idx].l = xstrdup (NTXT (""));
      data_items->append_hist_item (item);
      item = src_items->new_hist_item (NULL, AT_COM, empty);
      item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"),
					     comp_flags);
      data_items->append_hist_item (item);
    }
}

void
Module::set_dis_data (Function *func, int vis_bits, int cmpline_visible,
		      int src_visible, bool hex_vis, bool func_scope,
		      int funcline_visible)
{
  bool nextFile = false;

  // initialize the source output, if any
  curline = (srcContext->getLineCount () > 0) ? 1 : -1;
  if (func)
    nextFile = srcContext != func->getDefSrc ();
  curr_inc = srcContext;

  bool src_code = (src_visible & SRC_CODE);
  Anno_Types src_type = (src_visible & SRC_METRIC) ? AT_SRC : AT_SRC_ONLY;

  char *img_fname = func ? func->img_fname : NULL;

  // Build a new Function list
  Vector<Function*> *FuncLst = new Vector<Function*>;
  if (func_scope)
    {
      if (func)
	FuncLst->append (func);
    }
  else
    {
      for (int i = 0, sz = functions ? functions->size () : 0; i < sz; i++)
	{
	  Function *fitem = functions->fetch (i);
	  if (fitem != fitem->cardinal ())
	    continue;
	  if (img_fname == NULL)
	    img_fname = fitem->img_fname;
	  if (fitem->img_fname == NULL || strcmp (fitem->img_fname, img_fname))
	    continue;
	  FuncLst->append (fitem);
	}
    }
  if (FuncLst->size () == 0)
    { // no function is good
      delete FuncLst;
      return;
    }
  cmpSrcContext = srcContext;
  FuncLst->sort (func_cmp);

  disasm->set_hex_visible (hex_vis);
  for (int index = 0, sz = FuncLst->size (); index < sz; index++)
    {
      Function *fitem = FuncLst->fetch (index);
      uint64_t start_address, end_address;
      int64_t inst_size;
      if (fitem->getDefSrc () != srcContext && curline > 0)
	{
	  // now flush the left source line, if available
	  for (; curline <= srcContext->getLineCount (); curline++)
	    {
	      // see if there's a compiler comment line to dump
	      if (cline == curline)
		set_ComCom (vis_bits);
	      if (src_code)
		set_src (src_type, srcContext->find_dbeline (curline));
	    }
	  curline = -1;
	}

      curr_inc = NULL;
      // disassemble one function
      start_address = objStabs ?
	      objStabs->mapOffsetToAddress (fitem->img_offset) : 0;
      end_address = start_address + fitem->size;
      inst_size = 0;

      disasm->set_addr_end (end_address);
      if ((loadobject->flags & SEG_FLAG_DYNAMIC)
	   && loadobject->platform != Java)
	disasm->set_img_name (img_fname);

      for (uint64_t inst_address = start_address; inst_address < end_address;)
	{
	  uint64_t address = inst_address - start_address;
	  DbeInstr *instr = fitem->find_dbeinstr (0, address);
	  DbeLine *dbeline = (DbeLine *) (instr->convertto (Histable::LINE));
	  if (instr->lineno == -1 && dbeline && dbeline->lineno > 0)
	    instr->lineno = dbeline->lineno;

	  // now write the unannotated source line, if available
	  if (curline > 0)
	    { // source is present
	      int lineno = curline - 1;
	      if (instr->lineno != -1)
		{
		  if (dbeline && streq (dbeline->sourceFile->get_name (),
					srcContext->get_name ()))
		    lineno = instr->lineno;
		}
	      else if (curr_inc == NULL && srcContext == fitem->def_source
		       && fitem->line_first > 0)
		lineno = fitem->line_first;

	      for (; curline <= lineno; curline++)
		{
		  // see if there's a compiler comment line to dump
		  if (cline == curline)
		    set_ComCom (vis_bits);
		  if (mline == curline)
		    set_MPSlave ();
		  if (src_code)
		    set_src (src_type, srcContext->find_dbeline (curline));
		  if (curline >= srcContext->getLineCount ())
		    {
		      curline = -1;
		      break;
		    }
		}
	    }

	  if (funcline_visible)
	    { // show red lines
	      if (!curr_inc || (dbeline && curr_inc != dbeline->sourceFile))
		{
		  Hist_data::HistItem *item = dis_items->new_hist_item (dbeline, AT_FUNC, empty);
		  curr_inc = dbeline ? dbeline->sourceFile : srcContext;
		  char *str;
		  if (curr_inc != srcContext)
		    {
		      char *fileName = curr_inc->dbeFile->getResolvedPath ();
		      str = dbe_sprintf (GTXT ("<Function: %s, instructions from source file %s>"),
					 fitem->get_name (), fileName);
		    }
		  else
		    str = dbe_sprintf (GTXT ("<Function: %s>"),
				       fitem->get_name ());
		  item->value[name_idx].l = str;
		  data_items->append_hist_item (item);
		}
	    }

	  char *dis_str = get_disasm (inst_address, end_address, start_address,
				      fitem->img_offset, inst_size);
	  if (inst_size == 0)
	    break;
	  else if (instr->size == 0)
	    instr->size = (unsigned int) inst_size;
	  inst_address += inst_size;

	  // stomp out control characters
	  for (size_t i = 0, len = strlen (dis_str); i < len; i++)
	    {
	      if (dis_str[i] == '\t')
		dis_str[i] = ' ';
	    }

	  for (int i = 0; i < bTargets.size (); i++)
	    {
	      target_info_t *bTarget = bTargets.fetch (i);
	      if (bTarget->offset == fitem->img_offset + address)
		{
		  // insert a new line for the bTarget
		  size_t colon = strcspn (dis_str, NTXT (":"));
		  char *msg = GTXT ("*  <branch target>");
		  size_t len = colon + strlen (msg);
		  len = (len < 50) ? (50 - len) : 1;
		  char *new_dis_str = dbe_sprintf ("%.*s%s%*c  <===----<<<",
						   (int) colon, dis_str, msg,
						   (int) len, ' ');
		  DbeInstr *bt = fitem->find_dbeinstr (PCTrgtFlag, address);
		  bt->lineno = instr->lineno;
		  bt->size = 0;
		  set_dis (bt, AT_DIS, nextFile, new_dis_str);
		  break;
		}
	    }

	  // AnalyzerInfo/Datatype annotations
	  if (infoList != NULL)
	    {
	      inst_info_t *info = NULL;
	      int pinfo;
	      Vec_loop (inst_info_t*, infoList, pinfo, info)
	      {
		if (info->offset == fitem->img_offset + address) break;
	      }
	      if (info != NULL)
		{ // got a matching memop
		  char typetag[400];
		  typetag[0] = '\0';
		  long t;
		  datatype_t *dtype = NULL;
		  Vec_loop (datatype_t*, datatypes, t, dtype)
		  {
		    if (dtype->datatype_id == info->memop->datatype_id)
		      break;
		  }
		  if (datatypes != NULL)
		    {
		      size_t len = strlen (typetag);
		      if (dtype == NULL || t == datatypes->size ())
			snprintf (typetag + len, sizeof (typetag) - len, "%s",
				  PTXT (DOBJ_UNSPECIFIED));
		      else if (dtype->dobj == NULL)
			snprintf (typetag + len, sizeof (typetag) - len, "%s",
				  PTXT (DOBJ_UNDETERMINED));
		      else
			snprintf (typetag + len, sizeof (typetag) - len, "%s",
				  dtype->dobj->get_name ());
		    }
		  if (strlen (typetag) > 1)
		    {
		      char *new_dis_str;
		      new_dis_str = dbe_sprintf ("%-50s  %s", dis_str, typetag);
		      free (dis_str);
		      dis_str = new_dis_str;
		    }
		}
	    }
	  set_dis (instr, AT_DIS, nextFile, dis_str);
	}
    }

  // now flush the left source line, if available
  if (curline > 0)
    { // source is present
      for (; curline <= srcContext->getLineCount (); curline++)
	{
	  // see if there's a compiler comment line to dump
	  if (cline == curline)
	    set_ComCom (vis_bits);

	  if (src_code)
	    set_src (src_type, srcContext->find_dbeline (curline));
	}
    }

  // See if compiler flags are set; if so, append them
  if (cmpline_visible && comp_flags)
    {
      Hist_data::HistItem *item = dis_items->new_hist_item (NULL, AT_EMPTY,
							    empty);
      item->value[name_idx].l = dbe_strdup (NTXT (""));
      data_items->append_hist_item (item);
      item = dis_items->new_hist_item (NULL, AT_COM, empty);
      item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"),
					     comp_flags);
      data_items->append_hist_item (item);
    }
  delete FuncLst;
}

// set_src -- inserts one or more lines into the growing data list
void
Module::set_src (Anno_Types type, DbeLine *dbeline)
{
  Hist_data::HistItem *item;

  // Flush items that are not represented in source
  while (sline >= 0 && sline < curline)
    {
      item = src_items->fetch (sindex);
      if (((DbeLine*) item->obj)->lineno > 0)
	set_one (item, AT_QUOTE, item->obj->get_name ());

      if (++sindex < src_items->size ()) // get next line with metrics
	sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno;
      else
	sline = -1;
    }

  //  write values in the metric fields for the given source line
  if (curline == sline)
    { // got metrics for this line
      item = src_items->fetch (sindex);
      if (((DbeLine*) item->obj)->lineno > 0)
	set_one (item, AT_SRC, srcContext->getLine (curline));

      if (++sindex < src_items->size ()) // get next line metric index
	sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno;
      else
	sline = -1;
    }
  else
    {
      item = data_items->new_hist_item (dbeline, type, empty);
      if (size_index != -1)
	item->value[size_index].ll = dbeline->get_size ();
      if (addr_index != -1)
	item->value[addr_index].ll = dbeline->get_addr ();
      item->value[name_idx].l = dbe_strdup (srcContext->getLine (curline));
      data_items->append_hist_item (item);
    }
}

void
Module::set_dis (DbeInstr *instr, Anno_Types type, bool nextFile, char *dis_str)
{
  // Flush items that are not represented in disassembly
  while (daddr && daddr->pc_cmp (instr) < 0)
    {
      if (!nextFile)
	set_one (dis_items->fetch (dindex), AT_QUOTE, daddr->get_name ());
      if (++dindex < dis_items->size ()) // get next line metric index
	daddr = (DbeInstr*) dis_items->fetch (dindex)->obj;
      else
	daddr = NULL;
    }

  // Write values in the metric fields for the given pc index value
  if (instr->inlinedInd >= 0)
    {
      StringBuilder sb;
      sb.append (dis_str);
      instr->add_inlined_info (&sb);
      free (dis_str);
      dis_str = sb.toString ();
    }
  if (daddr && daddr->pc_cmp (instr) == 0)
    {
      Hist_data::HistItem *item = data_items->new_hist_item (instr, type,
					      dis_items->fetch (dindex)->value);
      item->value[name_idx].tag = VT_LABEL;
      item->value[name_idx].l = dis_str;
      data_items->append_hist_item (item);
      if (dis_items->get_callsite_mark ()->get (dis_items->fetch (dindex)->obj))
	data_items->get_callsite_mark ()->put (item->obj, 1);

      if (++dindex < dis_items->size ()) // get next line metric index
	daddr = (DbeInstr*) dis_items->fetch (dindex)->obj;
      else
	daddr = NULL;
    }
  else
    {
      // create a new item for this PC
      Hist_data::HistItem *item = dis_items->new_hist_item (instr, type, empty);
      if (size_index != -1)
	item->value[size_index].ll = instr->size;
      if (addr_index != -1)
	item->value[addr_index].ll = instr->get_addr ();
      item->value[name_idx].tag = VT_LABEL;
      item->value[name_idx].l = dis_str;
      data_items->append_hist_item (item);
    }
}

void
Module::set_MPSlave ()
{
  Hist_data::HistItem *item;
  Function *fp;
  int index;

  // write the inclusive metrics for slave threads
  while (mline == curline)
    {
      item = dis_items->fetch (mindex);
      DbeInstr *instr = (DbeInstr *) item->obj;
      Vec_loop (Function*, functions, index, fp)
      {
	if (fp->derivedNode == instr)
	  {
	    set_one (item, AT_QUOTE, (fp->isOutlineFunction) ?
		     GTXT ("<inclusive metrics for outlined functions>") :
		     GTXT ("<inclusive metrics for slave threads>"));
	    break;
	  }
      }

      mindex++;
      if (mindex < dis_items->size ())
	mline = (unsigned) ((DbeInstr*) (dis_items->fetch (mindex)->obj))->addr;
      else
	mline = -1;
    }
}//set_MPSlave

void
Module::set_one (Hist_data::HistItem *org_item, Anno_Types type,
		 const char *text)
{
  if (org_item == NULL)
    return;
  Hist_data::HistItem *item = data_items->new_hist_item (org_item->obj, type,
							 org_item->value);
  item->value[name_idx].tag = VT_LABEL;
  item->value[name_idx].l = dbe_strdup (text);
  data_items->append_hist_item (item);
  if (org_item != NULL && src_items != NULL
      && src_items->get_callsite_mark ()->get (org_item->obj))
    data_items->get_callsite_mark ()->put (item->obj, 1);
}//set_one

void
Module::set_ComCom (int vis_bits)
{
  Hist_data::HistItem *item;
  Function *func = dbeSession->get_Unknown_Function ();

  if (vis_bits)
    {
      // precede the compiler commentary with a blank line
      item = data_items->new_hist_item (func, AT_EMPTY, empty);
      item->value[name_idx].l = dbe_strdup (NTXT (""));
      data_items->append_hist_item (item);
    }
  while (cline == curline)
    {
      ComC *comm = comComs->fetch (cindex);
      if (comm->visible & vis_bits)
	{
	  // write the compiler commentary
	  item = data_items->new_hist_item (func, AT_COM, empty);
	  item->value[name_idx].l = dbe_strdup (comm->com_str);
	  data_items->append_hist_item (item);
	}
      if (++cindex < comComs->size ())
	cline = comComs->fetch (cindex)->line;
      else
	cline = -1;
    }
}

void
Module::dump_dataobjects (FILE *out)
{
  int index;
  datatype_t *dtype;
  Vec_loop (datatype_t*, datatypes, index, dtype)
  {
    fprintf (out, NTXT ("[0x%08X,%6lld] %4d %6d %s "), dtype->datatype_id,
	     dtype->dobj ? dtype->dobj->id : 0LL,
	     dtype->memop_refs, dtype->event_data,
	     (dtype->dobj != NULL ? (dtype->dobj->get_name () ?
		 dtype->dobj->get_name () : "<NULL>") : "<no object>"));
#if DEBUG
    Histable* scope = dtype->dobj ? dtype->dobj->get_scope () : NULL;
    if (scope != NULL)
      {
	switch (scope->get_type ())
	  {
	  case Histable::LOADOBJECT:
	  case Histable::FUNCTION:
	    fprintf (out, NTXT ("%s"), scope->get_name ());
	    break;
	  case Histable::MODULE:
	    {
	      char *filename = get_basename (scope->get_name ());
	      fprintf (out, NTXT ("%s"), filename);
	      break;
	    }
	  default:
	    fprintf (out, NTXT ("\tUnexpected scope %d:%s"),
		     scope->get_type (), scope->get_name ());
	  }
      }
#endif
    fprintf (out, NTXT ("\n"));
  }
}

void
Module::set_name (char *str)
{
  free (name);
  name = str;
}

void
Module::read_hwcprof_info ()
{
  if (hwcprof == 0)
    {
      hwcprof = 1;
      Stabs *stabs = openDebugInfo ();
      if (stabs)
	stabs->read_hwcprof_info (this);
    }
}

void
Module::reset_datatypes ()
{
  for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++)
    {
      datatype_t *t = datatypes->fetch (i);
      t->event_data = 0;
    }
}

DataObject *
Module::get_dobj (uint32_t dtype_id)
{
  read_hwcprof_info ();
  for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++)
    {
      datatype_t *t = datatypes->fetch (i);
      if (t->datatype_id == dtype_id)
	{
	  t->event_data++;
	  return t->dobj;
	}
    }
  return NULL;
}

int
Module::readFile ()
{
  return AE_OK;
}

Vector<Histable*> *
Module::get_comparable_objs ()
{
  update_comparable_objs ();
  if (comparable_objs || dbeSession->expGroups->size () <= 1 || loadobject == NULL)
    return comparable_objs;
  Vector<Histable*> *comparableLoadObjs = loadobject->get_comparable_objs ();
  if (comparableLoadObjs == NULL)
    return NULL;
  comparable_objs = new Vector<Histable*>(comparableLoadObjs->size ());
  for (int i = 0, sz = comparableLoadObjs->size (); i < sz; i++)
    {
      Module *mod = NULL;
      LoadObject *lo = (LoadObject*) comparableLoadObjs->fetch (i);
      if (lo)
	{
	  mod = lo->get_comparable_Module (this);
	  if (mod)
	    mod->comparable_objs = comparable_objs;
	}
      comparable_objs->store (i, mod);
    }
  dump_comparable_objs ();
  return comparable_objs;
}

JMethod *
Module::find_jmethod (const char *nm, const char *sig)
{
  // Vladimir: Probably we should not use linear search
  for (long i = 0, sz = VecSize (functions); i < sz; i++)
    {
      JMethod *jmthd = (JMethod*) functions->get (i);
      char *jmt_name = jmthd->get_name (Histable::SHORT);
      if (strcmp (jmt_name, nm) == 0
	  && strcmp (jmthd->get_signature (), sig) == 0)
	return jmthd;
    }
  return NULL;
}
