[RFC] Deprecating mtrace?

Ondřej Bílka neleai@seznam.cz
Mon Nov 4 13:05:00 GMT 2013


On Mon, Nov 04, 2013 at 10:28:38AM +0100, Florian Weimer wrote:
> On 11/03/2013 08:17 PM, Ondřej Bílka wrote:
> 
> >Please state which are platforms that are supported by glibc and
> >improtant programs running only on that platform needs mtrace
> 
> Does Emacs now run without unexec'ing?  That precluded using valgrind on it.
> 
> When I tracked down one rather prominent memory leak some time ago,
> mtrace was still useless because it only reported the address of
> Emacs' malloc wrapper, not the actual allocation site.  In the end,
> I had to use a LD_PRELOAD library to gather the data.
> 
That is among problems with threading and design that is just braindead
another reason why use something better than mtrace.

Creating faster leak detector is easy, you just keep a double linked
list of allocations and print what is left at end.

That is what I done in my simple leak detector that I attached.

If somebody wants mtrace/untrace to enable which allocations to trace
this is also easy, just add a flag for each allocation and print only
these that have flag set.
-------------- next part --------------
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>
#include <execinfo.h>
#include <dlfcn.h>

#define log(...) fprintf (stderr, __VA_ARGS__)

#ifndef TRACE
# define TRACE 4
#endif

typedef struct left_sentinel
{
  void *origin[TRACE];
  uint64_t res2;
  uint64_t iteration;
  struct left_sentinel *previous, *next;
  uint64_t size;
  uint64_t sentinel;
} left_sentinel;

#define ALIGN_UP(x, no) ((((uintptr_t) x) + no - 1) - (((uintptr_t) x) + no - 1) % no)
#define SENTINEL        5877815476798078077UL
#define BIG_SENTINEL    1204925723299285449UL
#define DOUBLE_FREE     10943064412161398437UL

static pthread_mutex_t mutex;
static int inited;
size_t page_size;
static void
init ()
{
  page_size = sysconf (_SC_PAGE_SIZE);
  pthread_mutex_init (&mutex, NULL);
  inited = 1;
}
static char *memory;
static size_t memsize = 0, capacity = 0;
void *
malloc (size_t len)
{
  void *ptr;
  posix_memalign (&ptr, 16, len);
  return ptr;
}
void *
calloc (size_t nmemb, size_t size)
{
  if (!nmemb | !size)
    return NULL;

  if (SIZE_MAX / nmemb < size)
    {
      log ("allocation overflow in calloc(%i, %i)\n", nmemb, size);
      abort ();
    }
  void *ptr = malloc (nmemb * size);
  if (ptr != NULL)
    memset (ptr, 0, nmemb * size);
  return ptr;
}

void *
valloc (size_t size)
{
  void *ret;
  posix_memalign (&ret, page_size, size);
  return ret;
}
void *
pvalloc (size_t size)
{
  void *ret;
  posix_memalign (&ret, page_size, size);
  return ret;
}
void *
memalign (size_t alignment, size_t size)
{
  void *ret;
  posix_memalign (&ret, alignment, size);
  return ret;
}
void *
realloc (void *ptr, size_t size)
{
  if (!ptr)
    return malloc (size);

  left_sentinel *cur = (left_sentinel *) (((char *) ptr) - sizeof (left_sentinel));

  if (cur->sentinel != SENTINEL)
    {
      if (cur->sentinel == DOUBLE_FREE)
	log ("realloc: already freed\n");
      log ("corrupt sentinel\n");
      abort ();
    }

  size_t old_size = cur->size;
  if (old_size == -1)
    {
      log ("realloc used freed memory");
      abort ();
    }

  if (old_size > size)
    return ptr;

  char *new = malloc (size);
  if (!new)
    return NULL;

  memcpy (new, ptr, old_size);
  free (ptr);
  return new;
}

