[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