# How to Debug Allocation Failures with Custom Fail Handlers in mulle-allocator

> Debug allocation failures in mulle-allocator by implementing custom fail handlers with mulle_allocator_set_fail. Intercept out-of-memory errors before process abort.

- Repository: [mulle-c/mulle-allocator](https://github.com/mulle-c/mulle-allocator)
- Tags: how-to-guide
- Published: 2026-03-07

---

**To debug allocation failures in mulle-allocator, implement a custom function matching the `mulle_allocator_fail_t` signature and install it with `mulle_allocator_set_fail()` to intercept and diagnose out-of-memory conditions before the process aborts.**

The `mulle-c/mulle-allocator` library provides a flexible memory allocation framework where every allocator instance can carry its own failure handler. When `malloc`, `calloc`, `realloc`, or `strdup` operations fail, instead of silently returning `NULL`, the allocator invokes this handler to give you full control over logging, stack tracing, or graceful degradation.

## Understanding the Default Fail Handler

By default, `mulle-allocator` uses the `mulle_allocation_fail` function defined in [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c) (lines 44-51). This handler prints the `errno` message to stderr and immediately aborts the process. While this prevents silent memory corruption, it offers little diagnostic context for debugging complex allocation failures.

The default implementation serves as a fallback when no custom handler is installed. All allocation helpers—including `_mulle_allocator_malloc`, `_mulle_allocator_calloc`, `_mulle_allocator_realloc`, and `_mulle_allocator_strdup`—check for `NULL` returns and forward failures to the allocator's stored `fail` function pointer.

## Implementing a Custom Fail Handler

### Writing the Handler Function

Create a function that conforms to the `mulle_allocator_fail_t` typedef declared in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h). The signature receives the allocator instance, the block pointer (often `NULL` for fresh allocations), and the requested size:

```c
#include "mulle-allocator.h"
#include <stdio.h>
#include <stdlib.h>

void my_fail_handler(struct mulle_allocator *allocator,
                     void *block,
                     size_t size)
{
    fprintf(stderr, "ALLOC FAIL: %zu bytes requested\n", size);
    /* Insert diagnostics: backtrace, allocator state dump, etc. */
    abort();   /* or exit(1), longjmp, etc. */
}

```

### Installing the Handler

Use `mulle_allocator_set_fail()`, defined inline in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h) (lines 187-194), to attach your handler to a specific allocator instance. Always initialize your allocator from a valid base such as `mulle_allocator_stdlib`:

```c
struct mulle_allocator my_allocator = mulle_allocator_stdlib;
mulle_allocator_set_fail(&my_allocator, my_fail_handler);

```

Passing `NULL` as the second argument restores the default `mulle_allocation_fail` handler. Once installed, any allocation through `my_allocator` that returns `NULL` will invoke your custom function instead of the default abort.

## Practical Debugging Examples

### Basic Logging Handler

This example logs the failure details to stderr before terminating, providing immediate visibility into allocation sizes that trigger failures:

```c
#include "mulle-allocator.h"
#include <stdio.h>
#include <stdlib.h>

static void log_fail(struct mulle_allocator *allocator,
                     void *block,
                     size_t size)
{
    fprintf(stderr,
            ">>> Allocation of %zu bytes failed (allocator=%p, block=%p)\n",
            size, (void *)allocator, block);
    abort();
}

int main(void)
{
    struct mulle_allocator alloc = mulle_allocator_stdlib;
    mulle_allocator_set_fail(&alloc, log_fail);
    
    /* Force failure with an impossible request */
    void *p = mulle_allocator_malloc(&alloc, (size_t)-1);
    (void)p;  /* never reached */
    return 0;
}

```

### Stack Trace Handler (POSIX)

