/* 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 <stdio.h>
#include <unistd.h>

#include "util.h"
#include "DbeSession.h"
#include "LoadObject.h"
#include "ArchiveExp.h"
#include "DbeFile.h"
#include "CallStack.h"
#include "gp-archive.h"
#include "Function.h"
#include "Module.h"

ArchiveExp::ArchiveExp (char *path) : Experiment ()
{
  force_flag = false;
  copyso_flag = false;
  use_fndr_archives = true;
  status = find_expdir (path);
  if (status == SUCCESS)
    read_log_file ();
}

ArchiveExp::~ArchiveExp () { }

void
ArchiveExp::read_data (int s_option)
{
  read_archives ();
  read_map_file ();
  if (read_java_classes_file () == SUCCESS)
    {
      for (int i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++)
	{
	  LoadObject *lo = loadObjs->get (i);
	  Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d loadObjs[%d]=%-25s %s\n"),
		   get_basename (__FILE__), (int) __LINE__, i,
		   STR (lo->get_name ()), STR (lo->get_pathname ()));
	  if ((lo->dbeFile->filetype & DbeFile::F_JAVACLASS) == 0)
	    continue;
	  lo->isUsed = true;
	  if ((s_option & ARCH_EXE_ONLY) != 0)
	    continue;
	  lo->sync_read_stabs ();
	}
    }
  if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0)
    {
      read_frameinfo_file ();
      resolveFrameInfo = true;
      Vector<DataDescriptor*> *ddscr = getDataDescriptors ();
      delete ddscr; // getDataDescriptors() forces reading of experiment data
      CallStack *callStack = callTree ();
      if (callStack)
	{
	  if (DEBUG_ARCHIVE)
	    {
	      Dprintf (DEBUG_ARCHIVE, NTXT ("stacks=%p\n"), callStack);
	      callStack->print (NULL);
	    }
	  for (int n = 0;; n++)
	    {
	      CallStackNode *node = callStack->get_node (n);
	      if (node == NULL)
		break;
	      do
		{
		  Histable *h = node->get_instr ();
		  Histable::Type t = h->get_type ();
		  if (t == Histable::INSTR)
		    {
		      DbeInstr *dbeInstr = (DbeInstr *) h;
		      if (!dbeInstr->isUsed)
			{
			  Function *func = (Function *) dbeInstr->convertto (Histable::FUNCTION);
			  if (!func->isUsed)
			    {
			      func->isUsed = true;
			      func->module->isUsed = true;
			      func->module->loadobject->isUsed = true;
			    }
			  DbeLine *dbeLine = (DbeLine *) dbeInstr->convertto (Histable::LINE);
			  if (dbeLine)
			    dbeLine->sourceFile->isUsed = true;
			}
		    }
		  else if (t == Histable::LINE)
		    {
		      DbeLine * dbeLine = (DbeLine *) h;
		      dbeLine->sourceFile->isUsed = true;
		    }
		  node = node->ancestor;
		}
	      while (node);
	    }
	}
    }
}

char *
ArchiveExp::createLinkToFndrArchive (LoadObject *lo, int /* hide_msg */)
{
  // For example, archives of libc.so will be:
  //  <exp>/archives/<libc.so_check_sum>
  //  <exp>/M_r0.er/archives/libc.so_<hash> -> ../../archives/<libc.so_check_sum>
  if (!create_dir (get_fndr_arch_name ()))
    {
      fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), get_fndr_arch_name ());
      return NULL;
    }
  uint32_t checksum = lo->get_checksum ();
  char *linkName = dbe_sprintf (NTXT ("../../%s/%u"), SP_ARCHIVES_DIR, checksum);
  char *nm = lo->get_pathname ();
  char *symLinkName = getNameInArchive (nm, false);
  if (symlink (linkName, symLinkName) != 0)
    {
      fprintf (stderr, GTXT ("Unable to create link `%s' -> `%s'\n"),
	       symLinkName, linkName);
      free (linkName);
      free (symLinkName);
      return NULL;
    }
  free (linkName);
  free (symLinkName);

  // Return a full path inside founder archive:
  return dbe_sprintf (NTXT ("%s/%u"), get_fndr_arch_name (), checksum);
}
