    /*********************************************************************

                   Copyright (c) 2001-2008 SKY Computers, Inc.

    Including this file, and any software and its file formats and  visual
    displays described herein, all  rights  reserved,  may  only  be  used
    pursuant  to  the  applicable  software  license  agreement;  contains
    confidential and proprietary information  of SKY  and/or third parties
    which is protected by copyright trade secret and trademark law and may
    not  be  provided or otherwise made available  without  prior  written
    authorization.

                       Restricted Rights Legend

    Use,  duplication,  or disclosure  by  the  Government  is  subject to
    restrictions as set  forth in subdivision  (c)(1)(ii) of the Rights in
    Technical Data and Computer  Software  clauses at  DFARS  252.227-7013
    (October 1988) and FAR 52.227-19(c) (June 1987).

    *********************************************************************/

/*
 *--------------------------------------------------------------------------
 *
 >++
 *
 *   File: ex_alloc.c
 *
 *   Function: To allocate, deallocate, and display, memory buffers 
 *   for the applications various modules.
 *
 *   Calling Sequence: int ex_mem_alloc (V U_DAT *m),
 *                     int ex_mem_valloc (V U_DAT *m),
 *                     int ex_mem_free (),
 *                     int ex_mem_dump (), DEBUG mode only.
 *                     int ex_html_mem_dump (), DEBUG mode only.
 *
 *   Implementation Details:
 *       1. Allocate (free) the requested memory.
 *       2. Save the allocation info in a local table.
 *       3. Return memory pointer to the user.
 *
 *   Restrictions:
 *       1. None.
 *
 >**
 *   Revision Information:
 *   Date        By   Rev      Changes Made....
 *   10/22/07    bwj  0.1      Ported from other code.
 >--
 *--------------------------------------------------------------------------
 */

#include "sky_ex_inc.h"

/* Save allocation results locally. */
#define HTML_PATH_NAME ("./ex_mem.html")

/* Increase for larger applications. */
#define MAX_BUFFS     (128)

#define SEM_INIT_VAL  ((unsigned int)1)

/*
 *  Define a data structure to contain info about
 *  the memory allocated.
 */
typedef struct memory_list
{
  int   rank;         /* index of the thread allocating the memory */
  void *vaddr;        /* virtual address from the skybolt view */
  int   size;         /* in bytes of the block allocated */
  int   align;        /* how are we aligned? */
  char  name[32];     /* name of the memory region */
}  memory_list_t;

/* Save info on what was allocated. */
static memory_list_t *MEM_list_bgn = (void *)-1;
static int MEM_index = -1;
static int MEM_total = -1;
static sem_t Mem_table_lock;

/* Externals that need ex_misc.c at link time. */
extern int ex_str_time ();

/* ---------------------------------------------------------------------- */ 

/*
 >++
 *
 *   void ex_mem_init (int rank):
 *       int rank: Asigned to the calling thread.
 *
 *       Sets up the internal data structures for keeping track of the memory 
 *       allocated.
 *
 *       Returns: No value.
 >--
 */

void ex_mem_init (int rank)
{
  int ret;
  
  /* If we have no list, allocate one. */
  if (MEM_list_bgn == (void *)-1)
    {
      MEM_list_bgn = (memory_list_t *) malloc (MAX_BUFFS * sizeof(memory_list_t));
      MEM_index = 0;
      MEM_total = 0;
      
      /* 
       *  Initialize a semaphore with known values. This semaphore is to be used
       *  as as lock for the memory table.
       */
      ret = sem_init (&Mem_table_lock, 0, SEM_INIT_VAL);
      if (ret)
	{
	  printf ("%d: PANIC; return code from sem_init() is %d in %s at line %d\n", 
		  rank, ret, __FILE__, __LINE__);
	  exit (-1);
	}
    }
  return;
}

/* ---------------------------------------------------------------------- */ 

/*
 >++
 *   void *ex_mem_alloc (int rank, int bytes, int align, char *str) :
 *       int rank: of the calling thread.
 *       int bytes: size in bytes, to be allocated.
 *       int align: the memory is allocated on a boundary specified
 *           by the align parameter. 
 *       char str: name of memory region (used for debug). 
 *
 *       Allocate a memory buffer and save info about it so it can be freed
 *       or inspected later.
 *
 *       Returns: On success, a buffer address. On failure, the program exits
 *           (without memory the application can not continue).
 >--
 */

