/*
debug_malloc.c 1.0

© Copyright 1993-1995 Linux Consulting/PiiPää Ltd
Liisankatu 16 D
FIN-00170 Helsinki
Finland
http://www.fi/

Jaakko Hyvätti
Jaakko.Hyvatti@www.fi
tel +358 40 5011222
fax +358 40 6011222
http://www.fi/~jaakko/

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to Linux Consulting.
*/

/*
Initialize debugging by calling init_debug_malloc("debugoutputfilename");
analyze later by looking at file and with accompanying debug_malloc.awk:

awk -f debug_malloc.awk debugoutputfilename

Someone should write code to do some stack trace on every malloc and free.
*/

#include <stdio.h>
#include <malloc.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

#ifdef JHLIB
#include "jh.h"
#endif

#ifndef DEBUGFILE
#define DEBUGFILE "malloc.debug"
#endif

#ifndef STOP
#define STOP sleep (1) /* We'll pause in a bad free() */
#endif

#ifndef HASH
#define HASH 200003 /* a prime number */
#endif

static void free_hook (__ptr_t osoitin);
static __ptr_t malloc_hook (size_t koko);
static __ptr_t realloc_hook (__ptr_t osoitin, size_t koko);

static FILE *debugfile = NULL;

struct db {
  __ptr_t addr;
  size_t size;
  struct db *next;
};

static struct db db [HASH];

void
init_debug_malloc (const char *nimi)
{
  if (NULL == debugfile && (NULL == nimi || NULL == (debugfile = fopen (nimi, "w")))) {
    debugfile = fopen (DEBUGFILE, "w");
  }
  if (NULL != debugfile) {
    __free_hook = free_hook;
    __malloc_hook = malloc_hook;
    __realloc_hook = realloc_hook;
    memset (db, 0, sizeof (db));
  }
}

/*
   osoitteet näyttävät olevan aina 8:n kerrannaisia
*/
static int
hash (__ptr_t a)
{
  return ((int)a/8 + (int)a/3) % HASH;
}

static void
add_db (__ptr_t a, size_t s)
{
  int h = hash (a);
  struct db *new;

  if (db [h].addr) {
    new = malloc (sizeof (struct db));
    new->next = db [h].next;
    db [h].next = new;
  }
  else
    new = &db [h];
  new->addr = a;
  new->size = s;
}


static void
remove_db (__ptr_t a)
{
  int h = hash (a);
  struct db *tmp, *tmp2;

  if (a == db [h].addr) {
    if (db [h].next) {
      tmp = db [h].next;
      memcpy (&db [h], tmp, sizeof (struct db));
      free (tmp);
    }
    else
      db [h].addr = NULL;
  }
  else {
    tmp = &db [h];
    while (a != tmp->next->addr)
      tmp = tmp->next;
    tmp2 = tmp->next;
    tmp->next = tmp->next->next;
    free (tmp2);
  }
}


static struct db *
lookup_db (__ptr_t a)
{
  struct db *tmp = &db [hash (a)];

  while (tmp && tmp->addr != a)
    tmp = tmp->next;
  return tmp;
}


static void
free_hook (__ptr_t osoitin)
{
  struct db *p = NULL;

  __free_hook = NULL;
  __malloc_hook = NULL;
  __realloc_hook = NULL;
  if (NULL == osoitin || NULL == (p = lookup_db (osoitin))) {
    STOP;
  }
  else
    remove_db (osoitin);
  free (osoitin);
  if (p)
    fprintf (debugfile, "free %08x %d\n", (unsigned int)osoitin, p->size);
  else
    fprintf (debugfile, "free %08x *ERROR*\n", (unsigned int)osoitin);
  fflush (debugfile);
  __free_hook = free_hook;
  __malloc_hook = malloc_hook;
  __realloc_hook = realloc_hook;
}

static __ptr_t
malloc_hook (size_t koko)
{
  __ptr_t tulos;
  struct db *p = NULL;

  __free_hook = NULL;
  __malloc_hook = NULL;
  __realloc_hook = NULL;
  tulos = malloc (koko);
  if (NULL == tulos || NULL != (p = lookup_db (tulos))) {
    STOP;
  }
  else
    add_db (tulos, koko);
  if (p) {
    fprintf (debugfile, "malloc %d == %08x *ERROR* %d\n", koko, (unsigned int)tulos, p->size);
    p->size = koko;
  }
  else if (tulos)
    fprintf (debugfile, "malloc %d == %08x\n", koko, (unsigned int)tulos);
  else
    fprintf (debugfile, "malloc %d == %08x *ERROR*\n", koko, (unsigned int)tulos);
  fflush (debugfile);
  __free_hook = free_hook;
  __malloc_hook = malloc_hook;
  __realloc_hook = realloc_hook;
  return tulos;
}

static __ptr_t
realloc_hook (__ptr_t osoitin, size_t koko)
{
  __ptr_t tulos;
  struct db *p = NULL, *q = NULL;

  __free_hook = NULL;
  __malloc_hook = NULL;
  __realloc_hook = NULL;
  if (NULL == osoitin || NULL == (p = lookup_db (osoitin))) {
    STOP;
  }
  tulos = realloc (osoitin, koko);
  if (NULL == tulos || (osoitin != tulos && NULL != (q = lookup_db (tulos)))) {
    STOP;
  }
  if (p) {
    if (q) {
      fprintf (debugfile, "realloc %08x %d == %08x *ERROR* %d\n", (unsigned int)osoitin, koko, (unsigned int)tulos, q->size);
      remove_db (osoitin);
      q->size = koko;
    }
    else if (tulos) {
      fprintf (debugfile, "realloc %08x %d == %08x\n", (unsigned int)osoitin, koko, (unsigned int)tulos);
      if (osoitin == tulos)
	p->size = koko;
      else {
	remove_db (osoitin);
	add_db (tulos, koko);
      }
    }
    else {
      fprintf (debugfile, "realloc %08x %d == %08x *ERROR*\n", (unsigned int)osoitin, koko, (unsigned int)tulos);
      remove_db (osoitin);
    }
  }
  else {
    if (q) {
      fprintf (debugfile, "realloc %08x *ERROR* %d == %08x *ERROR* %d\n", (unsigned int)osoitin, koko, (unsigned int)tulos, q->size);
      q->size = koko;
    }
    else if (tulos) {
      fprintf (debugfile, "realloc %08x *ERROR* %d == %08x\n", (unsigned int)osoitin, koko, (unsigned int)tulos);
      add_db (tulos, koko);
    }
    else {
      fprintf (debugfile, "realloc %08x *ERROR* %d == %08x *ERROR*\n", (unsigned int)osoitin, koko, (unsigned int)tulos);
    }
  }
    
  fflush (debugfile);
  __free_hook = free_hook;
  __malloc_hook = malloc_hook;
  __realloc_hook = realloc_hook;
  return tulos;
}

