/* Memory allocator debugger `mallocdebug'.
   Copyright 1993 Tristan Gingold
		  Written July 1993 by Tristan Gingold.

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.

This 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 the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA. */

#ifndef	_MALLOC_INTERNAL
#define _MALLOC_INTERNAL
#include "malloc.h"
#endif

#include <stdarg.h>

#ifdef CHECKER

#ifdef CHKR_GARBAGE
unsigned int be_red_zone = 0;
unsigned int af_red_zone = 32; /* must be a multiple of sizeof(void*) */
#endif /* CHKR_GARBAGE */

/* File where all error are written */
int chkr_out = 2; 	/* 2 is stderr */

void __default_chkr_trap(const int header, const char *message, ...)
{
 va_list param;
 va_start(param,message);
 if(header)
   chkr_printf("From Checker:\n\t");
 else
   chkr_printf("\t");
 chkr_vprintf(message,param);
 chkr_printf("\n");
 va_end(param);
}

void (*__chkr_trap) __P((const int header, const char *message, ...)) =
			 __default_chkr_trap;

#ifdef CHKR_DEBUG
/* Display the fragments lists */
void __chkr_dump_frag()
{
 size_t block;
 struct list *next;
 int i;
 
 if(!__malloc_initialized)
  return;
  
 for(i=0; i< BLOCKLOG; i++)
 {
   next = _fraghead[i].next;
   if(next == NULL)
   {
     (*__chkr_trap)(0,"Frag list #%u (size: %u) is empty.",i, 1<<i);
     continue;
   }
   else
     (*__chkr_trap)(0,"Frag list #%u (size: %u) contains:", i, 1<<i);
   while (next != (struct list*)0)
   {
     block = BLOCK(next);
     (*__chkr_trap)(0,"block %u, %u/%u free fragments",block, 
   			_heapinfo[block].busy.info.frag.nfree,BLOCKSIZE>>i);
     do
       next = next->next;   			
     while(BLOCK(next) == block);
   }
 }
}

/* Display the free blocks list */
void __chkr_dump_free()
{
 size_t block = 0;
 
 if(!__malloc_initialized)
  return;

 do
  (*__chkr_trap)(0,"Free blocks: %u - %u, size: %u Kb.",block,
  			block==0 ? 0 : block + _heapinfo[block].free.size - 1,
  			_heapinfo[block].free.size * BLOCKSIZE / 1024);
 while((block=_heapinfo[block].free.next) != 0);
}

/* Display the content of a block */
void __chkr_dump_block(const size_t block)
{
 malloc_info *bl=&_heapinfo[block];
 
 if(!__malloc_initialized)
  return;

 (*__chkr_trap)(1,"Block #%u (addr:%x) is", block, ADDRESS(block));
 switch(bl->status.state & 127)
 {
  case MDFREE:
  	(*__chkr_trap)(0,"A free block.");
  	(*__chkr_trap)(0,"Next: %u, Prev: %u; size: %u.",
  			     bl->free.next, bl->free.prev, bl->free.size);
	return;
  case MDHEAD:
  	if(bl->busy.type==0)
  	{
  	 (*__chkr_trap)(0,"A busy block.");
  	 (*__chkr_trap)(0,"Size: %u.", bl->busy.info.size);
  	}
  	else
  	{
  	 (*__chkr_trap)(0,"A fragmented block.");
  	 (*__chkr_trap)(0,"Type: %u; nfree: %u, first: %u.",
  			     bl->busy.type,
  			     bl->busy.info.frag.nfree,
  			     bl->busy.info.frag.first);
  	 }
  	 return;
  	default:
  	 (*__chkr_trap)(0,"A %d block.", (int)bl->status.state);
  	 return;
 }
} 
  	
