| |
| |
| |
| |
| |
| |
| #include "mimalloc.h" |
| #include "mimalloc/internal.h" |
| #include "mimalloc/atomic.h" |
| #include "mimalloc/prim.h" |
|
|
| #include <stdio.h> |
| #include <stdlib.h> |
|
|
| static long mi_max_error_count = 16; |
| static long mi_max_warning_count = 16; |
|
|
| static void mi_add_stderr_output(void); |
|
|
| int mi_version(void) mi_attr_noexcept { |
| return MI_MALLOC_VERSION; |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| #define MI_OPTION(opt) mi_option_##opt, #opt, NULL |
| #define MI_OPTION_LEGACY(opt,legacy) mi_option_##opt, #opt, #legacy |
|
|
| |
| |
| |
| |
| |
|
|
| #ifndef MI_DEFAULT_VERBOSE |
| #define MI_DEFAULT_VERBOSE 0 |
| #endif |
|
|
| #ifndef MI_DEFAULT_ARENA_EAGER_COMMIT |
| #define MI_DEFAULT_ARENA_EAGER_COMMIT 2 |
| #endif |
|
|
| |
| #ifndef MI_DEFAULT_ARENA_RESERVE |
| #if (MI_INTPTR_SIZE>4) |
| #define MI_DEFAULT_ARENA_RESERVE 1024L*1024L |
| #else |
| #define MI_DEFAULT_ARENA_RESERVE 128L*1024L |
| #endif |
| #endif |
|
|
| #ifndef MI_DEFAULT_ARENA_MAX_OBJECT_SIZE |
| #define MI_DEFAULT_ARENA_MAX_OBJECT_SIZE ((MI_SIZE_BITS * MI_ARENA_MAX_CHUNK_OBJ_SIZE)/MI_KiB) |
| #endif |
|
|
| #ifndef MI_DEFAULT_DISALLOW_ARENA_ALLOC |
| #define MI_DEFAULT_DISALLOW_ARENA_ALLOC 0 |
| #endif |
|
|
| #ifndef MI_DEFAULT_ALLOW_LARGE_OS_PAGES |
| #define MI_DEFAULT_ALLOW_LARGE_OS_PAGES 0 |
| #endif |
|
|
| #ifndef MI_DEFAULT_RESERVE_HUGE_OS_PAGES |
| #define MI_DEFAULT_RESERVE_HUGE_OS_PAGES 0 |
| #endif |
|
|
| #ifndef MI_DEFAULT_RESERVE_OS_MEMORY |
| #define MI_DEFAULT_RESERVE_OS_MEMORY 0 |
| #endif |
|
|
| #ifndef MI_DEFAULT_GUARDED_SAMPLE_RATE |
| #if MI_GUARDED && !MI_DEBUG |
| #define MI_DEFAULT_GUARDED_SAMPLE_RATE 4000 |
| #else |
| #define MI_DEFAULT_GUARDED_SAMPLE_RATE 0 |
| #endif |
| #endif |
|
|
| #ifndef MI_DEFAULT_PAGEMAP_COMMIT |
| #if defined(__APPLE__) |
| #define MI_DEFAULT_PAGEMAP_COMMIT 1 |
| #else |
| #define MI_DEFAULT_PAGEMAP_COMMIT 0 |
| #endif |
| #endif |
|
|
| #ifndef MI_DEFAULT_PAGE_MAX_RECLAIM |
| #define MI_DEFAULT_PAGE_MAX_RECLAIM (-1) |
| #endif |
|
|
| #ifndef MI_DEFAULT_PAGE_CROSS_THREAD_MAX_RECLAIM |
| #define MI_DEFAULT_PAGE_CROSS_THREAD_MAX_RECLAIM 32 |
| #endif |
|
|
| #ifndef MI_DEFAULT_ALLOW_THP |
| #if defined(__ANDROID__) |
| #define MI_DEFAULT_ALLOW_THP 0 |
| #else |
| #define MI_DEFAULT_ALLOW_THP 1 |
| #endif |
| #endif |
|
|
| |
| static mi_option_desc_t mi_options[_mi_option_last] = |
| { |
| |
| #if MI_DEBUG || defined(MI_SHOW_ERRORS) |
| { 1, MI_OPTION_UNINIT, MI_OPTION(show_errors) }, |
| #else |
| { 0, MI_OPTION_UNINIT, MI_OPTION(show_errors) }, |
| #endif |
| { 0, MI_OPTION_UNINIT, MI_OPTION(show_stats) }, |
| { MI_DEFAULT_VERBOSE, MI_OPTION_UNINIT, MI_OPTION(verbose) }, |
|
|
| |
| { 1, MI_OPTION_UNINIT, MI_OPTION(deprecated_eager_commit) }, |
| { MI_DEFAULT_ARENA_EAGER_COMMIT, |
| MI_OPTION_UNINIT, MI_OPTION_LEGACY(arena_eager_commit,eager_region_commit) }, |
| { 1, MI_OPTION_UNINIT, MI_OPTION_LEGACY(purge_decommits,reset_decommits) }, |
| { MI_DEFAULT_ALLOW_LARGE_OS_PAGES, |
| MI_OPTION_UNINIT, MI_OPTION_LEGACY(allow_large_os_pages,large_os_pages) }, |
| { MI_DEFAULT_RESERVE_HUGE_OS_PAGES, |
| MI_OPTION_UNINIT, MI_OPTION(reserve_huge_os_pages) }, |
| {-1, MI_OPTION_UNINIT, MI_OPTION(reserve_huge_os_pages_at) }, |
| { MI_DEFAULT_RESERVE_OS_MEMORY, |
| MI_OPTION_UNINIT, MI_OPTION(reserve_os_memory) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_segment_cache) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_page_reset) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_abandoned_page_purge) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(deprecated_segment_reset) }, |
| { 1, MI_OPTION_UNINIT, MI_OPTION(deprecated_eager_commit_delay) }, |
| { 1000,MI_OPTION_UNINIT, MI_OPTION_LEGACY(purge_delay,reset_delay) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(use_numa_nodes) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION_LEGACY(disallow_os_alloc,limit_os_alloc) }, |
| { 100, MI_OPTION_UNINIT, MI_OPTION(os_tag) }, |
| { 32, MI_OPTION_UNINIT, MI_OPTION(max_errors) }, |
| { 32, MI_OPTION_UNINIT, MI_OPTION(max_warnings) }, |
| { 10, MI_OPTION_UNINIT, MI_OPTION(deprecated_max_segment_reclaim)}, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(destroy_on_exit)}, |
| { MI_DEFAULT_ARENA_RESERVE, MI_OPTION_UNINIT, MI_OPTION(arena_reserve) }, |
| { 1, MI_OPTION_UNINIT, MI_OPTION(arena_purge_mult) }, |
| { 1, MI_OPTION_UNINIT, MI_OPTION_LEGACY(deprecated_purge_extend_delay, decommit_extend_delay) }, |
| { MI_DEFAULT_DISALLOW_ARENA_ALLOC, MI_OPTION_UNINIT, MI_OPTION(disallow_arena_alloc) }, |
| { 400, MI_OPTION_UNINIT, MI_OPTION(retry_on_oom) }, |
| #if defined(MI_VISIT_ABANDONED) |
| { 1, MI_OPTION_INITIALIZED, MI_OPTION(visit_abandoned) }, |
| #else |
| { 0, MI_OPTION_UNINIT, MI_OPTION(visit_abandoned) }, |
| #endif |
| { 0, MI_OPTION_UNINIT, MI_OPTION(guarded_min) }, |
| { MI_GiB, MI_OPTION_UNINIT, MI_OPTION(guarded_max) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(guarded_precise) }, |
| { MI_DEFAULT_GUARDED_SAMPLE_RATE, |
| MI_OPTION_UNINIT, MI_OPTION(guarded_sample_rate)}, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(guarded_sample_seed)}, |
| { 10000, MI_OPTION_UNINIT, MI_OPTION(generic_collect) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION_LEGACY(page_reclaim_on_free, abandoned_reclaim_on_free) }, |
| { 2, MI_OPTION_UNINIT, MI_OPTION(page_full_retain) }, |
| { 4, MI_OPTION_UNINIT, MI_OPTION(page_max_candidates) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(max_vabits) }, |
| { MI_DEFAULT_PAGEMAP_COMMIT, |
| MI_OPTION_UNINIT, MI_OPTION(pagemap_commit) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(page_commit_on_demand) }, |
| { MI_DEFAULT_PAGE_MAX_RECLAIM, |
| MI_OPTION_UNINIT, MI_OPTION(page_max_reclaim) }, |
| { MI_DEFAULT_PAGE_CROSS_THREAD_MAX_RECLAIM, |
| MI_OPTION_UNINIT, MI_OPTION(page_cross_thread_max_reclaim) }, |
| { MI_DEFAULT_ALLOW_THP, |
| MI_OPTION_UNINIT, MI_OPTION(allow_thp) }, |
| { 0, MI_OPTION_UNINIT, MI_OPTION(minimal_purge_size) }, |
| { MI_DEFAULT_ARENA_MAX_OBJECT_SIZE, |
| MI_OPTION_UNINIT, MI_OPTION(arena_max_object_size) }, |
| }; |
|
|
| static void mi_option_init(mi_option_desc_t* desc); |
|
|
| static bool mi_option_has_size_in_kib(mi_option_t option) { |
| return (option == mi_option_reserve_os_memory || option == mi_option_arena_reserve || |
| option == mi_option_minimal_purge_size || option == mi_option_arena_max_object_size); |
| } |
|
|
| void _mi_options_init(void) { |
| |
| for(int i = 0; i < _mi_option_last; i++ ) { |
| mi_option_t option = (mi_option_t)i; |
| long l = mi_option_get(option); MI_UNUSED(l); |
| } |
| mi_max_error_count = mi_option_get(mi_option_max_errors); |
| mi_max_warning_count = mi_option_get(mi_option_max_warnings); |
| #if MI_GUARDED |
| if (mi_option_get(mi_option_guarded_sample_rate) > 0) { |
| if (mi_option_is_enabled(mi_option_allow_large_os_pages)) { |
| mi_option_disable(mi_option_allow_large_os_pages); |
| _mi_warning_message("option 'allow_large_os_pages' is disabled to allow for guarded objects\n"); |
| } |
| } |
| #endif |
| } |
|
|
| |
| void _mi_options_post_init(void) { |
| mi_add_stderr_output(); |
| if (mi_option_is_enabled(mi_option_verbose)) { mi_options_print(); } |
| } |
|
|
| #define mi_stringifyx(str) #str |
| #define mi_stringify(str) mi_stringifyx(str) |
|
|
| mi_decl_export void mi_options_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept |
| { |
| |
| const int vermajor = MI_MALLOC_VERSION/10000; |
| const int verminor = (MI_MALLOC_VERSION%10000)/100; |
| const int verpatch = (MI_MALLOC_VERSION%100); |
| _mi_fprintf(out, arg, "v%i.%i.%i%s%s (built on %s, %s)\n", vermajor, verminor, verpatch, |
| #if defined(MI_CMAKE_BUILD_TYPE) |
| ", " mi_stringify(MI_CMAKE_BUILD_TYPE) |
| #else |
| "" |
| #endif |
| , |
| #if defined(MI_GIT_DESCRIBE) |
| ", git " mi_stringify(MI_GIT_DESCRIBE) |
| #else |
| "" |
| #endif |
| , __DATE__, __TIME__); |
|
|
| |
| for (int i = 0; i < _mi_option_last; i++) { |
| mi_option_t option = (mi_option_t)i; |
| long l = mi_option_get(option); MI_UNUSED(l); |
| mi_option_desc_t* desc = &mi_options[option]; |
| _mi_fprintf(out, arg, "option '%s': %ld %s\n", desc->name, desc->value, (mi_option_has_size_in_kib(option) ? "KiB" : "")); |
| } |
|
|
| |
| _mi_fprintf(out, arg, "debug level : %d\n", MI_DEBUG ); |
| _mi_fprintf(out, arg, "secure level: %d\n", MI_SECURE ); |
| _mi_fprintf(out, arg, "mem tracking: %s\n", MI_TRACK_TOOL); |
| #if MI_GUARDED |
| _mi_fprintf(out, arg, "guarded build: %s\n", mi_option_get(mi_option_guarded_sample_rate) != 0 ? "enabled" : "disabled"); |
| #endif |
| #if MI_TSAN |
| _mi_fprintf(out, arg, "thread santizer enabled\n"); |
| #endif |
| } |
|
|
| mi_decl_export void mi_options_print(void) mi_attr_noexcept { |
| mi_options_print_out(NULL, NULL); |
| } |
|
|
| long _mi_option_get_fast(mi_option_t option) { |
| mi_assert(option >= 0 && option < _mi_option_last); |
| mi_option_desc_t* desc = &mi_options[option]; |
| mi_assert(desc->option == option); |
| |
| return desc->value; |
| } |
|
|
|
|
| mi_decl_nodiscard long mi_option_get(mi_option_t option) { |
| mi_assert(option >= 0 && option < _mi_option_last); |
| if (option < 0 || option >= _mi_option_last) return 0; |
| mi_option_desc_t* desc = &mi_options[option]; |
| mi_assert(desc->option == option); |
| if mi_unlikely(desc->init == MI_OPTION_UNINIT) { |
| mi_option_init(desc); |
| } |
| return desc->value; |
| } |
|
|
| mi_decl_nodiscard long mi_option_get_clamp(mi_option_t option, long min, long max) { |
| long x = mi_option_get(option); |
| return (x < min ? min : (x > max ? max : x)); |
| } |
|
|
| mi_decl_nodiscard size_t mi_option_get_size(mi_option_t option) { |
| const long x = mi_option_get(option); |
| size_t size = (x < 0 ? 0 : (size_t)x); |
| if (mi_option_has_size_in_kib(option)) { |
| if (mi_mul_overflow(size, MI_KiB, &size)) { |
| size = MI_MAX_ALLOC_SIZE; |
| } |
| } |
| return size; |
| } |
|
|
| void mi_option_set(mi_option_t option, long value) { |
| mi_assert(option >= 0 && option < _mi_option_last); |
| if (option < 0 || option >= _mi_option_last) return; |
| mi_option_desc_t* desc = &mi_options[option]; |
| mi_assert(desc->option == option); |
| desc->value = value; |
| desc->init = MI_OPTION_INITIALIZED; |
| |
| if (desc->option == mi_option_guarded_min && _mi_option_get_fast(mi_option_guarded_max) < value) { |
| mi_option_set(mi_option_guarded_max, value); |
| } |
| else if (desc->option == mi_option_guarded_max && _mi_option_get_fast(mi_option_guarded_min) > value) { |
| mi_option_set(mi_option_guarded_min, value); |
| } |
| } |
|
|
| void mi_option_set_default(mi_option_t option, long value) { |
| mi_assert(option >= 0 && option < _mi_option_last); |
| if (option < 0 || option >= _mi_option_last) return; |
| mi_option_desc_t* desc = &mi_options[option]; |
| if (desc->init != MI_OPTION_INITIALIZED) { |
| desc->value = value; |
| } |
| } |
|
|
| mi_decl_nodiscard bool mi_option_is_enabled(mi_option_t option) { |
| return (mi_option_get(option) != 0); |
| } |
|
|
| void mi_option_set_enabled(mi_option_t option, bool enable) { |
| mi_option_set(option, (enable ? 1 : 0)); |
| } |
|
|
| void mi_option_set_enabled_default(mi_option_t option, bool enable) { |
| mi_option_set_default(option, (enable ? 1 : 0)); |
| } |
|
|
| void mi_option_enable(mi_option_t option) { |
| mi_option_set_enabled(option,true); |
| } |
|
|
| void mi_option_disable(mi_option_t option) { |
| mi_option_set_enabled(option,false); |
| } |
|
|
| static void mi_cdecl mi_out_stderr(const char* msg, void* arg) { |
| MI_UNUSED(arg); |
| if (msg != NULL && msg[0] != 0) { |
| _mi_prim_out_stderr(msg); |
| } |
| } |
|
|
| |
| |
| |
| |
| #ifndef MI_MAX_DELAY_OUTPUT |
| #define MI_MAX_DELAY_OUTPUT ((size_t)(16*1024)) |
| #endif |
| static char mi_output_buffer[MI_MAX_DELAY_OUTPUT+1]; |
| static _Atomic(size_t) out_len; |
|
|
| static void mi_cdecl mi_out_buf(const char* msg, void* arg) { |
| MI_UNUSED(arg); |
| if (msg==NULL) return; |
| if (mi_atomic_load_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return; |
| size_t n = _mi_strlen(msg); |
| if (n==0) return; |
| |
| size_t start = mi_atomic_add_acq_rel(&out_len, n); |
| if (start >= MI_MAX_DELAY_OUTPUT) return; |
| |
| if (start+n >= MI_MAX_DELAY_OUTPUT) { |
| n = MI_MAX_DELAY_OUTPUT-start-1; |
| } |
| mi_assert_internal(start + n <= MI_MAX_DELAY_OUTPUT); |
| _mi_memcpy(&mi_output_buffer[start], msg, n); |
| } |
|
|
| static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) { |
| if (out==NULL) return; |
| |
| size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1)); |
| |
| if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT; |
| mi_output_buffer[count] = 0; |
| out(mi_output_buffer,arg); |
| if (!no_more_buf) { |
| mi_output_buffer[count] = '\n'; |
| } |
| } |
|
|
|
|
| |
| |
| static void mi_cdecl mi_out_buf_stderr(const char* msg, void* arg) { |
| mi_out_stderr(msg,arg); |
| mi_out_buf(msg,arg); |
| } |
|
|
|
|
|
|
| |
| |
| |
|
|
| |
| |
| static mi_output_fun* volatile mi_out_default; |
| static _Atomic(void*) mi_out_arg; |
|
|
| static mi_output_fun* mi_out_get_default(void** parg) { |
| if (parg != NULL) { *parg = mi_atomic_load_ptr_acquire(void,&mi_out_arg); } |
| mi_output_fun* out = mi_out_default; |
| return (out == NULL ? &mi_out_buf : out); |
| } |
|
|
| void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept { |
| mi_out_default = (out == NULL ? &mi_out_stderr : out); |
| mi_atomic_store_ptr_release(void,&mi_out_arg, arg); |
| if (out!=NULL) mi_out_buf_flush(out,true,arg); |
| } |
|
|
| |
| static void mi_add_stderr_output(void) { |
| mi_assert_internal(mi_out_default == NULL); |
| if (mi_out_default==NULL) { |
| mi_out_buf_flush(&mi_out_stderr, false, NULL); |
| mi_out_default = &mi_out_buf_stderr; |
| } |
| } |
|
|
| |
| |
| |
| static _Atomic(size_t) error_count; |
| static _Atomic(size_t) warning_count; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static mi_decl_thread bool recurse = false; |
|
|
| static mi_decl_noinline bool mi_recurse_enter_prim(void) { |
| if (recurse) return false; |
| recurse = true; |
| return true; |
| } |
|
|
| static mi_decl_noinline void mi_recurse_exit_prim(void) { |
| recurse = false; |
| } |
|
|
| static bool mi_recurse_enter(void) { |
| #if defined(__APPLE__) || defined(__ANDROID__) || defined(MI_TLS_RECURSE_GUARD) |
| if (_mi_preloading()) return false; |
| #endif |
| return mi_recurse_enter_prim(); |
| } |
|
|
| static void mi_recurse_exit(void) { |
| #if defined(__APPLE__) || defined(__ANDROID__) || defined(MI_TLS_RECURSE_GUARD) |
| if (_mi_preloading()) return; |
| #endif |
| mi_recurse_exit_prim(); |
| } |
|
|
| void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) { |
| if (out==NULL || (void*)out==(void*)stdout || (void*)out==(void*)stderr) { |
| if (!mi_recurse_enter()) return; |
| out = mi_out_get_default(&arg); |
| if (prefix != NULL) out(prefix, arg); |
| out(message, arg); |
| mi_recurse_exit(); |
| } |
| else { |
| if (prefix != NULL) out(prefix, arg); |
| out(message, arg); |
| } |
| } |
|
|
| |
| |
| static void mi_vfprintf( mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args ) { |
| char buf[992]; |
| if (fmt==NULL) return; |
| if (!mi_recurse_enter()) return; |
| _mi_vsnprintf(buf, sizeof(buf)-1, fmt, args); |
| mi_recurse_exit(); |
| _mi_fputs(out,arg,prefix,buf); |
| } |
|
|
| void _mi_fprintf( mi_output_fun* out, void* arg, const char* fmt, ... ) { |
| va_list args; |
| va_start(args,fmt); |
| mi_vfprintf(out,arg,NULL,fmt,args); |
| va_end(args); |
| } |
|
|
| static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args) { |
| if (prefix != NULL && _mi_strnlen(prefix,33) <= 32 && !_mi_is_main_thread()) { |
| char tprefix[64]; |
| _mi_snprintf(tprefix, sizeof(tprefix), "%sthread 0x%tx: ", prefix, (uintptr_t)_mi_thread_id()); |
| mi_vfprintf(out, arg, tprefix, fmt, args); |
| } |
| else { |
| mi_vfprintf(out, arg, prefix, fmt, args); |
| } |
| } |
|
|
| void _mi_raw_message(const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| mi_vfprintf(NULL, NULL, NULL, fmt, args); |
| va_end(args); |
| } |
|
|
| void _mi_message(const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args); |
| va_end(args); |
| } |
|
|
| void _mi_trace_message(const char* fmt, ...) { |
| if (mi_option_get(mi_option_verbose) <= 1) return; |
| va_list args; |
| va_start(args, fmt); |
| mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args); |
| va_end(args); |
| } |
|
|
| void _mi_verbose_message(const char* fmt, ...) { |
| if (!mi_option_is_enabled(mi_option_verbose)) return; |
| va_list args; |
| va_start(args,fmt); |
| mi_vfprintf(NULL, NULL, "mimalloc: ", fmt, args); |
| va_end(args); |
| } |
|
|
| static void mi_show_error_message(const char* fmt, va_list args) { |
| if (!mi_option_is_enabled(mi_option_verbose)) { |
| if (!mi_option_is_enabled(mi_option_show_errors)) return; |
| if (mi_max_error_count >= 0 && (long)mi_atomic_increment_acq_rel(&error_count) > mi_max_error_count) return; |
| } |
| mi_vfprintf_thread(NULL, NULL, "mimalloc: error: ", fmt, args); |
| } |
|
|
| void _mi_warning_message(const char* fmt, ...) { |
| if (!mi_option_is_enabled(mi_option_verbose)) { |
| if (!mi_option_is_enabled(mi_option_show_errors)) return; |
| if (mi_max_warning_count >= 0 && (long)mi_atomic_increment_acq_rel(&warning_count) > mi_max_warning_count) return; |
| } |
| va_list args; |
| va_start(args,fmt); |
| mi_vfprintf_thread(NULL, NULL, "mimalloc: warning: ", fmt, args); |
| va_end(args); |
| } |
|
|
|
|
| #if MI_DEBUG |
| mi_decl_noreturn mi_decl_cold void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) mi_attr_noexcept { |
| _mi_fprintf(NULL, NULL, "mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion); |
| abort(); |
| } |
| #endif |
|
|
| |
| |
| |
|
|
| static mi_error_fun* volatile mi_error_handler; |
| static _Atomic(void*) mi_error_arg; |
|
|
| static void mi_error_default(int err) { |
| MI_UNUSED(err); |
| #if (MI_DEBUG>0) |
| if (err==EFAULT) { |
| #ifdef _MSC_VER |
| __debugbreak(); |
| #endif |
| abort(); |
| } |
| #endif |
| #if (MI_SECURE>0) |
| if (err==EFAULT) { |
| abort(); |
| } |
| #endif |
| #if defined(MI_XMALLOC) |
| if (err==ENOMEM || err==EOVERFLOW) { |
| abort(); |
| } |
| #endif |
| } |
|
|
| void mi_register_error(mi_error_fun* fun, void* arg) { |
| mi_error_handler = fun; |
| mi_atomic_store_ptr_release(void,&mi_error_arg, arg); |
| } |
|
|
| void _mi_error_message(int err, const char* fmt, ...) { |
| |
| va_list args; |
| va_start(args, fmt); |
| mi_show_error_message(fmt, args); |
| va_end(args); |
| |
| if (mi_error_handler != NULL) { |
| mi_error_handler(err, mi_atomic_load_ptr_acquire(void,&mi_error_arg)); |
| } |
| else { |
| mi_error_default(err); |
| } |
| } |
|
|
| |
| |
| |
|
|
| |
| #include <stdlib.h> |
| #include <string.h> |
|
|
|
|
| static void mi_option_init(mi_option_desc_t* desc) { |
| |
| char s[64 + 1]; |
| char buf[64+1]; |
| _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); |
| _mi_strlcat(buf, desc->name, sizeof(buf)); |
| bool found = _mi_getenv(buf, s, sizeof(s)); |
| if (!found && desc->legacy_name != NULL) { |
| _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); |
| _mi_strlcat(buf, desc->legacy_name, sizeof(buf)); |
| found = _mi_getenv(buf, s, sizeof(s)); |
| if (found) { |
| _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name); |
| } |
| } |
|
|
| if (found) { |
| size_t len = _mi_strnlen(s, sizeof(buf) - 1); |
| for (size_t i = 0; i < len; i++) { |
| buf[i] = _mi_toupper(s[i]); |
| } |
| buf[len] = 0; |
| if (buf[0] == 0 || _mi_streq(buf,"1") || _mi_streq(buf,"TRUE") || _mi_streq(buf,"YES") || _mi_streq(buf,"ON")) { |
| desc->value = 1; |
| desc->init = MI_OPTION_INITIALIZED; |
| } |
| else if (_mi_streq(buf,"0") || _mi_streq(buf,"FALSE") || _mi_streq(buf,"NO") || _mi_streq(buf,"OFF")) { |
| desc->value = 0; |
| desc->init = MI_OPTION_INITIALIZED; |
| } |
| else { |
| char* end = buf; |
| long value = strtol(buf, &end, 10); |
| if (mi_option_has_size_in_kib(desc->option)) { |
| |
| |
| size_t size = (value < 0 ? 0 : (size_t)value); |
| bool overflow = false; |
| if (*end == 'K') { end++; } |
| else if (*end == 'M') { overflow = mi_mul_overflow(size,MI_KiB,&size); end++; } |
| else if (*end == 'G') { overflow = mi_mul_overflow(size,MI_MiB,&size); end++; } |
| else if (*end == 'T') { overflow = mi_mul_overflow(size,MI_GiB,&size); end++; } |
| else { size = (size + MI_KiB - 1) / MI_KiB; } |
| if (end[0] == 'I' && end[1] == 'B') { end += 2; } |
| else if (*end == 'B') { end++; } |
| if (overflow || size > (MI_MAX_ALLOC_SIZE / MI_KiB)) { size = (MI_MAX_ALLOC_SIZE / MI_KiB); } |
| value = (size > LONG_MAX ? LONG_MAX : (long)size); |
| } |
| if (*end == 0) { |
| mi_option_set(desc->option, value); |
| } |
| else { |
| |
| desc->init = MI_OPTION_DEFAULTED; |
| if (desc->option == mi_option_verbose && desc->value == 0) { |
| |
| |
| desc->value = 1; |
| _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name); |
| desc->value = 0; |
| } |
| else { |
| _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name); |
| } |
| } |
| } |
| mi_assert_internal(desc->init != MI_OPTION_UNINIT); |
| } |
| else if (!_mi_preloading()) { |
| desc->init = MI_OPTION_DEFAULTED; |
| } |
| } |
|
|