[Evolution-hackers] memory fragmentation profiler




I've added some features to the fragment profiler and made it ld_preloadab'e.  I've also made it much more efficient, so if you don't have it writing out images very often, it barely loads the application.

See attached.

The next step would probably be to have it write to a shared-memory file and use an external application to monitor it in realtime - have it monitor stack traces too, and it might be really useful and not just a fun toy.

/*
  Copyright (C) 2005 Novell Inc.

  Michael Zucchi <notzed novell com>

 A fragmentation mapper.

 compile with:
   $(CC) -o fmap.o fmap.c -shared -g -lpthread

 use with:

 FMAP_OPS="--timeout 5 --width 640 --heapsize 64 --big" LD_PRELOAD=fmap.o program

  --timeout - number of seconds between outputting a snapshot, default off
  --width - width of image (heigh matched to suit), default 640
  --heapsize - amount of memory to monitor, default is 1MB or 128MB in big mode
  --big - whether to use 'big' mode, in small mode, 1 pixel represents 16 bytes, in big mode, 1 pixel represents 256 bytes 

 Generates a file in format /tmp/fragment.nnn.pnm for each snapshot.
 i.e. do not run as root or on a multi-user system!

 The file is in portable grey map format.

*/

#include <malloc.h>
#include <stdlib.h>

#include <sys/mman.h>
#include <stdio.h>

#define USE_PTHREADS

#ifdef USE_PTHREADS
#include <pthread.h>
#endif

#define d(x)
     
/* Prototypes for our hooks.  */
static void my_init_hook (void);
static void *my_malloc_hook (size_t, const void *);
static void my_free_hook (void*, const void *);
     
/* Override initializing hook from the C library. */
void (*__malloc_initialize_hook) (void) = my_init_hook;
static void *old_malloc_hook;
static void *old_free_hook;

/* base is taken from the first malloc call, for glibc malloc, all (normal) mallocs happen contiguously after this */
static unsigned char *base = (void *)-1;

/* There are two modes, small, which stores 2 4-bit words in each byte
 * of the 'bitmap', corresponding to the number of allocated bytes in
 * each 16 byte block.  In the bitfield map we store 2 4-bit words in
 * each byte, each one contains a counter of how many used bytes
 * there are used in this block.  This means we store 32 bytes worth
 * of allocations per byte of our table.
 *
 * This is somewhat redundant since you can't allocate smaller than 8
 * byte blocks anyway (x86), but why split hairs (could use half the
 * profiling memory)
 *
 * The second mode, 'big', is where we store 1 byte for every 256
 * bytes, and store the count of used bytes in that block again.
 */

static int bytesabyte = 32;
static unsigned char *bitmap;
static size_t heapsize = 1024*1024;
static size_t bitmapsize = 0;
static int timeout = 0;
static int width = 640;

#ifdef USE_PTHREADS
static pthread_t dump_id;
static volatile stop = 0;
#endif

static void
my_init_hook (void)
{
	old_malloc_hook = __malloc_hook;
	old_free_hook = __free_hook;
	__malloc_hook = my_malloc_hook;
	__free_hook = my_free_hook;
}

#ifndef MIN
#define MIN(a,b) (a)<(b)?(a):(b)
#endif
#ifndef MAX
#define MAX(a,b) (a)>(b)?(a):(b)
#endif

static void
dump_bm(void)
{
	int height, i, j;
	FILE *out;
	unsigned char *p;
	char filename[64];
	static int id = 0;

	sprintf(filename, "/tmp/fragment.%03d.pnm", id++);
	fprintf(stderr, "writing '%s'\n", filename);

	out = fopen(filename, "w");
	if (out == NULL)
		return;

	p = bitmap;
	if (bytesabyte==32) {
		height = bitmapsize/(width/2);
		fprintf(out, "P5\n%d\n%d\n15\n", width, height);
		for (i=0;i<height;i++) {
			for (j=0;j<width/2;j++) {
				unsigned char val = *p++;

				fprintf(out, "%c%c", (val>>4)&0x0f, val&0x0f);
			}
		}
	} else {
		height = bitmapsize/width;
		fprintf(out, "P5\n%d\n%d\n255\n", width, height);
		for (i=0;i<height;i++) {
			for (j=0;j<width;j++) {
				unsigned char val = *p++;

				fprintf(out, "%c", val);
			}
		}
	}
	fclose(out);
}

