X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fmono-codeman.c;h=fcef18e0f7dcddf886182188ce19566072cb2976;hb=090369e9295c411439a5330d122b3be5125ca719;hp=c8eb4c7818b55e60319d08f64360d0775b5ce220;hpb=53e266903ec6b2d822cf5b0c566f6374df5307a4;p=mono.git diff --git a/mono/utils/mono-codeman.c b/mono/utils/mono-codeman.c index c8eb4c7818b..fcef18e0f7d 100644 --- a/mono/utils/mono-codeman.c +++ b/mono/utils/mono-codeman.c @@ -1,25 +1,32 @@ #include "config.h" + +#ifdef HAVE_UNISTD_H #include +#endif #include #include #include #include -#ifdef PLATFORM_WIN32 -#include -#include -#else -#include -#include -#include -#include -#endif +/* For dlmalloc.h */ +#define USE_DL_PREFIX 1 #include "mono-codeman.h" - -#ifdef PLATFORM_WIN32 -#define FORCE_MALLOC +#include "mono-mmap.h" +#include "dlmalloc.h" +#include +#include +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include #endif + +/* + * AMD64 processors maintain icache coherency only for pages which are + * marked executable. Also, windows DEP requires us to obtain executable memory from + * malloc when using dynamic code managers. The system malloc can't do this so we use a + * slighly modified version of Doug Lea's Malloc package for this purpose: + * http://g.oswego.edu/dl/html/malloc.html + */ #define MIN_PAGES 16 @@ -37,20 +44,14 @@ #define MAX_WASTAGE 32 #define MIN_BSIZE 32 -#ifndef MAP_ANONYMOUS -#ifdef MAP_ANON -#define MAP_ANONYMOUS MAP_ANON -#else -#define FORCE_MALLOC -#endif -#endif - #ifdef __x86_64__ -#define ARCH_MAP_FLAGS MAP_32BIT +#define ARCH_MAP_FLAGS MONO_MMAP_32BIT #else #define ARCH_MAP_FLAGS 0 #endif +#define MONO_PROT_RWX (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC) + typedef struct _CodeChunck CodeChunk; enum { @@ -70,10 +71,24 @@ struct _CodeChunck { struct _MonoCodeManager { int dynamic; + int read_only; CodeChunk *current; CodeChunk *full; }; +#define ALIGN_INT(val,alignment) (((val) + (alignment - 1)) & ~(alignment - 1)) + +/** + * mono_code_manager_new: + * + * Creates a new code manager. A code manager can be used to allocate memory + * suitable for storing native code that can be later executed. + * A code manager allocates memory from the operating system in large chunks + * (typically 64KB in size) so that many methods can be allocated inside them + * close together, improving cache locality. + * + * Returns: the new code manager + */ MonoCodeManager* mono_code_manager_new (void) { @@ -83,9 +98,19 @@ mono_code_manager_new (void) cman->current = NULL; cman->full = NULL; cman->dynamic = 0; + cman->read_only = 0; return cman; } +/** + * mono_code_manager_new_dynamic: + * + * Creates a new code manager suitable for holding native code that can be + * used for single or small methods that need to be deallocated independently + * of other native code. + * + * Returns: the new code manager + */ MonoCodeManager* mono_code_manager_new_dynamic (void) { @@ -99,20 +124,36 @@ static void free_chunklist (CodeChunk *chunk) { CodeChunk *dead; + +#if defined(HAVE_VALGRIND_MEMCHECK_H) && defined (VALGRIND_JIT_UNREGISTER_MAP) + int valgrind_unregister = 0; + if (RUNNING_ON_VALGRIND) + valgrind_unregister = 1; +#define valgrind_unregister(x) do { if (valgrind_unregister) { VALGRIND_JIT_UNREGISTER_MAP(NULL,x); } } while (0) +#else +#define valgrind_unregister(x) +#endif + for (; chunk; ) { dead = chunk; + mono_profiler_code_chunk_destroy ((gpointer) dead->data); chunk = chunk->next; if (dead->flags == CODE_FLAG_MMAP) { -#ifndef FORCE_MALLOC - munmap (dead->data, dead->size); -#endif + mono_vfree (dead->data, dead->size); + /* valgrind_unregister(dead->data); */ } else if (dead->flags == CODE_FLAG_MALLOC) { - free (dead->data); + dlfree (dead->data); } free (dead); } } +/** + * mono_code_manager_destroy: + * @cman: a code manager + * + * Free all the memory associated with the code manager @cman. + */ void mono_code_manager_destroy (MonoCodeManager *cman) { @@ -121,7 +162,14 @@ mono_code_manager_destroy (MonoCodeManager *cman) free (cman); } -/* fill all the memory with the 0x2a (42) value */ +/** + * mono_code_manager_invalidate: + * @cman: a code manager + * + * Fill all the memory with an invalid native code value + * so that any attempt to execute code allocated in the code + * manager @cman will fail. This is used for debugging purposes. + */ void mono_code_manager_invalidate (MonoCodeManager *cman) { @@ -139,6 +187,27 @@ mono_code_manager_invalidate (MonoCodeManager *cman) memset (chunk->data, fill_value, chunk->size); } +/** + * mono_code_manager_set_read_only: + * @cman: a code manager + * + * Make the code manager read only, so further allocation requests cause an assert. + */ +void +mono_code_manager_set_read_only (MonoCodeManager *cman) +{ + cman->read_only = TRUE; +} + +/** + * mono_code_manager_foreach: + * @cman: a code manager + * @func: a callback function pointer + * @user_data: additional data to pass to @func + * + * Invokes the callback @func for each different chunk of memory allocated + * in the code manager @cman. + */ void mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void *user_data) { @@ -153,18 +222,6 @@ mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void } } -static int -query_pagesize (void) -{ -#ifdef PLATFORM_WIN32 - SYSTEM_INFO info; - GetSystemInfo (&info); - return info.dwAllocationGranularity; -#else - return getpagesize (); -#endif -} - /* BIND_ROOM is the divisor for the chunck of code size dedicated * to binding branches (branches not reachable with the immediate displacement) * bind_size = size/BIND_ROOM; @@ -180,9 +237,9 @@ query_pagesize (void) static CodeChunk* new_codechunk (int dynamic, int size) { - static int pagesize = 0; int minsize, flags = CODE_FLAG_MMAP; int chunk_size, bsize = 0; + int pagesize; CodeChunk *chunk; void *ptr; @@ -190,14 +247,12 @@ new_codechunk (int dynamic, int size) flags = CODE_FLAG_MALLOC; #endif - if (!pagesize) - pagesize = query_pagesize (); + pagesize = mono_pagesize (); if (dynamic) { chunk_size = size; flags = CODE_FLAG_MALLOC; - } - else { + } else { minsize = pagesize * MIN_PAGES; if (size < minsize) chunk_size = minsize; @@ -220,68 +275,29 @@ new_codechunk (int dynamic, int size) } #endif - /* does it make sense to use the mmap-like API? */ if (flags == CODE_FLAG_MALLOC) { - ptr = malloc (chunk_size); + ptr = dlmemalign (MIN_ALIGN, chunk_size + MIN_ALIGN - 1); + if (!ptr) + return NULL; + } else { + ptr = mono_valloc (NULL, chunk_size, MONO_PROT_RWX | ARCH_MAP_FLAGS); if (!ptr) return NULL; - - } - else { -#ifndef FORCE_MALLOC - ptr = mmap (0, chunk_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|ARCH_MAP_FLAGS, -1, 0); - if (ptr == (void*)-1) { - int fd = open ("/dev/zero", O_RDONLY); - if (fd != -1) { - ptr = mmap (0, chunk_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|ARCH_MAP_FLAGS, fd, 0); - close (fd); - } - if (ptr == (void*)-1) { - ptr = malloc (chunk_size); - if (!ptr) - return NULL; - flags = CODE_FLAG_MALLOC; - } - } -#else - return NULL; -#endif } if (flags == CODE_FLAG_MALLOC) { - /* - * AMD64 processors maintain icache coherency only for pages which are - * marked executable. - */ -#ifndef PLATFORM_WIN32 - { - char *page_start = (char *) (((gssize) (ptr)) & ~ (pagesize - 1)); - int pages = ((char*)ptr + chunk_size - page_start + pagesize - 1) / pagesize; - int err = mprotect (page_start, pages * pagesize, PROT_READ | PROT_WRITE | PROT_EXEC); - assert (!err); - } -#else - { - DWORD oldp; - int err = VirtualProtect (ptr, chunk_size, PAGE_EXECUTE_READWRITE, &oldp); - assert (err); - } -#endif - #ifdef BIND_ROOM - /* Make sure the thunks area is zeroed */ - memset (ptr, 0, bsize); + /* Make sure the thunks area is zeroed */ + memset (ptr, 0, bsize); #endif } chunk = malloc (sizeof (CodeChunk)); if (!chunk) { if (flags == CODE_FLAG_MALLOC) - free (ptr); -#ifndef FORCE_MALLOC + dlfree (ptr); else - munmap (ptr, chunk_size); -#endif + mono_vfree (ptr, chunk_size); return NULL; } chunk->next = NULL; @@ -290,19 +306,39 @@ new_codechunk (int dynamic, int size) chunk->flags = flags; chunk->pos = bsize; chunk->bsize = bsize; + mono_profiler_code_chunk_new((gpointer) chunk->data, chunk->size); /*printf ("code chunk at: %p\n", ptr);*/ return chunk; } +/** + * mono_code_manager_reserve: + * @cman: a code manager + * @size: size of memory to allocate + * @alignment: power of two alignment value + * + * Allocates at least @size bytes of memory inside the code manager @cman. + * + * Returns: the pointer to the allocated memory or #NULL on failure + */ void* -mono_code_manager_reserve (MonoCodeManager *cman, int size) +mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment) { CodeChunk *chunk, *prev; void *ptr; - - size += MIN_ALIGN; - size &= ~ (MIN_ALIGN - 1); + + g_assert (!cman->read_only); + + /* eventually allow bigger alignments, but we need to fix the dynamic alloc code to + * handle this before + */ + g_assert (alignment <= MIN_ALIGN); + + if (cman->dynamic) { + ++mono_stats.dynamic_code_alloc_count; + mono_stats.dynamic_code_bytes_count += size; + } if (!cman->current) { cman->current = new_codechunk (cman->dynamic, size); @@ -311,7 +347,8 @@ mono_code_manager_reserve (MonoCodeManager *cman, int size) } for (chunk = cman->current; chunk; chunk = chunk->next) { - if (chunk->pos + size <= chunk->size) { + if (ALIGN_INT (chunk->pos, alignment) + size <= chunk->size) { + chunk->pos = ALIGN_INT (chunk->pos, alignment); ptr = chunk->data + chunk->pos; chunk->pos += size; return ptr; @@ -339,25 +376,75 @@ mono_code_manager_reserve (MonoCodeManager *cman, int size) return NULL; chunk->next = cman->current; cman->current = chunk; + chunk->pos = ALIGN_INT (chunk->pos, alignment); ptr = chunk->data + chunk->pos; chunk->pos += size; return ptr; } -/* - * if we reserved too much room for a method and we didn't allocate - * already from the code manager, we can get back the excess allocation. +/** + * mono_code_manager_reserve: + * @cman: a code manager + * @size: size of memory to allocate + * + * Allocates at least @size bytes of memory inside the code manager @cman. + * + * Returns: the pointer to the allocated memory or #NULL on failure + */ +void* +mono_code_manager_reserve (MonoCodeManager *cman, int size) +{ + return mono_code_manager_reserve_align (cman, size, MIN_ALIGN); +} + +/** + * mono_code_manager_commit: + * @cman: a code manager + * @data: the pointer returned by mono_code_manager_reserve () + * @size: the size requested in the call to mono_code_manager_reserve () + * @newsize: the new size to reserve + * + * If we reserved too much room for a method and we didn't allocate + * already from the code manager, we can get back the excess allocation + * for later use in the code manager. */ void mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize) { - newsize += MIN_ALIGN; - newsize &= ~ (MIN_ALIGN - 1); - size += MIN_ALIGN; - size &= ~ (MIN_ALIGN - 1); + g_assert (newsize <= size); if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) { cman->current->pos -= size - newsize; } } +/** + * mono_code_manager_size: + * @cman: a code manager + * @used_size: pointer to an integer for the result + * + * This function can be used to get statistics about a code manager: + * the integer pointed to by @used_size will contain how much + * memory is actually used inside the code managed @cman. + * + * Returns: the amount of memory allocated in @cman + */ +int +mono_code_manager_size (MonoCodeManager *cman, int *used_size) +{ + CodeChunk *chunk; + guint32 size = 0; + guint32 used = 0; + for (chunk = cman->current; chunk; chunk = chunk->next) { + size += chunk->size; + used += chunk->pos; + } + for (chunk = cman->full; chunk; chunk = chunk->next) { + size += chunk->size; + used += chunk->pos; + } + if (used_size) + *used_size = used; + return size; +} +