Re: [uCsimm] simpler memory leak example

From: David Beckemeyer (david@corp.earthlink.net)
Date: Mon Jul 17 2000 - 22:43:18 EDT


On Mon, Jul 17, 2000 at 01:31:38PM -0700, David Smead wrote:
> David,
>
> This looks like a case where malloc has to split larger buffers to deal
> out one, but free isn't putting buffer fragments back together when it
> can.

Nope. malloc() and free() simply call the kernel system calls
mmap and munmap. It looks like munmap doesn't release memory.

If this is a "feature" of uClinux, I wish someone would tell me.

Rather than debug the kernel, I put together a little malloc/free
hack of my own that essentially does what you decribe above,
splitting larger buffers and only calling mmap for larger chunks
as needed (and never calling munmap since that doesn't do anything
anyway).

Warning: this is a hack. I compiled the code below into malloc.o
and replaced the malloc.o in the distributed libc.a (and removed
free.o from libc.a).

I'm submitting it in case anyone wants to use this same hack
(which works for my case). Also if anyone spots really bad bugs,
please let me know.

----------------------------------------------------------------
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>

struct memblk
  {
    int mb_magic;
    struct memblk *mb_next;
    struct memblk *mb_prev;
    size_t mb_len;
    struct poolblk *mb_pool;
  };

struct poolblk
  {
    struct poolblk *pb_next;
    struct memblk *pb_freelist;
  };

static struct poolblk *poollist;

#define POOLSIZE 8*1024

#define MEM_MAGIC_FREE 0x46524545
#define MEM_MAGIC_USED 0x55534544

static void *
  real_malloc (size_t len)
{
  void *result = mmap ((void *) 0, len, PROT_READ | PROT_WRITE,
                       MAP_SHARED | MAP_ANONYMOUS, 0, 0);
  if (result == (void *) -1)
    return 0;

  return result;

}

/* unlink from the freelist */
static
  mb_unlink (struct memblk *mb)
{
  if (mb->mb_prev)
    mb->mb_prev->mb_next = mb->mb_next;
  else
    mb->mb_pool->pb_freelist = mb->mb_next;
  if (mb->mb_next)
    mb->mb_next->mb_prev = mb->mb_prev;
  mb->mb_magic = MEM_MAGIC_USED;
}

/* relink onto the end of the freelist */
static
  mb_link (struct memblk *mb)
{
  struct memblk *lastmb;

  lastmb = mb->mb_pool->pb_freelist;
  if (lastmb)
    {
      while (lastmb->mb_next)
        lastmb = lastmb->mb_next;
      lastmb->mb_next = mb;
    }
  else
    {
      mb->mb_pool->pb_freelist = mb;
    }
  mb->mb_prev = lastmb;
  mb->mb_next = 0;
  mb->mb_magic = MEM_MAGIC_FREE;
}

static char *
  alloc_memblk (struct memblk *mb, size_t len)
{
  size_t newlen;
  struct memblk *newmb;

  newlen = mb->mb_len - len;
  if (newlen > 1023)
    {
      newmb = (struct memblk *) ((char *) mb + len);

      /* re-link the freelist */
      *newmb = *mb;
      if (mb->mb_prev)
        mb->mb_prev->mb_next = newmb;
      else
        mb->mb_pool->pb_freelist = newmb;
      if (mb->mb_next)
        mb->mb_next->mb_prev = newmb;
      newmb->mb_len = newlen;
      mb->mb_len = len;
    }
  else
    {
      mb_unlink (mb);
    }
  mb->mb_prev = mb->mb_next = 0;
  return ((char *) ++mb);
}

#ifdef MEMORY_DEBUG
show_pools ()
{
  struct poolblk *pb;
  struct memblk *mb;

  printf ("====\n");
  for (pb = poollist; pb; pb = pb->pb_next)
    {
      printf ("Pool %x\n", pb);
      for (mb = pb->pb_freelist; mb; mb = mb->mb_next)
        {
          printf (" Block: at %x %d bytes\n", mb, mb->mb_len);
        }
    }
  printf ("~~~~\n");
}
#endif

