Hi everyone,

while working on Lightspark (an open source flash player implementation)
I needed a way to keep track of all allocated objects. I though about
integrating with jemalloc and I have wrote a few patches that makes it
possible to iterate over all the regions allocated in a specified arena.

Please note that the patches do not solve the whole problem of iterating
over every object allocated by jemalloc, but only the smaller one of
iterating over objects allocated in an arena. tcache and huge regions
are not handled. Despite the limitations I think this is sufficient for
my purposes and I would like to share my work, hoping it might be
included upstream. This patch set, although limited, may be the base for
a full blown solution.

The last patch includes a simple test for the functionality. Still I
don't know how to integrate it in the build system since it requires
access to internal APIs only available in the statically linked version
of the library, while other test cases seems to link to the dynamic
version of it.

Regards,
Alessandro Pignotti

>From 7ce5b58aab20c95a852a9a2d501937a3ca040228 Mon Sep 17 00:00:00 2001
From: Alessandro Pignotti <a.pigno...@sssup.it>
Date: Sun, 19 Aug 2012 17:51:58 +0200
Subject: [PATCH 1/4] Add a configuration option to enable iteration over
 allocated regions

---
 configure.ac                        |   19 +++++++++++++++++++
 include/jemalloc/jemalloc_defs.h.in |    6 ++++++
 2 files changed, 25 insertions(+)

diff --git a/configure.ac b/configure.ac
index a72019e..e8cf1c3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1023,6 +1023,24 @@ if test "x$enable_lazy_lock" = "x1" ; then
 fi
 AC_SUBST([enable_lazy_lock])
 
