mimalloc-pprof 1.1.0

mimalloc global allocator with pprof-compatible sampled heap profiling
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
/* ABI layout probe for the Rust mirrors in src/sys.rs (issue: C/Rust API parity).

   Every `#[repr(C)]` struct in `src/sys.rs` is a hand-written mirror of a C struct, and
   every `mi_option_*` constant there is a hand-written mirror of an enumerator whose
   value is positional. Both are silent-corruption hazards: a mirror that drifts writes
   the wrong field, or sets the wrong option, with no diagnostic anywhere.

   So the C side publishes what it actually laid out. This file exports a name/value
   table -- `sizeof:<type>`, `offset:<type>.<field>`, `option:<enumerator>`, and the
   versioned/sentinel constants -- which `tests/t19_layout.rs` looks up BY NAME and
   compares against `core::mem::size_of` / `core::mem::offset_of!` and the Rust
   constants. Lookup by name (rather than by index) is deliberate: a field renamed or
   dropped on either side fails as "missing key", never as a coincidentally-equal number.

   This file lives under rust/ on purpose: it is a binding-verification artifact, not
   part of the C library, so it never enters the CMake build or the amalgamation. It is
   compiled into the crate's static library but referenced only from the test, so the
   linker drops it from ordinary consumers.

   Nothing here is gated on MI_PPROF: the profiler's public *types* are declared
   unconditionally in include/mimalloc/profile.h (only the implementation is gated), so
   the layout the mirrors must match is the same in both feature modes. */

#include <stddef.h>

#include "mimalloc-pprof-amalgamated.h"
#include "mimalloc-stats.h"

/* ---------------------------------------------------------------------------------------
   #322: SIGNATURES, not just layouts.

   The layout table below proves every `#[repr(C)]` mirror in src/sys.rs agrees with the C
   compiler about sizes and offsets, and ci/check_rust_surface.py proves every fork export
   exists in sys.rs and is called from lib.rs. Neither looks at a function's SIGNATURE:
   sys.rs declares each prototype by hand, the crate links against a bare symbol name, and
   the Rust declaration is simply believed. A parameter added, a `size_t` widened, a `bool`
   become `int`, two arguments swapped -- nothing here would have noticed, and on most ABIs
   the result is silent argument corruption. That is exactly the failure class the layout
   probe was added to eliminate for structs, and this fork re-pins its overlay to newer
   upstream/dev3 commits, which is the door such a change arrives through.

   So each line below is the signature src/sys.rs assumes, spelled in C, AFTER the real
   headers are included above. If a real prototype disagrees, this file stops the build
   with a redeclaration conflict that names the offending function -- rather than passing
   arguments through a wrong ABI at run time. Nothing calls these; they exist to be
   compared by the compiler.

   Generated from the `unsafe extern "C"` block in src/sys.rs; keep them in step when that
   block changes (a declaration removed here is a gap, not a fix). */

