From 3743e865a991244e76d753902e783399b2245e4c Mon Sep 17 00:00:00 2001 From: Martin Potter Date: Thu, 2 Nov 2017 09:51:23 -0700 Subject: [PATCH] Exclude the current thread when stopping the world if Instruments is attached When Instruments is attached to profile memory usage, it injects liboainject.dylib into the target process. This replaces the default mmap/munmap with version that lock for statistics collection. This will result in deadlock within Mono when the GC stops the world to collect and attemps to mmap. There is an internal __OAExcludeMachThreadID method which adds/removes the passed in thread from the internal exclusion list and prevents Instruments from collecting statistics for that thread. We add the current thread to the exclusion list after we stop the world, then remove the current thread from the exclusion list when we resume. --- mono/metadata/sgen-stw.c | 103 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/mono/metadata/sgen-stw.c b/mono/metadata/sgen-stw.c index a8a1b9bf32c9..4856de302b84 100644 --- a/mono/metadata/sgen-stw.c +++ b/mono/metadata/sgen-stw.c @@ -28,6 +28,94 @@ #include "utils/mono-threads.h" #include "utils/mono-threads-debug.h" +#if defined (PLATFORM_MACOSX) + +#include +#include +#include +#include +#include + +#if defined(__x86_64__) + +#define MACH_HEADER_TYPE struct mach_header_64 +#define NLIST_TYPE struct nlist_64 +#define OFFSET_TYPE uint64_t + +#elif (defined(i386) || defined(__i386__)) + +#define MACH_HEADER_TYPE struct mach_header +#define NLIST_TYPE struct nlist +#define OFFSET_TYPE uint32_t + +#endif + +const char *OAExcludeMachThreadID_function_name = "___OAExcludeMachThreadID"; +static void (*OAExcludeMachThreadID) (pthread_t, int) = NULL; + +static OFFSET_TYPE +offset_for_symbol (struct symtab_command *symtab, uint8_t *data, const char *symbol_name) +{ + NLIST_TYPE *nlist = (NLIST_TYPE *)(data + symtab->symoff); + char *strtab = (char *) (data + symtab->stroff); + + for (int i = 0; i < symtab->nsyms; ++i, nlist++) { + const char *name = nlist->n_un.n_strx ? strtab + nlist->n_un.n_strx : NULL; + if (name != NULL && strcmp(symbol_name, name) == 0) { + OFFSET_TYPE offset = nlist->n_value; + return offset; + } + } + + return 0; +} + +static void +mono_sgen_dylib_loaded (const struct mach_header *header, intptr_t slide) +{ + Dl_info image_info; + int result = dladdr(header, &image_info); + if (result == 0) + return; + + const char *image_name = image_info.dli_fname; + if (strstr(image_name, "liboainject.dylib") == NULL) + return; + + struct load_command *cmd = (struct load_command*)((char *) header + sizeof(MACH_HEADER_TYPE)); + + for (int commandIndex = 0; commandIndex < header->ncmds; commandIndex++) { + if (cmd->cmd == LC_SYMTAB) { + OFFSET_TYPE offset = offset_for_symbol((struct symtab_command *) cmd, (uint8_t *) header, OAExcludeMachThreadID_function_name); + if (offset != 0) { + OAExcludeMachThreadID = (void (*)(pthread_t, int)) (slide + offset); + } + + return; + } + + cmd = (struct load_command *) ((char *) cmd + cmd->cmdsize); + } +} + +static void +mono_sgen_dylib_unloaded (const struct mach_header *header, intptr_t slide) +{ + Dl_info image_info; + int result = dladdr(header, &image_info); + if (result == 0) + return; + + const char *image_name = image_info.dli_fname; + if (strstr(image_name, "liboainject.dylib") == NULL) + return; + + OAExcludeMachThreadID = NULL; +} + +#endif + + #define TV_DECLARE SGEN_TV_DECLARE #define TV_GETTIME SGEN_TV_GETTIME #define TV_ELAPSED SGEN_TV_ELAPSED @@ -133,12 +221,22 @@ sgen_client_stop_world (int generation) sgen_memgov_collection_start (generation); if (sgen_need_bridge_processing ()) sgen_bridge_reset_data (); + +#if defined (PLATFORM_MACOSX) + if (OAExcludeMachThreadID != NULL) + OAExcludeMachThreadID(pthread_self(), 1); +#endif } /* LOCKING: assumes the GC lock is held */ void sgen_client_restart_world (int generation, gint64 *stw_time) { +#if defined (PLATFORM_MACOSX) + if (OAExcludeMachThreadID != NULL) + OAExcludeMachThreadID(pthread_self(), 0); +#endif + TV_DECLARE (end_sw); TV_DECLARE (start_handshake); unsigned long usec; @@ -191,6 +289,11 @@ mono_sgen_init_stw (void) { mono_counters_register ("World stop", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_stop_world); mono_counters_register ("World restart", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_restart_world); + +#if defined (PLATFORM_MACOSX) + _dyld_register_func_for_add_image (&mono_sgen_dylib_loaded); + _dyld_register_func_for_remove_image (&mono_sgen_dylib_unloaded); +#endif } /* Unified suspend code */