/** * \file * MonoJitInfo functionality * * Author: * Dietmar Maurer (dietmar@ximian.com) * Patrik Torstensson * * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com) * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MonoJitInfoFindInAot jit_info_find_in_aot_func = NULL; #define JIT_INFO_TABLE_FILL_RATIO_NOM 3 #define JIT_INFO_TABLE_FILL_RATIO_DENOM 4 #define JIT_INFO_TABLE_FILLED_NUM_ELEMENTS (MONO_JIT_INFO_TABLE_CHUNK_SIZE * JIT_INFO_TABLE_FILL_RATIO_NOM / JIT_INFO_TABLE_FILL_RATIO_DENOM) #define JIT_INFO_TABLE_LOW_WATERMARK(n) ((n) / 2) #define JIT_INFO_TABLE_HIGH_WATERMARK(n) ((n) * 5 / 6) #define JIT_INFO_TOMBSTONE_MARKER ((MonoMethod*)NULL) #define IS_JIT_INFO_TOMBSTONE(ji) ((ji)->d.method == JIT_INFO_TOMBSTONE_MARKER) #define JIT_INFO_TABLE_HAZARD_INDEX 0 #define JIT_INFO_HAZARD_INDEX 1 static int jit_info_table_num_elements (MonoJitInfoTable *table) { return table->num_valid; } static MonoJitInfoTableChunk* jit_info_table_new_chunk (void) { MonoJitInfoTableChunk *chunk = g_new0 (MonoJitInfoTableChunk, 1); chunk->refcount = 1; return chunk; } MonoJitInfoTable * mono_jit_info_table_new (MonoDomain *domain) { MonoJitInfoTable *table = (MonoJitInfoTable *)g_malloc0 (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*)); table->domain = domain; table->num_chunks = 1; table->chunks [0] = jit_info_table_new_chunk (); table->num_valid = 0; return table; } void mono_jit_info_table_free (MonoJitInfoTable *table) { int i; int num_chunks = table->num_chunks; MonoDomain *domain = table->domain; mono_domain_lock (domain); table->domain->num_jit_info_tables--; if (table->domain->num_jit_info_tables <= 1) { GSList *list; for (list = table->domain->jit_info_free_queue; list; list = list->next) g_free (list->data); g_slist_free (table->domain->jit_info_free_queue); table->domain->jit_info_free_queue = NULL; } /* At this point we assume that there are no other threads still accessing the table, so we don't have to worry about hazardous pointers. */ for (i = 0; i < num_chunks; ++i) { MonoJitInfoTableChunk *chunk = table->chunks [i]; MonoJitInfo *tombstone; if (--chunk->refcount > 0) continue; for (tombstone = chunk->next_tombstone; tombstone; ) { MonoJitInfo *next = tombstone->n.next_tombstone; g_free (tombstone); tombstone = next; } g_free (chunk); } mono_domain_unlock (domain); g_free (table); } /* The jit_info_table is sorted in ascending order by the end * addresses of the compiled methods. The reason why we have to do * this is that once we introduce tombstones, it becomes possible for * code ranges to overlap, and if we sort by code start and insert at * the back of the table, we cannot guarantee that we won't overlook * an entry. * * There are actually two possible ways to do the sorting and * inserting which work with our lock-free mechanism: * * 1. Sort by start address and insert at the front. When looking for * an entry, find the last one with a start address lower than the one * you're looking for, then work your way to the front of the table. * * 2. Sort by end address and insert at the back. When looking for an * entry, find the first one with an end address higher than the one * you're looking for, then work your way to the end of the table. * * We chose the latter out of convenience. */ static int jit_info_table_index (MonoJitInfoTable *table, gint8 *addr) { int left = 0, right = table->num_chunks; g_assert (left < right); do { int pos = (left + right) / 2; MonoJitInfoTableChunk *chunk = table->chunks [pos]; if (addr < chunk->last_code_end) right = pos; else left = pos + 1; } while (left < right); g_assert (left == right); if (left >= table->num_chunks) return table->num_chunks - 1; return left; } static int jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointers *hp, gint8 *addr) { int left = 0, right = chunk->num_elements; while (left < right) { int pos = (left + right) / 2; MonoJitInfo *ji = (MonoJitInfo *)mono_get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX); gint8 *code_end = (gint8*)ji->code_start + ji->code_size; if (addr < code_end) right = pos; else left = pos + 1; } g_assert (left == right); return left; } static MonoJitInfo* jit_info_table_find (MonoJitInfoTable *table, MonoThreadHazardPointers *hp, gint8 *addr) { MonoJitInfo *ji; int chunk_pos, pos; chunk_pos = jit_info_table_index (table, (gint8*)addr); g_assert (chunk_pos < table->num_chunks); pos = jit_info_table_chunk_index (table->chunks [chunk_pos], hp, (gint8*)addr); /* We now have a position that's very close to that of the first element whose end address is higher than the one we're looking for. If we don't have the exact position, then we have a position below that one, so we'll just search upward until we find our element. */ do { MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos]; while (pos < chunk->num_elements) { ji = (MonoJitInfo *)mono_get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX); ++pos; if (IS_JIT_INFO_TOMBSTONE (ji)) { mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX); continue; } if ((gint8*)addr >= (gint8*)ji->code_start && (gint8*)addr < (gint8*)ji->code_start + ji->code_size) { mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX); return ji; } /* If we find a non-tombstone element which is already beyond what we're looking for, we have to end the search. */ if ((gint8*)addr < (gint8*)ji->code_start) goto not_found; } ++chunk_pos; pos = 0; } while (chunk_pos < table->num_chunks); not_found: if (hp) mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX); return NULL; } /* * mono_jit_info_table_find_internal: * * If TRY_AOT is FALSE, avoid loading information for missing methods from AOT images, which is currently not async safe. * In this case, only those AOT methods will be found whose jit info is already loaded. * If ALLOW_TRAMPOLINES is TRUE, this can return a MonoJitInfo which represents a trampoline (ji->is_trampoline is true). * ASYNC SAFETY: When called in an async context (mono_thread_info_is_async_context ()), this is async safe. * In this case, the returned MonoJitInfo might not have metadata information, in particular, * mono_jit_info_get_method () could fail. */ MonoJitInfo* mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot, gboolean allow_trampolines) { MonoJitInfoTable *table; MonoJitInfo *ji, *module_ji; MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); UnlockedIncrement (&mono_stats.jit_info_table_lookup_count); /* First we have to get the domain's jit_info_table. This is complicated by the fact that a writer might substitute a new table and free the old one. What the writer guarantees us is that it looks at the hazard pointers after it has changed the jit_info_table pointer. So, if we guard the table by a hazard pointer and make sure that the pointer is still there after we've made it hazardous, we don't have to worry about the writer freeing the table. */ table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX); ji = jit_info_table_find (table, hp, (gint8*)addr); if (hp) mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX); if (ji && ji->is_trampoline && !allow_trampolines) return NULL; if (ji) return ji; /* Maybe its an AOT module */ if (try_aot && mono_get_root_domain () && mono_get_root_domain ()->aot_modules) { table = (MonoJitInfoTable *)mono_get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX); module_ji = jit_info_table_find (table, hp, (gint8*)addr); if (module_ji) ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr); if (hp) mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX); } if (ji && ji->is_trampoline && !allow_trampolines) return NULL; return ji; } /** * mono_jit_info_table_find: * \param domain Domain that you want to look up * \param addr Points to an address with JITed code. * * Use this function to obtain a \c MonoJitInfo* object that can be used to get * some statistics. You should provide both the \p domain on which you will be * performing the probe, and an address. Since application domains can share code * the same address can be in use by multiple domains at once. * * This does not return any results for trampolines. * * \returns NULL if the address does not belong to JITed code (it might be native * code or a trampoline) or a valid pointer to a \c MonoJitInfo* . */ MonoJitInfo* mono_jit_info_table_find (MonoDomain *domain, char *addr) { return mono_jit_info_table_find_internal (domain, addr, TRUE, FALSE); } static G_GNUC_UNUSED void jit_info_table_check (MonoJitInfoTable *table) { int i; for (i = 0; i < table->num_chunks; ++i) { MonoJitInfoTableChunk *chunk = table->chunks [i]; int j; g_assert (chunk->refcount > 0 /* && chunk->refcount <= 8 */); if (chunk->refcount > 10) printf("warning: chunk refcount is %d\n", chunk->refcount); g_assert (chunk->num_elements <= MONO_JIT_INFO_TABLE_CHUNK_SIZE); for (j = 0; j < chunk->num_elements; ++j) { MonoJitInfo *this_ji = chunk->data [j]; MonoJitInfo *next; g_assert ((gint8*)this_ji->code_start + this_ji->code_size <= chunk->last_code_end); if (j < chunk->num_elements - 1) next = chunk->data [j + 1]; else if (i < table->num_chunks - 1) { int k; for (k = i + 1; k < table->num_chunks; ++k) if (table->chunks [k]->num_elements > 0) break; if (k >= table->num_chunks) return; g_assert (table->chunks [k]->num_elements > 0); next = table->chunks [k]->data [0]; } else return; g_assert ((gint8*)this_ji->code_start + this_ji->code_size <= (gint8*)next->code_start + next->code_size); } } } static MonoJitInfoTable* jit_info_table_realloc (MonoJitInfoTable *old) { int i; int num_elements = jit_info_table_num_elements (old); int required_size; int num_chunks; int new_chunk, new_element; MonoJitInfoTable *result; /* number of needed places for elements needed */ required_size = (int)((long)num_elements * JIT_INFO_TABLE_FILL_RATIO_DENOM / JIT_INFO_TABLE_FILL_RATIO_NOM); num_chunks = (required_size + MONO_JIT_INFO_TABLE_CHUNK_SIZE - 1) / MONO_JIT_INFO_TABLE_CHUNK_SIZE; if (num_chunks == 0) { g_assert (num_elements == 0); return mono_jit_info_table_new (old->domain); } g_assert (num_chunks > 0); result = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * num_chunks); result->domain = old->domain; result->num_chunks = num_chunks; result->num_valid = old->num_valid; for (i = 0; i < num_chunks; ++i) result->chunks [i] = jit_info_table_new_chunk (); new_chunk = 0; new_element = 0; for (i = 0; i < old->num_chunks; ++i) { MonoJitInfoTableChunk *chunk = old->chunks [i]; int chunk_num_elements = chunk->num_elements; int j; for (j = 0; j < chunk_num_elements; ++j) { if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j])) { g_assert (new_chunk < num_chunks); result->chunks [new_chunk]->data [new_element] = chunk->data [j]; if (++new_element >= JIT_INFO_TABLE_FILLED_NUM_ELEMENTS) { result->chunks [new_chunk]->num_elements = new_element; ++new_chunk; new_element = 0; } } } } if (new_chunk < num_chunks) { g_assert (new_chunk == num_chunks - 1); result->chunks [new_chunk]->num_elements = new_element; g_assert (result->chunks [new_chunk]->num_elements > 0); } for (i = 0; i < num_chunks; ++i) { MonoJitInfoTableChunk *chunk = result->chunks [i]; MonoJitInfo *ji = chunk->data [chunk->num_elements - 1]; result->chunks [i]->last_code_end = (gint8*)ji->code_start + ji->code_size; } return result; } static void jit_info_table_split_chunk (MonoJitInfoTableChunk *chunk, MonoJitInfoTableChunk **new1p, MonoJitInfoTableChunk **new2p) { MonoJitInfoTableChunk *new1 = jit_info_table_new_chunk (); MonoJitInfoTableChunk *new2 = jit_info_table_new_chunk (); g_assert (chunk->num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE); new1->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE / 2; new2->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE - new1->num_elements; memcpy ((void*)new1->data, (void*)chunk->data, sizeof (MonoJitInfo*) * new1->num_elements); memcpy ((void*)new2->data, (void*)(chunk->data + new1->num_elements), sizeof (MonoJitInfo*) * new2->num_elements); new1->last_code_end = (gint8*)new1->data [new1->num_elements - 1]->code_start + new1->data [new1->num_elements - 1]->code_size; new2->last_code_end = (gint8*)new2->data [new2->num_elements - 1]->code_start + new2->data [new2->num_elements - 1]->code_size; *new1p = new1; *new2p = new2; } static MonoJitInfoTable* jit_info_table_copy_and_split_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk) { MonoJitInfoTable *new_table = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * (table->num_chunks + 1)); int i, j; new_table->domain = table->domain; new_table->num_chunks = table->num_chunks + 1; new_table->num_valid = table->num_valid; j = 0; for (i = 0; i < table->num_chunks; ++i) { if (table->chunks [i] == chunk) { jit_info_table_split_chunk (chunk, &new_table->chunks [j], &new_table->chunks [j + 1]); j += 2; } else { new_table->chunks [j] = table->chunks [i]; ++new_table->chunks [j]->refcount; ++j; } } g_assert (j == new_table->num_chunks); return new_table; } static MonoJitInfoTableChunk* jit_info_table_purify_chunk (MonoJitInfoTableChunk *old) { MonoJitInfoTableChunk *result = jit_info_table_new_chunk (); int i, j; j = 0; for (i = 0; i < old->num_elements; ++i) { if (!IS_JIT_INFO_TOMBSTONE (old->data [i])) result->data [j++] = old->data [i]; } result->num_elements = j; if (result->num_elements > 0) result->last_code_end = (gint8*)result->data [j - 1]->code_start + result->data [j - 1]->code_size; else result->last_code_end = old->last_code_end; return result; } static MonoJitInfoTable* jit_info_table_copy_and_purify_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk) { MonoJitInfoTable *new_table = (MonoJitInfoTable *)g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * table->num_chunks); int i, j; new_table->domain = table->domain; new_table->num_chunks = table->num_chunks; new_table->num_valid = table->num_valid; j = 0; for (i = 0; i < table->num_chunks; ++i) { if (table->chunks [i] == chunk) new_table->chunks [j++] = jit_info_table_purify_chunk (table->chunks [i]); else { new_table->chunks [j] = table->chunks [i]; ++new_table->chunks [j]->refcount; ++j; } } g_assert (j == new_table->num_chunks); return new_table; } /* As we add an element to the table the case can arise that the chunk * to which we need to add is already full. In that case we have to * allocate a new table and do something about that chunk. We have * several strategies: * * If the number of elements in the table is below the low watermark * or above the high watermark, we reallocate the whole table. * Otherwise we only concern ourselves with the overflowing chunk: * * If there are no tombstones in the chunk then we split the chunk in * two, each half full. * * If the chunk does contain tombstones, we just make a new copy of * the chunk without the tombstones, which will have room for at least * the one element we have to add. */ static MonoJitInfoTable* jit_info_table_chunk_overflow (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk) { int num_elements = jit_info_table_num_elements (table); int i; if (num_elements < JIT_INFO_TABLE_LOW_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE) || num_elements > JIT_INFO_TABLE_HIGH_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)) { //printf ("reallocing table\n"); return jit_info_table_realloc (table); } /* count the number of non-tombstone elements in the chunk */ num_elements = 0; for (i = 0; i < chunk->num_elements; ++i) { if (!IS_JIT_INFO_TOMBSTONE (chunk->data [i])) ++num_elements; } if (num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE) { //printf ("splitting chunk\n"); return jit_info_table_copy_and_split_chunk (table, chunk); } //printf ("purifying chunk\n"); return jit_info_table_copy_and_purify_chunk (table, chunk); } /* We add elements to the table by first making space for them by * shifting the elements at the back to the right, one at a time. * This results in duplicate entries during the process, but during * all the time the table is in a sorted state. Also, when an element * is replaced by another one, the element that replaces it has an end * address that is equal to or lower than that of the replaced * element. That property is necessary to guarantee that when * searching for an element we end up at a position not higher than * the one we're looking for (i.e. we either find the element directly * or we end up to the left of it). */ static void jit_info_table_add (MonoDomain *domain, MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji) { MonoJitInfoTable *table; MonoJitInfoTableChunk *chunk; int chunk_pos, pos; int num_elements; int i; table = *table_ptr; restart: chunk_pos = jit_info_table_index (table, (gint8*)ji->code_start + ji->code_size); g_assert (chunk_pos < table->num_chunks); chunk = table->chunks [chunk_pos]; if (chunk->num_elements >= MONO_JIT_INFO_TABLE_CHUNK_SIZE) { MonoJitInfoTable *new_table = jit_info_table_chunk_overflow (table, chunk); /* Debugging code, should be removed. */ //jit_info_table_check (new_table); *table_ptr = new_table; mono_memory_barrier (); domain->num_jit_info_tables++; mono_thread_hazardous_try_free (table, (MonoHazardousFreeFunc)mono_jit_info_table_free); table = new_table; goto restart; } /* Debugging code, should be removed. */ //jit_info_table_check (table); num_elements = chunk->num_elements; pos = jit_info_table_chunk_index (chunk, NULL, (gint8*)ji->code_start + ji->code_size); /* First we need to size up the chunk by one, by copying the last item, or inserting the first one, if the table is empty. */ if (num_elements > 0) chunk->data [num_elements] = chunk->data [num_elements - 1]; else chunk->data [0] = ji; mono_memory_write_barrier (); chunk->num_elements = ++num_elements; /* Shift the elements up one by one. */ for (i = num_elements - 2; i >= pos; --i) { mono_memory_write_barrier (); chunk->data [i + 1] = chunk->data [i]; } /* Now we have room and can insert the new item. */ mono_memory_write_barrier (); chunk->data [pos] = ji; /* Set the high code end address chunk entry. */ chunk->last_code_end = (gint8*)chunk->data [chunk->num_elements - 1]->code_start + chunk->data [chunk->num_elements - 1]->code_size; ++table->num_valid; /* Debugging code, should be removed. */ //jit_info_table_check (table); } void mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji) { g_assert (ji->d.method != NULL); mono_domain_lock (domain); UnlockedIncrement (&mono_stats.jit_info_table_insert_count); jit_info_table_add (domain, &domain->jit_info_table, ji); mono_domain_unlock (domain); } static MonoJitInfo* mono_jit_info_make_tombstone (MonoJitInfoTableChunk *chunk, MonoJitInfo *ji) { MonoJitInfo *tombstone = g_new0 (MonoJitInfo, 1); tombstone->code_start = ji->code_start; tombstone->code_size = ji->code_size; tombstone->d.method = JIT_INFO_TOMBSTONE_MARKER; tombstone->n.next_tombstone = chunk->next_tombstone; chunk->next_tombstone = tombstone; return tombstone; } /* * LOCKING: domain lock */ static void mono_jit_info_free_or_queue (MonoDomain *domain, MonoJitInfo *ji) { if (domain->num_jit_info_tables <= 1) { /* Can it actually happen that we only have one table but ji is still hazardous? */ mono_thread_hazardous_try_free (ji, g_free); } else { domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji); } } static void jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji) { MonoJitInfoTableChunk *chunk; gpointer start = ji->code_start; int chunk_pos, pos; chunk_pos = jit_info_table_index (table, (gint8 *)start); g_assert (chunk_pos < table->num_chunks); pos = jit_info_table_chunk_index (table->chunks [chunk_pos], NULL, (gint8 *)start); do { chunk = table->chunks [chunk_pos]; while (pos < chunk->num_elements) { if (chunk->data [pos] == ji) goto found; g_assert (IS_JIT_INFO_TOMBSTONE (chunk->data [pos])); g_assert ((guint8*)chunk->data [pos]->code_start + chunk->data [pos]->code_size <= (guint8*)ji->code_start + ji->code_size); ++pos; } ++chunk_pos; pos = 0; } while (chunk_pos < table->num_chunks); found: g_assert (chunk->data [pos] == ji); chunk->data [pos] = mono_jit_info_make_tombstone (chunk, ji); --table->num_valid; /* Debugging code, should be removed. */ //jit_info_table_check (table); } void mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji) { MonoJitInfoTable *table; mono_domain_lock (domain); table = domain->jit_info_table; UnlockedIncrement (&mono_stats.jit_info_table_remove_count); jit_info_table_remove (table, ji); mono_jit_info_free_or_queue (domain, ji); mono_domain_unlock (domain); } void mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end) { MonoJitInfo *ji; MonoDomain *domain = mono_get_root_domain (); g_assert (domain); mono_domain_lock (domain); /* * We reuse MonoJitInfoTable to store AOT module info, * this gives us async-safe lookup. */ if (!domain->aot_modules) { domain->num_jit_info_tables ++; domain->aot_modules = mono_jit_info_table_new (domain); } ji = g_new0 (MonoJitInfo, 1); ji->d.image = image; ji->code_start = start; ji->code_size = (guint8*)end - (guint8*)start; jit_info_table_add (domain, &domain->aot_modules, ji); mono_domain_unlock (domain); } void mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func) { jit_info_find_in_aot_func = func; } int mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes) { int size = MONO_SIZEOF_JIT_INFO; size += num_clauses * sizeof (MonoJitExceptionInfo); if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO) size += sizeof (MonoGenericJitInfo); if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES) size += sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo); if (flags & JIT_INFO_HAS_ARCH_EH_INFO) size += sizeof (MonoArchEHJitInfo); if (flags & JIT_INFO_HAS_THUNK_INFO) size += sizeof (MonoThunkJitInfo); if (flags & JIT_INFO_HAS_UNWIND_INFO) size += sizeof (MonoUnwindJitInfo); return size; } void mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size, MonoJitInfoFlags flags, int num_clauses, int num_holes) { ji->d.method = method; ji->code_start = code; ji->code_size = code_size; ji->num_clauses = num_clauses; if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO) ji->has_generic_jit_info = 1; if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES) ji->has_try_block_holes = 1; if (flags & JIT_INFO_HAS_ARCH_EH_INFO) ji->has_arch_eh_info = 1; if (flags & JIT_INFO_HAS_THUNK_INFO) ji->has_thunk_info = 1; if (flags & JIT_INFO_HAS_UNWIND_INFO) ji->has_unwind_info = 1; } /** * mono_jit_info_get_code_start: * \param ji the JIT information handle * * Use this function to get the starting address for the method described by * the \p ji object. You can use this plus the \c mono_jit_info_get_code_size * to determine the start and end of the native code. * * \returns Starting address with the native code. */ gpointer mono_jit_info_get_code_start (MonoJitInfo* ji) { return ji->code_start; } /** * mono_jit_info_get_code_size: * \param ji the JIT information handle * * Use this function to get the code size for the method described by * the \p ji object. You can use this plus the \c mono_jit_info_get_code_start * to determine the start and end of the native code. * * \returns Starting address with the native code. */ int mono_jit_info_get_code_size (MonoJitInfo* ji) { return ji->code_size; } /** * mono_jit_info_get_method: * \param ji the JIT information handle * * Use this function to get the \c MonoMethod* that backs * the \p ji object. * * \returns The \c MonoMethod that represents the code tracked * by \p ji. */ MonoMethod* mono_jit_info_get_method (MonoJitInfo* ji) { g_assert (!ji->async); g_assert (!ji->is_trampoline); return ji->d.method; } static gpointer jit_info_key_extract (gpointer value) { MonoJitInfo *info = (MonoJitInfo*)value; return info->d.method; } static gpointer* jit_info_next_value (gpointer value) { MonoJitInfo *info = (MonoJitInfo*)value; return (gpointer*)&info->n.next_jit_code_hash; } void mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash) { mono_internal_hash_table_init (jit_code_hash, mono_aligned_addr_hash, jit_info_key_extract, jit_info_next_value); } MonoGenericJitInfo* mono_jit_info_get_generic_jit_info (MonoJitInfo *ji) { if (ji->has_generic_jit_info) return (MonoGenericJitInfo*)&ji->clauses [ji->num_clauses]; else return NULL; } /* * mono_jit_info_get_generic_sharing_context: * @ji: a jit info * * Returns the jit info's generic sharing context, or NULL if it * doesn't have one. */ MonoGenericSharingContext* mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji) { MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji); if (gi) return gi->generic_sharing_context; else return NULL; } /* * mono_jit_info_set_generic_sharing_context: * @ji: a jit info * @gsctx: a generic sharing context * * Sets the jit info's generic sharing context. The jit info must * have memory allocated for the context. */ void mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx) { MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji); g_assert (gi); gi->generic_sharing_context = gsctx; } MonoTryBlockHoleTableJitInfo* mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji) { if (ji->has_try_block_holes) { char *ptr = (char*)&ji->clauses [ji->num_clauses]; if (ji->has_generic_jit_info) ptr += sizeof (MonoGenericJitInfo); return (MonoTryBlockHoleTableJitInfo*)ptr; } else { return NULL; } } static int try_block_hole_table_size (MonoJitInfo *ji) { MonoTryBlockHoleTableJitInfo *table; table = mono_jit_info_get_try_block_hole_table_info (ji); g_assert (table); return sizeof (MonoTryBlockHoleTableJitInfo) + table->num_holes * sizeof (MonoTryBlockHoleJitInfo); } MonoArchEHJitInfo* mono_jit_info_get_arch_eh_info (MonoJitInfo *ji) { if (ji->has_arch_eh_info) { char *ptr = (char*)&ji->clauses [ji->num_clauses]; if (ji->has_generic_jit_info) ptr += sizeof (MonoGenericJitInfo); if (ji->has_try_block_holes) ptr += try_block_hole_table_size (ji); return (MonoArchEHJitInfo*)ptr; } else { return NULL; } } MonoThunkJitInfo* mono_jit_info_get_thunk_info (MonoJitInfo *ji) { if (ji->has_thunk_info) { char *ptr = (char*)&ji->clauses [ji->num_clauses]; if (ji->has_generic_jit_info) ptr += sizeof (MonoGenericJitInfo); if (ji->has_try_block_holes) ptr += try_block_hole_table_size (ji); if (ji->has_arch_eh_info) ptr += sizeof (MonoArchEHJitInfo); return (MonoThunkJitInfo*)ptr; } else { return NULL; } } MonoUnwindJitInfo* mono_jit_info_get_unwind_info (MonoJitInfo *ji) { if (ji->has_unwind_info) { char *ptr = (char*)&ji->clauses [ji->num_clauses]; if (ji->has_generic_jit_info) ptr += sizeof (MonoGenericJitInfo); if (ji->has_try_block_holes) ptr += try_block_hole_table_size (ji); if (ji->has_arch_eh_info) ptr += sizeof (MonoArchEHJitInfo); if (ji->has_thunk_info) ptr += sizeof (MonoThunkJitInfo); return (MonoUnwindJitInfo*)ptr; } else { return NULL; } }