void* mi_malloc(size_t size);
void* mi_zalloc(size_t size);
void* mi_calloc(size_t count, size_t size);
void* mi_realloc(void* p, size_t newsize);
void mi_free(void* p);
void* mi_malloc_aligned(size_t size, size_t alignment);
void* mi_zalloc_aligned(size_t size, size_t alignment);
void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment);
void* mi_rezalloc(void* p, size_t newsize);
void* mi_recalloc(void* p, size_t newcount, size_t size);
void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment);
void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment);
void* mi_expand(void* p, size_t newsize);
size_t mi_usable_size(const void* p);
bool mi_dhat_start(void);
void mi_dhat_stop(void);
bool mi_dhat_is_enabled(void);
bool mi_dhat_stats_get(mi_dhat_stats_t* stats);
bool mi_dhat_dump(const char* path);
bool mi_prof_start(size_t sample_rate);
bool mi_prof_start_seeded(size_t sample_rate, uint64_t seed);
bool mi_prof_start_ex(const mi_prof_config_t* config);
void mi_prof_stop(void);
bool mi_prof_is_enabled(void);
bool mi_prof_dump(const char* path);
bool mi_prof_dump_writer(mi_prof_write_fun* write, void* arg);
bool mi_prof_dump_proto(const char* path);
bool mi_prof_dump_proto_writer(mi_prof_write_fun* write, void* arg);
void mi_prof_reset(void);
void mi_prof_debug_stats(size_t* records, size_t* bytes, size_t* unique_stacks);
bool mi_prof_stats_get(mi_prof_stats_t* stats);
bool mi_prof_visit(mi_prof_visit_fun* visitor, void* arg);
mi_prof_snapshot_t* mi_prof_snapshot_new(void);
bool mi_prof_snapshot_visit(const mi_prof_snapshot_t* snap, mi_prof_visit_fun* visitor, void* arg);
void mi_prof_snapshot_free(mi_prof_snapshot_t* snap);
bool mi_prof_modules_visit(mi_prof_module_visit_fun* visitor, void* arg);
void* mi_unwrapped_malloc(size_t size, size_t alignment);
void mi_unwrapped_free(void* p);
void* mi_unwrapped_realloc(void* p, size_t new_size, size_t alignment);
char* mi_heap_dump_json(bool include_blocks, bool hash_addresses);
char* mi_heap_dump_json_ex(bool include_blocks, bool hash_addresses, size_t wait_ms);
size_t mi_heap_get_seq(mi_heap_t* heap);
int mi_heap_snapshot(int fd, unsigned int flags);
int mi_heap_snapshot_to_file(const char* path, unsigned int flags);
void mi_on_thread_idle(void);
bool mi_on_thread_idle_start(void);
void mi_on_thread_idle_end(void);
void mi_scavenger_stop(void);
void mi_purge_holes_stats_get(mi_purge_holes_stats_t* stats);
void mi_purge_holes_report(void);
int mi_purge_all_ex(mi_purge_flags_t flags, size_t wait_ms, mi_purge_all_report_t* report);
void mi_purge_all(bool force);
bool mi_option_is_enabled(mi_option_t option);
void mi_option_enable(mi_option_t option);
void mi_option_disable(mi_option_t option);
void mi_option_set_enabled(mi_option_t option, bool enable);
void mi_option_set_enabled_default(mi_option_t option, bool enable);
long mi_option_get(mi_option_t option);
long mi_option_get_clamp(mi_option_t option, long min, long max);
size_t mi_option_get_size(mi_option_t option);
void mi_option_set(mi_option_t option, long value);
void mi_option_set_default(mi_option_t option, long value);
void mi_options_print_out(mi_output_fun* out, void* arg);
bool mi_stats_get(mi_stats_t* stats);
char* mi_stats_get_json(size_t buf_size, char* buf);
char* mi_stats_as_json(mi_stats_t* stats, size_t buf_size, char* buf);
void mi_stats_print_out(mi_output_fun* out, void* arg);
size_t mi_stats_get_bin_size(size_t bin);
bool mi_heap_stats_get(mi_heap_t* heap, mi_stats_t* stats);
char* mi_heap_stats_get_json(mi_heap_t* heap, size_t buf_size, char* buf);
void mi_heap_stats_print_out(mi_heap_t* heap, mi_output_fun* out, void* arg);
void mi_heap_stats_merge_to_subproc(mi_heap_t* heap);
bool mi_subproc_stats_get(mi_subproc_id_t subproc_id, mi_stats_t* stats);
bool mi_subproc_stats_get_exclusive(mi_subproc_id_t subproc_id, mi_stats_t* stats);
char* mi_subproc_stats_get_json(mi_subproc_id_t subproc_id, size_t buf_size, char* buf);
void mi_subproc_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg);
void mi_subproc_heap_stats_print_out(mi_subproc_id_t subproc_id, mi_output_fun* out, void* arg);
mi_subproc_id_t mi_subproc_main(void);
mi_subproc_id_t mi_subproc_current(void);
bool mi_memory_tracking_set_enabled(bool enabled);
bool mi_memory_tracking_is_enabled(void);
bool mi_memory_snapshot(mi_memory_snapshot_t* out);
bool mi_memory_set_callbacks(const mi_memory_callbacks_t* callbacks);
bool mi_memory_visit_live_allocations(mi_memory_allocation_visit_fun* visitor, void* arg);

typedef struct mi_rs_layout_entry_s {
  const char* name;
  size_t      value;
} mi_rs_layout_entry_t;

#define MI_RS_SIZEOF(T)      { "sizeof:" #T, sizeof(T) },
#define MI_RS_OFFSET(T, F)   { "offset:" #T "." #F, offsetof(T, F) },
#define MI_RS_CONST(N)       { "const:" #N, (size_t)(N) },
#define MI_RS_OPTION(N)      { "option:" #N, (size_t)(N) },

