/*
 * Part:        Memory management framework. This framework is used to
 *              find any memory leak.
 *
 * Version:     $Id: memory.c,v 1.1.11 2005/03/01 01:22:13 acassen Exp $
 *
 * Authors:     Alexandre Cassen, <acassen@linux-vs.org>
 *              Jan Holmberg, <jan@artech.net>
 *
 *              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.
 *
 *              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
 *              2 of the License, or (at your option) any later version.
 *
 * Copyright (C) 2001-2005 Alexandre Cassen, <acassen@linux-vs.org>
 */

#include <assert.h>
#include "memory.h"

/*
 * Memory management. in debug mode,
 * help finding eventual memory leak.
 * Allocation memory types manipulated are :
 *
 * +type+--------meaning--------+
 * ! 0  ! Free slot             !
 * ! 1  ! Overrun               !
 * ! 2  ! free null             !
 * ! 3  ! realloc null          !
 * ! 4  ! Not previus allocated !
 * ! 8  ! Last free list        !
 * ! 9  ! Allocated             !
 * +----+-----------------------+
 *
 * global variable debug bit 9 ( 512 ) used to
 * flag some memory error.
 *
 */

#ifdef _DEBUG_

typedef struct {
	int type;
	int line;
	char *func;
	char *file;
	void *ptr;
	unsigned long size;
	long csum;
} MEMCHECK;

/* Last free pointers */
static MEMCHECK free_list[256];

static MEMCHECK alloc_list[MAX_ALLOC_LIST];
static int number_alloc_list = 0;
static int n = 0;		/* Alloc list pointer */
static int f = 0;		/* Free list pointer */

void *
dbg_malloc(unsigned long size, char *file, char *function, int line)
{
	void *buf;
	int i = 0;
	long check;

	buf = zalloc(size + sizeof (long));

	check = 0xa5a5 + size;
	*(long *) ((char *) buf + size) = check;

	while (i < number_alloc_list) {
		if (alloc_list[i].type == 0)
			break;
		i++;
	}

	if (i == number_alloc_list)
		number_alloc_list++;

	assert(number_alloc_list < MAX_ALLOC_LIST);

	alloc_list[i].ptr = buf;
	alloc_list[i].size = size;
	alloc_list[i].file = file;
	alloc_list[i].func = function;
	alloc_list[i].line = line;
	alloc_list[i].csum = check;
	alloc_list[i].type = 9;

	if (debug & 1)
		printf("zalloc[%3d:%3d], %p, %4ld at %s, %3d, %s\n",
		       i, number_alloc_list, buf, size, file, line,
		       function);

	n++;
	return buf;
}

char *
dbg_strdup(char *str, char *file, char *function, int line)
{
	void *buf;
	int i = 0;
	long check;
	long size;

	size = strlen(str) + 1;
	buf = zalloc(size + sizeof (long));
	strcat(buf, str);

	check = 0xa5a5 + size;
	*(long *) ((char *) buf + size) = check;

	while (i < number_alloc_list) {
		if (alloc_list[i].type == 0)
			break;
		i++;
	}

	if (i == number_alloc_list)
		number_alloc_list++;

	assert(number_alloc_list < MAX_ALLOC_LIST);

	alloc_list[i].ptr = buf;
	alloc_list[i].size = size;
	alloc_list[i].file = file;
	alloc_list[i].func = function;
	alloc_list[i].line = line;
	alloc_list[i].csum = check;
	alloc_list[i].type = 9;

	if (debug & 1)
		printf("strdup[%3d:%3d], %p, %4ld at %s, %3d, %s\n",
		       i, number_alloc_list, buf, size, file, line,
		       function);

	n++;
	return buf;
}



