Example #1
0
/* 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_free_area *heap, const char *str)
{
  typedef unsigned long ul_t;
  struct heap_free_area *fa, *prev;
  struct heap_free_area *first_fa = heap;

  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);
	}
    }
}
Example #2
0
static void
__heap_dump_freelist (struct heap_free_area *heap)
{
  struct heap_free_area *fa;
  for (fa = heap; fa; fa = fa->next)
    __malloc_debug_printf (0,
			   "0x%lx:  0x%lx - 0x%lx  (%d)\tP=0x%lx, N=0x%lx",
			   (long)fa,
			   (long)HEAP_FREE_AREA_START (fa),
			   (long)HEAP_FREE_AREA_END (fa),
			   fa->size,
			   (long)fa->prev,
			   (long)fa->next);
}
Example #3
0
static void
__heap_dump_freelist (struct heap *heap)
{
#ifdef MALLOC_DEBUGGING
  struct heap_free_area *fa;

  for (fa = heap->free_areas; fa; fa = fa->next)
    __malloc_debug_printf (0,
			   "0x%lx:  0x%lx - 0x%lx  (%d)\tP=0x%lx, N=0x%lx",
			   (long)fa,
			   (long)HEAP_FREE_AREA_START (fa),
			   (long)HEAP_FREE_AREA_END (fa),
			   fa->size,
			   (long)fa->prev,
			   (long)fa->next);
#endif
}
Example #4
0
/* Return the block of memory at MEM, of size SIZE, to HEAP.  */
struct heap_free_area *
	__heap_free (struct heap_free_area **heap, void *mem, size_t size)
{
	struct heap_free_area *fa, *prev_fa;
	void *end = (char *)mem + size;

	HEAP_DEBUG (*heap, "before __heap_free");

	/* Find the right position in the free-list entry to place the new block.
	This is the most speed critical loop in this malloc implementation:
	since we use a simple linked-list for the free-list, and we keep it in
	address-sorted order, it can become very expensive to insert something
	in the free-list when it becomes fragmented and long.  [A better
	implemention would use a balanced tree or something for the free-list,
	though that bloats the code-size and complexity quite a bit.]  */
	for (prev_fa = 0, fa = *heap; fa; prev_fa = fa, fa = fa->next)
		if (unlikely (HEAP_FREE_AREA_END (fa) >= mem))
			break;

	if (fa && HEAP_FREE_AREA_START (fa) <= end)
		/* The free-area FA is adjacent to the new block, merge them.  */
	{
		size_t fa_size = fa->size + size;

		if (HEAP_FREE_AREA_START (fa) == end)
			/* FA is just after the new block, grow down to encompass it. */
		{
			/* See if FA can now be merged with its predecessor. */
			if (prev_fa && mem == HEAP_FREE_AREA_END (prev_fa))
				/* Yup; merge PREV_FA's info into FA.  */
			{
				fa_size += prev_fa->size;
				__heap_link_free_area_after (heap, fa, prev_fa->prev);
			}
		}
		else
			/* FA is just before the new block, expand to encompass it. */
		{
			struct heap_free_area *next_fa = fa->next;

			/* See if FA can now be merged with its successor. */
			if (next_fa && end == HEAP_FREE_AREA_START (next_fa))
				/* Yup; merge FA's info into NEXT_FA.  */
			{
				fa_size += next_fa->size;
				__heap_link_free_area_after (heap, next_fa, prev_fa);
				fa = next_fa;
			}
			else
				/* FA can't be merged; move the descriptor for it to the tail-end
				of the memory block.  */
			{
				/* The new descriptor is at the end of the extended block,
				SIZE bytes later than the old descriptor.  */
				fa = (struct heap_free_area *)((char *)fa + size);
				/* Update links with the neighbors in the list.  */
				__heap_link_free_area (heap, fa, prev_fa, next_fa);
			}
		}

		fa->size = fa_size;
	}
	else
		/* Make the new block into a separate free-list entry.  */
		fa = __heap_add_free_area (heap, mem, size, prev_fa, fa);

	HEAP_DEBUG (*heap, "after __heap_free");

	return fa;
}
Example #5
0
static void
__free_to_heap (void *mem, struct heap_free_area **heap
#ifdef HEAP_USE_LOCKING
		, __UCLIBC_MUTEX_TYPE *heap_lock
#endif
	       )
{
  size_t size;
  struct heap_free_area *fa;

  /* Check for special cases.  */
  if (unlikely (! mem))
    return;

  /* Normal free.  */

  MALLOC_DEBUG (1, "free: 0x%lx (base = 0x%lx, total_size = %d)",
		(long)mem, (long)MALLOC_BASE (mem), MALLOC_SIZE (mem));

  size = MALLOC_SIZE (mem);
  mem = MALLOC_BASE (mem);

  __heap_lock (heap_lock);

  /* Put MEM back in the heap, and get the free-area it was placed in.  */
  fa = __heap_free (heap, mem, size);

  /* See if the free-area FA has grown big enough that it should be
     unmapped.  */
  if (HEAP_FREE_AREA_SIZE (fa) < MALLOC_UNMAP_THRESHOLD)
    /* Nope, nothing left to do, just release the lock.  */
    __heap_unlock (heap_lock);
  else
    /* Yup, try to unmap FA.  */
    {
      unsigned long start = (unsigned long)HEAP_FREE_AREA_START (fa);
      unsigned long end = (unsigned long)HEAP_FREE_AREA_END (fa);
#ifndef MALLOC_USE_SBRK
# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__
      struct malloc_mmb *mmb, *prev_mmb;
      unsigned long mmb_start, mmb_end;
# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
      unsigned long unmap_start, unmap_end;
# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
#endif /* !MALLOC_USE_SBRK */

#ifdef MALLOC_USE_SBRK
      /* Get the sbrk lock so that the two possible calls to sbrk below
	 are guaranteed to be contiguous.  */
      __malloc_lock_sbrk ();
      /* When using sbrk, we only shrink the heap from the end.  It would
	 be possible to allow _both_ -- shrinking via sbrk when possible,
	 and otherwise shrinking via munmap, but this results in holes in
	 memory that prevent the brk from every growing back down; since
	 we only ever grow the heap via sbrk, this tends to produce a
	 continuously growing brk (though the actual memory is unmapped),
	 which could eventually run out of address space.  Note that
	 `sbrk(0)' shouldn't normally do a system call, so this test is
	 reasonably cheap.  */
      if ((void *)end != sbrk (0))
	{
	  MALLOC_DEBUG (-1, "not unmapping: 0x%lx - 0x%lx (%ld bytes)",
			start, end, end - start);
	  __malloc_unlock_sbrk ();
	  __heap_unlock (heap_lock);
	  return;
	}
#endif

      MALLOC_DEBUG (0, "unmapping: 0x%lx - 0x%lx (%ld bytes)",
		    start, end, end - start);

      /* Remove FA from the heap.  */
      __heap_delete (heap, fa);

      if (__heap_is_empty (heap))
	/* We want to avoid the heap from losing all memory, so reserve
	   a bit.  This test is only a heuristic -- the existance of
	   another free area, even if it's smaller than
	   MALLOC_MIN_SIZE, will cause us not to reserve anything.  */
	{
	  /* Put the reserved memory back in the heap; we assume that
	     MALLOC_UNMAP_THRESHOLD is greater than MALLOC_MIN_SIZE, so
	     we use the latter unconditionally here.  */
	  __heap_free (heap, (void *)start, MALLOC_MIN_SIZE);
	  start += MALLOC_MIN_SIZE;
	}

#ifdef MALLOC_USE_SBRK

      /* Release the heap lock; we're still holding the sbrk lock.  */
      __heap_unlock (heap_lock);
      /* Lower the brk.  */
      sbrk (start - end);
      /* Release the sbrk lock too; now we hold no locks.  */
      __malloc_unlock_sbrk ();

#else /* !MALLOC_USE_SBRK */

# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__
      /* Using the uClinux broken munmap, we have to only munmap blocks
	 exactly as we got them from mmap, so scan through our list of
	 mmapped blocks, and return them in order.  */

      MALLOC_MMB_DEBUG (1, "walking mmb list for region 0x%x[%d]...",
			start, end - start);

      prev_mmb = 0;
      mmb = __malloc_mmapped_blocks;
      while (mmb
	     && ((mmb_end = (mmb_start = (unsigned long)mmb->mem) + mmb->size)
		 <= end))
	{
	  MALLOC_MMB_DEBUG (1, "considering mmb at 0x%x: 0x%x[%d]",
			    (unsigned)mmb, mmb_start, mmb_end - mmb_start);

	  if (mmb_start >= start
	      /* If the space between START and MMB_START is non-zero, but
		 too small to return to the heap, we can't unmap MMB.  */
	      && (start == mmb_start
		  || mmb_start - start > HEAP_MIN_FREE_AREA_SIZE))
	    {
	      struct malloc_mmb *next_mmb = mmb->next;

	      if (mmb_end != end && mmb_end + HEAP_MIN_FREE_AREA_SIZE > end)
		/* There's too little space left at the end to deallocate
		   this block, so give up.  */
		break;

	      MALLOC_MMB_DEBUG (1, "unmapping mmb at 0x%x: 0x%x[%d]",
				(unsigned)mmb, mmb_start, mmb_end - mmb_start);

	      if (mmb_start != start)
		/* We're going to unmap a part of the heap that begins after
		   start, so put the intervening region back into the heap.  */
		{
		  MALLOC_MMB_DEBUG (0, "putting intervening region back into heap: 0x%x[%d]",
				    start, mmb_start - start);
		  __heap_free (heap, (void *)start, mmb_start - start);
		}

	      MALLOC_MMB_DEBUG_INDENT (-1);

	      /* Unlink MMB from the list.  */
	      if (prev_mmb)
		prev_mmb->next = next_mmb;
	      else
		__malloc_mmapped_blocks = next_mmb;

	      /* Start searching again from the end of this block.  */
	      start = mmb_end;

	      /* Release the descriptor block we used.  */
	      free_to_heap (mmb, &__malloc_mmb_heap, &__malloc_mmb_heap_lock);

	      /* We have to unlock the heap before we recurse to free the mmb
		 descriptor, because we might be unmapping from the mmb
		 heap.  */
              __heap_unlock (heap_lock);

	      /* Do the actual munmap.  */
	      munmap ((void *)mmb_start, mmb_end - mmb_start);

	      __heap_lock (heap_lock);

#  ifdef __UCLIBC_HAS_THREADS__
	      /* In a multi-threaded program, it's possible that PREV_MMB has
		 been invalidated by another thread when we released the
		 heap lock to do the munmap system call, so just start over
		 from the beginning of the list.  It sucks, but oh well;
		 it's probably not worth the bother to do better.  */
	      prev_mmb = 0;
	      mmb = __malloc_mmapped_blocks;
#  else
	      mmb = next_mmb;
#  endif
	    }
	  else
	    {
	      prev_mmb = mmb;
	      mmb = mmb->next;
	    }

	  MALLOC_MMB_DEBUG_INDENT (-1);
	}

      if (start != end)
	/* Hmm, well there's something we couldn't unmap, so put it back
	   into the heap.  */
	{
	  MALLOC_MMB_DEBUG (0, "putting tail region back into heap: 0x%x[%d]",
			    start, end - start);
	  __heap_free (heap, (void *)start, end - start);
	}

      /* Finally release the lock for good.  */
      __heap_unlock (heap_lock);

      MALLOC_MMB_DEBUG_INDENT (-1);

# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */

      /* MEM/LEN may not be page-aligned, so we have to page-align them,
	 and return any left-over bits on the end to the heap.  */
      unmap_start = MALLOC_ROUND_UP_TO_PAGE_SIZE (start);
      unmap_end = MALLOC_ROUND_DOWN_TO_PAGE_SIZE (end);

      /* We have to be careful that any left-over bits are large enough to
	 return.  Note that we _don't check_ to make sure there's room to
	 grow/shrink the start/end by another page, we just assume that
	 the unmap threshold is high enough so that this is always safe
	 (i.e., it should probably be at least 3 pages).  */
      if (unmap_start > start)
	{
	  if (unmap_start - start < HEAP_MIN_FREE_AREA_SIZE)
	    unmap_start += MALLOC_PAGE_SIZE;
	  __heap_free (heap, (void *)start, unmap_start - start);
	}
      if (end > unmap_end)
	{
	  if (end - unmap_end < HEAP_MIN_FREE_AREA_SIZE)
	    unmap_end -= MALLOC_PAGE_SIZE;
	  __heap_free (heap, (void *)unmap_end, end - unmap_end);
	}

      /* Release the heap lock before we do the system call.  */
      __heap_unlock (heap_lock);

      if (unmap_end > unmap_start)
	/* Finally, actually unmap the memory.  */
	munmap ((void *)unmap_start, unmap_end - unmap_start);

# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */

#endif /* MALLOC_USE_SBRK */
    }

  MALLOC_DEBUG_INDENT (-1);
}