Skip to content

Latest commit

Β 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

UMR β€” Universal Memory Runtime

Modular, observable, high-performance memory management for C

License: MIT Language Version Platforms CI

Quick Start Β· Why UMR Β· Engines Β· Observability Β· Integration Β· Roadmap Β· Contributing


UMR is an open-source C memory runtime that sits between your application and the OS. It gives you a familiar malloc-style API plus six specialized allocator engines, a live-allocation tracker, leak detection, ASCII heap visualization, binary snapshots, four policy presets, and a CLI tool β€” all in a single dependency-free C library.

       Application
           β”‚
           β–Ό
   UMR Public API          umr_malloc Β· arena Β· pool Β· slab Β· buddy Β· fragment
           β”‚
           β–Ό
   Allocation Manager ─── Policy  (FAST Β· LOW_MEMORY Β· REALTIME Β· DEBUG)
           β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β–Ό      β–Ό      β–Ό        β–Ό         β–Ό
 General  Arena  Pool   Slab/Buddy  Fragment
 Engine   Bump   Fixed  Object      Boundary-tag
           ptr   size   cache       coalescing
           β”‚
           β–Ό
  Intelligence Layer     tracker Β· leaks Β· heatmap Β· snapshot Β· JSON
           β”‚
           β–Ό
   OS Memory Provider    mmap (Linux/macOS) Β· VirtualAlloc (Windows)

Note: engines are opt-in via their own APIs. umr_malloc routes through the general heap. Specialized engines are never silently selected.


✨ What makes UMR different

Traditional malloc UMR
Black-box heap β€” no visibility Every allocation is trackable with ID, size, and source location
One strategy for everything Six purpose-built engines you choose explicitly
Leaks are hard to find Built-in leak detector with per-allocation reports
Fragmentation is invisible Fragment engine with quantified external/internal frag ratios
No runtime tuning Four policy presets: FAST, LOW_MEMORY, REALTIME, DEBUG
No tooling umr-cli, ASCII heatmap, JSON dump, binary snapshots

⚑ Quick Start

Requirements

Tool Minimum version
CMake 3.20+
C compiler GCC 12+, Clang 15+, MSVC 2022
Build backend Ninja (recommended) or Make

Build and test

# Clone
git clone https://github.com/universalmemoryruntime/umr.git
cd umr

# Configure + build (Release)
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

# Run the test suite
ctest --test-dir build --output-on-failure

Debug build with memory checks, guard pages, and source-location tracking:

cmake -S . -B build-debug \
  -DCMAKE_BUILD_TYPE=Debug \
  -DUMR_ENABLE_DEBUG=ON \
  -DUMR_USE_GUARD_PAGES=ON
cmake --build build-debug
ctest --test-dir build-debug --output-on-failure

Link with CMake FetchContent

include(FetchContent)
FetchContent_Declare(umr
  GIT_REPOSITORY https://github.com/universalmemoryruntime/umr.git
  GIT_TAG        v0.1.0
)
FetchContent_MakeAvailable(umr)

target_link_libraries(my_app PRIVATE UMR::umr)

Link with pkg-config (after install)

gcc my_app.c $(pkg-config --cflags --libs umr) -o my_app

πŸš€ Usage

Drop-in general allocator

#include "umr.h"

int main(void) {
    umr_init();

    // Standard allocation patterns β€” familiar API, observable internals
    char* buf  = (char*)umr_malloc(64);
    int*  nums = (int*)umr_calloc(16, sizeof(int));   // zero-filled
    buf = (char*)umr_realloc(buf, 256);               // grow in place when possible
    umr_free(nums);
    umr_free(buf);

    umr_shutdown();
}

Debug macros record __FILE__ and __LINE__ in every block header (enabled with -DUMR_ENABLE_DEBUG=ON):

void* p = UMR_MALLOC(256);   // recorded: myfile.c:12
UMR_FREE(p);

πŸ”§ Allocator Engines

UMR ships six purpose-built engines. Pick the one that fits your workload.

Arena β€” bulk temporary allocation

Best for: game frame scratch, per-request server memory, parser nodes.

// One OS allocation on create. All allocs are a pointer bump β€” near-zero overhead.
umr_arena_t* arena = umr_arena_create(0);   // 0 = default 1 MiB chunk

void* node  = umr_arena_alloc(arena, sizeof(ASTNode));
void* simd  = umr_arena_alloc_aligned(arena, 256, 64);  // SIMD-aligned

umr_arena_reset(arena);    // rewind all β€” no OS round-trip, chunks are reused
umr_arena_destroy(arena);  // release OS memory