/* Display a buffer into a HEXA formatted output */
static void
dump_buffer(char *buff, int count)
{
	int i, j, c;
	int printnext = 1;

	if (count % 16)
		c = count + (16 - count % 16);
	else
		c = count;

	for (i = 0; i < c; i++) {
		if (printnext) {
			printnext--;
			printf("%.4x ", i & 0xffff);
		}
		if (i < count)
			printf("%3.2x", buff[i] & 0xff);
		else
			printf("   ");
		if (!((i + 1) % 8)) {
			if ((i + 1) % 16)
				printf(" -");
			else {
				printf("   ");
				for (j = i - 15; j <= i; j++)
					if (j < count) {
						if ((buff[j] & 0xff) >= 0x20
						    && (buff[j] & 0xff) <= 0x7e)
							printf("%c",
							       buff[j] & 0xff);
						else
							printf(".");
					} else
						printf(" ");
				printf("\n");
				printnext = 1;
			}
		}
	}
}

int
dbg_free(void *buffer, char *file, char *function, int line)
{
	int i = 0;
	void *buf;

	/* If nullpointer remember */
	if (buffer == NULL) {
		i = number_alloc_list++;

		assert(number_alloc_list < MAX_ALLOC_LIST);

		alloc_list[i].ptr = buffer;
		alloc_list[i].size = 0;
		alloc_list[i].file = file;
		alloc_list[i].func = function;
		alloc_list[i].line = line;
		alloc_list[i].type = 2;
		if (debug & 1)
			printf("free NULL in %s, %3d, %s\n", file,
			       line, function);

		debug |= 512;	/* Memory Error detect */

		return n;
	} else
		buf = buffer;

	while (i < number_alloc_list) {
		if (alloc_list[i].type == 9 && alloc_list[i].ptr == buf) {
			if (*
			    ((long *) ((char *) alloc_list[i].ptr +
				       alloc_list[i].size)) ==
			    alloc_list[i].csum)
				alloc_list[i].type = 0;	/* Release */
			else {
				alloc_list[i].type = 1;	/* Overrun */
				if (debug & 1) {
					printf("free corrupt, buffer overrun [%3d:%3d], %p, %4ld at %s, %3d, %s\n",
					       i, number_alloc_list,
					       buf, alloc_list[i].size, file,
					       line, function);
					dump_buffer(alloc_list[i].ptr,
						    alloc_list[i].size + sizeof (long));
					printf("Check_sum\n");
					dump_buffer((char *) &alloc_list[i].csum,
						    sizeof(long));

					debug |= 512;	/* Memory Error detect */
				}
			}
			break;
		}
		i++;
	}

	/*  Not found */
	if (i == number_alloc_list) {
		printf("Free ERROR %p\n", buffer);
		number_alloc_list++;

		assert(number_alloc_list < MAX_ALLOC_LIST);

		alloc_list[i].ptr = buf;
		alloc_list[i].size = 0;
		alloc_list[i].file = file;
		alloc_list[i].func = function;
		alloc_list[i].line = line;
		alloc_list[i].type = 4;
		debug |= 512;

		return n;
	}

	if (buffer != NULL)
		xfree(buffer);

	if (debug & 1)
		printf("free  [%3d:%3d], %p, %4ld at %s, %3d, %s\n",
		       i, number_alloc_list, buf,
		       alloc_list[i].size, file, line, function);

	free_list[f].file = file;
	free_list[f].line = line;
	free_list[f].func = function;
	free_list[f].ptr = buffer;
	free_list[f].type = 8;
	free_list[f].csum = i;	/* Using this field for row id */

	f++;
	f &= 255;
	n--;

	return n;
}