+dnl Disable iteration over regions by default.
+AC_ARG_ENABLE([iterate-regions],
+  [AS_HELP_STRING([--enable-iterate-regions],
+  [Enable iteration over allocated regions])],
+[if test "x$enable_iterate_regions" = "xyes" ; then
+  enable_iterate_regions="1"
+else
+  enable_iterate_regions="0"
+fi
+],
+[enable_iterate_regions="0"]
+)
+
+if test "x$enable_iterate_regions" = "x1" ; then
+  AC_DEFINE([JEMALLOC_ITERATE_REGIONS], [ ])
+fi
+AC_SUBST([enable_iterate_regions])
+
 AC_ARG_ENABLE([tls],
   [AS_HELP_STRING([--disable-tls], [Disable thread-local storage (__thread keyword)])],
 if test "x$enable_tls" = "xno" ; then
@@ -1283,4 +1301,5 @@ AC_MSG_RESULT([munmap             : ${enable_munmap}])
 AC_MSG_RESULT([dss                : ${enable_dss}])
 AC_MSG_RESULT([lazy_lock          : ${enable_lazy_lock}])
 AC_MSG_RESULT([tls                : ${enable_tls}])
+AC_MSG_RESULT([iterate-regions    : ${enable_iterate_regions}])
 AC_MSG_RESULT([===============================================================================])
diff --git a/include/jemalloc/jemalloc_defs.h.in b/include/jemalloc/jemalloc_defs.h.in
index c469142..9a37551 100644
--- a/include/jemalloc/jemalloc_defs.h.in
+++ b/include/jemalloc/jemalloc_defs.h.in
@@ -101,6 +101,12 @@
  */
 #undef JEMALLOC_MUTEX_INIT_CB
 
+/*
+ * Defined if the data structures and methods to iterate over all the
+ * allocated regions should be enabled
+ */
+#undef JEMALLOC_ITERATE_REGIONS
+
 /* Defined if __attribute__((...)) syntax is supported. */
 #undef JEMALLOC_HAVE_ATTR
 #ifdef JEMALLOC_HAVE_ATTR
-- 
1.7.9.5

>From c50e8dd2ecab8639c5e9dc78a41b0a959d4aaefd Mon Sep 17 00:00:00 2001
From: Alessandro Pignotti <a.pigno...@sssup.it>
Date: Sun, 19 Aug 2012 17:52:55 +0200
Subject: [PATCH 2/4] Keep track of all allocated chunks

---
 include/jemalloc/internal/arena.h |   10 ++++++++++
 src/arena.c                       |   12 ++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/include/jemalloc/internal/arena.h b/include/jemalloc/internal/arena.h
index 0b0f640..1f67e27 100644
--- a/include/jemalloc/internal/arena.h
+++ b/include/jemalloc/internal/arena.h
@@ -167,6 +167,11 @@ struct arena_chunk_s {
 	/* Linkage for the arena's chunks_dirty list. */
 	ql_elm(arena_chunk_t) link_dirty;
 
+#  ifdef JEMALLOC_ITERATE_REGIONS
+	/* Linkage for the arena's chuks list. */
+	ql_elm(arena_chunk_t)  link_chunks;
+#  endif
+
 	/*
 	 * True if the chunk is currently in the chunks_dirty list, due to
 	 * having at some point contained one or more dirty pages.  Removal
@@ -334,6 +339,11 @@ struct arena_s {
 	/* List of dirty-page-containing chunks this arena manages. */
 	ql_head(arena_chunk_t)	chunks_dirty;
 
+#  ifdef JEMALLOC_ITERATE_REGIONS
+	/* List of all chunks this arena manages. */
+	ql_head(arena_chunk_t)  chunks;
+#  endif
+
 	/*
 	 * In order to avoid rapid chunk allocation/deallocation when an arena
 	 * oscillates right on the cusp of needing a new chunk, cache the most
diff --git a/src/arena.c b/src/arena.c
index 2a6150f..9e7e2c4 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -381,6 +381,7 @@ arena_chunk_alloc(arena_t *arena)
 
 		chunk->arena = arena;
 		ql_elm_new(chunk, link_dirty);
+		ql_elm_new(chunk, link_chunks);
 		chunk->dirtied = false;
 
 		/*
@@ -418,6 +419,10 @@ arena_chunk_alloc(arena_t *arena)
 		    arena_mapp_get(chunk, map_bias));
 	}
 
+#ifdef JEMALLOC_ITERATE_REGIONS
+	ql_tail_insert(&arena->chunks, chunk, link_chunks);
+#endif
+
 	return (chunk);
 }
 
@@ -445,6 +450,10 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)
 		runs_avail = &arena->runs_avail_dirty;
 	arena_avail_tree_remove(runs_avail, arena_mapp_get(chunk, map_bias));
 
+#ifdef JEMALLOC_ITERATE_REGIONS
+	ql_remove(&arena->chunks, chunk, link_chunks);
+#endif
+
 	if (arena->spare != NULL) {
 		arena_chunk_t *spare = arena->spare;
 
@@ -1970,6 +1979,9 @@ arena_new(arena_t *arena, unsigned ind)
 
 	/* Initialize chunks. */
 	ql_new(&arena->chunks_dirty);
+#ifdef JEMALLOC_ITERATE_REGIONS
+	ql_new(&arena->chunks);
+#endif
 	arena->spare = NULL;
 
 	arena->nactive = 0;
-- 
1.7.9.5

>From c864e4bf6099c51088df412de70a951c939ef5a5 Mon Sep 17 00:00:00 2001
From: Alessandro Pignotti <a.pigno...@sssup.it>
Date: Mon, 20 Aug 2012 13:26:49 +0200
Subject: [PATCH 3/4] Add a method to iterate over objects allocated in an
 arena

---
 include/jemalloc/internal/arena.h |    1 +
 src/arena.c                       |   55 +++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/include/jemalloc/internal/arena.h b/include/jemalloc/internal/arena.h
index 1f67e27..aae9c6f 100644
--- a/include/jemalloc/internal/arena.h
+++ b/include/jemalloc/internal/arena.h
@@ -444,6 +444,7 @@ void	arena_boot(void);
 void	arena_prefork(arena_t *arena);
 void	arena_postfork_parent(arena_t *arena);
 void	arena_postfork_child(arena_t *arena);
+void	arena_iterate_regions(arena_t *arena, void (*callback)(void*, void*), void* arg);
 
 #endif /* JEMALLOC_H_EXTERNS */
 /******************************************************************************/
diff --git a/src/arena.c b/src/arena.c
index 9e7e2c4..421b1d3 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -2231,3 +2231,58 @@ arena_postfork_child(arena_t *arena)
 		malloc_mutex_postfork_child(&arena->bins[i].lock);
 	malloc_mutex_postfork_child(&arena->lock);
 }
+
+void
+arena_iterate_regions(arena_t *arena, void (*callback)(void*, void*), void* arg)
+{
+	arena_chunk_t* chunk;
+	ql_foreach(chunk, &arena->chunks, link_chunks)
+	{
+		int i;
+		size_t region_interval;
+		bitmap_t* bitmap;
+		bitmap_info_t* bitmapinfo;
+		uintptr_t regions;
+		int bitmap_index = 0;
+		/* Iterate over each page of the chunk, skipping the header ones */
+		for(i=0;i<(chunk_npages-map_bias);i++)
+		{
+			uintptr_t page_start;
+			if(!(chunk->map[i].bits&CHUNK_MAP_ALLOCATED))
+				continue;
+			page_start = ((uintptr_t)chunk)+((i+map_bias) << LG_PAGE);
+			if(chunk->map[i].bits&CHUNK_MAP_LARGE)
+			{
+				size_t run_size = chunk->map[i].bits & 0xfffff000;
+				callback(arg, page_start);
+				/* Skip the needed amount of pages */
+				i+=(run_size >> LG_PAGE)-1;
+			}
+			else
+			{
+				uintptr_t page_end = ((uintptr_t)chunk)+((i+map_bias+1) << LG_PAGE);
+				size_t page_offset = (chunk->map[i].bits >> 12);
+				if(page_offset == 0)
+				{
+					/* Get new run data */
+					arena_bin_info_t* bininfo;
+					size_t binind = (chunk->map[i].bits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT;
+					bininfo = &arena_bin_info[binind];
+					region_interval = bininfo->reg_interval;
+					regions = page_start + bininfo->reg0_offset;
+					bitmap = page_start + bininfo->bitmap_offset;
+					bitmapinfo = &bininfo->bitmap_info;
+					bitmap_index = 0;
+				}
+				for(;regions < page_end;regions+=region_interval)
+				{
+					if(bitmap_get(bitmap,bitmapinfo,bitmap_index))
+					{
+						callback(arg, (void*)regions);
+					}
+					bitmap_index++;
+				}
+			}
+		}
+	}
+}
-- 
1.7.9.5

>From bdfc7bd6a435fd35abed9b4279792f1da69c603f Mon Sep 17 00:00:00 2001
From: Alessandro Pignotti <a.pigno...@sssup.it>
Date: Mon, 20 Aug 2012 13:29:08 +0200
Subject: [PATCH 4/4] Add test for iterate regions

---
 test/iterate_regions.c   |  104 ++++++++++++++++++++++++++++++++++++++++++++++
 test/iterate_regions.exp |    4 ++
 2 files changed, 108 insertions(+)
 create mode 100644 test/iterate_regions.c
 create mode 100644 test/iterate_regions.exp

diff --git a/test/iterate_regions.c b/test/iterate_regions.c
new file mode 100644
index 0000000..4c8d63b
--- /dev/null
+++ b/test/iterate_regions.c
@@ -0,0 +1,104 @@
+#define	JEMALLOC_MANGLE
+#include "jemalloc_test.h"
+#include "jemalloc/internal/jemalloc_internal.h"
+
+struct allocations
+{
+	void** objs;
+	int obj_count;
+};
+
+static void iterate_callback(void* arg, void* obj)
+{
+	int i;
+	struct allocations* allocs = (struct allocations*)arg;
+	/* Find the object in the allocations */
+	for(i=0;i<allocs->obj_count;i++)
+	{
+		if(allocs->objs[i]==obj)
+		{
+			allocs->objs[i]=NULL;
+			return;
+		}
+	}
+	/* The object was not found. This is an error. */
+	malloc_printf("Unexpected object found at %p\n",obj);
+}
+
+static bool validate_allocations(struct allocations* allocs)
+{
+	int i;
+	for(i=0;i<allocs->obj_count;i++)
+	{
+		if(allocs->objs[i]!=NULL)
+			malloc_printf("Expected object at %p was not found\n",allocs->objs[i]);
+	}
+}
+
+static int allocate_small_objects(arena_t* arena)
+{
+	int i;
+	void* objs[10];
+	void* objs_copy[10];
+	struct allocations allocs;
+	malloc_printf("Allocate small objects\n");
+	memset(objs,0,10*sizeof(void*));
+
+	allocs.objs = objs;
+	allocs.obj_count = 10;
+	for(i=0;i<10;i++)
+	{
+		uint32_t size=rand()%4096;
+		objs[i]=arena_malloc(arena,size,true,false);
+	}
+	memcpy(objs_copy,objs,sizeof(void*)*10);
+	arena_iterate_regions(arena,iterate_callback,&allocs);
+	validate_allocations(&allocs);
+	for(i=0;i<10;i++)
+		arena_dalloc(arena, CHUNK_ADDR2BASE(objs_copy[i]), objs_copy[i], false);
+}
+
+static int allocate_large_objects(arena_t* arena)
+{
+	int i;
+	void* objs[10];
+	void* objs_copy[10];
+	struct allocations allocs;
+	malloc_printf("Allocate large objects\n");
+	memset(objs,0,10*sizeof(void*));
+
+	allocs.objs = objs;
+	allocs.obj_count = 10;
+	for(i=0;i<10;i++)
+	{
+		uint32_t size=(rand()+4096)%65536;
+		objs[i]=arena_malloc(arena,size,true,false);
+	}
+	memcpy(objs_copy,objs,sizeof(void*)*10);
+	arena_iterate_regions(arena,iterate_callback,&allocs);
+	validate_allocations(&allocs);
+	for(i=0;i<10;i++)
+		arena_dalloc(arena, CHUNK_ADDR2BASE(objs_copy[i]), objs_copy[i], false);
+}
+
+int
+main(void)
+{
+	unsigned arena_ind;
+	size_t size;
+	int err;
+	arena_t* arena;
+
+	malloc_printf("Test begin\n");
+	/* Get the arena */
+	size = sizeof(arena_ind);
+	if ((err = mallctl("thread.arena", &arena_ind, &size, NULL, 0))) {
+		malloc_printf("%s(): Error in mallctl(): %s\n", __func__,
+		    strerror(err));
+		return 1;
+	}
+	arena = arenas[arena_ind];
+	allocate_small_objects(arena);
+	allocate_large_objects(arena);
+	malloc_printf("Test end\n");
+}
diff --git a/test/iterate_regions.exp b/test/iterate_regions.exp
new file mode 100644
index 0000000..f2ca216
--- /dev/null
+++ b/test/iterate_regions.exp
@@ -0,0 +1,4 @@
+Test begin
+Allocate small objects
+Allocate large objects
+Test end
-- 
1.7.9.5

_______________________________________________
jemalloc-discuss mailing list
jemalloc-discuss@canonware.com
http://www.canonware.com/mailman/listinfo/jemalloc-discuss

Reply via email to