# 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