/* Check the intern structures */  			     
void __chkr_check_intern()
{
 size_t block, i, j;
 struct list *next, *next1;
 
 if(!__malloc_initialized)
  return;

#if 0
 (*__chkr_trap)(1,"sizeof(malloc_info): %u.",sizeof(malloc_info));
#endif 
 
 /* Check the free blocks */
 block=_heapinfo[0].free.next;	/* First free block */
 j=0;				/* flag: j==1: _heapindex is a free block */
 while(block != 0)		/* loop one time */
 {
    if(block == _heapindex)	/* The _heapindex check */
      j=1;
    if (_heapinfo[block].status.state != MDFREE) /* Check for free block */
    {
       (*__chkr_trap)(1,"Free block %u is marked busy.",block);
       __chkr_dump_block(block);
    }
    else
    {
       _heapinfo[block].status.state |= MDCHECK;
       /* Loop inside a big free block */
       for(i = block + _heapinfo[block].free.size - 1; i > block; i--)
         if(_heapinfo[i].status.state != MDFREE )
         {
           (*__chkr_trap)(1,"Block %u inside free block %u is marked busy.",
    				 i, block);
           __chkr_dump_block(i);
           __chkr_dump_block(block);
         }
         else
           _heapinfo[i].status.state |= MDCHECK;
    }
    block = _heapinfo[block].free.next;
 }
 if( j == 0 && _heapindex != 0)
 {
   (*__chkr_trap)(1,"_heapindex is not a free block. Changed.");
   __chkr_dump_block(_heapindex);
   _heapindex = _heapinfo[0].free.next;
 }
 
 /*  Check the free fragment */
 for(i=0; i < BLOCKLOG; i++)
 {
   next = _fraghead[i].next;
   if(next == NULL && _fraghead[i].prev != NULL)
   {
    (*__chkr_trap)(1,"Fraglist %u is broken.",i);
    continue;
   }
   while (next != NULL)
   {
     block = BLOCK(next);
     /* Check if the block is busy */
     if( _heapinfo[block].status.state != MDHEAD )
     {
      (*__chkr_trap)(1,"Allocated fragments are in a free block %u.", block);
      __chkr_dump_block(block);
     }
     if( _heapinfo[block].busy.type != i)
       (*__chkr_trap)(1,"Fragment %u is not registred in the good list.", i);
     next1 = (struct list *)((char*) ADDRESS(block) +
     				(_heapinfo[block].busy.info.frag.first << i));
     if( next1 != next)
       (*__chkr_trap)(1,"I have found a previous first 0x%x, 0x%x in a frag list.",
    			 next, next1);
     j=0;
     do
     {
       if (next->next != NULL)
         if (next->next->prev != next)
           (*__chkr_trap)(1,"Bad link in a fragment list.");
       j++;
     }
     while ( (next = next->next) && (BLOCK(next) == block));
     if(j != _heapinfo[block].busy.info.frag.nfree)
       (*__chkr_trap)(1,"Number of free frags is bad.");
     _heapinfo[block].status.state |= MDCHECK;
     if (next->next == NULL && _fraghead[i].prev != next)
     {
       (*__chkr_trap)(1,"Last block %u of frag list %u isn't the last. Changed.",
       			_fraghead[i].prev, i);
       	__chkr_dump_block(BLOCK(_fraghead[i].prev));       			
       	__chkr_dump_block(BLOCK(next));
       _fraghead[i].prev = next;
     }
/*     next = next->next; */
   }
 }
 
 /* Check all blocks and reset MDCHECK flag */
 /* Each free block and frag to _heaplimit must have a flag */
 /* Each busy block must not have a flag */
 for(i=0; i < _heaplimit; i++)
 {
   if( _heapinfo[i].status.state & MDCHECK )
   {
     _heapinfo[i].status.state &= ~MDCHECK;	/* reset the flag */
     if( _heapinfo[i].status.state != MDFREE  &&
        _heapinfo[i].status.state != MDINTERN && _heapinfo[i].busy.type == 0)
       (*__chkr_trap)(1,"Busy block %u has been check... Intern error.",i);
   }
   else
   {
     if( _heapinfo[i].status.state == MDFREE )
     {
       (*__chkr_trap)(1,"Free block %u is not link with other one.",i);
       __chkr_dump_block(i);
     }
     if( _heapinfo[i].status.state == MDHEAD && _heapinfo[i].busy.type == 0 )
     {
       for (j=1; j < _heapinfo[i].busy.info.size - 1; j++)
         if( _heapinfo[j + i].status.state != MDCORP )
           (*__chkr_trap)(1,"A corper block is not marked corper.");
       block = i + _heapinfo[i].busy.info.size - 1;	/* tail block */
       if (_heapinfo[i].busy.info.size > 1 && 
       		_heapinfo[block].status.state != MDTAIL)
       {	
         (*__chkr_trap)(1,"Tail block %u of free block %u is not marked tail.",
         			 block, i);
         __chkr_dump_block(block);
         __chkr_dump_block(i);
       }
     }    
   }
 }  
#if 0 
 /* Check all other blocks. They must be free */
 for(i=_heaplimit; i< heapsize; i++)
   if (_heapinfo[i].status.state != MDFREE)
     (*__chkr_trap)(1,"A no free block is outside.");
#endif   
}
 
void __chkr_show_busy()
{
 int i,j;
 char tags[BLOCKSIZE / sizeof(struct list)];
 struct list *next;
 
 for(i=0; i < _heaplimit; i++)
 {
   if(_heapinfo[i].status.state == MDHEAD)
   {
     /* A block */
     if(_heapinfo[i].busy.type == 0)
     {
      (*__chkr_trap)(1,"Block %u (addr: 0x%x), size %u kb is busy.",
      		   i, ADDRESS(i), _heapinfo[i].busy.info.size * BLOCKSIZE);
      i += _heapinfo[i].busy.info.size -1;
      continue;
     }
     /* A fragmented block */
     for(j = (BLOCKSIZE >> _heapinfo[i].busy.type) - 1; j >= 0; j--)
      tags[j] = 0;
     if(_heapinfo[i].busy.info.frag.nfree != 0)
     {
       next = (struct list *) ((char *) ADDRESS (i) +
 	       (_heapinfo[i].busy.info.frag.first << _heapinfo[i].busy.type));
       do
        tags[((unsigned int)next - (unsigned int)ADDRESS(i))
        			 >> _heapinfo[i].busy.type] = 1;
       while((next = next->next) && BLOCK(next) == i);
     }
     for(j = (BLOCKSIZE >> _heapinfo[i].busy.type) - 1; j >= 0; j--)
       if (!tags[j])
         (*__chkr_trap)(1,"Frag at 0x%x, of block %u, size %u bytes is busy.",
  	  (unsigned int) ADDRESS (i) + (j==0 ? 0 : j<<_heapinfo[i].busy.type),
  		i, 1 << _heapinfo[i].busy.type);
   }
 }
}

#ifdef CHKR_GARBAGE
static void chkr_show_hdr(__ptr_t ptr)
{
 struct hdr_red_zone *ptr1=(struct hdr_red_zone*)ptr;
 
 if(ptr1->magic != RED_ZONE_MAGIC)
  (*__chkr_trap)(1,"Bad magic number at address 0x%x",ptr1);
 if(ptr1->garbage_t != POINT_NOT )
  (*__chkr_trap)(1,"garbage_t != POINT_NOT at address 0x%x",ptr1);
  if(ptr1->g_flag != G_ALONE)
  (*__chkr_trap)(1,"g_flag != G_ALONE at address 0x%x",ptr1);
}

void __chkr_check_busy()
{
 foreach_busy(chkr_show_hdr);
}
#endif /* CHKR_GARBAGE */
#endif /* CHKR_DEBUG */
#endif /* CHECKER */