/* Check the internal state of Malloc
   Copyright 1993, 1994 Tristan Gingold
		  Written December 1993 by Tristan Gingold

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.

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
General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE
*/

#define _MALLOC_INTERNAL
#include "malloc.h"
#include "message.h"

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

/* Check all internal structure. If something bad is found, display it.
 * This is very useful when you debug malloc 
 */  
void
__chkr_check_intern()
{
 struct malloc_header *tmp;
 struct malloc_header *tmp1;
 int i;
 
 /* Do nothing if 'malloc' was never called */
 if (_firstblock == NULL_HEADER || 
     _lastblock == NULL_HEADER)
   {
     if (_firstblock != _lastblock)
       chkr_printf(M_C_FB_OR_LB_BAD);
     else
       chkr_printf(M_C_NO_BLOCKS);
     return;
   }
 
 /* first check */
 if (_firstblock->prev != NULL_HEADER)
   chkr_printf(M_C_FBLOCK_BAD);
 if (_lastblock->next != NULL_HEADER)
   chkr_printf(M_C_LBLOCK_BAD);
 
 /* Check for linearity: each next block must be after the block.
  * bl->next and bl->prev are checked here.
  */
 tmp1 = NULL_HEADER;
 for (tmp = _firstblock; tmp; tmp = tmp->next)
   {
     if(tmp < tmp1)
       chkr_printf(M_C_BAD_LINEARITY, tmp, tmp1);
     if(tmp->prev != tmp1)
       chkr_printf(M_C_BAD_PLINK, tmp);
     if(tmp->next && ((char*)tmp->next - (char*)tmp != (tmp->size + HEADER_SIZE)))
       chkr_printf(M_C_BAD_BLOCK_SIZE, tmp);
     tmp1 = tmp;
   }
 if (tmp1 != _lastblock)
   {
     chkr_printf(M_C_BAD_LAST_BLOCK);
     chkr_printf(M_C_STOP_HERE);
     return;
   }
 
 /* Clean the MDCHECK flag */
 for (tmp = _firstblock; tmp; tmp = tmp->next)
   tmp->state &= ~MDCHECK;

#ifndef NO_AGE
 /* Check all aged blocks */
 i = 0;
 tmp = _youngerblock;
 if (_agedblock == 0)
   {
     if (tmp != NULL_HEADER)
       chkr_printf(M_C_U_AGED_BLOCK, tmp);
   }
 else
   {
     if (tmp->info.aged.prev != NULL_HEADER)
       chkr_printf(M_C_BAD_PL_YOUNGB, tmp);
     while (tmp != NULL_HEADER)
       {
         if (tmp->state != MDAGED)
           chkr_printf(M_C_NON_AGED_BLOCK, tmp);
         tmp->state |= MDCHECK;
         tmp = tmp->info.aged.next;
         i++;
       }
   }
 if (i != _agedblock)
   chkr_printf(M_C_BAD_NUM_AGED_B, i, _agedblock);
 if (i > BLOCK_AGE)
   chkr_printf(M_C_TOO_AGED_BLOCK, i, BLOCK_AGE);
#endif /* NO_AGE */
  
 /* Check all free blocks */
 for(i=0; i < HASH_SIZE; i++)
   {
     tmp = _heapinfo[i];
     if (tmp == NULL_HEADER)
       continue;
     if (tmp->info.free.prev != NULL_HEADER)
       chkr_printf(M_C_BAD_PL_F_FREEB, tmp);
     tmp1 = NULL_HEADER;
     while(tmp)
       {
         if (tmp->state != MDFREE)
           {
             chkr_printf(M_C_FREEB_BAD_STAT, tmp);
             break;
           }
         if (log_size(tmp->size) != i)
           chkr_printf(M_C_BLOCK_BAD_LIST, tmp);
         tmp->state |= MDCHECK;
         if (tmp->info.free.prev != tmp1)
           chkr_printf(M_C_BAD_PLIN_FREEB, tmp);
         tmp1 = tmp;
         tmp = tmp->info.free.next;
       }
#ifndef NO_HEAPINDEX       
     if(_heapindex < i)
       chkr_printf(M_C_BAD_HEAPINDEX);
#endif /* NO_HEAPINDEX */
   }
   
 /* Are there other free blocks ? */
 for(tmp = _firstblock; tmp; tmp = tmp->next)
   {
     if (tmp->state == MDFREE)
       chkr_printf(M_C_UNKNOWN_FREEB, tmp);
     /* clean the flag */
     tmp->state &= ~MDCHECK;
   }
   
 /* Are there consecutive free blocks ? */
 for(tmp = _firstblock; tmp; tmp = tmp->next)
   {
     if (tmp->state == MDFREE && tmp->next != NULL_HEADER && tmp->next->state == MDFREE)
       chkr_printf(M_C_FREE_AND_FREE, tmp);
   }
 /* TODO : check for no coalision
           good order in free lists */
} 

void
__chkr_dump_busy()
{
 struct malloc_header *tmp;

 tmp = _firstblock;
 if (tmp == NULL_HEADER)
   {
     chkr_header(M_C_NO_BUSY_BLOCK);
     return;
   }
   
 chkr_header(M_C_BUSY_BLOCK_ARE);

#if 0  
 while (tmp != NULL)
   {
     if (tmp->state == MDBUSY)
       chkr_printf(M_C_BLOC_N_IS_BUSY, tmp, tmp->size / 1024);
     else if (tmp->state == MDINTRN)
       chkr_printf("MDINTRN: block at 0x%x, size %dkb\n", tmp, tmp->size / 1024);
     tmp = tmp->next;
   }
#else
 while (tmp != NULL)
   {
     switch(tmp->state)
       {
         case MDBUSY: chkr_printf("BUSY: ");
         	break;
         case MDFREE: chkr_printf("FREE: ");
         	break;
         case MDINTRN: chkr_printf("INTERN: ");
         	break;
         case MDAGED: chkr_printf("AGED: ");
         	break;
       }
     chkr_printf("block at 0x%x, size %dkb (%d bytes)\n", tmp, tmp->size / 1024, tmp->size);
     tmp = tmp->next;
   }
#endif  
}

/* Display the free blocks list */
void
__chkr_dump_free()
{
 int i;
 struct malloc_header *tmp;
 
 for (i = 0; i < HASH_SIZE; i++)
   {
     tmp = _heapinfo[i];
     if (tmp == NULL_HEADER)
       continue;
     chkr_printf(M_C_BLOCK_SIZ_LESS, 1 << i);
     while (tmp != NULL_HEADER)
       {
         chkr_printf(M_C_BLOCK_AT_SIZE, tmp, tmp->size);
         tmp = tmp->info.free.next;
       }
   }
}
