// Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved

// This source code has been published by Astra Network Inc. However, any
// use, reproduction, modification, distribution or transfer of this
// software, or any software which includes or is based upon any of this
// code, is only permitted if expressly authorized by a written license
// agreement from Astra. Contact your Astra representative directly for
// more information.

#include <olmod.h>
#include <mem_struct.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <sys/procfs.h>
#include <sys/mman.h>
#include <fcntl.h>

int64_t
mem_module_get (olmod_blackbox_t * data, olmod_arglist_t * dynamic_args)
{
  mem_data_t *blackbox = (mem_data_t *) data;
  struct stat pstat;
  size_t mem_used = 0;
  int i, num, fd;
  char filename[50];
  procfs_mapinfo *mapinfo;

  if (blackbox->process == 0)
    {				/* Return memory used by whole system.
				 * Source: qdn.public.qnxrtp.os/2975 & 324
				 */
      stat ("/proc", &pstat);
      return (pstat.st_size);
    }

  /*
   * else - add up memory used by the process as reported
   * by /proc/pid/as.
   * Source: Igor Kovalenko's spin.c
   * FIXME: not a good enough source - verify with QSSL
   */

  /* FIXME: How big can this go? */
  snprintf (filename, 50, "/proc/%d/as", blackbox->process);

  if ((fd = open (filename, O_RDONLY)) == -1)
    {
      fprintf (stderr, "mod_mem: open of %s failed\n", filename);
      return 0;
    }

  /* Get number of segments */
  devctl (fd, DCMD_PROC_MAPINFO, NULL, 0, &num);

  if (!(mapinfo = (procfs_mapinfo *) calloc (num, sizeof (*mapinfo))))
    {
      fprintf (stderr, "mod_mem: calloc failed : %s", strerror (errno));
      exit (-1);
    }

  devctl (fd, DCMD_PROC_PAGEDATA, mapinfo, sizeof (*mapinfo) * num, &num);

  /* Cycle through list of segments and add them up accordingly */
  for (i = 0; i < num; i++)
    {

      if ((mapinfo[i].flags & MAP_LAZY) && !(mapinfo[i].flags & PG_HWMAPPED))
	continue;		/* Just ignore, apparently this is reserved
				 * address space */

      if ((mapinfo[i].flags & MAP_ELF))
	{
	  if ((mapinfo[i].flags & MAP_TYPE) == MAP_SHARED)
	    {			/* Code */
	      if (blackbox->flags & CODE)
		mem_used += mapinfo[i].size;
	    }
	  else if ((mapinfo[i].flags & MAP_SYSRAM) &&
		   (mapinfo[i].vaddr >= 0xb0000000))
	    {			/* SO data */
	      if (blackbox->flags & SO)
		mem_used += mapinfo[i].size;
	    }
	  else
	    {			/* Executable Data */
	      if (blackbox->flags & DATA)
		mem_used += mapinfo[i].size;
	    }
	}
      else if (mapinfo[i].flags & MAP_STACK)
	{			/* Stack */
	  if (blackbox->flags & STACK)
	    mem_used += mapinfo[i].size;
	}
      else if ((mapinfo[i].flags & MAP_SYSRAM) &&
	       !(mapinfo[i].flags & MAP_PHYS))
	{			/* Heap allocation */
	  if (blackbox->flags & HEAP)
	    mem_used += mapinfo[i].size;
	}
      else
	{ /* Something else: mem-mapped file, shared mem, my screw up */
	  if (blackbox->flags & OTHER)
	    mem_used += mapinfo[i].size;
	}

    }




  close (fd);

  return mem_used;
}
