Add a freelist around the code chunk allocator to avoid mmap failures when a lot...
[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/metadata/class-internals.h>
19 #include <mono/metadata/profiler-private.h>
20 #ifdef HAVE_VALGRIND_MEMCHECK_H
21 #include <valgrind/memcheck.h>
22 #endif
23
24 #if defined(__native_client_codegen__) && defined(__native_client__)
25 #include <malloc.h>
26 #include <nacl/nacl_dyncode.h>
27 #endif
28
29 static uintptr_t code_memory_used = 0;
30
31 /*
32  * AMD64 processors maintain icache coherency only for pages which are 
33  * marked executable. Also, windows DEP requires us to obtain executable memory from
34  * malloc when using dynamic code managers. The system malloc can't do this so we use a 
35  * slighly modified version of Doug Lea's Malloc package for this purpose:
36  * http://g.oswego.edu/dl/html/malloc.html
37  */
38
39 #define MIN_PAGES 16
40
41 #if defined(__ia64__) || defined(__x86_64__)
42 /*
43  * We require 16 byte alignment on amd64 so the fp literals embedded in the code are 
44  * properly aligned for SSE2.
45  */
46 #define MIN_ALIGN 16
47 #else
48 #define MIN_ALIGN 8
49 #endif
50 #ifdef __native_client_codegen__
51 /* For Google Native Client, all targets of indirect control flow need to    */
52 /* be aligned to a 32-byte boundary. MIN_ALIGN was updated to 32 to force    */
53 /* alignment for calls from tramp-x86.c to mono_global_codeman_reserve()     */
54 /* and mono_domain_code_reserve().                                           */
55 #undef MIN_ALIGN
56 #define MIN_ALIGN 32
57 #endif
58
59 /* if a chunk has less than this amount of free space it's considered full */
60 #define MAX_WASTAGE 32
61 #define MIN_BSIZE 32
62
63 #ifdef __x86_64__
64 #define ARCH_MAP_FLAGS MONO_MMAP_32BIT
65 #else
66 #define ARCH_MAP_FLAGS 0
67 #endif
68
69 #define MONO_PROT_RWX (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC)
70
71 typedef struct _CodeChunck CodeChunk;
72
73 enum {
74         CODE_FLAG_MMAP,
75         CODE_FLAG_MALLOC
76 };
77
78 struct _CodeChunck {
79         char *data;
80         int pos;
81         int size;
82         CodeChunk *next;
83         unsigned int flags: 8;
84         /* this number of bytes is available to resolve addresses far in memory */
85         unsigned int bsize: 24;
86 };
87
88 struct _MonoCodeManager {
89         int dynamic;
90         int read_only;
91         CodeChunk *current;
92         CodeChunk *full;
93 #if defined(__native_client_codegen__) && defined(__native_client__)
94         GHashTable *hash;
95 #endif
96 };
97
98 #define ALIGN_INT(val,alignment) (((val) + (alignment - 1)) & ~(alignment - 1))
99
100 #if defined(__native_client_codegen__) && defined(__native_client__)
101 /* End of text segment, set by linker. 
102  * Dynamic text starts on the next allocated page.
103  */
104 extern char etext[];
105 char *next_dynamic_code_addr = NULL;
106
107 /*
108  * This routine gets the next available bundle aligned
109  * pointer in the dynamic code section.  It does not check
110  * for the section end, this error will be caught in the
111  * service runtime.
112  */
113 void*
114 allocate_code(intptr_t increment)
115 {
116         char *addr;
117         if (increment < 0) return NULL;
118         increment = increment & kNaClBundleMask ? (increment & ~kNaClBundleMask) + kNaClBundleSize : increment;
119         addr = next_dynamic_code_addr;
120         next_dynamic_code_addr += increment;
121         return addr;
122 }
123
124 int
125 nacl_is_code_address (void *target)
126 {
127         return (char *)target < next_dynamic_code_addr;
128 }
129
130 const int kMaxPatchDepth = 32;
131 __thread unsigned char **patch_source_base = NULL;
132 __thread unsigned char **patch_dest_base = NULL;
133 __thread int *patch_alloc_size = NULL;
134 __thread int patch_current_depth = -1;
135 __thread int allow_target_modification = 1;
136
137 void
138 nacl_allow_target_modification (int val)
139 {
140         allow_target_modification = val;
141 }
142
143 static void
144 nacl_jit_check_init ()
145 {
146         if (patch_source_base == NULL) {
147                 patch_source_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
148                 patch_dest_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
149                 patch_alloc_size = g_malloc (kMaxPatchDepth * sizeof(int));
150         }
151 }
152
153
154 /* Given a patch target, modify the target such that patching will work when
155  * the code is copied to the data section.
156  */
157 void*
158 nacl_modify_patch_target (unsigned char *target)
159 {
160         /* This seems like a bit of an ugly way to do this but the advantage
161          * is we don't have to worry about all the conditions in
162          * mono_resolve_patch_target, and it can be used by all the bare uses
163          * of <arch>_patch.
164          */
165         unsigned char *sb;
166         unsigned char *db;
167
168         if (!allow_target_modification) return target;
169
170         nacl_jit_check_init ();
171         sb = patch_source_base[patch_current_depth];
172         db = patch_dest_base[patch_current_depth];
173
174         if (target >= sb && (target < sb + patch_alloc_size[patch_current_depth])) {
175                 /* Do nothing.  target is in the section being generated.
176                  * no need to modify, the disp will be the same either way.
177                  */
178         } else {
179                 int target_offset = target - db;
180                 target = sb + target_offset;
181         }
182         return target;
183 }
184
185 void*
186 nacl_inverse_modify_patch_target (unsigned char *target)
187 {
188         unsigned char *sb;
189         unsigned char *db;
190         int target_offset;
191
192         if (!allow_target_modification) return target;
193
194         nacl_jit_check_init ();
195         sb = patch_source_base[patch_current_depth];
196         db = patch_dest_base[patch_current_depth];
197
198         target_offset = target - sb;
199         target = db + target_offset;
200         return target;
201 }
202
203
204 #endif /* __native_client_codegen && __native_client__ */
205
206 #define VALLOC_FREELIST_SIZE 16
207
208 static CRITICAL_SECTION valloc_mutex;
209 static GHashTable *valloc_freelists;
210
211 static void*
212 codechunk_valloc (guint32 size)
213 {
214         void *ptr;
215         GSList *freelist;
216
217         if (!valloc_freelists) {
218                 InitializeCriticalSection (&valloc_mutex);
219                 valloc_freelists = g_hash_table_new (NULL, NULL);
220         }
221
222         /*
223          * Keep a small freelist of memory blocks to decrease pressure on the kernel memory subsystem to avoid #3321.
224          */
225         EnterCriticalSection (&valloc_mutex);
226         freelist = g_hash_table_lookup (valloc_freelists, GUINT_TO_POINTER (size));
227         if (freelist) {
228                 ptr = freelist->data;
229                 memset (ptr, 0, size);
230                 freelist = g_slist_remove_link (freelist, freelist);
231                 g_hash_table_insert (valloc_freelists, GUINT_TO_POINTER (size), freelist);
232         } else {
233                 ptr = mono_valloc (NULL, size + MIN_ALIGN - 1, MONO_PROT_RWX | ARCH_MAP_FLAGS);
234         }
235         LeaveCriticalSection (&valloc_mutex);
236         return ptr;
237 }
238
239 static void
240 codechunk_vfree (void *ptr, guint32 size)
241 {
242         GSList *freelist;
243
244         EnterCriticalSection (&valloc_mutex);
245         freelist = g_hash_table_lookup (valloc_freelists, GUINT_TO_POINTER (size));
246         if (!freelist || g_slist_length (freelist) < VALLOC_FREELIST_SIZE) {
247                 freelist = g_slist_prepend (freelist, ptr);
248                 g_hash_table_insert (valloc_freelists, GUINT_TO_POINTER (size), freelist);
249         } else {
250                 mono_vfree (ptr, size);
251         }
252         LeaveCriticalSection (&valloc_mutex);
253 }               
254
255 static void
256 codechunk_cleanup (void)
257 {
258         GHashTableIter iter;
259         gpointer key, value;
260
261         if (!valloc_freelists)
262                 return;
263         g_hash_table_iter_init (&iter, valloc_freelists);
264         while (g_hash_table_iter_next (&iter, &key, &value)) {
265                 GSList *freelist = value;
266                 GSList *l;
267
268                 for (l = freelist; l; l = l->next) {
269                         mono_vfree (l->data, GPOINTER_TO_UINT (key));
270                 }
271                 g_slist_free (freelist);
272         }
273         g_hash_table_destroy (valloc_freelists);
274 }
275
276 void
277 mono_code_manager_init (void)
278 {
279 }
280
281 void
282 mono_code_manager_cleanup (void)
283 {
284         codechunk_cleanup ();
285 }
286
287 /**
288  * mono_code_manager_new:
289  *
290  * Creates a new code manager. A code manager can be used to allocate memory
291  * suitable for storing native code that can be later executed.
292  * A code manager allocates memory from the operating system in large chunks
293  * (typically 64KB in size) so that many methods can be allocated inside them
294  * close together, improving cache locality.
295  *
296  * Returns: the new code manager
297  */
298 MonoCodeManager* 
299 mono_code_manager_new (void)
300 {
301         MonoCodeManager *cman = malloc (sizeof (MonoCodeManager));
302         if (!cman)
303                 return NULL;
304         cman->current = NULL;
305         cman->full = NULL;
306         cman->dynamic = 0;
307         cman->read_only = 0;
308 #if defined(__native_client_codegen__) && defined(__native_client__)
309         if (next_dynamic_code_addr == NULL) {
310                 const guint kPageMask = 0xFFFF; /* 64K pages */
311                 next_dynamic_code_addr = (uintptr_t)(etext + kPageMask) & ~kPageMask;
312 #if defined (__GLIBC__)
313                 /* TODO: For now, just jump 64MB ahead to avoid dynamic libraries. */
314                 next_dynamic_code_addr += (uintptr_t)0x4000000;
315 #else
316                 /* Workaround bug in service runtime, unable to allocate */
317                 /* from the first page in the dynamic code section.    */
318                 next_dynamic_code_addr += (uintptr_t)0x10000;
319 #endif
320         }
321         cman->hash =  g_hash_table_new (NULL, NULL);
322         if (patch_source_base == NULL) {
323                 patch_source_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
324                 patch_dest_base = g_malloc (kMaxPatchDepth * sizeof(unsigned char *));
325                 patch_alloc_size = g_malloc (kMaxPatchDepth * sizeof(int));
326         }
327 #endif
328         return cman;
329 }
330
331 /**
332  * mono_code_manager_new_dynamic:
333  *
334  * Creates a new code manager suitable for holding native code that can be
335  * used for single or small methods that need to be deallocated independently
336  * of other native code.
337  *
338  * Returns: the new code manager
339  */
340 MonoCodeManager* 
341 mono_code_manager_new_dynamic (void)
342 {
343         MonoCodeManager *cman = mono_code_manager_new ();
344         cman->dynamic = 1;
345         return cman;
346 }
347
348
349 static void
350 free_chunklist (CodeChunk *chunk)
351 {
352         CodeChunk *dead;
353         
354 #if defined(HAVE_VALGRIND_MEMCHECK_H) && defined (VALGRIND_JIT_UNREGISTER_MAP)
355         int valgrind_unregister = 0;
356         if (RUNNING_ON_VALGRIND)
357                 valgrind_unregister = 1;
358 #define valgrind_unregister(x) do { if (valgrind_unregister) { VALGRIND_JIT_UNREGISTER_MAP(NULL,x); } } while (0) 
359 #else
360 #define valgrind_unregister(x)
361 #endif
362
363         for (; chunk; ) {
364                 dead = chunk;
365                 mono_profiler_code_chunk_destroy ((gpointer) dead->data);
366                 chunk = chunk->next;
367                 if (dead->flags == CODE_FLAG_MMAP) {
368                         codechunk_vfree (dead->data, dead->size);
369                         /* valgrind_unregister(dead->data); */
370                 } else if (dead->flags == CODE_FLAG_MALLOC) {
371                         dlfree (dead->data);
372                 }
373                 code_memory_used -= dead->size;
374                 free (dead);
375         }
376 }
377
378 /**
379  * mono_code_manager_destroy:
380  * @cman: a code manager
381  *
382  * Free all the memory associated with the code manager @cman.
383  */
384 void
385 mono_code_manager_destroy (MonoCodeManager *cman)
386 {
387         free_chunklist (cman->full);
388         free_chunklist (cman->current);
389         free (cman);
390 }
391
392 /**
393  * mono_code_manager_invalidate:
394  * @cman: a code manager
395  *
396  * Fill all the memory with an invalid native code value
397  * so that any attempt to execute code allocated in the code
398  * manager @cman will fail. This is used for debugging purposes.
399  */
400 void             
401 mono_code_manager_invalidate (MonoCodeManager *cman)
402 {
403         CodeChunk *chunk;
404
405 #if defined(__i386__) || defined(__x86_64__)
406         int fill_value = 0xcc; /* x86 break */
407 #else
408         int fill_value = 0x2a;
409 #endif
410
411         for (chunk = cman->current; chunk; chunk = chunk->next)
412                 memset (chunk->data, fill_value, chunk->size);
413         for (chunk = cman->full; chunk; chunk = chunk->next)
414                 memset (chunk->data, fill_value, chunk->size);
415 }
416
417 /**
418  * mono_code_manager_set_read_only:
419  * @cman: a code manager
420  *
421  * Make the code manager read only, so further allocation requests cause an assert.
422  */
423 void             
424 mono_code_manager_set_read_only (MonoCodeManager *cman)
425 {
426         cman->read_only = TRUE;
427 }
428
429 /**
430  * mono_code_manager_foreach:
431  * @cman: a code manager
432  * @func: a callback function pointer
433  * @user_data: additional data to pass to @func
434  *
435  * Invokes the callback @func for each different chunk of memory allocated
436  * in the code manager @cman.
437  */
438 void
439 mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void *user_data)
440 {
441         CodeChunk *chunk;
442         for (chunk = cman->current; chunk; chunk = chunk->next) {
443                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
444                         return;
445         }
446         for (chunk = cman->full; chunk; chunk = chunk->next) {
447                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
448                         return;
449         }
450 }
451
452 /* BIND_ROOM is the divisor for the chunck of code size dedicated
453  * to binding branches (branches not reachable with the immediate displacement)
454  * bind_size = size/BIND_ROOM;
455  * we should reduce it and make MIN_PAGES bigger for such systems
456  */
457 #if defined(__ppc__) || defined(__powerpc__)
458 #define BIND_ROOM 4
459 #endif
460 #if defined(__arm__)
461 #define BIND_ROOM 8
462 #endif
463
464 static CodeChunk*
465 new_codechunk (int dynamic, int size)
466 {
467         int minsize, flags = CODE_FLAG_MMAP;
468         int chunk_size, bsize = 0;
469         int pagesize;
470         CodeChunk *chunk;
471         void *ptr;
472
473 #ifdef FORCE_MALLOC
474         flags = CODE_FLAG_MALLOC;
475 #endif
476
477         pagesize = mono_pagesize ();
478
479         if (dynamic) {
480                 chunk_size = size;
481                 flags = CODE_FLAG_MALLOC;
482         } else {
483                 minsize = pagesize * MIN_PAGES;
484                 if (size < minsize)
485                         chunk_size = minsize;
486                 else {
487                         chunk_size = size;
488                         chunk_size += pagesize - 1;
489                         chunk_size &= ~ (pagesize - 1);
490                 }
491         }
492 #ifdef BIND_ROOM
493         bsize = chunk_size / BIND_ROOM;
494         if (bsize < MIN_BSIZE)
495                 bsize = MIN_BSIZE;
496         bsize += MIN_ALIGN -1;
497         bsize &= ~ (MIN_ALIGN - 1);
498         if (chunk_size - size < bsize) {
499                 chunk_size = size + bsize;
500                 chunk_size += pagesize - 1;
501                 chunk_size &= ~ (pagesize - 1);
502         }
503 #endif
504
505         if (flags == CODE_FLAG_MALLOC) {
506                 ptr = dlmemalign (MIN_ALIGN, chunk_size + MIN_ALIGN - 1);
507                 if (!ptr)
508                         return NULL;
509         } else {
510                 /* Allocate MIN_ALIGN-1 more than we need so we can still */
511                 /* guarantee MIN_ALIGN alignment for individual allocs    */
512                 /* from mono_code_manager_reserve_align.                  */
513                 ptr = codechunk_valloc (chunk_size);
514                 if (!ptr)
515                         return NULL;
516         }
517
518         if (flags == CODE_FLAG_MALLOC) {
519 #ifdef BIND_ROOM
520                 /* Make sure the thunks area is zeroed */
521                 memset (ptr, 0, bsize);
522 #endif
523         }
524
525         chunk = malloc (sizeof (CodeChunk));
526         if (!chunk) {
527                 if (flags == CODE_FLAG_MALLOC)
528                         dlfree (ptr);
529                 else
530                         mono_vfree (ptr, chunk_size);
531                 return NULL;
532         }
533         chunk->next = NULL;
534         chunk->size = chunk_size;
535         chunk->data = ptr;
536         chunk->flags = flags;
537         chunk->pos = bsize;
538         chunk->bsize = bsize;
539         mono_profiler_code_chunk_new((gpointer) chunk->data, chunk->size);
540
541         code_memory_used += chunk_size;
542         mono_runtime_resource_check_limit (MONO_RESOURCE_JIT_CODE, code_memory_used);
543         /*printf ("code chunk at: %p\n", ptr);*/
544         return chunk;
545 }
546
547 /**
548  * mono_code_manager_reserve:
549  * @cman: a code manager
550  * @size: size of memory to allocate
551  * @alignment: power of two alignment value
552  *
553  * Allocates at least @size bytes of memory inside the code manager @cman.
554  *
555  * Returns: the pointer to the allocated memory or #NULL on failure
556  */
557 void*
558 mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment)
559 {
560 #if !defined(__native_client__) || !defined(__native_client_codegen__)
561         CodeChunk *chunk, *prev;
562         void *ptr;
563         guint32 align_mask = alignment - 1;
564
565         g_assert (!cman->read_only);
566
567         /* eventually allow bigger alignments, but we need to fix the dynamic alloc code to
568          * handle this before
569          */
570         g_assert (alignment <= MIN_ALIGN);
571
572         if (cman->dynamic) {
573                 ++mono_stats.dynamic_code_alloc_count;
574                 mono_stats.dynamic_code_bytes_count += size;
575         }
576
577         if (!cman->current) {
578                 cman->current = new_codechunk (cman->dynamic, size);
579                 if (!cman->current)
580                         return NULL;
581         }
582
583         for (chunk = cman->current; chunk; chunk = chunk->next) {
584                 if (ALIGN_INT (chunk->pos, alignment) + size <= chunk->size) {
585                         chunk->pos = ALIGN_INT (chunk->pos, alignment);
586                         /* Align the chunk->data we add to chunk->pos */
587                         /* or we can't guarantee proper alignment     */
588                         ptr = (void*)((((uintptr_t)chunk->data + align_mask) & ~(uintptr_t)align_mask) + chunk->pos);
589                         chunk->pos = ((char*)ptr - chunk->data) + size;
590                         return ptr;
591                 }
592         }
593         /* 
594          * no room found, move one filled chunk to cman->full 
595          * to keep cman->current from growing too much
596          */
597         prev = NULL;
598         for (chunk = cman->current; chunk; prev = chunk, chunk = chunk->next) {
599                 if (chunk->pos + MIN_ALIGN * 4 <= chunk->size)
600                         continue;
601                 if (prev) {
602                         prev->next = chunk->next;
603                 } else {
604                         cman->current = chunk->next;
605                 }
606                 chunk->next = cman->full;
607                 cman->full = chunk;
608                 break;
609         }
610         chunk = new_codechunk (cman->dynamic, size);
611         if (!chunk)
612                 return NULL;
613         chunk->next = cman->current;
614         cman->current = chunk;
615         chunk->pos = ALIGN_INT (chunk->pos, alignment);
616         /* Align the chunk->data we add to chunk->pos */
617         /* or we can't guarantee proper alignment     */
618         ptr = (void*)((((uintptr_t)chunk->data + align_mask) & ~(uintptr_t)align_mask) + chunk->pos);
619         chunk->pos = ((char*)ptr - chunk->data) + size;
620         return ptr;
621 #else
622         unsigned char *temp_ptr, *code_ptr;
623         /* Round up size to next bundle */
624         alignment = kNaClBundleSize;
625         size = (size + kNaClBundleSize) & (~kNaClBundleMask);
626         /* Allocate a temp buffer */
627         temp_ptr = memalign (alignment, size);
628         g_assert (((uintptr_t)temp_ptr & kNaClBundleMask) == 0);
629         /* Allocate code space from the service runtime */
630         code_ptr = allocate_code (size);
631         /* Insert pointer to code space in hash, keyed by buffer ptr */
632         g_hash_table_insert (cman->hash, temp_ptr, code_ptr);
633
634         nacl_jit_check_init ();
635
636         patch_current_depth++;
637         patch_source_base[patch_current_depth] = temp_ptr;
638         patch_dest_base[patch_current_depth] = code_ptr;
639         patch_alloc_size[patch_current_depth] = size;
640         g_assert (patch_current_depth < kMaxPatchDepth);
641         return temp_ptr;
642 #endif
643 }
644
645 /**
646  * mono_code_manager_reserve:
647  * @cman: a code manager
648  * @size: size of memory to allocate
649  *
650  * Allocates at least @size bytes of memory inside the code manager @cman.
651  *
652  * Returns: the pointer to the allocated memory or #NULL on failure
653  */
654 void*
655 mono_code_manager_reserve (MonoCodeManager *cman, int size)
656 {
657         return mono_code_manager_reserve_align (cman, size, MIN_ALIGN);
658 }
659
660 /**
661  * mono_code_manager_commit:
662  * @cman: a code manager
663  * @data: the pointer returned by mono_code_manager_reserve ()
664  * @size: the size requested in the call to mono_code_manager_reserve ()
665  * @newsize: the new size to reserve
666  *
667  * If we reserved too much room for a method and we didn't allocate
668  * already from the code manager, we can get back the excess allocation
669  * for later use in the code manager.
670  */
671 void
672 mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize)
673 {
674 #if !defined(__native_client__) || !defined(__native_client_codegen__)
675         g_assert (newsize <= size);
676
677         if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) {
678                 cman->current->pos -= size - newsize;
679         }
680 #else
681         unsigned char *code;
682         int status;
683         g_assert (newsize <= size);
684         code = g_hash_table_lookup (cman->hash, data);
685         g_assert (code != NULL);
686         /* Pad space after code with HLTs */
687         /* TODO: this is x86/amd64 specific */
688         while (newsize & kNaClBundleMask) {
689                 *((char *)data + newsize) = 0xf4;
690                 newsize++;
691         }
692         status = nacl_dyncode_create (code, data, newsize);
693         if (status != 0) {
694                 unsigned char *codep;
695                 fprintf(stderr, "Error creating Native Client dynamic code section attempted to be\n"
696                                 "emitted at %p (hex dissasembly of code follows):\n", code);
697                 for (codep = data; codep < data + newsize; codep++)
698                         fprintf(stderr, "%02x ", *codep);
699                 fprintf(stderr, "\n");
700                 g_assert_not_reached ();
701         }
702         g_hash_table_remove (cman->hash, data);
703         g_assert (data == patch_source_base[patch_current_depth]);
704         g_assert (code == patch_dest_base[patch_current_depth]);
705         patch_current_depth--;
706         g_assert (patch_current_depth >= -1);
707         free (data);
708 #endif
709 }
710
711 #if defined(__native_client_codegen__) && defined(__native_client__)
712 void *
713 nacl_code_manager_get_code_dest (MonoCodeManager *cman, void *data)
714 {
715         return g_hash_table_lookup (cman->hash, data);
716 }
717 #endif
718
719 /**
720  * mono_code_manager_size:
721  * @cman: a code manager
722  * @used_size: pointer to an integer for the result
723  *
724  * This function can be used to get statistics about a code manager:
725  * the integer pointed to by @used_size will contain how much
726  * memory is actually used inside the code managed @cman.
727  *
728  * Returns: the amount of memory allocated in @cman
729  */
730 int
731 mono_code_manager_size (MonoCodeManager *cman, int *used_size)
732 {
733         CodeChunk *chunk;
734         guint32 size = 0;
735         guint32 used = 0;
736         for (chunk = cman->current; chunk; chunk = chunk->next) {
737                 size += chunk->size;
738                 used += chunk->pos;
739         }
740         for (chunk = cman->full; chunk; chunk = chunk->next) {
741                 size += chunk->size;
742                 used += chunk->pos;
743         }
744         if (used_size)
745                 *used_size = used;
746         return size;
747 }
748