For systems supporting [`execinfo.h`](https://github.com/mulle-c/mulle-allocator/blob/main/execinfo.h), capture the call stack at the moment of allocation failure to identify which code path requested the invalid memory:

```c
#define _GNU_SOURCE
#include <execinfo.h>
#include "mulle-allocator.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void backtrace_fail(struct mulle_allocator *allocator,
                           void *block,
                           size_t size)
{
    void *buf[32];
    int cnt = backtrace(buf, 32);
    fprintf(stderr, "Allocation of %zu bytes failed, backtrace:\n", size);
    backtrace_symbols_fd(buf, cnt, STDERR_FILENO);
    abort();
}

int main(void)
{
    struct mulle_allocator a = mulle_allocator_stdlib;
    mulle_allocator_set_fail(&a, backtrace_fail);
    
    mulle_allocator_calloc(&a, SIZE_MAX, SIZE_MAX);
    return 0;  /* never reached */
}

```

### Test Harness Example

The repository includes a reference implementation in [`test/coverage/fail-stuff.c`](https://github.com/mulle-c/mulle-allocator/blob/main/test/coverage/fail-stuff.c) demonstrating fail handler usage in a test context. This file shows how to set up a `mulle_fail_allocator` instance with a custom handler to exercise failure paths:

```c
#include "mulle-allocator.h"
#include <stdio.h>

static void fail_fail(struct mulle_allocator *a, void *block, size_t size)
{
    fprintf(stderr, "FAIL HANDLER: %zu bytes (allocator=%p)\n", 
            size, (void *)a);
    abort();
}

/* Run under a debugger to break at the exact failure point */

```

Compile with `-g` and run under GDB or LLDB to break inside your handler and inspect the allocator state, requested size, and calling context.

## Key Source Files and Implementation Details

The allocation failure mechanism spans three critical files in the `mulle-c/mulle-allocator` repository:

- **[`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h)** — Declares the `mulle_allocator_fail_t` typedef and `mulle_allocator_set_fail()` inline function (lines 187-194). Also contains the inline allocation helpers that check for `NULL` returns and invoke the fail handler (lines 101-113, 124-130, 136-144).

- **[`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c)** — Implements the default `mulle_allocation_fail()` handler that prints `errno` and aborts (lines 44-51).

- **[`test/coverage/fail-stuff.c`](https://github.com/mulle-c/mulle-allocator/blob/main/test/coverage/fail-stuff.c)** — Provides a working example of custom fail handler installation for testing purposes.

These locations contain the exact logic that forwards allocation failures to your handler, making them essential references when debugging complex memory scenarios.

## Summary

- **Custom fail handlers** in mulle-allocator let you intercept allocation failures for detailed diagnostics instead of immediate abort.
- Implement handlers using the `mulle_allocator_fail_t` signature: `void handler(struct mulle_allocator *, void *, size_t)`.
- Install handlers with `mulle_allocator_set_fail()` defined in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h).
- The default `mulle_allocation_fail` in [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c) prints `errno` and aborts.
- Allocation helpers (`_mulle_allocator_malloc`, `_mulle_allocator_calloc`, etc.) automatically invoke the fail handler when underlying system calls return `NULL`.
- Use POSIX `backtrace()` functions inside your handler to capture stack traces during debugging sessions.

## Frequently Asked Questions

### What signature must a custom fail handler use?

Your handler must match the `mulle_allocator_fail_t` typedef: `void handler(struct mulle_allocator *allocator, void *block, size_t size)`. The `block` parameter receives the existing pointer for `realloc` failures, or `NULL` for `malloc`, `calloc`, and `strdup` failures.

### How do I restore the default fail handler?

Call `mulle_allocator_set_fail(&allocator, NULL)`. This reassigns the default `mulle_allocation_fail` function that prints the errno message and aborts the process.

### Can I use a custom handler with the standard allocator?

Yes. Copy `mulle_allocator_stdlib` to a local `struct mulle_allocator` instance, then install your handler with `mulle_allocator_set_fail()`. Never modify the global `mulle_allocator_stdlib` directly; always work with copies to avoid side effects on other code using the default allocator.

### Which allocation functions trigger the fail handler?

All internal allocation helpers check for failure and invoke the handler: `_mulle_allocator_malloc`, `_mulle_allocator_calloc`, `_mulle_allocator_realloc`, and `_mulle_allocator_strdup` in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h). When you use the public API functions like `mulle_allocator_malloc()`, they forward to these inline helpers which perform the `NULL` check and call your handler.