static struct memblk *
  m_search_pool (size_t len)
{
  struct poolblk *pb;
  struct memblk *mb;

  for (pb = poollist; pb; pb = pb->pb_next)
    {
      for (mb = pb->pb_freelist; mb; mb = mb->mb_next)
        if (mb->mb_len >= len)
          {
#ifdef MEMORY_DEBUG
            printf ("found free segment at %x\n", mb);
#endif
            return (mb);
          }
    }
  return ((struct memblk *) 0);
}

static
  defrag_heap ()
{
  struct poolblk *pb;
  struct memblk *mb, *mb2, *mbend;

#ifdef MEMORY_DEBUG
  printf ("Before defrag: ");
  show_pools ();
#endif

  for (pb = poollist; pb; pb = pb->pb_next)
    {
    mb_again:
      for (mb = pb->pb_freelist; mb; mb = mb->mb_next)
        {
          mbend = (struct memblk *) ((char *) mb + mb->mb_len);
          for (mb2 = pb->pb_freelist; mb2; mb2 = mb2->mb_next)
            {
              if (mb2 == mbend)
                {
                  /* unlink mb2 */
                  mb_unlink (mb2);
                  mb->mb_len += mb2->mb_len;
                  goto mb_again;
                }
            }
        }
    }

#ifdef MEMORY_DEBUG
  printf ("After defrag: ");
  show_pools ();
#endif
}

void *
  malloc (size_t len)
{
  struct poolblk *pb, *newpb;
  struct memblk *mb;
  size_t lenadj, poolsz;
  char *end;

  lenadj = (len + sizeof (struct memblk) + 1023) & ~1023;
#ifdef MEMORY_DEBUG
  printf ("searching for segment of %d bytes\n", lenadj);
#endif

  if (poollist)
    {
      mb = m_search_pool (lenadj);
      if (mb)
        return (alloc_memblk (mb, lenadj));
#ifdef MEMORY_DEBUG
      printf ("cleaning the help\n");
#endif
      defrag_heap ();
      mb = m_search_pool (lenadj);
      if (mb)
        return (alloc_memblk (mb, lenadj));
    }
#ifdef MEMORY_DEBUG
  printf ("allocating new pool\n");
#endif
  if (lenadj + sizeof (struct poolblk) > POOLSIZE)
    poolsz = (lenadj + sizeof (struct poolblk) + 1023) & ~1023;
  else
    poolsz = POOLSIZE;
  newpb = (struct poolblk *) real_malloc (poolsz);
  if (!newpb)
    return ((char *) 0);
  if (poollist)
    {
      for (pb = poollist; pb->pb_next; pb = pb->pb_next)
        ;
      pb->pb_next = newpb;
    }
  else
    {
      poollist = newpb;
    }
  newpb->pb_next = 0;
  end = (char *) newpb + poolsz;
  mb = newpb->pb_freelist = (struct memblk *) (newpb + 1);
  mb->mb_next = 0;
  mb->mb_prev = 0;
  mb->mb_pool = newpb;
  mb->mb_len = end - (char *) newpb->pb_freelist;
  mb->mb_magic = MEM_MAGIC_FREE;
#ifdef MEMORY_DEBUG
  printf ("newpb=%x\n", newpb);
  printf ("newpb->pb_freelist=%x\n", newpb->pb_freelist);
  printf ("newpb->pb_freelist->mb_len=%x\n", newpb->pb_freelist->mb_len);
  show_pools ();
#endif
  mb = m_search_pool (lenadj);
  if (mb)
    return (alloc_memblk (mb, lenadj));
#ifdef MEMORY_DEBUG
  printf ("huh? m_search_pool failed?");
#endif
  return ((char *) 0);
}

void
  free (void *p)
{
  struct memblk *mb;

  mb = (struct memblk *) p;
  if (mb->mb_magic != MEM_MAGIC_USED)
    assert("attempted to free block with corrupt MAGIC");
  mb_link (--mb);
}
This message resent by the ucsimm@uclinux.com list server http://www.uClinux.com/



This archive was generated by hypermail 2b30 : Sun Apr 07 2002 - 00:01:37 EST