Nested arenas (child lives inside parent's memory, destroyed with parent):

umr_arena_t* child = umr_arena_create_child(parent, 0);

Pool β€” fixed-size object recycling

Best for: game entities, network connections, message objects.

umr_pool_t* pool = umr_pool_create(sizeof(Enemy), 256);  // 256 objects / chunk

Enemy* e = (Enemy*)umr_pool_alloc(pool);   // O(1) from freelist
// ... use e ...
umr_pool_free(pool, e);                    // O(1) back to freelist

printf("live: %llu\n", (unsigned long long)umr_pool_live_count(pool));
umr_pool_destroy(pool);

Slab β€” named object caches

Best for: multiple named types, kernel-style object caches, per-cache stats.

umr_slab_cache_t* cache = umr_slab_create("conn_state", sizeof(ConnState));

ConnState* c = (ConnState*)umr_slab_alloc(cache);
// ... use c ...
umr_slab_free(cache, c);

printf("live=%llu  slabs=%llu\n",
       (unsigned long long)umr_slab_live_count(cache),
       (unsigned long long)umr_slab_count(cache));

umr_slab_destroy(cache);

Buddy β€” power-of-two region management

Best for: GPU/DMA buffers, large variable-size blocks, sub-allocating fixed OS reservations.

// Mode 1: UMR manages backing memory
umr_buddy_t* b = umr_buddy_create(8 * 1024 * 1024);  // 8 MiB pool

void* tex = umr_buddy_alloc(b, 512 * 1024);  // rounded to next power-of-two
void* buf = umr_buddy_alloc(b, 4   * 1024);
umr_buddy_free(b, tex);   // buddy pairs merged eagerly
umr_buddy_destroy(b);

// Mode 2: caller supplies the memory (must be power-of-two size)
umr_buddy_t* b2 = umr_buddy_create_from(my_gpu_region, 4 * 1024 * 1024);
umr_buddy_destroy(b2);    // does NOT free your buffer

Fragment Heap β€” explicit split/merge with fragmentation metrics

Best for: fixed-size memory budgets (embedded, GPU VRAM, network buffers) where you need quantified fragmentation control.

// Three search strategies, selectable at creation
umr_fragment_heap_t* h = umr_fragment_heap_create(256 * 1024, UMR_FRAG_BEST_FIT);
//                                                              UMR_FRAG_FIRST_FIT
//                                                              UMR_FRAG_SEGREGATED

void* a = umr_fragment_alloc(h, 1024);
void* b = umr_fragment_alloc(h, 512);

umr_fragment_release(h, a);   // immediate coalescing with adjacent free blocks

// Resize in-place (shrink) or copy-and-grow (like realloc)
b = umr_fragment_resize(h, b, 2048);

// Quantified fragmentation metrics
umr_frag_metrics_t m;
umr_fragment_metrics_get(h, &m);
printf("external frag: %.1f%%  largest free: %zu bytes\n",
       m.external_frag_ratio * 100.0, m.largest_free_block);

umr_fragment_metrics_print(h, stdout);   // full table to terminal
umr_fragment_heap_destroy(h);

Picking the right engine

Workload Recommended engine
Bulk temporaries sharing one lifetime Arena
Hot single-type alloc/free cycle Pool
Multiple named types with per-cache stats Slab
Variable power-of-two sizes, GPU/DMA Buddy
Fixed budget, arbitrary sizes, frag visibility Fragment
Everything else umr_malloc

πŸ” Observability

The intelligence layer is the feature that separates UMR from every other allocator.

Live stats β€” always available

umr_stats_t s;
umr_stats_get(&s);
printf("active: %zu bytes  peak: %zu bytes  frag: %.2f%%  live: %llu allocs\n",
       s.active_bytes, s.peak_active_bytes,
       s.fragmentation_ratio * 100.0,
       (unsigned long long)s.live_count);

umr_stats_print(stdout);    // formatted table
umr_viz_print_stats(stdout); // extended breakdown with per-engine summary

Allocation tracker

umr_tracker_enable();          // zero overhead when disabled (default)

// ... run workload ...

umr_viz_print_tracker(stdout); // tabular live allocation list
umr_leak_print(stdout);        // leak report

umr_leak_clear();              // declare current state as clean baseline
umr_tracker_disable();

Sampling mode for high-throughput paths:

umr_tracker_set_sample_rate(16);  // record 1 in 16 allocations

ASCII heap heatmap

umr_viz_print_heatmap(stdout, 64);  // 64 chars wide
=== UMR Heap Heatmap (25 spans, 1 cell = 1 span ~256 KiB) ===
  Legend: '.' empty  'l' low(1-25%)  'm' mid  'h' high  'F' full  'L' large

.l..l...l...l...l.lllllLL

Leak detection

// Programmatic check with full report
umr_leak_entry_t entries[64];
umr_leak_report_t report = { .entries = entries, .capacity = 64 };
size_t leaked = umr_leak_check(&report);
if (leaked) {
    for (size_t i = 0; i < report.count; ++i)
        printf("LEAK  %p  %zu bytes\n",
               report.entries[i].ptr, report.entries[i].size);
}

// Or one-liner to stderr
umr_leak_print(NULL);   // NULL β†’ stderr

umr_shutdown() automatically reports any live allocations when the tracker is enabled.

JSON snapshot for tooling

umr_viz_dump_json(stdout);      // machine-readable stats + live allocations
umr_snapshot_save("heap.snap"); // binary snapshot for offline analysis
umr-cli json > heap-snapshot.json   # via CLI

πŸŽ›οΈ Policies

Apply a preset that tunes the runtime for your use case:

umr_policy_apply(UMR_POLICY_FAST_MODE);       // default β€” max throughput
umr_policy_apply(UMR_POLICY_LOW_MEMORY_MODE); // aggressively decommit idle spans
umr_policy_apply(UMR_POLICY_REALTIME_MODE);   // pre-warm, avoid syscalls on hot path
umr_policy_apply(UMR_POLICY_DEBUG_MODE);      // full tracker + canaries + quarantine

List all policies with descriptions:

umr_policy_list(stdout);
Available UMR policies:
  0  FAST_MODE        Maximum throughput. Tracker off. Default.
  1  LOW_MEMORY_MODE  Minimise OS footprint. Idle spans decommitted on free.
  2  REALTIME_MODE    Low-latency. Common size classes pre-warmed.
  3  DEBUG_MODE       Full observability. Tracker on. Leak report on shutdown.

πŸ› οΈ umr-cli

The umr-cli tool links against the library and exercises the in-process observability API directly:

umr-cli stats                    # extended heap stats
umr-cli leaks --enable-tracker   # leak report (exit code 2 if leaks found)
umr-cli heatmap 80               # ASCII heatmap at 80 columns
umr-cli tracker --enable-tracker # live allocation table
umr-cli json                     # JSON snapshot to stdout

Exit codes: 0 success Β· 1 usage error Β· 2 leaks detected.


βš™οΈ CMake & Integration

CMake options

Option Default Description
UMR_BUILD_SHARED OFF Build as shared library instead of static
UMR_BUILD_TESTS ON Build the unit + thread test suite
UMR_BUILD_EXAMPLES ON Build all example programs
UMR_BUILD_BENCHMARKS OFF Build microbenchmarks (vs libc)
UMR_ENABLE_DEBUG OFF Magic checks, source metadata, canaries
UMR_USE_GUARD_PAGES OFF Inaccessible guard page after large allocs
UMR_BUILD_CLI ON Build umr-cli tool
UMR_INSTALL ON Generate install rules + CMake package config

Full example with debug + guard pages

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Debug \
  -DUMR_ENABLE_DEBUG=ON \
  -DUMR_USE_GUARD_PAGES=ON \
  -DUMR_BUILD_BENCHMARKS=ON

cmake --build build --parallel
ctest --test-dir build --output-on-failure

AddressSanitizer / ThreadSanitizer

# ASan
cmake -S . -B build-asan \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DUMR_ENABLE_DEBUG=ON \
  -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address"
cmake --build build-asan
ASAN_OPTIONS=detect_leaks=0:halt_on_error=1 ctest --test-dir build-asan

# TSan
cmake -S . -B build-tsan \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DCMAKE_C_FLAGS="-fsanitize=thread -fno-omit-frame-pointer" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread"
cmake --build build-tsan
ctest --test-dir build-tsan

Using WSL on Windows (bypasses AppLocker)

If your Windows environment blocks freshly compiled executables, build and run through WSL:

wsl bash run_simple_test.sh

πŸ“ Repository Layout

umr/
β”œβ”€β”€ core/               Runtime lifecycle, policy, TLS scratch heap
β”œβ”€β”€ allocator/          Engine implementations
β”‚   β”œβ”€β”€ general_allocator.c
β”‚   β”œβ”€β”€ arena.c
β”‚   β”œβ”€β”€ pool.c
β”‚   β”œβ”€β”€ slab.c
β”‚   └── buddy.c
β”œβ”€β”€ fragment/           Fragment engine (split, merge, manager)
β”œβ”€β”€ heap/               Global heap singleton + metadata
β”œβ”€β”€ debug/              Canaries, quarantine, guard pages
β”œβ”€β”€ profiler/           Tracker, leak detector, visualizer, snapshot
β”œβ”€β”€ platform/           OS memory provider (linux / windows / macos)
β”œβ”€β”€ include/
β”‚   β”œβ”€β”€ umr.h           ← Public API (stable, ABI freeze post v1)
β”‚   └── umr/internal/   ← Private headers (no ABI guarantee)
β”œβ”€β”€ tests/              One test file per module + test_harness.h
β”œβ”€β”€ benchmarks/         Microbenchmarks vs libc
β”œβ”€β”€ examples/           Self-contained usage programs
β”œβ”€β”€ tools/umr-cli/      In-process CLI
β”œβ”€β”€ docs/               Architecture, vision, feature specs, roadmap
└── cmake/              CMake package config + pkg-config templates

πŸ§ͺ Running the Simple Test

A single file covering all four areas β€” allocator, memory clearing, fragmentation, and visualization:

# On Linux / macOS
cmake --build build --target umr_simple_test
./build/umr_simple_test

# On Windows via WSL
wsl bash run_simple_test.sh

Expected output:

UMR Simple Test  (v0.1.0)
============================================================

=== 1. Allocator ===
  [PASS] malloc(0) returns NULL
  [PASS] calloc bytes are zero
  ...

=== 3. Fragmentation (fragment heap) ===
  -- strategy: BEST_FIT --
  fragment metrics after checkerboard:
    region=262144  live=1024  free=260800  ext_frag=0.002

=== 4. Visualization (terminal output) ===
  --- umr_viz_print_heatmap (64 cols) ---
  .l..l...l...l...l.lllllLL

============================================================
Results: 76 passed, 0 failed

πŸ—ΊοΈ Roadmap

Version Status Contents
v0.1.0 Current General allocator Β· Arena Β· Pool Β· Slab Β· Buddy Β· Fragment engine Β· Tracker Β· Leak detector Β· Visualizer Β· Snapshot Β· Policies Β· CLI Β· CI
v0.2.0 Planned Performance tuning Β· Benchmark suite vs mimalloc/jemalloc Β· Thread-local caches
v0.3.0 Planned Relocatable handles (UMR_Handle) Β· Compaction experiments
v1.0.0 Planned Stable ABI promise Β· Doxygen + MkDocs site Β· Published benchmark report
v2.x Future Auto-fragmentation optimization Β· Cold memory compression Β· Remote heap debugger

What's not in v1 by design: garbage collection, automatic pointer relocation, compiler plugins, kernel-mode operation.


πŸ“– Documentation

Doc Contents
docs/vision.md Why UMR exists and what it's trying to be
docs/plan.md Phased roadmap and delivery plan
docs/feature.md Feature-by-feature specification and API contracts
docs/architecture.md System design, module layout, threading model
CONTRIBUTING.md Build setup, coding standards, PR process

Build the full docs site locally:

pip install mkdocs-material
mkdocs serve

Generate API reference:

doxygen Doxyfile

🀝 Contributing

Contributions are welcome. Before opening a PR:

# 1. Configure a development build
cmake -S . -B build-dev \
  -DCMAKE_BUILD_TYPE=Debug \
  -DUMR_ENABLE_DEBUG=ON \
  -DUMR_BUILD_TESTS=ON \
  -DUMR_BUILD_BENCHMARKS=ON

# 2. Build and run all tests
cmake --build build-dev
ctest --test-dir build-dev --output-on-failure

# 3. Check for regressions
./build-dev/umr_bench_suite

See CONTRIBUTING.md for the full guide: coding standards, naming conventions, commit style, PR template, and design principles.

Good first issues

  • Adding a new example program in examples/
  • Improving Doxygen comments in include/umr.h
  • Writing a benchmark comparing two strategies in benchmarks/
  • Extending test_fragment.c with edge-case merge tests

CI

Every push and pull request runs the full matrix via GitHub Actions:

Job Platform Checks
Build + test Ubuntu, Windows, macOS Release + Debug
AddressSanitizer Ubuntu Buffer overflow, use-after-free
ThreadSanitizer Ubuntu Data races

πŸ“„ License

MIT Β© 2026 UniversalMemoryRuntime β€” see LICENSE for the full text.


Built with care for systems programmers, game developers, and anyone who believes memory should be observable, controllable, and fast.

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages