/*
 * libc/stdlib/malloc/heap_debug.c -- optional heap debugging routines
 *
 *  Copyright (C) 2002  NEC Corporation
 *  Copyright (C) 2002  Miles Bader <miles@gnu.org>
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License.  See the file COPYING.LIB in the main
 * directory of this archive for more details.
 * 
 * Written by Miles Bader <miles@gnu.org>
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "heap.h"


#ifdef HEAP_DEBUGGING
int __heap_debug = 0;
#endif


static void
__heap_dump_freelist (struct heap *heap)
{
  struct heap_free_area *fa;
  for (fa = heap->free_areas; fa; fa = fa->next)
    fprintf (stderr,
	     "    0x%lx:  0x%lx - 0x%lx  (%d)\tP=0x%lx, N=0x%lx\n",
	     (long)fa,
	     (long)HEAP_FREE_AREA_START (fa),
	     (long)HEAP_FREE_AREA_END (fa),
	     fa->size,
	     (long)fa->prev,
	     (long)fa->next);
}

/* Output a text representation of HEAP to stderr, labelling it with STR.  */
void
__heap_dump (struct heap *heap, const char *str)
{
  static int recursed = 0;

  if (! recursed)
    {
      __heap_check (heap, str);

      recursed = 1;

      fprintf (stderr, "  %s: heap @0x%lx:\n", str, (long)heap);
      __heap_dump_freelist (heap);

      recursed = 0;
    }
}


/* Output an error message to stderr, and exit.  STR is printed with the
   failure message.  */
static void
__heap_check_failure (struct heap *heap, struct heap_free_area *fa,
		      const char *str, char *fmt, ...)
{
  va_list val;

  if (str)
    fprintf (stderr, "\nHEAP CHECK FAILURE %s: ", str);
  else
    fprintf (stderr, "\nHEAP CHECK FAILURE: ");

  va_start (val, fmt);
  vfprintf (stderr, fmt, val);
  va_end (val);

  fprintf (stderr, "\nheap dump:\n");
  __heap_dump_freelist (heap);

  exit (22);
}

/* Do some consistency checks on HEAP.  If they fail, output an error
   message to stderr, and exit.  STR is printed with the failure message.  */
void
__heap_check (struct heap *heap, const char *str)
{
  typedef unsigned long ul_t;
  struct heap_free_area *fa, *prev;
  struct heap_free_area *first_fa = heap->free_areas;

  if (first_fa && first_fa->prev)
    __heap_check_failure (heap, first_fa, str,
"first free-area has non-zero prev pointer:\n\
    first free-area = 0x%lx\n\
    (0x%lx)->prev   = 0x%lx\n",
			      (ul_t)first_fa,
			      (ul_t)first_fa, (ul_t)first_fa->prev);

  for (prev = 0, fa = first_fa; fa; prev = fa, fa = fa->next)
    {
      if (((ul_t)HEAP_FREE_AREA_END (fa) & (HEAP_GRANULARITY - 1))
	  || (fa->size & (HEAP_GRANULARITY - 1)))
	__heap_check_failure (heap, fa, str, "alignment error:\n\
    (0x%lx)->start = 0x%lx\n\
    (0x%lx)->size  = 0x%lx\n",
			      (ul_t)fa,
			      (ul_t)HEAP_FREE_AREA_START (fa),
			      (ul_t)fa, fa->size);

      if (fa->prev != prev)
	__heap_check_failure (heap, fa, str, "prev pointer corrupted:\n\
    (0x%lx)->next = 0x%lx\n\
    (0x%lx)->prev = 0x%lx\n",
			      (ul_t)prev, (ul_t)prev->next,
			      (ul_t)fa, (ul_t)fa->prev);

      if (prev)
	{
	  ul_t start = (ul_t)HEAP_FREE_AREA_START (fa);
	  ul_t prev_end = (ul_t)HEAP_FREE_AREA_END (prev);

	  if (prev_end >= start)
	    __heap_check_failure (heap, fa, str,
				  "start %s with prev free-area end:\n\
    (0x%lx)->prev  = 0x%lx\n\
    (0x%lx)->start = 0x%lx\n\
    (0x%lx)->end   = 0x%lx\n",
				  (prev_end == start ? "unmerged" : "overlaps"),
				  (ul_t)fa, (ul_t)prev,
				  (ul_t)fa, start,
				  (ul_t)prev, prev_end);
	}
    }
}
