/* 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 */

#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_printf("Frag list #%u (size: %u) is empty.\n",i, 1<<i);
     continue;
   }
   else
     chkr_printf("Frag list #%u (size: %u) contains:\n", i, 1<<i);
   while (next != (struct list*)0)
   {
     block = BLOCK(next);
     chkr_printf("block %u, %u/%u free fragments\n",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_printf("Free blocks: %u - %u, size: %u Kb.\n",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_header("");
 chkr_printf("Block #%u (addr:%x) is\n", block, ADDRESS(block));
 switch(bl->status.state & 127)
 {
  case MDFREE:
  	chkr_printf("A free block.\n");
  	chkr_printf("Next: %u, Prev: %u; size: %u.\n",
  			     bl->free.next, bl->free.prev, bl->free.size);
	return;
  case MDHEAD:
  	if(bl->busy.type==0)
  	{
  	 chkr_printf("A busy block.\n");
  	 chkr_printf("Size: %u.\n", bl->busy.info.size);
  	}
  	else
  	{
  	 chkr_printf("A fragmented block.\n");
  	 chkr_printf("Type: %u; nfree: %u, first: %u.\n",
  			     bl->busy.type,
  			     bl->busy.info.frag.nfree,
  			     bl->busy.info.frag.first);
  	 }
  	 return;
  	default:
  	 chkr_printf("A %d block.\n", (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;
 chkr_header("Checking internal informations...\n");
 
#if 0
 chkr_printf("sizeof(malloc_info): %u.\n",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_printf("Free block %u is marked busy.\n",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_printf("Block %u inside free block %u is marked busy.\n",
    				 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_printf("_heapindex is not a free block. Changed.\n");
   __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_printf("Fraglist %u is broken.\n",i);
     continue;
   }
   while (next != NULL)
   {
     block = BLOCK(next);
     /* Check if the block is busy */
     if( _heapinfo[block].status.state != MDHEAD )
     {
       chkr_printf("Allocated fragments are in a free block %u.\n", block);
       __chkr_dump_block(block);
     }
     if( _heapinfo[block].busy.type != i)
       chkr_printf("Fragment %u is not registred in the good list.\n", i);
     next1 = (struct list *)((char*) ADDRESS(block) +
     				(_heapinfo[block].busy.info.frag.first << i));
     if( next1 != next)
       chkr_printf("I have found a previous first 0x%x, 0x%x in a frag list.\n",
    			 next, next1);
     j=0;
     do
     {
       if (next->next != NULL)
         if (next->next->prev != next)
           chkr_printf("Bad link in a fragment list.\n");
       j++;
     }
     while ( (next = next->next) && (BLOCK(next) == block));
     if(j != _heapinfo[block].busy.info.frag.nfree)
       chkr_printf("Number of free frags is bad.\n");
     _heapinfo[block].status.state |= MDCHECK;
     if (next->next == NULL && _fraghead[i].prev != next)
     {
       chkr_printf("Last block %u of frag list %u isn't the last. Changed.\n",
       			_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_printf("Busy block %u has been check... Intern error.\n",i);
   }
   else
   {
     if( _heapinfo[i].status.state == MDFREE )
     {
       chkr_printf("Free block %u is not link with other one.\n",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_printf("A corper block is not marked corper.\n");
       block = i + _heapinfo[i].busy.info.size - 1;	/* tail block */
       if (_heapinfo[i].busy.info.size > 1 && 
       		_heapinfo[block].status.state != MDTAIL)
       {	
         chkr_printf("Tail block %u of free block %u is not marked tail.\n",
         			 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_printf("A no free block is outside.\n");
#endif   
}
 
void __chkr_dump_busy()
{
 int i,j;
 char tags[BLOCKSIZE / sizeof(struct list)];
 struct list *next;

 chkr_header("Busy blocks are:\n");
  
 for(i=0; i < _heaplimit; i++)
 {
   if(_heapinfo[i].status.state == MDHEAD)
   {
     /* A block */
     if(_heapinfo[i].busy.type == 0)
     {
       chkr_printf("Block %u (addr: 0x%x), size %u kb is busy.\n",
      		   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_printf("Frag at 0x%x, of block %u, size %u bytes is busy.\n",
  	  (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;
 
 chkr_header("Checking informations...\n");
 if(ptr1->magic != RED_ZONE_MAGIC)
   chkr_printf("Bad magic number at address 0x%x\n",ptr1);
 if(ptr1->garbage_t != POINT_NOT )
   chkr_printf("garbage_t != POINT_NOT at address 0x%x\n",ptr1);
 if(ptr1->g_flag != G_ALONE)
  chkr_printf("g_flag != G_ALONE at address 0x%x\n",ptr1);
}

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