diff --git a/include/mimalloc-internal.h b/include/mimalloc-internal.h index 97619765..3ddb734d 100644 --- a/include/mimalloc-internal.h +++ b/include/mimalloc-internal.h @@ -22,7 +22,7 @@ terms of the MIT license. A copy of the license can be found in the file // "options.c" -void _mi_fprintf(FILE* out, const char* fmt, ...); +void _mi_fprintf(mi_output_fun* out, const char* fmt, ...); void _mi_error_message(const char* fmt, ...); void _mi_warning_message(const char* fmt, ...); void _mi_verbose_message(const char* fmt, ...); diff --git a/include/mimalloc.h b/include/mimalloc.h index 9fd455da..ed75f617 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -69,8 +69,8 @@ terms of the MIT license. A copy of the license can be found in the file // Includes // ------------------------------------------------------ +#include // size_t #include // bool -#include // FILE #ifdef __cplusplus extern "C" { @@ -107,19 +107,23 @@ mi_decl_export mi_decl_allocator void* mi_reallocf(void* p, size_t newsize) mi_decl_export size_t mi_usable_size(const void* p) mi_attr_noexcept; mi_decl_export size_t mi_good_size(size_t size) mi_attr_noexcept; +typedef void (mi_deferred_free_fun)(bool force, unsigned long long heartbeat); +mi_decl_export void mi_register_deferred_free(mi_deferred_free_fun* deferred_free) mi_attr_noexcept; + +typedef void (mi_output_fun)(const char* msg); +mi_decl_export void mi_register_output(mi_output_fun* out) mi_attr_noexcept; + mi_decl_export void mi_collect(bool force) mi_attr_noexcept; -mi_decl_export void mi_stats_print(FILE* out) mi_attr_noexcept; +mi_decl_export int mi_version(void) mi_attr_noexcept; mi_decl_export void mi_stats_reset(void) mi_attr_noexcept; mi_decl_export void mi_stats_merge(void) mi_attr_noexcept; -mi_decl_export int mi_version(void) mi_attr_noexcept; +mi_decl_export void mi_stats_print(mi_output_fun* out) mi_attr_noexcept; mi_decl_export void mi_process_init(void) mi_attr_noexcept; mi_decl_export void mi_thread_init(void) mi_attr_noexcept; mi_decl_export void mi_thread_done(void) mi_attr_noexcept; -mi_decl_export void mi_thread_stats_print(FILE* out) mi_attr_noexcept; +mi_decl_export void mi_thread_stats_print(mi_output_fun* out) mi_attr_noexcept; -typedef void (mi_deferred_free_fun)(bool force, unsigned long long heartbeat); -mi_decl_export void mi_register_deferred_free(mi_deferred_free_fun* deferred_free) mi_attr_noexcept; // ------------------------------------------------------ // Aligned allocation diff --git a/src/alloc-override-win.c b/src/alloc-override-win.c index 0bd05deb..dc4796ab 100644 --- a/src/alloc-override-win.c +++ b/src/alloc-override-win.c @@ -16,6 +16,7 @@ terms of the MIT license. A copy of the license can be found in the file #include #include // getenv +#include // _setmaxstdio #include // strstr diff --git a/src/options.c b/src/options.c index 8654550e..cd9b4e48 100644 --- a/src/options.c +++ b/src/options.c @@ -134,6 +134,32 @@ void mi_option_disable(mi_option_t option) { } +static void mi_out_stderr(const char* msg) { + #ifdef _WIN32 + // on windows with redirection, the C runtime cannot handle locale dependent output + // after the main thread closes so we use direct console output. + _cputs(msg); + #else + fputs(msg, stderr); + #endif +} + +// -------------------------------------------------------- +// Default output handler +// -------------------------------------------------------- + +static volatile _Atomic(mi_output_fun*) mi_out_default; // = NULL + +static mi_output_fun* mi_out_get_default(void) { + mi_output_fun* out = (mi_output_fun*)mi_atomic_read_ptr(mi_atomic_cast(void*, &mi_out_default)); + return (out == NULL ? &mi_out_stderr : out); +} + +void mi_register_output(mi_output_fun* out) mi_attr_noexcept { + mi_atomic_write_ptr(mi_atomic_cast(void*,&mi_out_default),out); +} + + // -------------------------------------------------------- // Messages // -------------------------------------------------------- @@ -146,31 +172,20 @@ static mi_decl_thread bool recurse = false; // Define our own limited `fprintf` that avoids memory allocation. // We do this using `snprintf` with a limited buffer. -static void mi_vfprintf( FILE* out, const char* prefix, const char* fmt, va_list args ) { +static void mi_vfprintf( mi_output_fun* out, const char* prefix, const char* fmt, va_list args ) { char buf[256]; if (fmt==NULL) return; if (_mi_preloading() || recurse) return; recurse = true; - if (out==NULL) out = stdout; + if (out==NULL) out = mi_out_get_default(); vsnprintf(buf,sizeof(buf)-1,fmt,args); - #ifdef _WIN32 - // on windows with redirection, the C runtime cannot handle locale dependent output - // after the main thread closes so use direct console output. - if (out==stderr) { - if (prefix != NULL) _cputs(prefix); - _cputs(buf); - } - else - #endif - { - if (prefix != NULL) fputs(prefix,out); - fputs(buf,out); - } + if (prefix != NULL) out(prefix); + out(buf); recurse = false; return; } -void _mi_fprintf( FILE* out, const char* fmt, ... ) { +void _mi_fprintf( mi_output_fun* out, const char* fmt, ... ) { va_list args; va_start(args,fmt); mi_vfprintf(out,NULL,fmt,args); @@ -181,7 +196,7 @@ void _mi_trace_message(const char* fmt, ...) { if (mi_option_get(mi_option_verbose) <= 1) return; // only with verbose level 2 or higher va_list args; va_start(args, fmt); - mi_vfprintf(stderr, "mimalloc: ", fmt, args); + mi_vfprintf(NULL, "mimalloc: ", fmt, args); va_end(args); } @@ -189,7 +204,7 @@ 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(stderr, "mimalloc: ", fmt, args); + mi_vfprintf(NULL, "mimalloc: ", fmt, args); va_end(args); } @@ -198,7 +213,7 @@ void _mi_error_message(const char* fmt, ...) { if (mi_atomic_increment(&error_count) > MAX_ERROR_COUNT) return; va_list args; va_start(args,fmt); - mi_vfprintf(stderr, "mimalloc: error: ", fmt, args); + mi_vfprintf(NULL, "mimalloc: error: ", fmt, args); va_end(args); mi_assert(false); } @@ -208,14 +223,14 @@ void _mi_warning_message(const char* fmt, ...) { if (mi_atomic_increment(&error_count) > MAX_ERROR_COUNT) return; va_list args; va_start(args,fmt); - mi_vfprintf(stderr, "mimalloc: warning: ", fmt, args); + mi_vfprintf(NULL, "mimalloc: warning: ", fmt, args); va_end(args); } #if MI_DEBUG void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) { - _mi_fprintf(stderr,"mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion); + _mi_fprintf(NULL,"mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion); abort(); } #endif diff --git a/src/stats.c b/src/stats.c index 1ecc8b3a..37a7bde4 100644 --- a/src/stats.c +++ b/src/stats.c @@ -8,6 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc-internal.h" #include "mimalloc-atomic.h" +#include // fputs, stderr #include // memset @@ -120,7 +121,7 @@ static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { Display statistics ----------------------------------------------------------- */ -static void mi_printf_amount(int64_t n, int64_t unit, FILE* out, const char* fmt) { +static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, const char* fmt) { char buf[32]; int len = 32; const char* suffix = (unit <= 0 ? " " : "b"); @@ -141,16 +142,16 @@ static void mi_printf_amount(int64_t n, int64_t unit, FILE* out, const char* fmt } -static void mi_print_amount(int64_t n, int64_t unit, FILE* out) { +static void mi_print_amount(int64_t n, int64_t unit, mi_output_fun* out) { mi_printf_amount(n,unit,out,NULL); } -static void mi_print_count(int64_t n, int64_t unit, FILE* out) { +static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out) { if (unit==1) _mi_fprintf(out,"%11s"," "); else mi_print_amount(n,0,out); } -static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, FILE* out ) { +static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out ) { _mi_fprintf(out,"%10s:", msg); if (unit>0) { mi_print_amount(stat->peak, unit, out); @@ -179,24 +180,24 @@ static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t } } -static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, FILE* out ) { +static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out ) { _mi_fprintf(out, "%10s:", msg); mi_print_amount(stat->total, -1, out); _mi_fprintf(out, "\n"); } -static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, FILE* out) { +static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out) { double avg = (stat->count == 0 ? 0.0 : (double)stat->total / (double)stat->count); _mi_fprintf(out, "%10s: %7.1f avg\n", msg, avg); } -static void mi_print_header( FILE* out ) { +static void mi_print_header(mi_output_fun* out ) { _mi_fprintf(out,"%10s: %10s %10s %10s %10s %10s\n", "heap stats", "peak ", "total ", "freed ", "unit ", "count "); } #if MI_STAT>1 -static void mi_stats_print_bins(mi_stat_count_t* all, const mi_stat_count_t* bins, size_t max, const char* fmt, FILE* out) { +static void mi_stats_print_bins(mi_stat_count_t* all, const mi_stat_count_t* bins, size_t max, const char* fmt, mi_output_fun* out) { bool found = false; char buf[64]; for (size_t i = 0; i <= max; i++) { @@ -220,8 +221,7 @@ static void mi_stats_print_bins(mi_stat_count_t* all, const mi_stat_count_t* bin static void mi_process_info(double* utime, double* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit); -static void _mi_stats_print(mi_stats_t* stats, double secs, FILE* out) mi_attr_noexcept { - if (out == NULL) out = stderr; +static void _mi_stats_print(mi_stats_t* stats, double secs, mi_output_fun* out) mi_attr_noexcept { mi_print_header(out); #if MI_STAT>1 mi_stat_count_t normal = { 0,0,0,0 }; @@ -304,16 +304,16 @@ void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done` } -static void mi_stats_print_ex(mi_stats_t* stats, double secs, FILE* out) { +static void mi_stats_print_ex(mi_stats_t* stats, double secs, mi_output_fun* out) { mi_stats_merge_from(stats); _mi_stats_print(&_mi_stats_main, secs, out); } -void mi_stats_print(FILE* out) mi_attr_noexcept { +void mi_stats_print(mi_output_fun* out) mi_attr_noexcept { mi_stats_print_ex(mi_stats_get_default(),_mi_clock_end(mi_time_start),out); } -void mi_thread_stats_print(FILE* out) mi_attr_noexcept { +void mi_thread_stats_print(mi_output_fun* out) mi_attr_noexcept { _mi_stats_print(mi_stats_get_default(), _mi_clock_end(mi_time_start), out); }