mimalloc-pprof 0.10.0

mimalloc global allocator with pprof-compatible sampled heap profiling
Documentation
# mimalloc-pprof — single-file C amalgamation

[github.com/zackees/mimalloc-pprof](https://github.com/zackees/mimalloc-pprof)

This directory packages [mimalloc](https://github.com/microsoft/mimalloc) (Microsoft's
general-purpose allocator) plus this fork's additions — a pprof-compatible statistical
sampling heap profiler and an opt-in memory-event accounting/callback API (issue #20) — as
one `.h` + `.c` pair (plus two small verbatim support headers, see below), for C/C++
projects that want the allocator and profiler without integrating this repo's CMake build.

If you're building this repo itself (CMake, tests, the Rust crate), you don't need this
directory — it's a release artifact. See the repo root `readme-upstream.md` and `CLAUDE.md`
for that workflow.

## What's in here

- `mimalloc-pprof-amalgamated.h` — every public declaration (`mimalloc.h` +
  `mimalloc/profile.h` + `mimalloc/memory-events.h`) concatenated into one header.
- `mimalloc-pprof-amalgamated.c` — the entire implementation (`src/static.c` and everything
  it locally `#include`s) as a single translation unit.
- `mimalloc.h`, `mimalloc-stats.h` — two small verbatim support headers. The amalgamated
  `.c` resolves a couple of `#include <angle-bracket>` lines (not rewritten by the
  amalgamation step, since those are indistinguishable from real system headers) against
  these two files, so they must stay physically present, unmodified, and **in the same
  directory** as the two amalgamated files — don't rename or relocate them independently.

Both amalgamated files are generated by `rust/xtask` (`cargo run -p xtask -- amalgamate-c`
/ `amalgamate-h`) from the real sources in `src/` and `include/`, and checked into the repo
so they're always current and reviewable like any other file — see the header comment at
the top of each generated file for the exact source commit.

## How to use it

1. Copy all four files (`mimalloc-pprof-amalgamated.c`, `mimalloc-pprof-amalgamated.h`,
   `mimalloc.h`, `mimalloc-stats.h`) into your project, keeping them together in one
   directory.
2. `#include "mimalloc-pprof-amalgamated.h"` wherever you need the allocator or profiler
   API.
3. Compile `mimalloc-pprof-amalgamated.c` as one extra translation unit and link it into
   your binary, with **both** of the following:
   - **`-I<that directory>`** (e.g. `-I.` if it's your compiler's working directory).
     `mimalloc-pprof-amalgamated.c` resolves `mimalloc.h`/`mimalloc-stats.h` via
     `#include <angle-bracket>` (see "What's in here" above for why); unlike `"quoted"`
     includes, compilers do **not** automatically search the including file's own
     directory for `<angle-bracket>` includes, only `-I` paths and system include
     directories — omitting this flag fails the build with
     `fatal error: mimalloc-stats.h: No such file or directory`.
   - **`-DMI_PPROF=1`**. Without this define, the profiler and memory-event APIs compile
     to no-op stubs (`mi_prof_start`/`mi_prof_start_ex`/etc. all silently return `false`,
     never fail to *compile*) — only the base allocator (`mi_malloc`/`mi_free`/...) is
     unconditionally live. If you only want the allocator, you can skip this define; if
     you want profiling or memory-event accounting, it is required. (This mirrors
     `rust/mimalloc-pprof/build.rs`'s own `cc::Build` invocation, which always sets it.)

   Example: `gcc -c mimalloc-pprof-amalgamated.c -o mimalloc-pprof.o -I. -DMI_PPROF=1`,
   then link `mimalloc-pprof.o` into your binary (add `-lpthread` on Linux; Windows needs
   `psapi`/`shell32`/`user32`/`advapi32`/`bcrypt` per `rust/mimalloc-pprof/build.rs`, which
   `cc`-crate builds link automatically but a manual `gcc`/`cl` invocation must add by hand).

No other build steps, no CMake, no external dependencies beyond a **C11** (or any C++, or
MSVC) compiler and the target platform's own libc/libSystem. (C11, not C99: `mimalloc`'s
atomics use `<stdatomic.h>` in C mode; this repo's own `CMakeLists.txt` requires
`CMAKE_C_STANDARD 11` for the same reason.)

### Minimal example

```c
#include "mimalloc-pprof-amalgamated.h"

int main(void) {
    if (!mi_prof_start(0)) return 1;      /* start sampling at the default rate */

    void* p = mi_malloc(1024);
    /* ... your workload ... */
    mi_free(p);

    if (!mi_prof_dump_proto("heap.pb")) return 1;  /* write a pprof-compatible snapshot */
    mi_prof_stop();
    return 0;
}
```

`inuse_objects`/`inuse_space` (current live allocations) are always populated;
`alloc_objects`/`alloc_space` (cumulative totals) only populate once accum mode is on --
set the `MIMALLOC_PROF_ACCUM=1` environment variable, or `mi_prof_config_t.accum` via
`mi_prof_start_ex`, before starting the profiler.

## Key entry points

- **Allocator:** `mi_malloc`, `mi_free`, `mi_realloc`, and the rest of the standard
  mimalloc `mi_`-prefixed API — see the inlined `mimalloc.h` section of the amalgamated
  header (source: repo root `include/mimalloc.h`). This amalgamation does not include
  mimalloc's optional `malloc`/`free` override machinery, so existing `malloc()` calls in
  your code are untouched unless you call the `mi_`-prefixed functions directly.
- **Sampling profiler:** `mi_prof_start` / `mi_prof_start_ex` to begin statistical
  allocation sampling, `mi_prof_dump` / `mi_prof_dump_proto` (plus their `_writer` variants)
  to emit a heap profile. Full contract, including the sampling/scaling math and the
  memory-safety bounds table, is documented in the header comments — source:
  `include/mimalloc/profile.h`. **Requires `-DMI_PPROF=1`** (see "How to use it" above).
- **Memory-event accounting (opt-in, issue #20):** `mi_memory_tracking_set_enabled` to turn
  allocation-change accounting on/off at runtime (or set `MIMALLOC_MEMORY_EVENTS`), and
  `mi_memory_set_callbacks` to register per-event-kind handlers (allocate/free/resize).
  Source: `include/mimalloc/memory-events.h`. Independent of `MI_PPROF` -- always available.

This README is for orientation, not a full API reference — the header comments already
document every function and struct field thoroughly; read them in the amalgamated header
or at the two source paths above.

## Ingesting and analyzing the profile output

`mi_prof_dump_proto` / `mi_prof_dump_proto_writer` write a binary `profile.proto` message —
the standard [google/pprof `Profile`](https://github.com/google/pprof/blob/main/proto/profile.proto)
wire format (this codebase's proto writer, `src/profile.c`, has its field numbers
individually verified in comments against that upstream `.proto` file). `mi_prof_dump` /
`mi_prof_dump_writer` instead write the legacy gperftools-style `"heap profile:"` **text**
format — `pprof` auto-detects and reads both, so either works as input.

The standard way to consume either format is Google's [`pprof`](https://github.com/google/pprof)
tool:

```sh
pprof -http=:8080 heap.pb     # interactive flamegraph / callgraph in the browser
pprof -top heap.pb            # text summary of the hottest allocation sites
pprof -base old.pb new.pb     # diff two snapshots (e.g. before/after a suspected leak)
```

The dumped profile carries four sample types — `alloc_objects`/`alloc_space` (cumulative
counts, only meaningful with `accum` mode enabled) and `inuse_objects`/`inuse_space`
(current live allocations, the default view `pprof` selects) — scaled from the raw sampled
counts up to an estimate of the true, un-sampled allocation stream using the same
probability-based correction Go's `runtime/pprof` uses (`scaleHeapSample` in
`go/src/runtime/pprof/protomem.go`; this project's `src/profile.c` explicitly ports and
cites that algorithm).

### Compatibility with jemalloc/tcmalloc-style profiling

This profiler is a **statistical, byte-interval sampler** (samples roughly every
`sample_interval` bytes allocated, not every allocation) in the same family as
jemalloc's `--enable-prof` and tcmalloc/gperftools' heap profiler — including the
zero-cost-to-the-app failure policy on internal sampling-memory pressure ("the app never
pays": a dropped sample never fails the caller's allocation, matching Go/tcmalloc's
policy per `include/mimalloc/profile.h`).

The concrete, verifiable compatibility claim is about **output shape**: this library's
`profile.proto` dump uses the same `Profile` message structure and the same
`alloc_objects`/`alloc_space`/`inuse_objects`/`inuse_space` sample-type set that jemalloc's
own `jeprof`/pprof pipeline produces. Tooling built around that shape — `pprof` itself, and
any visualization or CI integration that expects jemalloc-style heap-profile sample
types — reads this library's dumps unmodified. This is *not* a claim of jemalloc wire-level
or API-level compatibility (no `MALLOC_CONF`, no `je_*` symbols, no shared internal
format) — just a compatible pprof output shape.

## Testing

Not part of this release artifact (the zip does not include `test/`). The source repo's
`test/test-profile.c` and `test/test-memory-events.c` are framework-free, `assert()`-based
tests (numbered `T`-prefixed functions, one `main()` driving all of them) you can read or
crib from as a starting point for your own smoke test of this amalgamation.

## Platform support

Tested via this project's CI matrix on:

- **Windows** — MSVC and MinGW (win-gnu)
- **Linux** — glibc, both x86_64 and aarch64
- **macOS** — Intel and Apple Silicon, including the PAC-stripped stack-capture fix for
  arm64e (issue #35) so profiler stack traces resolve correctly on Apple Silicon