/*
 * memoryleak.c
 * Copyright (C) Steffen Pohle 2009 <steffen@gulpe.de>
 * 
 * main.c 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 of the License, or
 * (at your option) any later version.
 * 
 * main.c 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, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "memoryleak.h"
#include "system.h"

#define MEMORY_DUMPINFO 1000

struct memoryleak_data memory[MEMORYLEAK_MAX];
int memory_used = 0;
int memory_size = 0;
int memory_blocknext = 0;

struct memoryleak_data *ml_findfree ();
struct memoryleak_data *ml_find (void *ptr);


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

struct memoryleak_data *ml_findfree () {
	int i;

	for (i = 0; i < MEMORYLEAK_MAX; i++)
		if (memory[i].ptr == NULL) return &memory[i];

	return NULL;
};


struct memoryleak_data *ml_find (void *ptr) {
	int i;

	for (i = 0; i < MEMORYLEAK_MAX; i++)
		if (memory[i].ptr == ptr) return &memory[i];
	
	return NULL;
};


void *ml_calloc(size_t nmemb, size_t size) {
	void *ptr;

	ptr = calloc(nmemb, size);
	d_printf ("memoryleak: function not yet fully implemented.");
	errorexit (-2);
	
	return ptr;
};


void *ml_malloc(size_t size) {
	struct memoryleak_data *md = ml_findfree ();
    int i, j;
    void *buffer[ML_BT_SIZE];
	char **strings;

	if (md == NULL) {
		d_printf ("memoryleak malloc MEMORYLEAK_MAX reached\n");

		for (j = 0; j < MEMORYLEAK_MAX; j++) {
			d_printf ("memoryleak : (%6d) size:%d  ptr:%p  text:%s", j, memory[j].size, memory[j].ptr, memory[j].text);
			for (i = 1; i < memory[j].btcnt; i++) if (memory[j].ptr) // don't show ml_malloc call.
				d_printf ("    frame:%3d %s", i, memory[j].btstr[i]);
		}
		errorexit (-1);
	}

	if (memory_size+size > MEMORYLEAK_MAXMEM) {
		d_printf ("ml_malloc: too much memory (%lu+%lu) %lu", memory_size, size, MEMORYLEAK_MAXMEM);
		errorexit (-1);
	}

	md->ptr = malloc (size);
	md->size = size;
#if !defined(__MINGW32CE__) && !defined(_WIN32_WCE) && !defined(__MINGW32__) && !defined(ANDROID)
	md->btcnt = backtrace(buffer, ML_BT_SIZE);
	/* if someone finds out how this should really work please let me know? */
	strings = (char **) backtrace_symbols(buffer, md->btcnt);
	if (strings == NULL) {
		d_printf ("backtrace_symbols: %s", strerror(errno));
		exit(EXIT_FAILURE);
	}
#else
	md->btcnt = 0;
	strings = NULL;
#endif
	for (i = 0; (i < md->btcnt) && (i < ML_BT_SIZE); i++) {
		snprintf (md->btstr[i], ML_BT_TEXT, "%p:%s", buffer[i], strings[i]);
	}
	free(strings);

	md->text[0] = 0;
	
	memory_used ++;
	memory_size += size;

	return md->ptr;
};


void ml_free(void *ptr) {
	struct memoryleak_data *md = ml_find (ptr);

	if (ptr == NULL) return;

	if (md == NULL) {
		d_printf ("memoryleak free NULL! allocated without unsing ml_ function?\n");
		errorexit (-1);
	}

	memory_used--;
	memory_size -= md->size;

	free (md->ptr);
	md->ptr = NULL;
	md->text[0] = 0;
	md->size = 0;

};


void *ml_realloc(void *ptr, size_t size) {
	struct memoryleak_data *md = ml_find (ptr);
	void *test = NULL;
	
	if (ptr == NULL) md = ml_findfree ();
	if (md == NULL) {
		d_printf ("memoryleak realloc NULL\n");
		errorexit (-1);
	}

	memory_size = memory_size - md->size + size;
	if (memory_size > MEMORYLEAK_MAXMEM) {
		d_printf ("ml_realloc: too much memory %lld > %lld", memory_size, MEMORYLEAK_MAXMEM);
		errorexit (-1);
	}

	test = realloc (ptr, size);
	if (test == NULL && size > 0) {
		d_printf ("%s:%d (%s) test == NULL", __FILE__, __LINE__, __FUNCTION__);
		errorexit (-1);
	}

	md->ptr = test;
	md->size = size;

	return md->ptr;
};


void ml_addinfo (void *ptr, char *text) {
	struct memoryleak_data *md = ml_find (ptr);

	if (md) {
		strncpy (md->text, text, ML_BT_TEXT);
	}
};


void ml_get_stats (struct memoryleak_info *m) {
	m->blocks = memory_used;
	m->size = memory_size;

//	if (memory_used > memory_blocknext) {
//		ml_debug ();
//		while (memory_used > memory_blocknext) memory_blocknext += MEMORY_DUMPINFO;
//	}
};


void ml_init () {
	int i;
	
	for (i = 0; i < MEMORYLEAK_MAX; i++) {
		memory[i].size = 0;
		memory[i].ptr = NULL;
	}	
};


void ml_debug () {
	int i, j;
	void *ptr1, *ptr2;

	ptr1 = (void*)&i;
	ptr2 = ptr1+1;
	
	d_printf ("*** memoryleak debug: %p, %p", ptr1, ptr2);
	d_printf ("size void:%d char:%d", sizeof (void), sizeof (char));
	for (j = 0; j < MEMORYLEAK_MAX; j++) if (memory[j].ptr) {
		d_printf (" memory(%6d) size:%d  ptr:%p   text:%s", j, memory[j].size, memory[j].ptr, memory[j].text);
		for (i = 1; i < memory[j].btcnt; i++) if (memory[j].ptr) // don't show ml_malloc call.
			d_printf ("    frame:%3d %s", i, memory[j].btstr[i]);
	}
};
