/* ``The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved via the world wide web at http://www.erlang.org/.
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 * AB. All Rights Reserved.''
 * 
 *     $Id$
 */
/* Purpose: Access to elib memory statistics */

#include "sys.h"
#include "erl_driver.h"
#include "elib_stat.h"

#define MAP_BUF_SIZE    1000   /* Max map size */
#define HISTO_BUF_SIZE  100    /* Max histogram buckets */

static ErlDrvData mem_start(ErlDrvPort, char*);
static int mem_init(void); 
static void mem_stop(ErlDrvData);
static void mem_command(ErlDrvData);

ErlDrvEntry mem_driver_entry = {
    mem_init,
    mem_start,
    mem_stop,
    mem_command,
    NULL,
    NULL,
    "mem_drv"
};

static int mem_init(void)
{
    return 0;
}

static ErlDrvData mem_start(ErlDrvPort port, char* buf)
{
    return (ErlDrvData)port;
}

static void mem_stop(ErlDrvData port)
{
}

void putint32(p, v)
byte* p; int v;
{
    p[0] = (v >> 24) & 0xff;
    p[1] = (v >> 16) & 0xff;
    p[2] = (v >> 8) & 0xff;
    p[3] = (v) & 0xff;
}

int getint16(p)
byte* p;
{
    return (p[0] << 8) | p[1];
}

/*
** Command:
**   m L1 L0  -> a heap map of length L1*256 + L0 is returned
**   s        -> X3 X2 X1 X0 Y3 Y2 Y1 Y0 Z3 Z2 Z1 Z0
**               X == Total heap size bytes
**               Y == Total free bytes
**               Z == Size of largest free block in bytes
**
**   h L1 L0 B0 -> Generate a logarithm histogram base B with L buckets
**   l L1 L0 S0 -> Generate a linear histogram with step S with L buckets
*/
unsigned char outbuf[HISTO_BUF_SIZE*2*4];

static void mem_command(ErlDrvData port, char* buf, int count)
{
    if ((count == 1) && buf[0] == 's') {
	struct elib_stat info;
	char v[3*4];

	elib_stat(&info);

	putint32(v, info.mem_total*4);
	putint32(v+4, info.mem_free*4);
	putint32(v+8, info.max_free*4);
	driver_output((ErlDrvPort)port, v, 12);
	return;
    }
    else if ((count == 3) && buf[0] == 'm') {
	char w[MAP_BUF_SIZE];
	int n = getint16(buf+1);

	if (n > MAP_BUF_SIZE)
	    n = MAP_BUF_SIZE;
	elib_heap_map(w, n);
	driver_output((ErlDrvPort)port, w, n);
	return;
    }
    else if ((count == 4) && (buf[0] == 'h' || buf[0] == 'l')) {
	unsigned long vf[HISTO_BUF_SIZE];
	unsigned long va[HISTO_BUF_SIZE];
	int n = getint16(buf+1);
	int base = (unsigned char) buf[3];

	if (n >= HISTO_BUF_SIZE)
	    n = HISTO_BUF_SIZE;
	if (buf[0] == 'l')
	    base = -base;
	if (elib_histo(vf, va, n, base) < 0) {
	    driver_failure((ErlDrvPort)port, -1);
	    return;
	}
	else {
	    char* p = outbuf;
	    int i;

	    for (i = 0; i < n; i++) {
		putint32(p, vf[i]);
		p += 4;
	    }
	    for (i = 0; i < n; i++) {
		putint32(p, va[i]);
		p += 4;
	    }
	    driver_output((ErlDrvPort)port, outbuf, n*8);
	}
	return;
    }
    driver_failure((ErlDrvPort)port, -1);
}

