ba3ec4ea132a8e8acd7d9c4ab62f53ec0ac88c14
[mono.git] / mono / utils / mono-codeman.c
1 #include "config.h"
2
3 #ifdef HAVE_UNISTD_H
4 #include <unistd.h>
5 #endif
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <glib.h>
10
11 /* For dlmalloc.h */
12 #define USE_DL_PREFIX 1
13
14 #include "mono-codeman.h"
15 #include "mono-mmap.h"
16 #include "mono-counters.h"
17 #include "dlmalloc.h"
18 #include <mono/io-layer/io-layer.h>
19 #include <mono/metadata/profiler-private.h>
20 #ifdef HAVE_VALGRIND_MEMCHECK_H
21 #include <valgrind/memcheck.h>
22 #endif
23
24 #include <mono/utils/mono-os-mutex.h>
25
26
27 static uintptr_t code_memory_used = 0;
28 static size_t dynamic_code_alloc_count;
29 static size_t dynamic_code_bytes_count;
30 static size_t dynamic_code_frees_count;
31 static MonoCodeManagerCallbacks code_manager_callbacks;
32
33 /*
34  * AMD64 processors maintain icache coherency only for pages which are 
35  * marked executable. Also, windows DEP requires us to obtain executable memory from
36  * malloc when using dynamic code managers. The system malloc can't do this so we use a 
37  * slighly modified version of Doug Lea's Malloc package for this purpose:
38  * http://g.oswego.edu/dl/html/malloc.html
39  */
40
41 #define MIN_PAGES 16
42
43 #if defined(__ia64__) || defined(__x86_64__) || defined (_WIN64)
44 /*
45  * We require 16 byte alignment on amd64 so the fp literals embedded in the code are 
46  * properly aligned for SSE2.
47  */
48 #define MIN_ALIGN 16
49 #else
50 #define MIN_ALIGN 8
51 #endif
52
53 /* if a chunk has less than this amount of free space it's considered full */
54 #define MAX_WASTAGE 32
55 #define MIN_BSIZE 32
56
57 #ifdef __x86_64__
58 #define ARCH_MAP_FLAGS MONO_MMAP_32BIT
59 #else
60 #define ARCH_MAP_FLAGS 0
61 #endif
62
63 #define MONO_PROT_RWX (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC)
64
65 typedef struct _CodeChunck CodeChunk;
66
67 enum {
68         CODE_FLAG_MMAP,
69         CODE_FLAG_MALLOC
70 };
71
72 struct _CodeChunck {
73         char *data;
74         int pos;
75         int size;
76         CodeChunk *next;
77         unsigned int flags: 8;
78         /* this number of bytes is available to resolve addresses far in memory */
79         unsigned int bsize: 24;
80 };
81
82 struct _MonoCodeManager {
83         int dynamic;
84         int read_only;
85         CodeChunk *current;
86         CodeChunk *full;
87         CodeChunk *last;
88 };
89
90 #define ALIGN_INT(val,alignment) (((val) + (alignment - 1)) & ~(alignment - 1))
91
92 #define VALLOC_FREELIST_SIZE 16
93
94 static mono_mutex_t valloc_mutex;
95 static GHashTable *valloc_freelists;
96
97 static void*
98 codechunk_valloc (void *preferred, guint32 size)
99 {
100         void *ptr;
101         GSList *freelist;
102
103         if (!valloc_freelists) {
104                 mono_os_mutex_init_recursive (&valloc_mutex);
105                 valloc_freelists = g_hash_table_new (NULL, NULL);
106         }
107
108         /*
109          * Keep a small freelist of memory blocks to decrease pressure on the kernel memory subsystem to avoid #3321.
110          */
111         mono_os_mutex_lock (&valloc_mutex);
112         freelist = (GSList *) g_hash_table_lookup (valloc_freelists, GUINT_TO_POINTER (size));
113         if (freelist) {
114                 ptr = freelist->data;
115                 memset (ptr, 0, size);
116                 freelist = g_slist_delete_link (freelist, freelist);
117                 g_hash_table_insert (valloc_freelists, GUINT_TO_POINTER (size), freelist);
118         } else {
119                 ptr = mono_valloc (preferred, size, MONO_PROT_RWX | ARCH_MAP_FLAGS, MONO_MEM_ACCOUNT_CODE);
120                 if (!ptr && preferred)
121                         ptr = mono_valloc (NULL, size, MONO_PROT_RWX | ARCH_MAP_FLAGS, MONO_MEM_ACCOUNT_CODE);
122         }
123         mono_os_mutex_unlock (&valloc_mutex);
124         return ptr;
125 }
126
127 static void
128 codechunk_vfree (void *ptr, guint32 size)
129 {
130         GSList *freelist;
131
132         mono_os_mutex_lock (&valloc_mutex);
133         freelist = (GSList *) g_hash_table_lookup (valloc_freelists, GUINT_TO_POINTER (size));
134         if (!freelist || g_slist_length (freelist) < VALLOC_FREELIST_SIZE) {
135                 freelist = g_slist_prepend (freelist, ptr);
136                 g_hash_table_insert (valloc_freelists, GUINT_TO_POINTER (size), freelist);
137         } else {
138                 mono_vfree (ptr, size, MONO_MEM_ACCOUNT_CODE);
139         }
140         mono_os_mutex_unlock (&valloc_mutex);
141 }               
142
143 static void
144 codechunk_cleanup (void)
145 {
146         GHashTableIter iter;
147         gpointer key, value;
148
149         if (!valloc_freelists)
150                 return;
151         g_hash_table_iter_init (&iter, valloc_freelists);
152         while (g_hash_table_iter_next (&iter, &key, &value)) {
153                 GSList *freelist = (GSList *) value;
154                 GSList *l;
155
156                 for (l = freelist; l; l = l->next) {
157                         mono_vfree (l->data, GPOINTER_TO_UINT (key), MONO_MEM_ACCOUNT_CODE);
158                 }
159                 g_slist_free (freelist);
160         }
161         g_hash_table_destroy (valloc_freelists);
162 }
163
164 void
165 mono_code_manager_init (void)
166 {
167         mono_counters_register ("Dynamic code allocs", MONO_COUNTER_JIT | MONO_COUNTER_ULONG, &dynamic_code_alloc_count);
168         mono_counters_register ("Dynamic code bytes", MONO_COUNTER_JIT | MONO_COUNTER_ULONG, &dynamic_code_bytes_count);
169         mono_counters_register ("Dynamic code frees", MONO_COUNTER_JIT | MONO_COUNTER_ULONG, &dynamic_code_frees_count);
170 }
171
172 void
173 mono_code_manager_cleanup (void)
174 {
175         codechunk_cleanup ();
176 }
177
178 void
179 mono_code_manager_install_callbacks (MonoCodeManagerCallbacks* callbacks)
180 {
181         code_manager_callbacks = *callbacks;
182 }
183
184 /**
185  * mono_code_manager_new:
186  *
187  * Creates a new code manager. A code manager can be used to allocate memory
188  * suitable for storing native code that can be later executed.
189  * A code manager allocates memory from the operating system in large chunks
190  * (typically 64KB in size) so that many methods can be allocated inside them
191  * close together, improving cache locality.
192  *
193  * Returns: the new code manager
194  */
195 MonoCodeManager* 
196 mono_code_manager_new (void)
197 {
198         return (MonoCodeManager *) g_malloc0 (sizeof (MonoCodeManager));
199 }
200
201 /**
202  * mono_code_manager_new_dynamic:
203  *
204  * Creates a new code manager suitable for holding native code that can be
205  * used for single or small methods that need to be deallocated independently
206  * of other native code.
207  *
208  * Returns: the new code manager
209  */
210 MonoCodeManager* 
211 mono_code_manager_new_dynamic (void)
212 {
213         MonoCodeManager *cman = mono_code_manager_new ();
214         cman->dynamic = 1;
215         return cman;
216 }
217
218
219 static void
220 free_chunklist (CodeChunk *chunk)
221 {
222         CodeChunk *dead;
223         
224 #if defined(HAVE_VALGRIND_MEMCHECK_H) && defined (VALGRIND_JIT_UNREGISTER_MAP)
225         int valgrind_unregister = 0;
226         if (RUNNING_ON_VALGRIND)
227                 valgrind_unregister = 1;
228 #define valgrind_unregister(x) do { if (valgrind_unregister) { VALGRIND_JIT_UNREGISTER_MAP(NULL,x); } } while (0) 
229 #else
230 #define valgrind_unregister(x)
231 #endif
232
233         for (; chunk; ) {
234                 dead = chunk;
235                 mono_profiler_code_chunk_destroy ((gpointer) dead->data);
236                 if (code_manager_callbacks.chunk_destroy)
237                         code_manager_callbacks.chunk_destroy ((gpointer)dead->data);
238                 chunk = chunk->next;
239                 if (dead->flags == CODE_FLAG_MMAP) {
240                         codechunk_vfree (dead->data, dead->size);
241                         /* valgrind_unregister(dead->data); */
242                 } else if (dead->flags == CODE_FLAG_MALLOC) {
243                         dlfree (dead->data);
244                 }
245                 code_memory_used -= dead->size;
246                 g_free (dead);
247         }
248 }
249
250 /**
251  * mono_code_manager_destroy:
252  * @cman: a code manager
253  *
254  * Free all the memory associated with the code manager @cman.
255  */
256 void
257 mono_code_manager_destroy (MonoCodeManager *cman)
258 {
259         free_chunklist (cman->full);
260         free_chunklist (cman->current);
261         g_free (cman);
262 }
263
264 /**
265  * mono_code_manager_invalidate:
266  * @cman: a code manager
267  *
268  * Fill all the memory with an invalid native code value
269  * so that any attempt to execute code allocated in the code
270  * manager @cman will fail. This is used for debugging purposes.
271  */
272 void             
273 mono_code_manager_invalidate (MonoCodeManager *cman)
274 {
275         CodeChunk *chunk;
276
277 #if defined(__i386__) || defined(__x86_64__)
278         int fill_value = 0xcc; /* x86 break */
279 #else
280         int fill_value = 0x2a;
281 #endif
282
283         for (chunk = cman->current; chunk; chunk = chunk->next)
284                 memset (chunk->data, fill_value, chunk->size);
285         for (chunk = cman->full; chunk; chunk = chunk->next)
286                 memset (chunk->data, fill_value, chunk->size);
287 }
288
289 /**
290  * mono_code_manager_set_read_only:
291  * @cman: a code manager
292  *
293  * Make the code manager read only, so further allocation requests cause an assert.
294  */
295 void             
296 mono_code_manager_set_read_only (MonoCodeManager *cman)
297 {
298         cman->read_only = TRUE;
299 }
300
301 /**
302  * mono_code_manager_foreach:
303  * @cman: a code manager
304  * @func: a callback function pointer
305  * @user_data: additional data to pass to @func
306  *
307  * Invokes the callback @func for each different chunk of memory allocated
308  * in the code manager @cman.
309  */
310 void
311 mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void *user_data)
312 {
313         CodeChunk *chunk;
314         for (chunk = cman->current; chunk; chunk = chunk->next) {
315                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
316                         return;
317         }
318         for (chunk = cman->full; chunk; chunk = chunk->next) {
319                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
320                         return;
321         }
322 }
323
324 /* BIND_ROOM is the divisor for the chunck of code size dedicated
325  * to binding branches (branches not reachable with the immediate displacement)
326  * bind_size = size/BIND_ROOM;
327  * we should reduce it and make MIN_PAGES bigger for such systems
328  */
329 #if defined(__ppc__) || defined(__powerpc__)
330 #define BIND_ROOM 4
331 #endif
332 #if defined(TARGET_ARM64)
333 #define BIND_ROOM 4
334 #endif
335
336 static CodeChunk*
337 new_codechunk (CodeChunk *last, int dynamic, int size)
338 {
339         int minsize, flags = CODE_FLAG_MMAP;
340         int chunk_size, bsize = 0;
341         int pagesize, valloc_granule;
342         CodeChunk *chunk;
343         void *ptr;
344
345 #ifdef FORCE_MALLOC
346         flags = CODE_FLAG_MALLOC;
347 #endif
348
349         pagesize = mono_pagesize ();
350         valloc_granule = mono_valloc_granule ();
351
352         if (dynamic) {
353                 chunk_size = size;
354                 flags = CODE_FLAG_MALLOC;
355         } else {
356                 minsize = MAX (pagesize * MIN_PAGES, valloc_granule);
357                 if (size < minsize)
358                         chunk_size = minsize;
359                 else {
360                         /* Allocate MIN_ALIGN-1 more than we need so we can still */
361                         /* guarantee MIN_ALIGN alignment for individual allocs    */
362                         /* from mono_code_manager_reserve_align.                  */
363                         size += MIN_ALIGN - 1;
364                         size &= ~(MIN_ALIGN - 1);
365                         chunk_size = size;
366                         chunk_size += valloc_granule - 1;
367                         chunk_size &= ~ (valloc_granule - 1);
368                 }
369         }
370 #ifdef BIND_ROOM
371         if (dynamic)
372                 /* Reserve more space since there are no other chunks we might use if this one gets full */
373                 bsize = (chunk_size * 2) / BIND_ROOM;
374         else
375                 bsize = chunk_size / BIND_ROOM;
376         if (bsize < MIN_BSIZE)
377                 bsize = MIN_BSIZE;
378         bsize += MIN_ALIGN -1;
379         bsize &= ~ (MIN_ALIGN - 1);
380         if (chunk_size - size < bsize) {
381                 chunk_size = size + bsize;
382                 if (!dynamic) {
383                         chunk_size += valloc_granule - 1;
384                         chunk_size &= ~ (valloc_granule - 1);
385                 }
386         }
387 #endif
388
389         if (flags == CODE_FLAG_MALLOC) {
390                 ptr = dlmemalign (MIN_ALIGN, chunk_size + MIN_ALIGN - 1);
391                 if (!ptr)
392                         return NULL;
393         } else {
394                 /* Try to allocate code chunks next to each other to help the VM */
395                 ptr = NULL;
396                 if (last)
397                         ptr = codechunk_valloc ((guint8*)last->data + last->size, chunk_size);
398                 if (!ptr)
399                         ptr = codechunk_valloc (NULL, chunk_size);
400                 if (!ptr)
401                         return NULL;
402         }
403
404         if (flags == CODE_FLAG_MALLOC) {
405 #ifdef BIND_ROOM
406                 /* Make sure the thunks area is zeroed */
407                 memset (ptr, 0, bsize);
408 #endif
409         }
410
411         chunk = (CodeChunk *) g_malloc (sizeof (CodeChunk));
412         if (!chunk) {
413                 if (flags == CODE_FLAG_MALLOC)
414                         dlfree (ptr);
415                 else
416                         mono_vfree (ptr, chunk_size, MONO_MEM_ACCOUNT_CODE);
417                 return NULL;
418         }
419         chunk->next = NULL;
420         chunk->size = chunk_size;
421         chunk->data = (char *) ptr;
422         chunk->flags = flags;
423         chunk->pos = bsize;
424         chunk->bsize = bsize;
425         if (code_manager_callbacks.chunk_new)
426                 code_manager_callbacks.chunk_new ((gpointer)chunk->data, chunk->size);
427         mono_profiler_code_chunk_new((gpointer) chunk->data, chunk->size);
428
429         code_memory_used += chunk_size;
430         mono_runtime_resource_check_limit (MONO_RESOURCE_JIT_CODE, code_memory_used);
431         /*printf ("code chunk at: %p\n", ptr);*/
432         return chunk;
433 }
434
435 /**
436  * mono_code_manager_reserve:
437  * @cman: a code manager
438  * @size: size of memory to allocate
439  * @alignment: power of two alignment value
440  *
441  * Allocates at least @size bytes of memory inside the code manager @cman.
442  *
443  * Returns: the pointer to the allocated memory or #NULL on failure
444  */
445 void*
446 mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment)
447 {
448         CodeChunk *chunk, *prev;
449         void *ptr;
450         guint32 align_mask = alignment - 1;
451
452         g_assert (!cman->read_only);
453
454         /* eventually allow bigger alignments, but we need to fix the dynamic alloc code to
455          * handle this before
456          */
457         g_assert (alignment <= MIN_ALIGN);
458
459         if (cman->dynamic) {
460                 ++dynamic_code_alloc_count;
461                 dynamic_code_bytes_count += size;
462         }
463
464         if (!cman->current) {
465                 cman->current = new_codechunk (cman->last, cman->dynamic, size);
466                 if (!cman->current)
467                         return NULL;
468                 cman->last = cman->current;
469         }
470
471         for (chunk = cman->current; chunk; chunk = chunk->next) {
472                 if (ALIGN_INT (chunk->pos, alignment) + size <= chunk->size) {
473                         chunk->pos = ALIGN_INT (chunk->pos, alignment);
474                         /* Align the chunk->data we add to chunk->pos */
475                         /* or we can't guarantee proper alignment     */
476                         ptr = (void*)((((uintptr_t)chunk->data + align_mask) & ~(uintptr_t)align_mask) + chunk->pos);
477                         chunk->pos = ((char*)ptr - chunk->data) + size;
478                         return ptr;
479                 }
480         }
481         /* 
482          * no room found, move one filled chunk to cman->full 
483          * to keep cman->current from growing too much
484          */
485         prev = NULL;
486         for (chunk = cman->current; chunk; prev = chunk, chunk = chunk->next) {
487                 if (chunk->pos + MIN_ALIGN * 4 <= chunk->size)
488                         continue;
489                 if (prev) {
490                         prev->next = chunk->next;
491                 } else {
492                         cman->current = chunk->next;
493                 }
494                 chunk->next = cman->full;
495                 cman->full = chunk;
496                 break;
497         }
498         chunk = new_codechunk (cman->last, cman->dynamic, size);
499         if (!chunk)
500                 return NULL;
501         chunk->next = cman->current;
502         cman->current = chunk;
503         cman->last = cman->current;
504         chunk->pos = ALIGN_INT (chunk->pos, alignment);
505         /* Align the chunk->data we add to chunk->pos */
506         /* or we can't guarantee proper alignment     */
507         ptr = (void*)((((uintptr_t)chunk->data + align_mask) & ~(uintptr_t)align_mask) + chunk->pos);
508         chunk->pos = ((char*)ptr - chunk->data) + size;
509         return ptr;
510 }
511
512 /**
513  * mono_code_manager_reserve:
514  * @cman: a code manager
515  * @size: size of memory to allocate
516  *
517  * Allocates at least @size bytes of memory inside the code manager @cman.
518  *
519  * Returns: the pointer to the allocated memory or #NULL on failure
520  */
521 void*
522 mono_code_manager_reserve (MonoCodeManager *cman, int size)
523 {
524         return mono_code_manager_reserve_align (cman, size, MIN_ALIGN);
525 }
526
527 /**
528  * mono_code_manager_commit:
529  * @cman: a code manager
530  * @data: the pointer returned by mono_code_manager_reserve ()
531  * @size: the size requested in the call to mono_code_manager_reserve ()
532  * @newsize: the new size to reserve
533  *
534  * If we reserved too much room for a method and we didn't allocate
535  * already from the code manager, we can get back the excess allocation
536  * for later use in the code manager.
537  */
538 void
539 mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize)
540 {
541         g_assert (newsize <= size);
542
543         if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) {
544                 cman->current->pos -= size - newsize;
545         }
546 }
547
548 /**
549  * mono_code_manager_size:
550  * @cman: a code manager
551  * @used_size: pointer to an integer for the result
552  *
553  * This function can be used to get statistics about a code manager:
554  * the integer pointed to by @used_size will contain how much
555  * memory is actually used inside the code managed @cman.
556  *
557  * Returns: the amount of memory allocated in @cman
558  */
559 int
560 mono_code_manager_size (MonoCodeManager *cman, int *used_size)
561 {
562         CodeChunk *chunk;
563         guint32 size = 0;
564         guint32 used = 0;
565         for (chunk = cman->current; chunk; chunk = chunk->next) {
566                 size += chunk->size;
567                 used += chunk->pos;
568         }
569         for (chunk = cman->full; chunk; chunk = chunk->next) {
570                 size += chunk->size;
571                 used += chunk->pos;
572         }
573         if (used_size)
574                 *used_size = used;
575         return size;
576 }