static const mi_rs_layout_entry_t mi_rs_layout_entries[] = {
  /* ---- include/mimalloc/profile.h ---- */
  MI_RS_SIZEOF(mi_prof_stats_t)
  MI_RS_OFFSET(mi_prof_stats_t, size)
  MI_RS_OFFSET(mi_prof_stats_t, version)
  MI_RS_OFFSET(mi_prof_stats_t, enabled)
  MI_RS_OFFSET(mi_prof_stats_t, accum)
  MI_RS_OFFSET(mi_prof_stats_t, sample_rate)
  MI_RS_OFFSET(mi_prof_stats_t, live_samples)
  MI_RS_OFFSET(mi_prof_stats_t, live_bytes)
  MI_RS_OFFSET(mi_prof_stats_t, accum_samples)
  MI_RS_OFFSET(mi_prof_stats_t, accum_bytes)
  MI_RS_OFFSET(mi_prof_stats_t, unique_stacks)
  MI_RS_OFFSET(mi_prof_stats_t, arena_committed)
  MI_RS_OFFSET(mi_prof_stats_t, stack_table_overflows)
  MI_RS_OFFSET(mi_prof_stats_t, dropped_samples)
  MI_RS_OFFSET(mi_prof_stats_t, heap_committed)
  MI_RS_OFFSET(mi_prof_stats_t, heap_reserved)
  MI_RS_OFFSET(mi_prof_stats_t, heap_malloc_requested)
  MI_RS_OFFSET(mi_prof_stats_t, heap_pages)
  MI_RS_OFFSET(mi_prof_stats_t, heap_pages_abandoned)
  MI_RS_OFFSET(mi_prof_stats_t, heap_count)
  MI_RS_OFFSET(mi_prof_stats_t, theap_count)
  MI_RS_OFFSET(mi_prof_stats_t, heap_purged)
  MI_RS_OFFSET(mi_prof_stats_t, heap_stats_detailed)

  MI_RS_SIZEOF(mi_prof_config_t)
  MI_RS_OFFSET(mi_prof_config_t, size)
  MI_RS_OFFSET(mi_prof_config_t, version)
  MI_RS_OFFSET(mi_prof_config_t, mode)
  MI_RS_OFFSET(mi_prof_config_t, sample_interval)
  MI_RS_OFFSET(mi_prof_config_t, max_profiler_bytes)
  MI_RS_OFFSET(mi_prof_config_t, seed)
  MI_RS_OFFSET(mi_prof_config_t, accum)
  MI_RS_OFFSET(mi_prof_config_t, max_stack_depth)
  MI_RS_OFFSET(mi_prof_config_t, dump_at_exit)
  MI_RS_OFFSET(mi_prof_config_t, dump_format)

  MI_RS_SIZEOF(mi_prof_sample_info_t)
  MI_RS_OFFSET(mi_prof_sample_info_t, stack)
  MI_RS_OFFSET(mi_prof_sample_info_t, depth)
  MI_RS_OFFSET(mi_prof_sample_info_t, live_objects)
  MI_RS_OFFSET(mi_prof_sample_info_t, live_bytes)
  MI_RS_OFFSET(mi_prof_sample_info_t, accum_objects)
  MI_RS_OFFSET(mi_prof_sample_info_t, accum_bytes)

  MI_RS_SIZEOF(mi_prof_module_info_t)
  MI_RS_OFFSET(mi_prof_module_info_t, path)
  MI_RS_OFFSET(mi_prof_module_info_t, base)
  MI_RS_OFFSET(mi_prof_module_info_t, size)

  MI_RS_CONST(MI_PROF_STAT_VERSION)
  MI_RS_CONST(MI_PROF_CONFIG_VERSION)
  MI_RS_CONST(MI_PROF_FORMAT_TEXT)
  MI_RS_CONST(MI_PROF_FORMAT_PROTO)
  MI_RS_CONST(MI_PROF_CONFIG_FALLBACK)
  MI_RS_CONST(MI_PROF_CONFIG_OVERRIDE)

  /* ---- include/mimalloc/dhat.h ---- */
  MI_RS_SIZEOF(mi_dhat_stats_t)
  MI_RS_OFFSET(mi_dhat_stats_t, size)
  MI_RS_OFFSET(mi_dhat_stats_t, version)
  MI_RS_OFFSET(mi_dhat_stats_t, enabled)
  MI_RS_OFFSET(mi_dhat_stats_t, incomplete)
  MI_RS_OFFSET(mi_dhat_stats_t, total_bytes)
  MI_RS_OFFSET(mi_dhat_stats_t, total_blocks)
  MI_RS_OFFSET(mi_dhat_stats_t, live_bytes)
  MI_RS_OFFSET(mi_dhat_stats_t, live_blocks)
  MI_RS_OFFSET(mi_dhat_stats_t, peak_bytes)
  MI_RS_OFFSET(mi_dhat_stats_t, peak_blocks)
  MI_RS_OFFSET(mi_dhat_stats_t, dropped)
  MI_RS_OFFSET(mi_dhat_stats_t, internal_bytes)
  MI_RS_CONST(MI_DHAT_STATS_VERSION)

  /* ---- include/mimalloc/memory-events.h ---- */
  MI_RS_SIZEOF(mi_memory_change_t)
  MI_RS_OFFSET(mi_memory_change_t, kind)
  MI_RS_OFFSET(mi_memory_change_t, total_bytes)
  MI_RS_OFFSET(mi_memory_change_t, delta_bytes)
  MI_RS_OFFSET(mi_memory_change_t, request_size)

  MI_RS_SIZEOF(mi_memory_callbacks_t)
  MI_RS_OFFSET(mi_memory_callbacks_t, handlers)
  MI_RS_OFFSET(mi_memory_callbacks_t, args)

  MI_RS_SIZEOF(mi_memory_snapshot_t)
  MI_RS_OFFSET(mi_memory_snapshot_t, size)
  MI_RS_OFFSET(mi_memory_snapshot_t, version)
  MI_RS_OFFSET(mi_memory_snapshot_t, live_bytes)
  MI_RS_OFFSET(mi_memory_snapshot_t, accum_bytes)
  MI_RS_OFFSET(mi_memory_snapshot_t, live_count)
  MI_RS_OFFSET(mi_memory_snapshot_t, accum_count)

  MI_RS_CONST(MI_MEMORY_SNAPSHOT_VERSION)
  MI_RS_CONST(MI_MEMORY_ALLOCATE)
  MI_RS_CONST(MI_MEMORY_FREE)
  MI_RS_CONST(MI_MEMORY_RESIZE)
  MI_RS_CONST(MI_MEMORY_CHANGE_COUNT)

  /* ---- include/mimalloc.h: hole purging (issue #272, Bun parity P7b) ---- */
  MI_RS_SIZEOF(mi_purge_holes_stats_t)
  MI_RS_OFFSET(mi_purge_holes_stats_t, purged_bytes)
  MI_RS_OFFSET(mi_purge_holes_stats_t, purged_blocks)
  MI_RS_OFFSET(mi_purge_holes_stats_t, purged_bytes_total)
  MI_RS_OFFSET(mi_purge_holes_stats_t, discard_calls)
  MI_RS_OFFSET(mi_purge_holes_stats_t, reuse_calls)
  MI_RS_OFFSET(mi_purge_holes_stats_t, pages_freed)
  MI_RS_OFFSET(mi_purge_holes_stats_t, ineligible_pages)
  MI_RS_OFFSET(mi_purge_holes_stats_t, ineligible_bytes)
  MI_RS_OFFSET(mi_purge_holes_stats_t, ineligible_free_bytes)
  MI_RS_OFFSET(mi_purge_holes_stats_t, unformed_bytes)
  MI_RS_OFFSET(mi_purge_holes_stats_t, unformed_bytes_total)
  MI_RS_OFFSET(mi_purge_holes_stats_t, unformed_discard_calls)
  MI_RS_OFFSET(mi_purge_holes_stats_t, unformed_reuse_calls)
  MI_RS_OFFSET(mi_purge_holes_stats_t, pages_skipped)
  MI_RS_OFFSET(mi_purge_holes_stats_t, blocks_visited)
  MI_RS_OFFSET(mi_purge_holes_stats_t, full_sweeps)

  /* ---- include/mimalloc.h: mi_purge_all (issue #366) ---- */
  MI_RS_SIZEOF(mi_purge_all_report_t)
  MI_RS_OFFSET(mi_purge_all_report_t, arena_bytes)
  MI_RS_OFFSET(mi_purge_all_report_t, hole_bytes)
  MI_RS_OFFSET(mi_purge_all_report_t, theaps_swept)
  MI_RS_OFFSET(mi_purge_all_report_t, theaps_pending)
  MI_RS_OFFSET(mi_purge_all_report_t, theaps_orphaned)
  MI_RS_OFFSET(mi_purge_all_report_t, gated)
  MI_RS_OFFSET(mi_purge_all_report_t, complete)
  MI_RS_OFFSET(mi_purge_all_report_t, arenas_reclaimed)
  MI_RS_OFFSET(mi_purge_all_report_t, arena_reclaim_bytes)
  MI_RS_OFFSET(mi_purge_all_report_t, arenas_kept)
  MI_RS_OFFSET(mi_purge_all_report_t, subprocs_pending)
  MI_RS_OFFSET(mi_purge_all_report_t, reclaimed)
  MI_RS_SIZEOF(mi_purge_flags_t)
  MI_RS_CONST(MI_PURGE_FORCE)
  MI_RS_CONST(MI_PURGE_RECLAIM)
  MI_RS_CONST(MI_PURGE_OK)
  MI_RS_CONST(MI_PURGE_PARTIAL)
  MI_RS_CONST(MI_PURGE_BUSY)

  /* ---- include/mimalloc-stats.h ---- */
  MI_RS_SIZEOF(mi_stat_count_t)
  MI_RS_OFFSET(mi_stat_count_t, total)
  MI_RS_OFFSET(mi_stat_count_t, peak)
  MI_RS_OFFSET(mi_stat_count_t, current)
  MI_RS_SIZEOF(mi_stat_counter_t)
  MI_RS_OFFSET(mi_stat_counter_t, total)

  MI_RS_SIZEOF(mi_stats_t)
  MI_RS_OFFSET(mi_stats_t, size)
  MI_RS_OFFSET(mi_stats_t, version)
  /* Generated straight from the header's own field list, so a field added or reordered
     upstream shows up here without this file being touched -- and then fails the Rust
     test as a missing/moved key, which is exactly the intent. */
#define MI_STAT_COUNT(stat)     MI_RS_OFFSET(mi_stats_t, stat)
#define MI_STAT_COUNTER(stat)   MI_RS_OFFSET(mi_stats_t, stat)
  MI_STAT_FIELDS()
#undef MI_STAT_COUNT
#undef MI_STAT_COUNTER
  MI_RS_OFFSET(mi_stats_t, _stat_reserved)
  MI_RS_OFFSET(mi_stats_t, _stat_counter_reserved)
  MI_RS_OFFSET(mi_stats_t, malloc_bins)
  MI_RS_OFFSET(mi_stats_t, page_bins)
  MI_RS_OFFSET(mi_stats_t, chunk_bins)
  MI_RS_CONST(MI_STAT_VERSION)
  MI_RS_CONST(MI_BIN_HUGE)
  MI_RS_CONST(MI_CBIN_COUNT)

  MI_RS_SIZEOF(mi_subproc_id_t)

  /* ---- include/mimalloc.h: mi_option_t ----
     Positional and silently wrong when mirrored stale: the fork inserted thirteen
     enumerators (prof_*, memory_events, purge_zeroes, scavenger, purge_holes*) BEFORE
     `_mi_option_last`, so any Rust mirror copied from upstream sets a different option
     than the caller named. Every enumerator the Rust side mirrors is listed here. */
  MI_RS_OPTION(mi_option_show_errors)
  MI_RS_OPTION(mi_option_show_stats)
  MI_RS_OPTION(mi_option_verbose)
  MI_RS_OPTION(mi_option_deprecated_eager_commit)
  MI_RS_OPTION(mi_option_arena_eager_commit)
  MI_RS_OPTION(mi_option_purge_decommits)
  MI_RS_OPTION(mi_option_allow_large_os_pages)
  MI_RS_OPTION(mi_option_reserve_huge_os_pages)
  MI_RS_OPTION(mi_option_reserve_huge_os_pages_at)
  MI_RS_OPTION(mi_option_reserve_os_memory)
  MI_RS_OPTION(mi_option_deprecated_segment_cache)
  MI_RS_OPTION(mi_option_deprecated_page_reset)
  MI_RS_OPTION(mi_option_deprecated_abandoned_page_purge)
  MI_RS_OPTION(mi_option_deprecated_segment_reset)
  MI_RS_OPTION(mi_option_deprecated_eager_commit_delay)
  MI_RS_OPTION(mi_option_purge_delay)
  MI_RS_OPTION(mi_option_use_numa_nodes)
  MI_RS_OPTION(mi_option_disallow_os_alloc)
  MI_RS_OPTION(mi_option_os_tag)
  MI_RS_OPTION(mi_option_max_errors)
  MI_RS_OPTION(mi_option_max_warnings)
  MI_RS_OPTION(mi_option_deprecated_max_segment_reclaim)
  MI_RS_OPTION(mi_option_destroy_on_exit)
  MI_RS_OPTION(mi_option_arena_reserve)
  MI_RS_OPTION(mi_option_arena_purge_mult)
  MI_RS_OPTION(mi_option_deprecated_purge_extend_delay)
  MI_RS_OPTION(mi_option_disallow_arena_alloc)
  MI_RS_OPTION(mi_option_retry_on_oom)
  MI_RS_OPTION(mi_option_deprecated_visit_abandoned)
  MI_RS_OPTION(mi_option_guarded_min)
  MI_RS_OPTION(mi_option_guarded_max)
  MI_RS_OPTION(mi_option_guarded_precise)
  MI_RS_OPTION(mi_option_guarded_sample_rate)
  MI_RS_OPTION(mi_option_guarded_sample_seed)
  MI_RS_OPTION(mi_option_generic_collect)
  MI_RS_OPTION(mi_option_page_reclaim_on_free)
  MI_RS_OPTION(mi_option_page_full_retain)
  MI_RS_OPTION(mi_option_page_max_candidates)
  MI_RS_OPTION(mi_option_max_vabits)
  MI_RS_OPTION(mi_option_pagemap_commit)
  MI_RS_OPTION(mi_option_page_commit_on_demand)
  MI_RS_OPTION(mi_option_page_max_reclaim)
  MI_RS_OPTION(mi_option_page_cross_thread_max_reclaim)
  MI_RS_OPTION(mi_option_allow_thp)
  MI_RS_OPTION(mi_option_minimal_purge_size)
  MI_RS_OPTION(mi_option_arena_max_object_size)
  MI_RS_OPTION(mi_option_arena_is_numa_local)
  /* fork additions start here (indices 47..59) */
  MI_RS_OPTION(mi_option_prof)
  MI_RS_OPTION(mi_option_prof_sample_rate)
  MI_RS_OPTION(mi_option_prof_bt_max)
  MI_RS_OPTION(mi_option_prof_accum)
  MI_RS_OPTION(mi_option_prof_seed)
  MI_RS_OPTION(mi_option_prof_max_bytes)
  MI_RS_OPTION(mi_option_memory_events)
  MI_RS_OPTION(mi_option_purge_zeroes)
  MI_RS_OPTION(mi_option_scavenger)
  MI_RS_OPTION(mi_option_purge_holes)
  MI_RS_OPTION(mi_option_purge_holes_eager_zero)
  MI_RS_OPTION(mi_option_purge_holes_min_interval)
  MI_RS_OPTION(mi_option_purge_holes_full_every)
  MI_RS_OPTION(mi_option_snapshot_on_exit)
  MI_RS_OPTION(mi_option_page_reserve)
  MI_RS_OPTION(mi_option_resident_first)
  MI_RS_OPTION(mi_option_large_span)
  MI_RS_OPTION(mi_option_large_span_max)
  MI_RS_OPTION(_mi_option_last)
  /* deprecated aliases, defined after the sentinel with explicit values */
  MI_RS_OPTION(mi_option_large_os_pages)
  MI_RS_OPTION(mi_option_eager_region_commit)
  MI_RS_OPTION(mi_option_reset_decommits)
  MI_RS_OPTION(mi_option_reset_delay)
  MI_RS_OPTION(mi_option_limit_os_alloc)
};

/* Returns the entry table and writes its length to `*count`. The table is static
   `const` data with static-string names; the caller never frees anything. */
/* #322: this probe's own export gets the same treatment as the API it checks -- the
   prototype src/sys.rs assumes, so the definition below is compared against it. It sits
   here rather than in the block above because it needs the entry typedef. */
const mi_rs_layout_entry_t* mi_rs_layout_table(size_t* count);

mi_decl_export const mi_rs_layout_entry_t* mi_rs_layout_table(size_t* count) mi_attr_noexcept {
  if (count != NULL) { *count = sizeof(mi_rs_layout_entries) / sizeof(mi_rs_layout_entries[0]); }
  return mi_rs_layout_entries;
}