Skip to main content

mimalloc_pprof/
sys.rs

1//! Raw bindings to the in-tree mimalloc static amalgamation.
2
3use core::ffi::c_char;
4use core::ffi::c_int;
5use core::ffi::c_void;
6
7pub type MiProfWriteFun = unsafe extern "C" fn(*mut c_void, *const c_char, usize);
8
9/// Mirrors `MI_PROF_STAT_VERSION` in `include/mimalloc/profile.h`.
10pub const MI_PROF_STAT_VERSION: c_int = 3;
11
12/// Mirrors `MI_DHAT_STATS_VERSION` in `include/mimalloc/dhat.h`.
13pub const MI_DHAT_STATS_VERSION: c_int = 1;
14
15/// Mirrors `mi_dhat_stats_t` (include/mimalloc/dhat.h) field-for-field.
16#[repr(C)]
17#[allow(non_camel_case_types)]
18pub struct mi_dhat_stats_t {
19    pub size: usize,
20    pub version: c_int,
21    pub enabled: bool,
22    pub incomplete: bool,
23    pub total_bytes: u64,
24    pub total_blocks: u64,
25    pub live_bytes: u64,
26    pub live_blocks: u64,
27    pub peak_bytes: u64,
28    pub peak_blocks: u64,
29    pub dropped: u64,
30    pub internal_bytes: u64,
31}
32
33/// Mirrors `mi_prof_stats_t` (include/mimalloc/profile.h) field-for-field.
34#[repr(C)]
35#[allow(non_camel_case_types)]
36pub struct mi_prof_stats_t {
37    pub size: usize,
38    pub version: c_int,
39    pub enabled: bool,
40    pub accum: bool,
41    pub sample_rate: usize,
42    pub live_samples: usize,
43    pub live_bytes: usize,
44    pub accum_samples: usize,
45    pub accum_bytes: usize,
46    pub unique_stacks: usize,
47    pub arena_committed: usize,
48    pub stack_table_overflows: usize,
49    /// v2. Mirrors `mi_prof_stats_t.dropped_samples`: count of ALL dropped
50    /// samples (record-alloc failure, stack-intern failure, including the
51    /// `MI_PROF_STACK_CAP` cap); `stack_table_overflows` is a subset, so
52    /// `dropped_samples >= stack_table_overflows` always.
53    pub dropped_samples: usize,
54    /// v3. Allocator-level ("ground truth") counters read from `mi_stats_get()`.
55    /// Unlike every field above, these are exact rather than sampled.
56    pub heap_committed: usize,
57    pub heap_reserved: usize,
58    pub heap_malloc_requested: usize,
59    pub heap_pages: usize,
60    pub heap_pages_abandoned: usize,
61    pub heap_count: usize,
62    /// Live thread-local heaps. The main thread's statically-initialized theap
63    /// is not counted, so a single-threaded process reports 0.
64    pub theap_count: usize,
65    pub heap_purged: usize,
66    /// True when the C library was built with `MI_STAT >= 2`. `heap_malloc_requested`
67    /// is only maintained at that level; a default release build reports 0.
68    pub heap_stats_detailed: bool,
69}
70
71/// Mirrors `MI_PROF_CONFIG_VERSION` in `include/mimalloc/profile.h`.
72pub const MI_PROF_CONFIG_VERSION: c_int = 1;
73
74/// Mirrors `MI_PROF_FORMAT_TEXT` / `MI_PROF_FORMAT_PROTO` (include/mimalloc/profile.h).
75pub const MI_PROF_FORMAT_TEXT: c_int = 0;
76pub const MI_PROF_FORMAT_PROTO: c_int = 1;
77
78/// Mirrors `mi_prof_config_mode_t`'s `MI_PROF_CONFIG_FALLBACK` /
79/// `MI_PROF_CONFIG_OVERRIDE` (include/mimalloc/profile.h).
80pub const MI_PROF_CONFIG_FALLBACK: c_int = 0;
81pub const MI_PROF_CONFIG_OVERRIDE: c_int = 1;
82
83/// Mirrors `mi_prof_config_t` (include/mimalloc/profile.h) field-for-field.
84#[repr(C)]
85#[allow(non_camel_case_types)]
86pub struct mi_prof_config_t {
87    pub size: usize,
88    pub version: c_int,
89    pub mode: c_int, // mi_prof_config_mode_t
90    pub sample_interval: usize,
91    pub max_profiler_bytes: usize,
92    pub seed: u64,
93    pub accum: bool,
94    pub max_stack_depth: usize,
95    pub dump_at_exit: *const c_char,
96    pub dump_format: c_int,
97}
98
99/// Mirrors `mi_prof_sample_info_t` (include/mimalloc/profile.h) field-for-field.
100#[repr(C)]
101#[allow(non_camel_case_types)]
102pub struct mi_prof_sample_info_t {
103    pub stack: *const *const c_void,
104    pub depth: usize,
105    pub live_objects: usize,
106    pub live_bytes: usize,
107    pub accum_objects: usize,
108    pub accum_bytes: usize,
109}
110
111#[allow(non_camel_case_types)]
112pub type mi_prof_visit_fun =
113    unsafe extern "C" fn(info: *const mi_prof_sample_info_t, arg: *mut c_void) -> bool;
114
115/// Opaque handle for `mi_prof_snapshot_t`; the profiler never hands out a
116/// value, only a pointer, so this type is never constructed on the Rust side.
117#[allow(non_camel_case_types)]
118pub enum mi_prof_snapshot_t {}
119
120/// Mirrors `mi_prof_module_info_t` (include/mimalloc/profile.h) field-for-field.
121#[repr(C)]
122#[allow(non_camel_case_types)]
123pub struct mi_prof_module_info_t {
124    pub path: *const c_char,
125    pub base: usize,
126    pub size: usize,
127}
128
129#[allow(non_camel_case_types)]
130pub type mi_prof_module_visit_fun =
131    unsafe extern "C" fn(info: *const mi_prof_module_info_t, arg: *mut c_void) -> bool;
132
133/// Opaque handle for `mi_heap_t` (issue #269, Bun parity P4): only ever seen behind a
134/// pointer here (mi_heap_get_seq), so this type is never constructed on the Rust side.
135#[allow(non_camel_case_types)]
136pub enum mi_heap_t {}
137
138/// Mirrors `mi_purge_holes_stats_t` (issue #272, Bun parity P7b): what page hole purging
139/// actually reclaimed, process wide. Field order and types must match `mimalloc.h` exactly.
140#[repr(C)]
141#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
142pub struct MiPurgeHolesStats {
143    pub purged_bytes: usize,
144    pub purged_blocks: usize,
145    pub purged_bytes_total: usize,
146    pub discard_calls: usize,
147    pub reuse_calls: usize,
148    pub pages_freed: usize,
149    pub ineligible_pages: usize,
150    pub ineligible_bytes: usize,
151    pub ineligible_free_bytes: usize,
152    pub unformed_bytes: usize,
153    pub unformed_bytes_total: usize,
154    pub unformed_discard_calls: usize,
155    pub unformed_reuse_calls: usize,
156    pub pages_skipped: usize,
157    pub blocks_visited: usize,
158    pub full_sweeps: usize,
159}
160
161// ---------------------------------------------------------------------------------------
162// include/mimalloc-stats.h -- exact allocator statistics (upstream API)
163// ---------------------------------------------------------------------------------------
164
165/// Mirrors `MI_STAT_VERSION` in `include/mimalloc-stats.h`.
166pub const MI_STAT_VERSION: usize = 5;
167
168/// Mirrors `MI_BIN_HUGE` in `include/mimalloc-stats.h`; the bin arrays below hold
169/// `MI_BIN_HUGE + 1` entries.
170pub const MI_BIN_HUGE: usize = 73;
171
172/// Mirrors `MI_CBIN_COUNT` (`mi_chunkbin_t`) in `include/mimalloc-stats.h`.
173pub const MI_CBIN_COUNT: usize = 6;
174
175/// Mirrors `mi_stat_count_t`: a quantity tracked over time.
176#[repr(C)]
177#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
178#[allow(non_camel_case_types)]
179pub struct mi_stat_count_t {
180    /// Total ever allocated.
181    pub total: i64,
182    /// Peak simultaneous value.
183    pub peak: i64,
184    /// Value right now.
185    pub current: i64,
186}
187
188/// Mirrors `mi_stat_counter_t`: a monotonically increasing counter.
189#[repr(C)]
190#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
191#[allow(non_camel_case_types)]
192pub struct mi_stat_counter_t {
193    /// Total count.
194    pub total: i64,
195}
196
197/// Mirrors `mi_stats_t` (include/mimalloc-stats.h) field-for-field, including the
198/// reserved-for-future-use arrays and the three size-segregated bin arrays.
199///
200/// This is upstream's struct, unchanged by the fork -- in particular it carries **no**
201/// hole-purging or idle-sweep gauges. Those live in [`MiPurgeHolesStats`], because the
202/// sweep also covers pages no heap owns and `mi_stats_t` cannot grow (it is embedded in
203/// a theap, at the meta-allocator's 8 KB block limit).
204///
205/// Field order, offsets and total size are checked against the C library at test time by
206/// `tests/t19_layout.rs` (see `layout_probe.c`), which walks the header's own
207/// `MI_STAT_FIELDS` list -- so an upstream reordering fails loudly instead of silently
208/// reading the wrong counter.
209#[repr(C)]
210#[derive(Clone, Copy, Debug)]
211#[allow(non_camel_case_types)]
212pub struct mi_stats_t {
213    /// `sizeof(mi_stats_t)` as the C library sees it.
214    pub size: usize,
215    /// [`MI_STAT_VERSION`] as the C library sees it.
216    pub version: usize,
217    /// Count of mimalloc pages.
218    pub pages: mi_stat_count_t,
219    /// Reserved memory bytes.
220    pub reserved: mi_stat_count_t,
221    /// Committed bytes.
222    pub committed: mi_stat_count_t,
223    /// Reset bytes.
224    pub reset: mi_stat_counter_t,
225    /// Purged bytes.
226    pub purged: mi_stat_counter_t,
227    /// Committed memory inside pages.
228    pub page_committed: mi_stat_count_t,
229    /// Abandoned page count.
230    pub pages_abandoned: mi_stat_count_t,
231    /// Number of threads.
232    pub threads: mi_stat_count_t,
233    /// Allocated bytes `<= MI_LARGE_OBJ_SIZE_MAX`.
234    pub malloc_normal: mi_stat_count_t,
235    /// Allocated bytes in huge pages.
236    pub malloc_huge: mi_stat_count_t,
237    /// Bytes the application actually asked for; only maintained at `MI_STAT >= 2`.
238    pub malloc_requested: mi_stat_count_t,
239    /// `mmap`/`VirtualAlloc` calls.
240    pub mmap_calls: mi_stat_counter_t,
241    /// Commit calls.
242    pub commit_calls: mi_stat_counter_t,
243    /// Reset calls.
244    pub reset_calls: mi_stat_counter_t,
245    /// Purge calls.
246    pub purge_calls: mi_stat_counter_t,
247    /// Number of memory arenas.
248    pub arena_count: mi_stat_counter_t,
249    /// Number of blocks `<= MI_LARGE_OBJ_SIZE_MAX`.
250    pub malloc_normal_count: mi_stat_counter_t,
251    /// Number of huge blocks.
252    pub malloc_huge_count: mi_stat_counter_t,
253    /// Number of allocations with guard pages.
254    pub malloc_guarded_count: mi_stat_counter_t,
255    /// Internal: arena rollbacks.
256    pub arena_rollback_count: mi_stat_counter_t,
257    /// Internal: arena purges.
258    pub arena_purges: mi_stat_counter_t,
259    /// Internal: page extensions.
260    pub pages_extended: mi_stat_counter_t,
261    /// Internal: retired pages.
262    pub pages_retire: mi_stat_counter_t,
263    /// Internal: total pages searched for a fresh page.
264    pub page_searches: mi_stat_counter_t,
265    /// Internal: searched count for a fresh page.
266    pub page_searches_count: mi_stat_counter_t,
267    /// v1/v2 only; always zero on this v3 line.
268    pub segments: mi_stat_count_t,
269    /// v1/v2 only; always zero on this v3 line.
270    pub segments_abandoned: mi_stat_count_t,
271    /// v1/v2 only; always zero on this v3 line.
272    pub segments_cache: mi_stat_count_t,
273    /// v1/v2 only; always zero on this v3 line.
274    pub _segments_reserved: mi_stat_count_t,
275    /// v3 only: first-class heaps.
276    pub heaps: mi_stat_count_t,
277    /// v3 only: thread-local heaps (`mi_theap_t`).
278    pub theaps: mi_stat_count_t,
279    /// v3 only: pages reclaimed on allocation.
280    pub pages_reclaim_on_alloc: mi_stat_counter_t,
281    /// v3 only: pages reclaimed on free.
282    pub pages_reclaim_on_free: mi_stat_counter_t,
283    /// v3 only: full pages re-abandoned.
284    pub pages_reabandon_full: mi_stat_counter_t,
285    /// v3 only: busy waits while unabandoning a page.
286    pub pages_unabandon_busy_wait: mi_stat_counter_t,
287    /// v3 only: waits while deleting a heap.
288    pub heaps_delete_wait: mi_stat_counter_t,
289    /// Upstream's future-extension padding; do not read.
290    pub _stat_reserved: [mi_stat_count_t; 4],
291    /// Upstream's future-extension padding; do not read.
292    pub _stat_counter_reserved: [mi_stat_counter_t; 4],
293    /// Allocation per size bin.
294    pub malloc_bins: [mi_stat_count_t; MI_BIN_HUGE + 1],
295    /// Pages allocated per size bin.
296    pub page_bins: [mi_stat_count_t; MI_BIN_HUGE + 1],
297    /// Chunks per page size (`mi_chunkbin_t`).
298    pub chunk_bins: [mi_stat_count_t; MI_CBIN_COUNT],
299}
300
301/// Mirrors `mi_subproc_id_t` (include/mimalloc.h): an abstract, pointer-sized handle.
302#[repr(C)]
303#[derive(Clone, Copy, Debug, PartialEq, Eq)]
304#[allow(non_camel_case_types)]
305pub struct mi_subproc_id_t {
306    /// The opaque identifier. Only ever produced by `mi_subproc_main`/`mi_subproc_current`.
307    pub _mi_subproc_id: *mut c_void,
308}
309
310/// Mirrors `mi_output_fun` (include/mimalloc.h): mimalloc's text output sink.
311#[allow(non_camel_case_types)]
312pub type mi_output_fun = unsafe extern "C" fn(msg: *const c_char, arg: *mut c_void);
313
314// ---------------------------------------------------------------------------------------
315// include/mimalloc/memory-events.h -- allocation-change accounting (always compiled in)
316// ---------------------------------------------------------------------------------------
317
318/// Mirrors `MI_MEMORY_SNAPSHOT_VERSION` in `include/mimalloc/memory-events.h`.
319pub const MI_MEMORY_SNAPSHOT_VERSION: c_int = 1;
320
321/// Mirrors `mi_memory_change_kind_t`. Declared as a plain `c_int` rather than a Rust
322/// `enum` on purpose: the value is produced by C, and materialising an out-of-range
323/// discriminant into a `#[repr(i32)]` enum would be undefined behaviour.
324#[allow(non_camel_case_types)]
325pub type mi_memory_change_kind_t = c_int;
326
327/// Mirrors `MI_MEMORY_ALLOCATE`.
328pub const MI_MEMORY_ALLOCATE: mi_memory_change_kind_t = 0;
329/// Mirrors `MI_MEMORY_FREE`.
330pub const MI_MEMORY_FREE: mi_memory_change_kind_t = 1;
331/// Mirrors `MI_MEMORY_RESIZE`.
332pub const MI_MEMORY_RESIZE: mi_memory_change_kind_t = 2;
333/// Mirrors `MI_MEMORY_CHANGE_COUNT`: the number of callback slots, not a kind.
334pub const MI_MEMORY_CHANGE_COUNT: usize = 3;
335
336/// Mirrors `mi_memory_change_t` (include/mimalloc/memory-events.h) field-for-field.
337#[repr(C)]
338#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
339#[allow(non_camel_case_types)]
340pub struct mi_memory_change_t {
341    /// One of [`MI_MEMORY_ALLOCATE`] / [`MI_MEMORY_FREE`] / [`MI_MEMORY_RESIZE`].
342    pub kind: mi_memory_change_kind_t,
343    /// Tracked global live usable bytes after this operation.
344    pub total_bytes: u64,
345    /// Signed change in tracked live usable bytes caused by this operation.
346    pub delta_bytes: i64,
347    /// Caller-requested size for allocation and resize; zero for free.
348    pub request_size: u64,
349}
350
351/// Mirrors `mi_memory_change_fun` (include/mimalloc/memory-events.h).
352#[allow(non_camel_case_types)]
353pub type mi_memory_change_fun =
354    unsafe extern "C" fn(change: *const mi_memory_change_t, arg: *mut c_void);
355
356/// Mirrors `mi_memory_callbacks_t` (include/mimalloc/memory-events.h): one handler and
357/// one caller-owned `arg` per [`mi_memory_change_kind_t`], indexed by the kind.
358#[repr(C)]
359#[derive(Clone, Copy)]
360#[allow(non_camel_case_types)]
361pub struct mi_memory_callbacks_t {
362    /// Handler per change kind; `None` leaves that kind unobserved.
363    pub handlers: [Option<mi_memory_change_fun>; MI_MEMORY_CHANGE_COUNT],
364    /// Caller-owned context per change kind, passed back to the matching handler.
365    pub args: [*mut c_void; MI_MEMORY_CHANGE_COUNT],
366}
367
368/// Mirrors `mi_memory_snapshot_t` (include/mimalloc/memory-events.h) field-for-field.
369#[repr(C)]
370#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
371#[allow(non_camel_case_types)]
372pub struct mi_memory_snapshot_t {
373    /// `sizeof(mi_memory_snapshot_t)`; fill in before calling `mi_memory_snapshot`.
374    pub size: usize,
375    /// [`MI_MEMORY_SNAPSHOT_VERSION`]; fill in before calling `mi_memory_snapshot`.
376    pub version: c_int,
377    /// Tracked live usable bytes right now.
378    pub live_bytes: u64,
379    /// Cumulative usable bytes ever allocated.
380    pub accum_bytes: u64,
381    /// Tracked live allocation count right now.
382    pub live_count: u64,
383    /// Cumulative count of successful allocate events.
384    pub accum_count: u64,
385}
386
387/// Mirrors `mi_memory_allocation_visit_fun` (include/mimalloc/memory-events.h).
388#[allow(non_camel_case_types)]
389pub type mi_memory_allocation_visit_fun =
390    unsafe extern "C" fn(allocation: *mut c_void, usable_size: usize, arg: *mut c_void) -> bool;
391
392// ---------------------------------------------------------------------------------------
393// include/mimalloc.h -- mi_option_t
394// ---------------------------------------------------------------------------------------
395
396/// Mirrors `mi_option_t` (include/mimalloc.h).
397///
398/// **Positional, and silently wrong if it drifts.** This fork inserts thirteen
399/// enumerators (`mi_option_prof*`, `mi_option_memory_events`, `mi_option_purge_zeroes`,
400/// `mi_option_scavenger`, `mi_option_purge_holes*`) at indices 47..=59, immediately
401/// before `_mi_option_last` -- so a mirror copied from upstream mimalloc would set a
402/// *different* option than the caller named, with no diagnostic. Every value below is
403/// checked against the C enum at test time by `tests/t19_layout.rs`, and the ordering is
404/// checked against `include/mimalloc.h` by `ci/check_rust_surface.py`.
405#[allow(non_camel_case_types)]
406pub type mi_option_t = c_int;
407
408macro_rules! mi_options {
409    ($($(#[$attr:meta])* $name:ident = $value:expr;)*) => {
410        $(
411            $(#[$attr])*
412            #[allow(non_upper_case_globals)]
413            pub const $name: mi_option_t = $value;
414        )*
415        /// Every enumerator of `mi_option_t` up to and including `_mi_option_last`, in C
416        /// declaration order, as `(name, value)` pairs. Used by `tests/t19_layout.rs` to
417        /// check this mirror against the C enum, and by `ci/check_rust_surface.py` to
418        /// check it against `include/mimalloc.h`.
419        pub const MI_OPTIONS_IN_ORDER: &[(&str, mi_option_t)] = &[$((stringify!($name), $name)),*];
420    };
421}
422
423mi_options! {
424    /// Print error messages.
425    mi_option_show_errors = 0;
426    /// Print statistics on termination.
427    mi_option_show_stats = 1;
428    /// Print verbose messages.
429    mi_option_verbose = 2;
430    /// Deprecated; kept for numbering.
431    mi_option_deprecated_eager_commit = 3;
432    /// Eagerly commit arena memory.
433    mi_option_arena_eager_commit = 4;
434    /// Purge decommits rather than resets.
435    mi_option_purge_decommits = 5;
436    /// Allow large (2 MiB) OS pages.
437    mi_option_allow_large_os_pages = 6;
438    /// Reserve N huge OS pages at startup.
439    mi_option_reserve_huge_os_pages = 7;
440    /// Reserve huge OS pages at a specific NUMA node.
441    mi_option_reserve_huge_os_pages_at = 8;
442    /// Reserve N KiB of OS memory at startup.
443    mi_option_reserve_os_memory = 9;
444    /// Deprecated; kept for numbering.
445    mi_option_deprecated_segment_cache = 10;
446    /// Deprecated; kept for numbering.
447    mi_option_deprecated_page_reset = 11;
448    /// Deprecated; kept for numbering.
449    mi_option_deprecated_abandoned_page_purge = 12;
450    /// Deprecated; kept for numbering.
451    mi_option_deprecated_segment_reset = 13;
452    /// Deprecated; kept for numbering.
453    mi_option_deprecated_eager_commit_delay = 14;
454    /// Milliseconds to delay purging.
455    mi_option_purge_delay = 15;
456    /// Number of NUMA nodes to use.
457    mi_option_use_numa_nodes = 16;
458    /// Refuse to allocate from the OS.
459    mi_option_disallow_os_alloc = 17;
460    /// Tag passed to the OS allocator.
461    mi_option_os_tag = 18;
462    /// Maximum error messages printed.
463    mi_option_max_errors = 19;
464    /// Maximum warning messages printed.
465    mi_option_max_warnings = 20;
466    /// Deprecated; kept for numbering.
467    mi_option_deprecated_max_segment_reclaim = 21;
468    /// Release all memory on process exit.
469    mi_option_destroy_on_exit = 22;
470    /// Bytes to reserve per new arena.
471    mi_option_arena_reserve = 23;
472    /// Multiplier on the arena purge delay.
473    mi_option_arena_purge_mult = 24;
474    /// Deprecated; kept for numbering.
475    mi_option_deprecated_purge_extend_delay = 25;
476    /// Refuse to allocate from arenas.
477    mi_option_disallow_arena_alloc = 26;
478    /// Retry count on OS out-of-memory.
479    mi_option_retry_on_oom = 27;
480    /// Deprecated; kept for numbering.
481    mi_option_deprecated_visit_abandoned = 28;
482    /// Minimum size for guard-page allocation.
483    mi_option_guarded_min = 29;
484    /// Maximum size for guard-page allocation.
485    mi_option_guarded_max = 30;
486    /// Place guard pages precisely after the block.
487    mi_option_guarded_precise = 31;
488    /// Sample every N-th allocation for guard pages.
489    mi_option_guarded_sample_rate = 32;
490    /// Seed for guard-page sampling.
491    mi_option_guarded_sample_seed = 33;
492    /// Collect generically every N-th generic allocation.
493    mi_option_generic_collect = 34;
494    /// Reclaim abandoned pages on free.
495    mi_option_page_reclaim_on_free = 35;
496    /// Number of full pages to retain per bin.
497    mi_option_page_full_retain = 36;
498    /// Candidate pages searched per allocation.
499    mi_option_page_max_candidates = 37;
500    /// Maximum virtual address bits to assume.
501    mi_option_max_vabits = 38;
502    /// Commit the page map eagerly.
503    mi_option_pagemap_commit = 39;
504    /// Commit page memory on demand.
505    mi_option_page_commit_on_demand = 40;
506    /// Maximum pages reclaimed at once.
507    mi_option_page_max_reclaim = 41;
508    /// Maximum cross-thread pages reclaimed at once.
509    mi_option_page_cross_thread_max_reclaim = 42;
510    /// Allow transparent huge pages.
511    mi_option_allow_thp = 43;
512    /// Smallest range worth purging.
513    mi_option_minimal_purge_size = 44;
514    /// Largest object served from an arena.
515    mi_option_arena_max_object_size = 45;
516    /// Treat arenas as NUMA local.
517    mi_option_arena_is_numa_local = 46;
518    /// **Fork addition.** Enable the allocation sampling profiler at process start
519    /// (`MIMALLOC_PROF`).
520    mi_option_prof = 47;
521    /// **Fork addition.** Average byte interval between profiler samples.
522    mi_option_prof_sample_rate = 48;
523    /// **Fork addition.** Maximum captured stack depth for the profiler.
524    mi_option_prof_bt_max = 49;
525    /// **Fork addition.** Keep cumulative profiler counters until `mi_prof_reset`.
526    mi_option_prof_accum = 50;
527    /// **Fork addition.** Profiler sampling PRNG seed; 0 = nondeterministic.
528    mi_option_prof_seed = 51;
529    /// **Fork addition.** Budget in bytes for profiler-internal arena memory.
530    mi_option_prof_max_bytes = 52;
531    /// **Fork addition.** Enable allocation-change accounting/callbacks
532    /// (`MIMALLOC_MEMORY_EVENTS`).
533    mi_option_memory_events = 53;
534    /// **Fork addition, dead since #80.** The slot is kept so nothing renumbers; setting
535    /// it parses but has no effect. Unrelated to `mi_option_purge_holes_eager_zero`.
536    mi_option_purge_zeroes = 54;
537    /// **Fork addition (Bun).** Run a background thread that purges scheduled arena memory.
538    mi_option_scavenger = 55;
539    /// **Fork addition (Bun).** Discard the memory of free blocks inside still-used pages
540    /// on `mi_on_thread_idle`.
541    mi_option_purge_holes = 56;
542    /// **Fork addition (Bun).** Zero a range before discarding it, so a mis-scoped discard
543    /// corrupts visibly.
544    mi_option_purge_holes_eager_zero = 57;
545    /// **Fork addition (Bun).** Minimum milliseconds between sweeps of one thread's heaps.
546    mi_option_purge_holes_min_interval = 58;
547    /// **Fork addition (Bun).** Every N-th sweep walks every page, ignoring the per-page
548    /// skip check; 0 disables.
549    mi_option_purge_holes_full_every = 59;
550    /// Sentinel: one past the last real option.
551    _mi_option_last = 60;
552}
553
554/// Deprecated upstream alias for [`mi_option_allow_large_os_pages`].
555#[allow(non_upper_case_globals)]
556pub const mi_option_large_os_pages: mi_option_t = mi_option_allow_large_os_pages;
557/// Deprecated upstream alias for [`mi_option_arena_eager_commit`].
558#[allow(non_upper_case_globals)]
559pub const mi_option_eager_region_commit: mi_option_t = mi_option_arena_eager_commit;
560/// Deprecated upstream alias for [`mi_option_purge_decommits`].
561#[allow(non_upper_case_globals)]
562pub const mi_option_reset_decommits: mi_option_t = mi_option_purge_decommits;
563/// Deprecated upstream alias for [`mi_option_purge_delay`].
564#[allow(non_upper_case_globals)]
565pub const mi_option_reset_delay: mi_option_t = mi_option_purge_delay;
566/// Deprecated upstream alias for [`mi_option_disallow_os_alloc`].
567#[allow(non_upper_case_globals)]
568pub const mi_option_limit_os_alloc: mi_option_t = mi_option_disallow_os_alloc;
569
570/// One entry of the ABI layout table published by [`mi_rs_layout_table`].
571#[repr(C)]
572#[derive(Clone, Copy)]
573pub struct MiRsLayoutEntry {
574    /// NUL-terminated static key, e.g. `sizeof:mi_stats_t` or `option:mi_option_prof`.
575    pub name: *const c_char,
576    /// The value the C compiler computed for that key.
577    pub value: usize,
578}
579
580unsafe extern "C" {
581    pub fn mi_malloc(size: usize) -> *mut c_void;
582    pub fn mi_zalloc(size: usize) -> *mut c_void;
583    pub fn mi_calloc(count: usize, size: usize) -> *mut c_void;
584    pub fn mi_realloc(p: *mut c_void, newsize: usize) -> *mut c_void;
585    pub fn mi_free(p: *mut c_void);
586    pub fn mi_malloc_aligned(size: usize, alignment: usize) -> *mut c_void;
587    pub fn mi_zalloc_aligned(size: usize, alignment: usize) -> *mut c_void;
588    pub fn mi_realloc_aligned(p: *mut c_void, newsize: usize, alignment: usize) -> *mut c_void;
589    // Zeroing reallocation (issue #83). These grow a block AND zero the new tail, which
590    // `GlobalAlloc` cannot express -- it has no `grow_zeroed` -- so a Rust caller would
591    // otherwise grow and `memset` by hand, redoing work mimalloc has already done.
592    //
593    // What they zero is NOT [old_requested, new): mimalloc measures from the block's
594    // old *usable* size, so the slack between requested and usable is left as-is. A
595    // block requested at 64 bytes may be usable to 80, and growing to 70 is served in
596    // place with nothing zeroed. See `prof::rezalloc` for the safe-wrapper docs.
597    pub fn mi_rezalloc(p: *mut c_void, newsize: usize) -> *mut c_void;
598    pub fn mi_recalloc(p: *mut c_void, newcount: usize, size: usize) -> *mut c_void;
599    pub fn mi_rezalloc_aligned(p: *mut c_void, newsize: usize, alignment: usize) -> *mut c_void;
600    pub fn mi_recalloc_aligned(
601        p: *mut c_void,
602        newcount: usize,
603        size: usize,
604        alignment: usize,
605    ) -> *mut c_void;
606    /// Grow in place only; returns NULL if the block cannot be extended without moving.
607    pub fn mi_expand(p: *mut c_void, newsize: usize) -> *mut c_void;
608    pub fn mi_usable_size(p: *const c_void) -> usize;
609    pub fn mi_dhat_start() -> bool;
610    pub fn mi_dhat_stop();
611    pub fn mi_dhat_is_enabled() -> bool;
612    pub fn mi_dhat_stats_get(stats: *mut mi_dhat_stats_t) -> bool;
613    pub fn mi_dhat_dump(path: *const c_char) -> bool;
614    pub fn mi_prof_start(sample_rate: usize) -> bool;
615    pub fn mi_prof_start_seeded(sample_rate: usize, seed: u64) -> bool;
616    pub fn mi_prof_start_ex(config: *const mi_prof_config_t) -> bool;
617    pub fn mi_prof_stop();
618    pub fn mi_prof_is_enabled() -> bool;
619    pub fn mi_prof_dump(path: *const c_char) -> bool;
620    pub fn mi_prof_dump_writer(write: Option<MiProfWriteFun>, arg: *mut c_void) -> bool;
621    /// profile.proto (google/pprof) writer: same sample/period/mapping semantics as
622    /// `mi_prof_dump`/`mi_prof_dump_writer` but encoded as an uncompressed, binary
623    /// pprof Profile message instead of the legacy "heap profile:" text.
624    pub fn mi_prof_dump_proto(path: *const c_char) -> bool;
625    pub fn mi_prof_dump_proto_writer(write: Option<MiProfWriteFun>, arg: *mut c_void) -> bool;
626    pub fn mi_prof_reset();
627    pub fn mi_prof_debug_stats(records: *mut usize, bytes: *mut usize, unique_stacks: *mut usize);
628    pub fn mi_prof_stats_get(stats: *mut mi_prof_stats_t) -> bool;
629    pub fn mi_prof_visit(visitor: mi_prof_visit_fun, arg: *mut c_void) -> bool;
630    pub fn mi_prof_snapshot_new() -> *mut mi_prof_snapshot_t;
631    pub fn mi_prof_snapshot_visit(
632        snap: *const mi_prof_snapshot_t,
633        visitor: mi_prof_visit_fun,
634        arg: *mut c_void,
635    ) -> bool;
636    pub fn mi_prof_snapshot_free(snap: *mut mi_prof_snapshot_t);
637    /// Structured module (mapping) enumeration, e.g. to build pprof Mapping entries
638    /// yourself. No profiler lock is taken: module lists are OS-owned, not part of
639    /// the sampled-allocation table. `info` (and `info->path`) are valid only for
640    /// the duration of the callback.
641    pub fn mi_prof_modules_visit(visitor: mi_prof_module_visit_fun, arg: *mut c_void) -> bool;
642
643    /// Mirrors `mi_unwrapped_malloc` (include/mimalloc/memory-events.h): backed
644    /// directly by the raw OS layer, never by the hooked `mi_malloc` family.
645    /// See that header's "Stable public unwrapped instrumentation allocation
646    /// path" comment for the full contract.
647    pub fn mi_unwrapped_malloc(size: usize, alignment: usize) -> *mut c_void;
648    /// Mirrors `mi_unwrapped_free` (include/mimalloc/memory-events.h).
649    pub fn mi_unwrapped_free(p: *mut c_void);
650    /// Mirrors `mi_unwrapped_realloc` (include/mimalloc/memory-events.h).
651    pub fn mi_unwrapped_realloc(p: *mut c_void, new_size: usize, alignment: usize) -> *mut c_void;
652
653    /// Live per-heap -> per-page -> (optional) per-block JSON snapshot (issue #269, Bun
654    /// parity P4). Backs Bun's shipped `bun:jsc` `heapStats({dump:true|"blocks"})`. Returns
655    /// NULL on allocation failure; a non-NULL result is `mi_malloc`-family memory the
656    /// caller must free with `mi_free` (see `prof::heap_dump_json` for the safe wrapper).
657    pub fn mi_heap_dump_json(include_blocks: bool, hash_addresses: bool) -> *mut c_char;
658    /// Mirrors `mi_heap_get_seq` (include/mimalloc-stats.h): the monotonic sequence
659    /// number assigned to `heap` at creation, or 0 for a NULL heap.
660    pub fn mi_heap_get_seq(heap: *mut mi_heap_t) -> usize;
661
662    /// Mirrors `mi_on_thread_idle` (issue #272, Bun parity P7a).
663    pub fn mi_on_thread_idle();
664    /// Mirrors `mi_on_thread_idle_start`; `false` means nothing was handed off and
665    /// `mi_on_thread_idle_end` must NOT be called.
666    pub fn mi_on_thread_idle_start() -> bool;
667    /// Mirrors `mi_on_thread_idle_end`.
668    pub fn mi_on_thread_idle_end();
669    /// Mirrors `mi_scavenger_stop` (issue #272).
670    pub fn mi_scavenger_stop();
671    /// Mirrors `mi_purge_holes_stats_get` (issue #272, Bun parity P7b).
672    pub fn mi_purge_holes_stats_get(stats: *mut MiPurgeHolesStats);
673
674    /// Mirrors `mi_purge_holes_report` (issue #272, Bun parity P7b): prints, per size
675    /// class, the free bytes hole purging could NOT discard. Read-only; purges nothing.
676    pub fn mi_purge_holes_report();
677
678    // ---- include/mimalloc.h: options ----
679    /// Mirrors `mi_option_is_enabled`.
680    pub fn mi_option_is_enabled(option: mi_option_t) -> bool;
681    /// Mirrors `mi_option_enable`.
682    pub fn mi_option_enable(option: mi_option_t);
683    /// Mirrors `mi_option_disable`.
684    pub fn mi_option_disable(option: mi_option_t);
685    /// Mirrors `mi_option_set_enabled`.
686    pub fn mi_option_set_enabled(option: mi_option_t, enable: bool);
687    /// Mirrors `mi_option_set_enabled_default`.
688    pub fn mi_option_set_enabled_default(option: mi_option_t, enable: bool);
689    /// Mirrors `mi_option_get`.
690    pub fn mi_option_get(option: mi_option_t) -> core::ffi::c_long;
691    /// Mirrors `mi_option_get_clamp`.
692    pub fn mi_option_get_clamp(
693        option: mi_option_t,
694        min: core::ffi::c_long,
695        max: core::ffi::c_long,
696    ) -> core::ffi::c_long;
697    /// Mirrors `mi_option_get_size`.
698    pub fn mi_option_get_size(option: mi_option_t) -> usize;
699    /// Mirrors `mi_option_set`.
700    pub fn mi_option_set(option: mi_option_t, value: core::ffi::c_long);
701    /// Mirrors `mi_option_set_default`.
702    pub fn mi_option_set_default(option: mi_option_t, value: core::ffi::c_long);
703    /// Mirrors `mi_options_print_out`: prints every option's current value.
704    pub fn mi_options_print_out(out: Option<mi_output_fun>, arg: *mut c_void);
705
706    // ---- include/mimalloc-stats.h: exact statistics ----
707    /// Mirrors `mi_stats_get`: aggregated stats for the current subprocess and its heaps.
708    pub fn mi_stats_get(stats: *mut mi_stats_t) -> bool;
709    /// Mirrors `mi_stats_get_json`. With `buf == NULL` the result is `mi_malloc`-family
710    /// memory the caller must release with [`mi_free`].
711    pub fn mi_stats_get_json(buf_size: usize, buf: *mut c_char) -> *mut c_char;
712    /// Mirrors `mi_stats_as_json`: render an already-captured [`mi_stats_t`].
713    pub fn mi_stats_as_json(
714        stats: *mut mi_stats_t,
715        buf_size: usize,
716        buf: *mut c_char,
717    ) -> *mut c_char;
718    /// Mirrors `mi_stats_print_out`.
719    pub fn mi_stats_print_out(out: Option<mi_output_fun>, arg: *mut c_void);
720    /// Mirrors `mi_stats_get_bin_size`: the block size served by size bin `bin`.
721    pub fn mi_stats_get_bin_size(bin: usize) -> usize;
722    /// Mirrors `mi_heap_stats_get`.
723    pub fn mi_heap_stats_get(heap: *mut mi_heap_t, stats: *mut mi_stats_t) -> bool;
724    /// Mirrors `mi_heap_stats_get_json`.
725    pub fn mi_heap_stats_get_json(
726        heap: *mut mi_heap_t,
727        buf_size: usize,
728        buf: *mut c_char,
729    ) -> *mut c_char;
730    /// Mirrors `mi_heap_stats_print_out`.
731    pub fn mi_heap_stats_print_out(
732        heap: *mut mi_heap_t,
733        out: Option<mi_output_fun>,
734        arg: *mut c_void,
735    );
736    /// Mirrors `mi_heap_stats_merge_to_subproc`: fold a heap's stats into its subprocess
737    /// and clear the heap's own.
738    pub fn mi_heap_stats_merge_to_subproc(heap: *mut mi_heap_t);
739    /// Mirrors `mi_subproc_stats_get`.
740    pub fn mi_subproc_stats_get(subproc_id: mi_subproc_id_t, stats: *mut mi_stats_t) -> bool;
741    /// Mirrors `mi_subproc_stats_get_exclusive`: the subprocess's own stats, without
742    /// aggregating its heaps.
743    pub fn mi_subproc_stats_get_exclusive(
744        subproc_id: mi_subproc_id_t,
745        stats: *mut mi_stats_t,
746    ) -> bool;
747    /// Mirrors `mi_subproc_stats_get_json`.
748    pub fn mi_subproc_stats_get_json(
749        subproc_id: mi_subproc_id_t,
750        buf_size: usize,
751        buf: *mut c_char,
752    ) -> *mut c_char;
753    /// Mirrors `mi_subproc_stats_print_out`.
754    pub fn mi_subproc_stats_print_out(
755        subproc_id: mi_subproc_id_t,
756        out: Option<mi_output_fun>,
757        arg: *mut c_void,
758    );
759    /// Mirrors `mi_subproc_heap_stats_print_out`: the subprocess and each of its heaps,
760    /// printed separately.
761    pub fn mi_subproc_heap_stats_print_out(
762        subproc_id: mi_subproc_id_t,
763        out: Option<mi_output_fun>,
764        arg: *mut c_void,
765    );
766    /// Mirrors `mi_subproc_main`: the process-wide default subprocess.
767    pub fn mi_subproc_main() -> mi_subproc_id_t;
768    /// Mirrors `mi_subproc_current`: the subprocess this thread belongs to.
769    pub fn mi_subproc_current() -> mi_subproc_id_t;
770
771    // ---- include/mimalloc/memory-events.h ----
772    /// Mirrors `mi_memory_tracking_set_enabled`; returns the previous state. An explicit
773    /// call is always authoritative over the `MIMALLOC_MEMORY_EVENTS` environment read.
774    pub fn mi_memory_tracking_set_enabled(enabled: bool) -> bool;
775    /// Mirrors `mi_memory_tracking_is_enabled`.
776    pub fn mi_memory_tracking_is_enabled() -> bool;
777    /// Mirrors `mi_memory_set_callbacks`. `callbacks == NULL` clears the table. The `arg`
778    /// pointers are caller-owned and must stay valid until replaced or cleared.
779    pub fn mi_memory_set_callbacks(callbacks: *const mi_memory_callbacks_t) -> bool;
780    /// Mirrors `mi_memory_snapshot`; fill `size`/`version` in before calling.
781    pub fn mi_memory_snapshot(out: *mut mi_memory_snapshot_t) -> bool;
782    /// Mirrors `mi_memory_visit_live_allocations`. Best effort, not a consistent global
783    /// snapshot; the visitor must not allocate, free, or reenter mimalloc.
784    pub fn mi_memory_visit_live_allocations(
785        visitor: mi_memory_allocation_visit_fun,
786        arg: *mut c_void,
787    ) -> bool;
788
789    /// Publishes what the C compiler laid out for every mirrored type, constant and
790    /// option in this module. Defined by `layout_probe.c` (a rust/-side verification
791    /// artifact, not part of the C library) and consumed by `tests/t19_layout.rs`.
792    pub fn mi_rs_layout_table(count: *mut usize) -> *const MiRsLayoutEntry;
793}