• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------------
2 Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
3 This is free software; you can redistribute it and/or modify it under the
4 terms of the MIT license. A copy of the license can be found in the file
5 "LICENSE" at the root of this distribution.
6 -----------------------------------------------------------------------------*/
7 #include "mimalloc.h"
8 #include "mimalloc/internal.h"
9 #include "mimalloc/atomic.h"
10 #include "mimalloc/prim.h"  // mi_prim_out_stderr
11 
12 #include <stdio.h>      // FILE
13 #include <stdlib.h>     // abort
14 #include <stdarg.h>
15 
16 
17 static long mi_max_error_count   = 16; // stop outputting errors after this (use < 0 for no limit)
18 static long mi_max_warning_count = 16; // stop outputting warnings after this (use < 0 for no limit)
19 
20 static void mi_add_stderr_output(void);
21 
mi_version(void)22 int mi_version(void) mi_attr_noexcept {
23   return MI_MALLOC_VERSION;
24 }
25 
26 
27 // --------------------------------------------------------
28 // Options
29 // These can be accessed by multiple threads and may be
30 // concurrently initialized, but an initializing data race
31 // is ok since they resolve to the same value.
32 // --------------------------------------------------------
33 typedef enum mi_init_e {
34   UNINIT,       // not yet initialized
35   DEFAULTED,    // not found in the environment, use default value
36   INITIALIZED   // found in environment or set explicitly
37 } mi_init_t;
38 
39 typedef struct mi_option_desc_s {
40   long        value;  // the value
41   mi_init_t   init;   // is it initialized yet? (from the environment)
42   mi_option_t option; // for debugging: the option index should match the option
43   const char* name;   // option name without `mimalloc_` prefix
44   const char* legacy_name; // potential legacy option name
45 } mi_option_desc_t;
46 
47 #define MI_OPTION(opt)                  mi_option_##opt, #opt, NULL
48 #define MI_OPTION_LEGACY(opt,legacy)    mi_option_##opt, #opt, #legacy
49 
50 static mi_option_desc_t options[_mi_option_last] =
51 {
52   // stable options
53   #if MI_DEBUG || defined(MI_SHOW_ERRORS)
54   { 1, UNINIT, MI_OPTION(show_errors) },
55   #else
56   { 0, UNINIT, MI_OPTION(show_errors) },
57   #endif
58   { 0, UNINIT, MI_OPTION(show_stats) },
59   { 0, UNINIT, MI_OPTION(verbose) },
60 
61   // the following options are experimental and not all combinations make sense.
62   { 1, UNINIT, MI_OPTION(eager_commit) },               // commit per segment directly (4MiB)  (but see also `eager_commit_delay`)
63   { 2, UNINIT, MI_OPTION_LEGACY(arena_eager_commit,eager_region_commit) }, // eager commit arena's? 2 is used to enable this only on an OS that has overcommit (i.e. linux)
64   { 1, UNINIT, MI_OPTION_LEGACY(purge_decommits,reset_decommits) },        // purge decommits memory (instead of reset) (note: on linux this uses MADV_DONTNEED for decommit)
65   { 0, UNINIT, MI_OPTION_LEGACY(allow_large_os_pages,large_os_pages) },    // use large OS pages, use only with eager commit to prevent fragmentation of VMA's
66   { 0, UNINIT, MI_OPTION(reserve_huge_os_pages) },      // per 1GiB huge pages
67   {-1, UNINIT, MI_OPTION(reserve_huge_os_pages_at) },   // reserve huge pages at node N
68   { 0, UNINIT, MI_OPTION(reserve_os_memory)     },
69   { 0, UNINIT, MI_OPTION(deprecated_segment_cache) },   // cache N segments per thread
70   { 0, UNINIT, MI_OPTION(deprecated_page_reset) },      // reset page memory on free
71   { 0, UNINIT, MI_OPTION_LEGACY(abandoned_page_purge,abandoned_page_reset) },       // reset free page memory when a thread terminates
72   { 0, UNINIT, MI_OPTION(deprecated_segment_reset) },   // reset segment memory on free (needs eager commit)
73 #if defined(__NetBSD__)
74   { 0, UNINIT, MI_OPTION(eager_commit_delay) },         // the first N segments per thread are not eagerly committed
75 #else
76   { 1, UNINIT, MI_OPTION(eager_commit_delay) },         // the first N segments per thread are not eagerly committed (but per page in the segment on demand)
77 #endif
78   { 10,  UNINIT, MI_OPTION_LEGACY(purge_delay,reset_delay) },  // purge delay in milli-seconds
79   { 0,   UNINIT, MI_OPTION(use_numa_nodes) },           // 0 = use available numa nodes, otherwise use at most N nodes.
80   { 0,   UNINIT, MI_OPTION(limit_os_alloc) },           // 1 = do not use OS memory for allocation (but only reserved arenas)
81   { 100, UNINIT, MI_OPTION(os_tag) },                   // only apple specific for now but might serve more or less related purpose
82   { 16,  UNINIT, MI_OPTION(max_errors) },               // maximum errors that are output
83   { 16,  UNINIT, MI_OPTION(max_warnings) },             // maximum warnings that are output
84   { 8,   UNINIT, MI_OPTION(max_segment_reclaim)},       // max. number of segment reclaims from the abandoned segments per try.
85   { 0,   UNINIT, MI_OPTION(destroy_on_exit)},           // release all OS memory on process exit; careful with dangling pointer or after-exit frees!
86   #if (MI_INTPTR_SIZE>4)
87   { 1024L * 1024L, UNINIT, MI_OPTION(arena_reserve) },  // reserve memory N KiB at a time
88   #else
89   {  128L * 1024L, UNINIT, MI_OPTION(arena_reserve) },
90   #endif
91   { 10,  UNINIT, MI_OPTION(arena_purge_mult) },        // purge delay multiplier for arena's
92   { 1,   UNINIT, MI_OPTION_LEGACY(purge_extend_delay, decommit_extend_delay) },
93 };
94 
95 static void mi_option_init(mi_option_desc_t* desc);
96 
_mi_options_init(void)97 void _mi_options_init(void) {
98   // called on process load; should not be called before the CRT is initialized!
99   // (e.g. do not call this from process_init as that may run before CRT initialization)
100   mi_add_stderr_output(); // now it safe to use stderr for output
101   for(int i = 0; i < _mi_option_last; i++ ) {
102     mi_option_t option = (mi_option_t)i;
103     long l = mi_option_get(option); MI_UNUSED(l); // initialize
104     // if (option != mi_option_verbose)
105     {
106       mi_option_desc_t* desc = &options[option];
107       _mi_verbose_message("option '%s': %ld\n", desc->name, desc->value);
108     }
109   }
110   mi_max_error_count = mi_option_get(mi_option_max_errors);
111   mi_max_warning_count = mi_option_get(mi_option_max_warnings);
112 }
113 
mi_option_get(mi_option_t option)114 mi_decl_nodiscard long mi_option_get(mi_option_t option) {
115   mi_assert(option >= 0 && option < _mi_option_last);
116   if (option < 0 || option >= _mi_option_last) return 0;
117   mi_option_desc_t* desc = &options[option];
118   mi_assert(desc->option == option);  // index should match the option
119   if mi_unlikely(desc->init == UNINIT) {
120     mi_option_init(desc);
121   }
122   return desc->value;
123 }
124 
mi_option_get_clamp(mi_option_t option,long min,long max)125 mi_decl_nodiscard long mi_option_get_clamp(mi_option_t option, long min, long max) {
126   long x = mi_option_get(option);
127   return (x < min ? min : (x > max ? max : x));
128 }
129 
mi_option_get_size(mi_option_t option)130 mi_decl_nodiscard size_t mi_option_get_size(mi_option_t option) {
131   mi_assert_internal(option == mi_option_reserve_os_memory || option == mi_option_arena_reserve);
132   long x = mi_option_get(option);
133   return (x < 0 ? 0 : (size_t)x * MI_KiB);
134 }
135 
mi_option_set(mi_option_t option,long value)136 void mi_option_set(mi_option_t option, long value) {
137   mi_assert(option >= 0 && option < _mi_option_last);
138   if (option < 0 || option >= _mi_option_last) return;
139   mi_option_desc_t* desc = &options[option];
140   mi_assert(desc->option == option);  // index should match the option
141   desc->value = value;
142   desc->init = INITIALIZED;
143 }
144 
mi_option_set_default(mi_option_t option,long value)145 void mi_option_set_default(mi_option_t option, long value) {
146   mi_assert(option >= 0 && option < _mi_option_last);
147   if (option < 0 || option >= _mi_option_last) return;
148   mi_option_desc_t* desc = &options[option];
149   if (desc->init != INITIALIZED) {
150     desc->value = value;
151   }
152 }
153 
mi_option_is_enabled(mi_option_t option)154 mi_decl_nodiscard bool mi_option_is_enabled(mi_option_t option) {
155   return (mi_option_get(option) != 0);
156 }
157 
mi_option_set_enabled(mi_option_t option,bool enable)158 void mi_option_set_enabled(mi_option_t option, bool enable) {
159   mi_option_set(option, (enable ? 1 : 0));
160 }
161 
mi_option_set_enabled_default(mi_option_t option,bool enable)162 void mi_option_set_enabled_default(mi_option_t option, bool enable) {
163   mi_option_set_default(option, (enable ? 1 : 0));
164 }
165 
mi_option_enable(mi_option_t option)166 void mi_option_enable(mi_option_t option) {
167   mi_option_set_enabled(option,true);
168 }
169 
mi_option_disable(mi_option_t option)170 void mi_option_disable(mi_option_t option) {
171   mi_option_set_enabled(option,false);
172 }
173 
mi_out_stderr(const char * msg,void * arg)174 static void mi_cdecl mi_out_stderr(const char* msg, void* arg) {
175   MI_UNUSED(arg);
176   if (msg != NULL && msg[0] != 0) {
177     _mi_prim_out_stderr(msg);
178   }
179 }
180 
181 // Since an output function can be registered earliest in the `main`
182 // function we also buffer output that happens earlier. When
183 // an output function is registered it is called immediately with
184 // the output up to that point.
185 #ifndef MI_MAX_DELAY_OUTPUT
186 #define MI_MAX_DELAY_OUTPUT ((size_t)(32*1024))
187 #endif
188 static char out_buf[MI_MAX_DELAY_OUTPUT+1];
189 static _Atomic(size_t) out_len;
190 
mi_out_buf(const char * msg,void * arg)191 static void mi_cdecl mi_out_buf(const char* msg, void* arg) {
192   MI_UNUSED(arg);
193   if (msg==NULL) return;
194   if (mi_atomic_load_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return;
195   size_t n = _mi_strlen(msg);
196   if (n==0) return;
197   // claim space
198   size_t start = mi_atomic_add_acq_rel(&out_len, n);
199   if (start >= MI_MAX_DELAY_OUTPUT) return;
200   // check bound
201   if (start+n >= MI_MAX_DELAY_OUTPUT) {
202     n = MI_MAX_DELAY_OUTPUT-start-1;
203   }
204   _mi_memcpy(&out_buf[start], msg, n);
205 }
206 
mi_out_buf_flush(mi_output_fun * out,bool no_more_buf,void * arg)207 static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) {
208   if (out==NULL) return;
209   // claim (if `no_more_buf == true`, no more output will be added after this point)
210   size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1));
211   // and output the current contents
212   if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT;
213   out_buf[count] = 0;
214   out(out_buf,arg);
215   if (!no_more_buf) {
216     out_buf[count] = '\n'; // if continue with the buffer, insert a newline
217   }
218 }
219 
220 
221 // Once this module is loaded, switch to this routine
222 // which outputs to stderr and the delayed output buffer.
mi_out_buf_stderr(const char * msg,void * arg)223 static void mi_cdecl mi_out_buf_stderr(const char* msg, void* arg) {
224   mi_out_stderr(msg,arg);
225   mi_out_buf(msg,arg);
226 }
227 
228 
229 
230 // --------------------------------------------------------
231 // Default output handler
232 // --------------------------------------------------------
233 
234 // Should be atomic but gives errors on many platforms as generally we cannot cast a function pointer to a uintptr_t.
235 // For now, don't register output from multiple threads.
236 static mi_output_fun* volatile mi_out_default; // = NULL
237 static _Atomic(void*) mi_out_arg; // = NULL
238 
mi_out_get_default(void ** parg)239 static mi_output_fun* mi_out_get_default(void** parg) {
240   if (parg != NULL) { *parg = mi_atomic_load_ptr_acquire(void,&mi_out_arg); }
241   mi_output_fun* out = mi_out_default;
242   return (out == NULL ? &mi_out_buf : out);
243 }
244 
mi_register_output(mi_output_fun * out,void * arg)245 void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept {
246   mi_out_default = (out == NULL ? &mi_out_stderr : out); // stop using the delayed output buffer
247   mi_atomic_store_ptr_release(void,&mi_out_arg, arg);
248   if (out!=NULL) mi_out_buf_flush(out,true,arg);         // output all the delayed output now
249 }
250 
251 // add stderr to the delayed output after the module is loaded
mi_add_stderr_output(void)252 static void mi_add_stderr_output(void) {
253   mi_assert_internal(mi_out_default == NULL);
254   mi_out_buf_flush(&mi_out_stderr, false, NULL); // flush current contents to stderr
255   mi_out_default = &mi_out_buf_stderr;           // and add stderr to the delayed output
256 }
257 
258 // --------------------------------------------------------
259 // Messages, all end up calling `_mi_fputs`.
260 // --------------------------------------------------------
261 static _Atomic(size_t) error_count;   // = 0;  // when >= max_error_count stop emitting errors
262 static _Atomic(size_t) warning_count; // = 0;  // when >= max_warning_count stop emitting warnings
263 
264 // When overriding malloc, we may recurse into mi_vfprintf if an allocation
265 // inside the C runtime causes another message.
266 // In some cases (like on macOS) the loader already allocates which
267 // calls into mimalloc; if we then access thread locals (like `recurse`)
268 // this may crash as the access may call _tlv_bootstrap that tries to
269 // (recursively) invoke malloc again to allocate space for the thread local
270 // variables on demand. This is why we use a _mi_preloading test on such
271 // platforms. However, C code generator may move the initial thread local address
272 // load before the `if` and we therefore split it out in a separate funcion.
273 static mi_decl_thread bool recurse = false;
274 
mi_recurse_enter_prim(void)275 static mi_decl_noinline bool mi_recurse_enter_prim(void) {
276   if (recurse) return false;
277   recurse = true;
278   return true;
279 }
280 
mi_recurse_exit_prim(void)281 static mi_decl_noinline void mi_recurse_exit_prim(void) {
282   recurse = false;
283 }
284 
mi_recurse_enter(void)285 static bool mi_recurse_enter(void) {
286   #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
287   if (_mi_preloading()) return false;
288   #endif
289   return mi_recurse_enter_prim();
290 }
291 
mi_recurse_exit(void)292 static void mi_recurse_exit(void) {
293   #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
294   if (_mi_preloading()) return;
295   #endif
296   mi_recurse_exit_prim();
297 }
298 
_mi_fputs(mi_output_fun * out,void * arg,const char * prefix,const char * message)299 void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) {
300   if (out==NULL || (void*)out==(void*)stdout || (void*)out==(void*)stderr) { // TODO: use mi_out_stderr for stderr?
301     if (!mi_recurse_enter()) return;
302     out = mi_out_get_default(&arg);
303     if (prefix != NULL) out(prefix, arg);
304     out(message, arg);
305     mi_recurse_exit();
306   }
307   else {
308     if (prefix != NULL) out(prefix, arg);
309     out(message, arg);
310   }
311 }
312 
313 // Define our own limited `fprintf` that avoids memory allocation.
314 // We do this using `snprintf` with a limited buffer.
mi_vfprintf(mi_output_fun * out,void * arg,const char * prefix,const char * fmt,va_list args)315 static void mi_vfprintf( mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args ) {
316   char buf[512];
317   if (fmt==NULL) return;
318   if (!mi_recurse_enter()) return;
319   vsnprintf(buf,sizeof(buf)-1,fmt,args);
320   mi_recurse_exit();
321   _mi_fputs(out,arg,prefix,buf);
322 }
323 
_mi_fprintf(mi_output_fun * out,void * arg,const char * fmt,...)324 void _mi_fprintf( mi_output_fun* out, void* arg, const char* fmt, ... ) {
325   va_list args;
326   va_start(args,fmt);
327   mi_vfprintf(out,arg,NULL,fmt,args);
328   va_end(args);
329 }
330 
mi_vfprintf_thread(mi_output_fun * out,void * arg,const char * prefix,const char * fmt,va_list args)331 static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args) {
332   if (prefix != NULL && _mi_strnlen(prefix,33) <= 32 && !_mi_is_main_thread()) {
333     char tprefix[64];
334     snprintf(tprefix, sizeof(tprefix), "%sthread 0x%llx: ", prefix, (unsigned long long)_mi_thread_id());
335     mi_vfprintf(out, arg, tprefix, fmt, args);
336   }
337   else {
338     mi_vfprintf(out, arg, prefix, fmt, args);
339   }
340 }
341 
_mi_trace_message(const char * fmt,...)342 void _mi_trace_message(const char* fmt, ...) {
343   if (mi_option_get(mi_option_verbose) <= 1) return;  // only with verbose level 2 or higher
344   va_list args;
345   va_start(args, fmt);
346   mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args);
347   va_end(args);
348 }
349 
_mi_verbose_message(const char * fmt,...)350 void _mi_verbose_message(const char* fmt, ...) {
351   if (!mi_option_is_enabled(mi_option_verbose)) return;
352   va_list args;
353   va_start(args,fmt);
354   mi_vfprintf(NULL, NULL, "mimalloc: ", fmt, args);
355   va_end(args);
356 }
357 
mi_show_error_message(const char * fmt,va_list args)358 static void mi_show_error_message(const char* fmt, va_list args) {
359   if (!mi_option_is_enabled(mi_option_verbose)) {
360     if (!mi_option_is_enabled(mi_option_show_errors)) return;
361     if (mi_max_error_count >= 0 && (long)mi_atomic_increment_acq_rel(&error_count) > mi_max_error_count) return;
362   }
363   mi_vfprintf_thread(NULL, NULL, "mimalloc: error: ", fmt, args);
364 }
365 
_mi_warning_message(const char * fmt,...)366 void _mi_warning_message(const char* fmt, ...) {
367   if (!mi_option_is_enabled(mi_option_verbose)) {
368     if (!mi_option_is_enabled(mi_option_show_errors)) return;
369     if (mi_max_warning_count >= 0 && (long)mi_atomic_increment_acq_rel(&warning_count) > mi_max_warning_count) return;
370   }
371   va_list args;
372   va_start(args,fmt);
373   mi_vfprintf_thread(NULL, NULL, "mimalloc: warning: ", fmt, args);
374   va_end(args);
375 }
376 
377 
378 #if MI_DEBUG
_mi_assert_fail(const char * assertion,const char * fname,unsigned line,const char * func)379 void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) {
380   _mi_fprintf(NULL, NULL, "mimalloc: assertion failed: at \"%s\":%u, %s\n  assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion);
381   abort();
382 }
383 #endif
384 
385 // --------------------------------------------------------
386 // Errors
387 // --------------------------------------------------------
388 
389 static mi_error_fun* volatile  mi_error_handler; // = NULL
390 static _Atomic(void*) mi_error_arg;     // = NULL
391 
mi_error_default(int err)392 static void mi_error_default(int err) {
393   MI_UNUSED(err);
394 #if (MI_DEBUG>0)
395   if (err==EFAULT) {
396     #ifdef _MSC_VER
397     __debugbreak();
398     #endif
399     abort();
400   }
401 #endif
402 #if (MI_SECURE>0)
403   if (err==EFAULT) {  // abort on serious errors in secure mode (corrupted meta-data)
404     abort();
405   }
406 #endif
407 #if defined(MI_XMALLOC)
408   if (err==ENOMEM || err==EOVERFLOW) { // abort on memory allocation fails in xmalloc mode
409     abort();
410   }
411 #endif
412 }
413 
mi_register_error(mi_error_fun * fun,void * arg)414 void mi_register_error(mi_error_fun* fun, void* arg) {
415   mi_error_handler = fun;  // can be NULL
416   mi_atomic_store_ptr_release(void,&mi_error_arg, arg);
417 }
418 
_mi_error_message(int err,const char * fmt,...)419 void _mi_error_message(int err, const char* fmt, ...) {
420   // show detailed error message
421   va_list args;
422   va_start(args, fmt);
423   mi_show_error_message(fmt, args);
424   va_end(args);
425   // and call the error handler which may abort (or return normally)
426   if (mi_error_handler != NULL) {
427     mi_error_handler(err, mi_atomic_load_ptr_acquire(void,&mi_error_arg));
428   }
429   else {
430     mi_error_default(err);
431   }
432 }
433 
434 // --------------------------------------------------------
435 // Initialize options by checking the environment
436 // --------------------------------------------------------
_mi_toupper(char c)437 char _mi_toupper(char c) {
438   if (c >= 'a' && c <= 'z') return (c - 'a' + 'A');
439                        else return c;
440 }
441 
_mi_strnicmp(const char * s,const char * t,size_t n)442 int _mi_strnicmp(const char* s, const char* t, size_t n) {
443   if (n == 0) return 0;
444   for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) {
445     if (_mi_toupper(*s) != _mi_toupper(*t)) break;
446   }
447   return (n == 0 ? 0 : *s - *t);
448 }
449 
_mi_strlcpy(char * dest,const char * src,size_t dest_size)450 void _mi_strlcpy(char* dest, const char* src, size_t dest_size) {
451   if (dest==NULL || src==NULL || dest_size == 0) return;
452   // copy until end of src, or when dest is (almost) full
453   while (*src != 0 && dest_size > 1) {
454     *dest++ = *src++;
455     dest_size--;
456   }
457   // always zero terminate
458   *dest = 0;
459 }
460 
_mi_strlcat(char * dest,const char * src,size_t dest_size)461 void _mi_strlcat(char* dest, const char* src, size_t dest_size) {
462   if (dest==NULL || src==NULL || dest_size == 0) return;
463   // find end of string in the dest buffer
464   while (*dest != 0 && dest_size > 1) {
465     dest++;
466     dest_size--;
467   }
468   // and catenate
469   _mi_strlcpy(dest, src, dest_size);
470 }
471 
_mi_strlen(const char * s)472 size_t _mi_strlen(const char* s) {
473   if (s==NULL) return 0;
474   size_t len = 0;
475   while(s[len] != 0) { len++; }
476   return len;
477 }
478 
_mi_strnlen(const char * s,size_t max_len)479 size_t _mi_strnlen(const char* s, size_t max_len) {
480   if (s==NULL) return 0;
481   size_t len = 0;
482   while(s[len] != 0 && len < max_len) { len++; }
483   return len;
484 }
485 
486 #ifdef MI_NO_GETENV
mi_getenv(const char * name,char * result,size_t result_size)487 static bool mi_getenv(const char* name, char* result, size_t result_size) {
488   MI_UNUSED(name);
489   MI_UNUSED(result);
490   MI_UNUSED(result_size);
491   return false;
492 }
493 #else
mi_getenv(const char * name,char * result,size_t result_size)494 static bool mi_getenv(const char* name, char* result, size_t result_size) {
495   if (name==NULL || result == NULL || result_size < 64) return false;
496   return _mi_prim_getenv(name,result,result_size);
497 }
498 #endif
499 
500 // TODO: implement ourselves to reduce dependencies on the C runtime
501 #include <stdlib.h> // strtol
502 #include <string.h> // strstr
503 
504 
mi_option_init(mi_option_desc_t * desc)505 static void mi_option_init(mi_option_desc_t* desc) {
506   // Read option value from the environment
507   char s[64 + 1];
508   char buf[64+1];
509   _mi_strlcpy(buf, "mimalloc_", sizeof(buf));
510   _mi_strlcat(buf, desc->name, sizeof(buf));
511   bool found = mi_getenv(buf, s, sizeof(s));
512   if (!found && desc->legacy_name != NULL) {
513     _mi_strlcpy(buf, "mimalloc_", sizeof(buf));
514     _mi_strlcat(buf, desc->legacy_name, sizeof(buf));
515     found = mi_getenv(buf, s, sizeof(s));
516     if (found) {
517       _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name);
518     }
519   }
520 
521   if (found) {
522     size_t len = _mi_strnlen(s, sizeof(buf) - 1);
523     for (size_t i = 0; i < len; i++) {
524       buf[i] = _mi_toupper(s[i]);
525     }
526     buf[len] = 0;
527     if (buf[0] == 0 || strstr("1;TRUE;YES;ON", buf) != NULL) {
528       desc->value = 1;
529       desc->init = INITIALIZED;
530     }
531     else if (strstr("0;FALSE;NO;OFF", buf) != NULL) {
532       desc->value = 0;
533       desc->init = INITIALIZED;
534     }
535     else {
536       char* end = buf;
537       long value = strtol(buf, &end, 10);
538       if (desc->option == mi_option_reserve_os_memory || desc->option == mi_option_arena_reserve) {
539         // this option is interpreted in KiB to prevent overflow of `long`
540         if (*end == 'K') { end++; }
541         else if (*end == 'M') { value *= MI_KiB; end++; }
542         else if (*end == 'G') { value *= MI_MiB; end++; }
543         else { value = (value + MI_KiB - 1) / MI_KiB; }
544         if (end[0] == 'I' && end[1] == 'B') { end += 2; }
545         else if (*end == 'B') { end++; }
546       }
547       if (*end == 0) {
548         desc->value = value;
549         desc->init = INITIALIZED;
550       }
551       else {
552         // set `init` first to avoid recursion through _mi_warning_message on mimalloc_verbose.
553         desc->init = DEFAULTED;
554         if (desc->option == mi_option_verbose && desc->value == 0) {
555           // if the 'mimalloc_verbose' env var has a bogus value we'd never know
556           // (since the value defaults to 'off') so in that case briefly enable verbose
557           desc->value = 1;
558           _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name);
559           desc->value = 0;
560         }
561         else {
562           _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name);
563         }
564       }
565     }
566     mi_assert_internal(desc->init != UNINIT);
567   }
568   else if (!_mi_preloading()) {
569     desc->init = DEFAULTED;
570   }
571 }
572