void
dbg_free_final(char *banner)
{
	unsigned int sum = 0, overrun = 0, badptr = 0;
	int i, j;
	i = 0;

	printf("\n---[ Memory dump for (%s)]---\n\n", banner);

	while (i < number_alloc_list) {
		switch (alloc_list[i].type) {
		case 3:
			badptr++;
			printf
			    ("null pointer to realloc(nil,%ld)! at %s, %3d, %s\n",
			     alloc_list[i].size, alloc_list[i].file,
			     alloc_list[i].line, alloc_list[i].func);
			break;
		case 4:
			badptr++;
			printf
			    ("pointer not found in table to free(%p) [%3d:%3d], at %s, %3d, %s\n",
			     alloc_list[i].ptr, i, number_alloc_list,
			     alloc_list[i].file, alloc_list[i].line,
			     alloc_list[i].func);
			for (j = 0; j < 256; j++)
				if (free_list[j].ptr == alloc_list[i].ptr)
					if (free_list[j].type == 8)
						printf
						    ("  -> pointer already released at [%3d:%3d], at %s, %3d, %s\n",
						     (int) free_list[j].csum,
						     number_alloc_list,
						     free_list[j].file,
						     free_list[j].line,
						     free_list[j].func);
			break;
		case 2:
			badptr++;
			printf("null pointer to free(nil)! at %s, %3d, %s\n",
			       alloc_list[i].file, alloc_list[i].line,
			       alloc_list[i].func);
			break;
		case 1:
			overrun++;
			printf("%p [%3d:%3d], %4ld buffer overrun!:\n",
			       alloc_list[i].ptr, i, number_alloc_list,
			       alloc_list[i].size);
			printf(" --> source of malloc: %s, %3d, %s\n",
			       alloc_list[i].file, alloc_list[i].line,
			       alloc_list[i].func);
			break;
		case 9:
			sum += alloc_list[i].size;
			printf("%p [%3d:%3d], %4ld not released!:\n",
			       alloc_list[i].ptr, i, number_alloc_list,
			       alloc_list[i].size);
			printf(" --> source of malloc: %s, %3d, %s\n",
			       alloc_list[i].file, alloc_list[i].line,
			       alloc_list[i].func);
			break;
		}
		i++;
	}

	printf("\n\n---[ Memory dump summary for (%s) ]---\n", banner);
	printf("Total number of bytes not freed...: %d\n", sum);
	printf("Number of entries not freed.......: %d\n", n);
	printf("Maximum allocated entries.........: %d\n", number_alloc_list);
	printf("Number of bad entries.............: %d\n", badptr);
	printf("Number of buffer overrun..........: %d\n\n", overrun);

	if (sum || n || badptr || overrun)
		printf("=> Program seems to have some memory problem !!!\n\n");
	else
		printf("=> Program seems to be memory allocation safe...\n\n");
}

void *
dbg_realloc(void *buffer, unsigned long size, char *file, char *function,
		   int line)
{
	int i = 0;
	void *buf, *buf2;
	long check;

	if (buffer == NULL) {
		printf("realloc %p %s, %3d %s\n", buffer, file, line, function);
		i = number_alloc_list++;

		assert(number_alloc_list < MAX_ALLOC_LIST);

		alloc_list[i].ptr = NULL;
		alloc_list[i].size = 0;
		alloc_list[i].file = file;
		alloc_list[i].func = function;
		alloc_list[i].line = line;
		alloc_list[i].type = 3;
		return dbg_malloc(size, file, function, line);
	}

	buf = buffer;

	while (i < number_alloc_list) {
		if (alloc_list[i].ptr == buf) {
			buf = alloc_list[i].ptr;
			break;
		}
		i++;
	}

	/* not found */
	if (i == number_alloc_list) {
		printf("realloc ERROR no matching zalloc %p \n", buffer);
		number_alloc_list++;

		assert(number_alloc_list < MAX_ALLOC_LIST);

		alloc_list[i].ptr = buf;
		alloc_list[i].size = 0;
		alloc_list[i].file = file;
		alloc_list[i].func = function;
		alloc_list[i].line = line;
		alloc_list[i].type = 9;
		debug |= 512;	/* Memory Error detect */
		return NULL;
	}

	buf2 = ((char *) buf) + alloc_list[i].size;

	if (*(long *) (buf2) != alloc_list[i].csum) {
		alloc_list[i].type = 1;
		debug |= 512;	/* Memory Error detect */
	}
	buf = realloc(buffer, size + sizeof (long));

	check = 0xa5a5 + size;
	*(long *) ((char *) buf + size) = check;
	alloc_list[i].csum = check;

	if (debug & 1)
		printf("realloc [%3d:%3d] %p, %4ld %s %d %s -> %p %4ld %s %d %s\n",
		       i, number_alloc_list, alloc_list[i].ptr,
		       alloc_list[i].size, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func,
		       buf, size, file, line, function);

	alloc_list[i].ptr = buf;
	alloc_list[i].size = size;
	alloc_list[i].file = file;
	alloc_list[i].line = line;
	alloc_list[i].func = function;

	return buf;
}

#endif