void *ex_mem_alloc (int rank, int bytes, int align, char *name)
{
  int            ret;          /* from function call */
  void          *buffer;
  memory_list_t *mem_list;

#ifdef DEBUG
  if (MEM_index == -1)
    {
      printf ("PANIC -- Tried to allocate memory before initialized "
	      "(%s, line %d)\n",
	      __FILE__, __LINE__);
      fflush (stdout);
      exit (-1);
    }
#endif
  
  /* Need to be single threaded here ... */
  sem_wait (&Mem_table_lock);

  mem_list = &MEM_list_bgn[MEM_index];

#ifdef SOL8
  /* Allocate memory for Solaris. */
  buffer = memalign (align, bytes);
  if (buffer == (void *)-1)
    {
      printf ("AP%d: Failed to allocate memory for %s, size = %d in %s at line %d.\n",
	      rank, name, bytes, __FILE__, __LINE__);
      fflush (stdout);
      exit (-1);
    }

#else
  /* Allocate the memory that we asked for. */
  ret = posix_memalign (&buffer, align, bytes);
  if (ret != 0)
    {
      printf ("AP%d: Failed to allocate memory for %s, size = %d in %s at line %d.\n",
	      rank, name, bytes, __FILE__, __LINE__);
      fflush (stdout);
      exit (-1);
    }
#endif

#ifdef DEBUGx
  printf ("AP%d: Allocated memory %d for %s, size = %d.\n",
	  rank, MEM_index, name, bytes);
  fflush (stdout);
#endif

  /* Save the name of the buffer, .... */
  if (name[0] == '\0')
      strcpy (mem_list->name, "Unknown");
  else
    {
      strncpy (mem_list->name, name, 32);       /* save as fixed length */
      mem_list->name[32] = '\0';                /* null terminate */
    }
  
  /* and the buffer details. */
  mem_list->vaddr = buffer;
  mem_list->size = bytes;
  mem_list->rank = rank;
  mem_list->align = align;
  mem_list++;
  MEM_index++;
  MEM_total += bytes;

  /* Give the lock back. */
  sem_post (&Mem_table_lock);

#ifdef DEBUG
  if (MEM_index >= MAX_BUFFS)
    {
      printf ("AP\\Th %d: PANIC in ex_alloc.c, too many buffers "
	      "(%d vs %d) requested.\n",
	      rank, MEM_index, MAX_BUFFS);
      fflush (stdout);
      exit (-1);
    }
#endif

  return (buffer);
}

/* ---------------------------------------------------------------------- */ 

/*
 >++
 *   int ex_mem_free (rank):
 *       int rank: of the calling thread
 *
 *       Free the memory allocated above. Walks the list that was created 
 *       as the memory was allocated and frees all of the memory blocks.
 *       
 *       Returns: 0.
 >--
 */

int ex_mem_free (int rank)
{
  int i;
  memory_list_t *mem_list;
  
  /* Make sure that we have a list. */
  if (MEM_list_bgn == (memory_list_t *)-1)
    {
      printf ("%2d: No memory freed (none allocated).\n"
	      "Test terminated.\n\n", rank);
      fflush (stdout);
      return (0);
    }
  
  /* 
   *  Start at the end of the list and free the memory.
   */
  MEM_index--;
  mem_list = &MEM_list_bgn[MEM_index];
  for (i = MEM_index; i >= 0 ; i--)
    {
#ifdef DEBUGx
      if (i == MEM_index)
	{
	  printf ("AP%d: Freeing the allocated memory ....\n",
		  Rank);
	  fflush (stdout);
	}
      
      printf ("%5d  vaddr = 0x%8x, size = %7d for %16s.\n", 
	      i, mem_list->vaddr, mem_list->size, mem_list->name);
      fflush (stdout);
#endif      
      free (mem_list->vaddr);
      mem_list--;
    }

  /* Remove the list. */
  MEM_list_bgn = (memory_list_t *)-1;
  MEM_index = -1;
    
  return (0);
}

/* ---------------------------------------------------------------------- */ 

#ifdef DEBUG

/*
 >++
 *   int ex_mem_dump (rank): 
 *       int rank: of the calling thread
 *
 *       Dump to stdio, a list of the memory buffers that have been 
 *       allocated (DEBUG mode only).
 *
 *       Returns: 0.
 >--
 */

int ex_mem_dump (int rank)
{
  int i;
  char *t_addr;
  memory_list_t *mem_list;

  mem_list = MEM_list_bgn;

  /* Need to be single threaded here ... */
  sem_wait (&Mem_table_lock);

  if (MEM_index == 0)
    {
      printf ("\nXX %d: No memory allocated.\n", rank);
      fflush (stdout);
      return (-1);
    }
    
  printf ("\nAlloc'ed Memory (%d blocks, %d bytes) at:\n"
	  " Indx   Virt Addr            Alignm't Rank     Bytes   "
	  "Buffer name\n"
	  " ----   -------------------  -------- ----  --------   "
	  "------------\n",
	  MEM_index, MEM_total);

  for (i = 0; i < MEM_index; i++, mem_list++) 
    {
      if (mem_list->vaddr == (void *)0)
	  break;

      t_addr = mem_list->vaddr;
      t_addr = &t_addr[mem_list->size];
      
      printf ("%5d    %8lx->%8lx    %6d %4d  %8d   %s\n",
	      i, (long)mem_list->vaddr, (long)t_addr,
	      mem_list->align, mem_list->rank,
	      mem_list->size, mem_list->name);
    }
  printf ("\n");
  fflush (stdout);

  /* Give the lock back. */
  sem_post (&Mem_table_lock);

  return (0);
}

