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