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