#ifdef USE_PTHREADS
static void *
dump_thread(void *d)
{
	while (!stop) {
		sleep(timeout);
		dump_bm();
	}

	return NULL;
}
#endif

static void
dump_end(void)
{
#ifdef USE_PTHREADS
	if (timeout) {
		stop = 1;
		pthread_join(dump_id,NULL);
	}
#endif
	dump_bm();
}

static void
bitmap_init(void)
{
	char *op;
	static int init = 0;
	size_t heap = 0;

	if (init)
		return;

	init = 1;

	op = getenv("FMAP_OPTS");
	while (op && *op) {
		while (*op && *op != '-')
			op++;
		if (!strncmp(op, "--timeout ", 10) || !strncmp(op, "--timeout=", 10)) {
			timeout = MAX(atoi(op+10), 1);
			op += 10;
		} else if (!strncmp(op, "--heapsize ", 11) || !strncmp(op, "--heapsize=", 11)) {
			heap = MAX(atoi(op+11), 1);
			heap *= 1024 * 1024;
			op += 11;
		} else if (!strncmp(op, "--width ", 8) || !strncmp(op, "--width=", 8)) {
			width = MAX(atoi(op+8), 320);
			op += 8;
		} else if (!strncmp(op, "--big", 5)) {
			bytesabyte=256;
			heapsize = 1024*1024*64;
			op += 5;
		} else if (*op) {
			fprintf(stderr, "FMAP_OPTS Usage: [--timeout seconds] [--heapsize megabytes] [--width pixels] [--big]\n");
			abort();
		}
	}

	if (heap)
		heapsize = heap;

	if (timeout)
		fprintf(stderr, "Timeout: %d seconds\n", timeout);
	fprintf(stderr, "Monitored Heap Size: %d MB\n", heapsize/1024/1024);
	fprintf(stderr, "Image Width: %d\n", width);

	bitmapsize = (heapsize/bytesabyte);

	/* mmap our own memory rather than using malloc, so we don't interfere with malloc or the application */
	bitmap = mmap(NULL, bitmapsize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
	if (bitmap == (void *)-1) {
		fprintf(stderr, "Cannot map %d bytes anonymously\n", bitmapsize);
		abort();
	}

#ifdef USE_PTHREADS
	if (timeout)
		pthread_create(&dump_id, NULL, dump_thread, NULL);
#endif

	atexit(dump_end);
}

static void
mark32(unsigned char *start, size_t size, int mark)
{
	int off, i, bit, byte, n1, n2, len1, len2;
	unsigned char val;

	bitmap_init();

	if (base == (void *)-1) {
		base = start;
	} else if (((unsigned char *)start) < base) {
		d(printf("start lower than base %p < %p\n", start, base));
	}

	off = (((unsigned char *)start)-base);

	d(printf("%smarking %d bytes from %d\n", mark?"":"un", size, off));

	byte = off >> 5;
	if (byte >= bitmapsize)
		return;

	if ((byte << 5) == off)
		len1 = MIN(16, size);
	else
		len1 = MIN(off & 0x0f, size);
	if (len1 < size)
		len2 = MIN(16, size-len1);
	else
		len2 = 0;

	d(printf("first block %d and %d used\n", len1, len2));

	val = bitmap[byte];
	if (mark) {
		n1 = ((val & 0xf0) >> 4)+len1;
		if (n1>15)
			n1=15;
		n2 = (val & 0x0f)+len2;
		if (n2>15)
			n2=15;
	} else {
		n1 = ((val & 0xf0) >> 4)-len1;
		if (n1<0)
			n1=0;
		n2 = (val & 0x0f)-len2;
		if (n2<0)
			n2=0;
		d(printf("bitmap[%d] n1 = %d->%d, n2 = %d->%d\n", byte, (val&0xf0)>>4, n1, val&0x0f, n2));
	}
	bitmap[byte++] = (n1<<4) | n2;
	if (byte >= bitmapsize)
		return;

	size -= len1 + len2;
	if (size) {
		d(printf(" %d blocks full\n", size/32));
		val = mark?0xff:0x00;
		for (;size > 32 && byte<bitmapsize;size-=32)
			bitmap[byte++] = val;

		if (size && byte<bitmapsize) {
			len1 = MIN(16, size);
			if (len1 < size)
				len2 = MIN(16, size-len1);
			else
				len2 = 0;

			d(printf("last block %d and %d used\n", len1, len2));

			val = bitmap[byte];
			if (mark) {
				n1 = ((val & 0xf0) >> 4)+len1;
				if (n1>15)
					n1=15;
				n2 = (val & 0x0f)+len2;
				if (n2>15)
					n2=15;
			} else {
				n1 = ((val & 0xf0) >> 4)-len1;
				if (n1<0)
					n1=0;
				n2 = (val & 0x0f)-len2;
				if (n2<0)
					n2=0;
			}
			bitmap[byte++] = (n1<<4) | n2;
		}
	}
}

static void
mark256(unsigned char *start, size_t size, int mark)
{
	int off, i, bit, byte, len;
	unsigned int val;

	bitmap_init();

	if (base == (void *)-1) {
		base = start;
	} else if (((unsigned char *)start) < base) {
		d(printf("start lower than base %p < %p\n", start, base));
	}

	off = (((unsigned char *)start)-base);

	d(printf("%smarking %d bytes from %d\n", mark?"":"un", size, off));

	byte = off >> 8;
	if (byte >= bitmapsize)
		return;

	if ((byte << 8) == off)
		len = MIN(256, size);
	else
		len = MIN(off & 0xff, size);

	d(printf("first block %d and %d used\n", len1, len2));

	val = bitmap[byte];
	if (mark) {
		val += len;
		if (val > 255)
			val = 255;
	} else {
		val -= len;
		if (len<0)
			len=0;
	}
	bitmap[byte++] = val;
	if (byte >= bitmapsize)
		return;

	size -= len;
	if (size) {
		d(printf(" %d blocks full\n", size/256));
		val = mark?0xff:0x00;
		for (;size > 256 && byte<bitmapsize;size-=256)
			bitmap[byte++] = val;

		if (size && byte<bitmapsize) {
			len = MIN(256, size);

			val = bitmap[byte];
			if (mark) {
				val += len;
				if (val > 255)
					val = 255;
			} else {
				val -= len;
				if (val < 0)
					val = 0;
			}
			bitmap[byte++] = val;
		}
	}
}

static void *
my_malloc_hook (size_t size, const void *caller)
{
	void *result;
	/* Restore all old hooks */
	__malloc_hook = old_malloc_hook;
	__free_hook = old_free_hook;
	/* Call recursively */
	result = malloc (size);

	/* Save underlying hooks */
	old_malloc_hook = __malloc_hook;
	old_free_hook = __free_hook;

#if 0
	/* `printf' might call `malloc', so protect it too. */
	d(printf ("malloc (%u) returns %p\n", (unsigned int) size, result));
	/* Restore our own hooks */
#endif
	if (result != NULL) {
		if (bytesabyte==32)
			mark32(result, size, 1);
		else
			mark256(result, size, 1);
	}

	__malloc_hook = my_malloc_hook;
	__free_hook = my_free_hook;
	return result;
}
     
static void
my_free_hook (void *ptr, const void *caller)
{
	size_t size = 0;

	/* Restore all old hooks */
	__malloc_hook = old_malloc_hook;
	__free_hook = old_free_hook;

	if (ptr != NULL) {
		/* glibc malloc stores size immediately before allocated pointer.
		   It also stores up to 3(?) flag bits in the size, so we need to mask them out */
		size = ((size_t *)ptr)[-1] & ~(7);
	}

	/* Call recursively */
	free (ptr);
	/* Save underlying hooks */
	old_malloc_hook = __malloc_hook;
	old_free_hook = __free_hook;
	/* `printf' might call `free', so protect it too. */
	if (ptr != NULL) {
		if (bytesabyte==32)
			mark32(ptr, size, 0);
		else
			mark256(ptr, size, 0);
	}

	/*d(printf ("freed pointer %p\n", ptr));*/
	/* Restore our own hooks */
	__malloc_hook = my_malloc_hook;
	__free_hook = my_free_hook;
}

#if 0
int main(int argc, char **argv)
{
	void *mem[1024];
	int i, j;

	mem[0] = malloc(1);
	mem[1] = malloc(2);
	mem[2] = malloc(3);
	mem[3] = malloc(4);
	free(mem[0]);	free(mem[1]);	free(mem[2]);	free(mem[3]);

	mem[0] = malloc(107);
	free(mem[0]);

	for (i=0;i<1024;i++)
		mem[i] = NULL;

	for (i=0;i<1000000;i++) {
		j = random() % 1024;
		if (mem[j])
			free(mem[j]);
		mem[j] = malloc(random() & 1023);
	}

	dump_bm();
}
#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]