/* Called with lock held */ static ucs_status_t ucm_mmap_test(int events) { static int installed_events = 0; ucm_event_handler_t handler; int out_events; void *p; if (ucs_test_all_flags(installed_events, events)) { /* All requested events are already installed */ return UCS_OK; } /* Install a temporary event handler which will add the supported event * type to out_events bitmap. */ handler.events = events; handler.priority = -1; handler.cb = ucm_mmap_event_test_callback; handler.arg = &out_events; out_events = 0; ucm_event_handler_add(&handler); if (events & (UCM_EVENT_MMAP|UCM_EVENT_MUNMAP|UCM_EVENT_MREMAP)) { p = mmap(NULL, 0, 0, 0, -1 ,0); p = mremap(p, 0, 0, 0); munmap(p, 0); } if (events & (UCM_EVENT_SHMAT|UCM_EVENT_SHMDT)) { p = shmat(0, NULL, 0); shmdt(p); } if (events & UCM_EVENT_SBRK) { (void)sbrk(0); } ucm_event_handler_remove(&handler); /* TODO check address / stop all threads */ installed_events |= out_events; ucm_debug("mmap test: got 0x%x out of 0x%x, total: 0x%x", out_events, events, installed_events); /* Return success iff we caught all wanted events */ if (!ucs_test_all_flags(out_events, events)) { return UCS_ERR_UNSUPPORTED; } return UCS_OK; }
/* Has to be called with install_mutex held */ static void ucm_malloc_test(int events) { static const size_t small_alloc_count = 128; static const size_t small_alloc_size = 4096; static const size_t large_alloc_size = 4 * UCS_MBYTE; ucm_event_handler_t handler; void *p[small_alloc_count]; int out_events; int i; ucm_debug("testing malloc..."); /* Install a temporary event handler which will add the supported event * type to out_events bitmap. */ handler.events = events; handler.priority = -1; handler.cb = ucm_mmap_event_test_callback; handler.arg = &out_events; out_events = 0; ucm_event_handler_add(&handler); /* Trigger both small and large allocations * TODO check address / stop all threads */ for (i = 0; i < small_alloc_count; ++i) { p[i] = malloc(small_alloc_size); } for (i = 0; i < small_alloc_count; ++i) { free(p[i]); } p[0] = malloc(large_alloc_size); p[0] = realloc(p[0], large_alloc_size * 2); free(p[0]); if (ucm_malloc_hook_state.hook_called) { ucm_dlmalloc_trim(0); } ucm_event_handler_remove(&handler); ucm_malloc_hook_state.installed_events |= out_events; ucm_debug("malloc test: have 0x%x out of 0x%x, hooks were%s called", ucm_malloc_hook_state.installed_events, events, ucm_malloc_hook_state.hook_called ? "" : " not"); }
ucs_status_t ucm_malloc_install(int events) { ucs_status_t status; pthread_mutex_lock(&ucm_malloc_hook_state.install_mutex); events &= UCM_EVENT_MMAP | UCM_EVENT_MUNMAP | UCM_EVENT_MREMAP | UCM_EVENT_SBRK; if (ucs_malloc_is_ready(events)) { goto out_succ; } ucm_malloc_test(events); if (ucs_malloc_is_ready(events)) { goto out_succ; } if (!ucm_malloc_hook_state.hook_called) { /* Try to leak less memory from original malloc */ malloc_trim(0); } if (!(ucm_malloc_hook_state.install_state & UCM_MALLOC_INSTALLED_SBRK_EVH)) { ucm_debug("installing malloc-sbrk event handler"); ucm_event_handler_add(&ucm_malloc_sbrk_handler); ucm_malloc_hook_state.install_state |= UCM_MALLOC_INSTALLED_SBRK_EVH; } /* When running on valgrind, don't even try malloc hooks. * We want to release original blocks to silence the leak check, so we must * have a way to call the original free(), also these hooks don't work with * valgrind anyway. */ #if HAVE_MALLOC_HOOK if (ucm_global_config.enable_malloc_hooks) { /* Install using malloc hooks. * TODO detect glibc support in configure-time. */ if (!(ucm_malloc_hook_state.install_state & UCM_MALLOC_INSTALLED_HOOKS)) { ucm_debug("installing malloc hooks"); __free_hook = ucm_free; __realloc_hook = ucm_realloc; __malloc_hook = ucm_malloc; __memalign_hook = ucm_memalign; ucm_malloc_hook_state.install_state |= UCM_MALLOC_INSTALLED_HOOKS; } /* Just installed the hooks, test again. */ ucm_malloc_test(events); if (ucm_malloc_hook_state.hook_called) { goto out_install_opt_syms; } } else #endif { ucm_debug("using malloc hooks is disabled by configuration"); } /* Install using malloc symbols */ if (ucm_global_config.enable_malloc_reloc) { if (!(ucm_malloc_hook_state.install_state & UCM_MALLOC_INSTALLED_MALL_SYMS)) { ucm_debug("installing malloc relocations"); ucm_malloc_populate_glibc_cache(); ucm_malloc_install_symbols(ucm_malloc_symbol_patches); ucs_assert(ucm_malloc_symbol_patches[0].value == ucm_free); ucm_malloc_hook_state.free = ucm_malloc_symbol_patches[0].prev_value; ucm_malloc_hook_state.install_state |= UCM_MALLOC_INSTALLED_MALL_SYMS; } } else { ucm_debug("installing malloc relocations is disabled by configuration"); } /* Just installed the symbols, test again */ ucm_malloc_test(events); if (ucm_malloc_hook_state.hook_called) { goto out_install_opt_syms; } status = UCS_ERR_UNSUPPORTED; goto out_unlock; out_install_opt_syms: ucm_malloc_install_optional_symbols(); ucm_malloc_install_mallopt(); out_succ: status = UCS_OK; out_unlock: pthread_mutex_unlock(&ucm_malloc_hook_state.install_mutex); return status; }