#endif

/* ---------------------------------------------------------------------- */ 

#ifdef DEBUG

/*
 >++
 *   int ex_html_mem_dump (rank):
 *       int rank: of the calling thread
 *
 *       Dump to file, a list of the memory buffers that have been allocated
 *       in html format for viewing with any common browser.
 *
 *       Returns: 0.
 >--
 */

int ex_html_mem_dump (int rank)
{
  int i;
  char *t_addr;
  int ret;
  FILE *fd;
  char time_str[32];
  memory_list_t *mem_list;
  struct utsname pUtsname;

  mem_list = MEM_list_bgn;

  ret = ex_str_time (&time_str, 32);
  uname (&pUtsname);
  
  fd = fopen (HTML_PATH_NAME, "w");

  if (fd != (FILE *)NULL)
    {
      /* Create the header for the html file. */
      fprintf (fd, 
	       "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"
	       "<!--  Written by TAC 08/12/05 //-->\n"
	       "<html xmlns =\" http://www.w3.org/1999/xhtml\">\n"
	       "\n"
	       "<head>\n"
	       "<title>MEM -- Example Memory Allocation</title>\n"
	       "<link type=\"text/css\" rel=\"stylesheet\" href=\"../html/mem.css\"\n"
	       "</head>\n"
	       "\n"
	       "<body bgcolor=\"#fff0ff\">\n"
	       "\n"
	       "<h1 class=\"c1\">\n"
	       "  Example Memory Allocation <br> %s \n"
	       "</h1>\n",
	       pUtsname.nodename);
      
      /* Generate a simple table for the summary information. */
      fprintf (fd,
	       "<table width=\"98%%\" cellspacing=\"0\" cellpadding=\"1\" "
	       "align=\"center\" border=\"0\" >\n"
	       );
      fprintf (fd,
	       "  <tr>\n"
	       "    <td align=\"left\">\n"
	       "      Entries %d <br> Total memory %d\n"
	       "    </td>\n" 
	       "    <td align=\"right\">\n"
	       "      Last Update <br> %s\n"
	       "    </td>\n" 
	       "  </tr>\n", MEM_index, MEM_total, time_str
	       );
      fprintf (fd, "  </table>\n");

      /* Need to be single threaded here ... */
      sem_wait (&Mem_table_lock);
      
      if (MEM_index == 0)
	{
	  fprintf (fd, 
		   "<p align = \"center\'>"
		   "No memory allocated.");
	}
      else
	{
	  /* Now a table for the memory entries, .... */
	  fprintf (fd,
		   "<table width=\"98%%\" cellspacing=\"0\" cellpadding=\"1\" "
		   "align=\"center\" border=\"2\">\n"
		   );
	  
	  /* .... table headings....  */
          fprintf (fd,
		   "  <tr>\n"
		   "    <th align =\"center\">\n"
		   "      Index\n"
		   "    </th>\n"
		   "    <th align =\"center\">\n"
		   "      Virt Addr\n"
                   "    </th>\n"
		   "    <th align =\"center\">\n"
		   "      Alignmt\n"
                   "    </th>\n"
		   "    <th align =\"center\">\n"
		   "      Rank\n"
                   "    </th>\n"
		   "    <th align =\"center\">\n"
		   "      Bytes\n"
		   "    </th>\n"
		   "    <th align =\"center\">\n"
		   "      Name\n"
                   "    </th>\n"
                   "  </tr>\n");
      
	  for (i = 0; i < MEM_index; i++, mem_list++) 
	    {
	      if (mem_list->vaddr == (void *)0)
		  break;

	      t_addr = mem_list->vaddr;
	      t_addr = &t_addr[mem_list->size];
      
	      /* .... one line per entry. */
	      fprintf (fd,
		       "  <tr>\n"
		       "    <td align=\"center\">\n"
		       "      %d\n"
		       "    </td>\n"
		       "    <td align=\"center\">\n"
		       "      0x%8lx->0x%8lx\n"
		       "    </td>\n"
		       "    <td align=\"center\">\n"
		       "      %d\n"
		       "    </td>\n"
		       "    <td align=\"center\">\n"
		       "      %d\n"
		       "    </td>\n"
		       "    <td align=\"center\">\n"
		       "      %d\n"
		       "    </td>\n",
		       i, (long unsigned int) mem_list->vaddr,  
		       (long unsigned int) t_addr,
		       mem_list->align, mem_list->rank,
		       mem_list->size);
	      
	      fprintf (fd, "    <td align=\"center\">%s</td>\n    </tr>\n", 
		       mem_list->name);
	    }
  
	  fprintf (fd, "  </table>\n");
	}
      
      /* Give the lock back. */
      sem_post (&Mem_table_lock);
	  
      fprintf (fd, 
	       "</body>\n"
	       "</html>\n"
	       );
      fflush (fd);
    }
  
  return (0);
}

#endif

/* --------------------------- End of Module ---------------------------- */

