/* 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"); }
static void ucm_malloc_install_mallopt() { /* copy values of M_MMAP_THRESHOLD and M_TRIM_THRESHOLD * if they were overriden by the user */ char *p; p = getenv("MALLOC_TRIM_THRESHOLD_"); if (p) { ucm_debug("set trim_thresh to %d", atoi(p)); ucm_dlmallopt(M_TRIM_THRESHOLD, atoi(p)); } p = getenv("MALLOC_MMAP_THRESHOLD_"); if (p) { ucm_debug("set mmap_thresh to %d", atoi(p)); ucm_dlmallopt(M_MMAP_THRESHOLD, atoi(p)); } }
/* 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; }
/* Called with lock held */ static ucs_status_t ucs_mmap_install_reloc(int events) { static int installed_events = 0; ucm_mmap_func_t *entry; ucs_status_t status; if (!ucm_global_config.enable_mmap_reloc) { ucm_debug("installing mmap relocations is disabled by configuration"); return UCS_ERR_UNSUPPORTED; } for (entry = ucm_mmap_funcs; entry->patch.symbol != NULL; ++entry) { if (!(entry->event_type & events)) { /* Not required */ continue; } if (entry->event_type & installed_events) { /* Already installed */ continue; } ucm_debug("mmap: installing relocation table entry for %s = %p for event 0x%x", entry->patch.symbol, entry->patch.value, entry->event_type); status = ucm_reloc_modify(&entry->patch); if (status != UCS_OK) { ucm_warn("failed to install relocation table entry for '%s'", entry->patch.symbol); return status; } installed_events |= entry->event_type; } return UCS_OK; }
ucs_status_t ucm_mmap_install(int events) { static pthread_mutex_t install_mutex = PTHREAD_MUTEX_INITIALIZER; ucs_status_t status; pthread_mutex_lock(&install_mutex); status = ucm_mmap_test(events); if (status == UCS_OK) { goto out_unlock; } status = ucs_mmap_install_reloc(events); if (status != UCS_OK) { ucm_debug("failed to install relocations for mmap"); goto out_unlock; } status = ucm_mmap_test(events); out_unlock: pthread_mutex_unlock(&install_mutex); return status; }
size_t ucm_get_shm_seg_size(const void *shmaddr) { unsigned long start_addr, end_addr; char *ptr, *newline; size_t read_offset; char buffer[1024]; size_t seg_size; ssize_t nread; int fd; int ret; seg_size = 0; fd = open(UCM_PROCESS_MAPS_FILE, O_RDONLY); if (fd < 0) { ucm_debug("cannot open %s for reading: %m", UCM_PROCESS_MAPS_FILE); goto out; } read_offset = 0; for (;;) { nread = read(fd, buffer + read_offset, sizeof(buffer) - 1 - read_offset); if (nread < 0) { if (errno == EINTR) { continue; } else { ucm_debug("failed to read from %s: %m", UCM_PROCESS_MAPS_FILE); goto out_close; } } else if (nread == 0) { goto out_close; } else { buffer[nread + read_offset] = '\0'; } ptr = buffer; while ( (newline = strchr(ptr, '\n')) != NULL ) { /* 00400000-0040b000 r-xp ... \n */ ret = sscanf(ptr, "%lx-%lx ", &start_addr, &end_addr); if (ret != 2) { ucm_debug("Failed to parse `%s'", ptr); continue; } if (start_addr == (uintptr_t)shmaddr) { seg_size = end_addr - start_addr; goto out_close; } newline = strchr(ptr, '\n'); if (newline == NULL) { break; } ptr = newline + 1; } read_offset = strlen(ptr); memmove(buffer, ptr, read_offset); } out_close: close(fd); out: return seg_size; }
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; }