static char *
wrap_mmap (size_t size)
{
  char *ret = mmap (NULL, ALIGN_UP (size, page_size) + 2 * page_size,
		    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (!ret)
    abort;
  munmap (ret, page_size);
  munmap (ret + page_size + ALIGN_UP (size, page_size), page_size);
  return ret + page_size;
}

static void **bucket[64];

static void
return_bucket (char *ptr, size_t size)
{
  int pow;
  for (pow = 0; 1UL << pow < size; pow++)
    ;
  char *old = (char *) bucket[pow];
  bucket[pow] = (void **) ptr;
  *bucket[pow] = old;
}


static void
add_bucket (size_t size)
{
  int i, pow;
  for (pow = 0; 1UL << pow < size; pow++)
    ;
  if (1UL << pow >= page_size)
    { /* tighter checks.  */
      char *ptr = wrap_mmap ((1UL << pow));
      return_bucket (ptr, size);
    }
  else
    {
      char *ptr = wrap_mmap (page_size);
      for (i = 0; i < page_size; i += 1UL << pow)
	{
	  return_bucket (ptr + i, size);
	}
    }
}

static void *
get_bucket (size_t size)
{
  int pow;
  for (pow = 0; 1UL << pow < size; pow++)
    ;
  if (!bucket[pow])
    add_bucket (size);
  void **ret = bucket[pow];
  bucket[pow] = *ret;
  return (void *) ret;
}

static left_sentinel *previous = NULL;

int
posix_memalign (void **memptr, size_t alignment, size_t size)
{
  int i;
  if (!inited)
    init ();

  void *origin[TRACE + 2];
  for (i = 0; i < TRACE; i++)
    origin[i + 2] = NULL;

  static int in_backtrace = 0;
  if (!in_backtrace)
    {
      in_backtrace = 1;
      backtrace (origin, TRACE + 2);
      in_backtrace = 0;
    }

  if (pthread_mutex_lock (&mutex))
    abort ();

  /* separate for tigther checks.  */
  if (size < alignment)
    size = alignment;

  if (alignment > 16)
    { /*TODO, excessive alignment can cause leaks.  */
      *memptr = get_bucket (size + alignment + sizeof (left_sentinel) + 8);
      *memptr = ((char *) ALIGN_UP ((((char *) *memptr) + sizeof (left_sentinel)), alignment)) - sizeof (left_sentinel);
    }
  else
    *memptr = get_bucket (size + sizeof (left_sentinel) + 8);


  left_sentinel *cur = *memptr;

  *memptr += sizeof (left_sentinel);

  for (i = 0; i < TRACE; i++)
    cur->origin[i] = origin[i + 2];


  cur->previous = previous;

  if (previous)
    previous->next = cur;


  cur->next = NULL;
  previous = cur;

  cur->size = size;
  cur->sentinel = SENTINEL;
  ((uint64_t *) (((char *) *memptr) + size))[0] = SENTINEL;


  if (pthread_mutex_unlock (&mutex))
    abort ();

  return 0;
}

void
free (void *ptr)
{
  if (!ptr)
    return;

  if (pthread_mutex_lock (&mutex))
    abort ();
  left_sentinel *cur = (left_sentinel *) (((char *) ptr) - sizeof (left_sentinel));

  if (cur->sentinel != SENTINEL)
    {
      if (cur->sentinel == DOUBLE_FREE)
	log ("free: double free\n");
      else
	log ("free: corrupt left sentinel\n");
      abort ();
    }
  cur->sentinel = DOUBLE_FREE;

  if (cur->previous)
    cur->previous->next = cur->next;
  if (cur->next)
    cur->next->previous = cur->previous;
  if (previous == cur)
    previous = cur->previous;

  size_t old_size = cur->size;
  if (old_size == -1)
    {
      log ("double free");
      abort ();
    }

  if (((uint64_t *) (ptr + old_size))[0] != SENTINEL)
    {
      log ("free: corrupt rigth sentinel\n");
      abort ();
    }

  return_bucket (((char *) cur), old_size + sizeof (left_sentinel) + 8);
  if (pthread_mutex_unlock (&mutex))
    abort ();
}

struct leak
{
  void *origin[TRACE];
  size_t size;
};

static void __attribute__ ((destructor))
report_leaks ()
{
  size_t i, j, origins = 0;
  left_sentinel *s = previous;
  while (s)
    {
      origins++;
      s = s->previous;
    }

  /* TODO sort */
  struct leak *leaks = (struct leak *) wrap_mmap ((origins + 1) * sizeof (struct leak));
  s = previous;
  for (i = 0; i < origins; i++)
    {
      for (j = 0; j < TRACE; j++)
	leaks[i].origin[j] = previous->origin[j];
      leaks[i].size = previous->size;
    }

  for (i = 0; i < origins; i++)
    {
      Dl_info info;
      if (leaks[i].origin[0] != leaks[i + 1].origin[0])
	{
	  dladdr (leaks[i].origin[0], &info);
	  log ("Leak leak of size %i\n", leaks[i].size);
	  for (j = 0; j < TRACE; j++)
	    {
	      dladdr (leaks[i].origin[j], &info);
	      log ("...  %s:%lx\t(%s)\n", info.dli_fname, info.dli_saddr, info.dli_sname);
	    }
	}
    }
}


More information about the Libc-alpha mailing list