# Stack frames in stdlib getting too deep?

**URL:** <https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258>\
**Category:** Help\
**Tags:** memory-management\
**Created:** [June 19, 2026, 11:12pm UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258 "2026-06-19T23:12:25Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![pasta](https://ziggit.dev/user_avatar/ziggit.dev/pasta/32/6273_2.png) [@pasta](https://ziggit.dev/u/pasta)\
**Post date:** [June 19, 2026, 11:12pm UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/1 "2026-06-19T23:12:25Z")

</div>

Started to hit some instances where the 10-frame leak detection in `std.testing.allocator` isn’t deep enough for some parts of the standard library. In this case the 11th frame would have told me where the `put` call in question was from.

```txt
[DebugAllocator] (warn): memory address 0x77e540720000 leaked: 
.../zig/x86_64-linux-0.16.0/lib/std/mem/Allocator.zig:142:26: 0x105e3d2 in rawAlloc (std.zig)
    return a.vtable.alloc(a.ptr, len, alignment, ret_addr);
                        ^
.../zig/x86_64-linux-0.16.0/lib/std/mem/Allocator.zig:286:40: 0x108f7d5 in allocWithSizeAndAlignment__anon_9891 (std.zig)
    return self.allocBytesWithAlignment(alignment, byte_count, return_address);
                                        ^
.../zig/x86_64-linux-0.16.0/lib/std/mem/Allocator.zig:274:89: 0x108f49b in allocAdvancedWithRetAddr (std.zig)
    const ptr: [*]align(a.toByteUnits()) T = @ptrCast(try self.allocWithSizeAndAlignment(@sizeOf(T), a, n, return_address));
                                                                                        ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1477:53: 0x12441c6 in allocate (std.zig)
            const slice = try allocator.alignedAlloc(u8, max_align, total_size);
                                                    ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1434:29: 0x1242794 in grow (std.zig)
            try map.allocate(allocator, new_cap);
                            ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1295:30: 0x124258d in growIfNeeded (std.zig)
                try self.grow(allocator, capacityForSize(self.load() + new_count), ctx);
                            ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1115:34: 0x124046b in getOrPutContextAdapted__anon_44507 (std.zig)
                self.growIfNeeded(allocator, 1, ctx) catch |err| {
                                ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1100:56: 0x12400f5 in getOrPutContext (std.zig)
            const gop = try self.getOrPutContextAdapted(allocator, key, ctx, ctx);
                                                        ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1026:52: 0x123ff3a in putContext (std.zig)
            const result = try self.getOrPutContext(allocator, key, ctx);
                                                    ^
.../zig/x86_64-linux-0.16.0/lib/std/hash_map.zig:1023:35: 0x123fdaf in put (std.zig)
            return self.putContext(allocator, key, value, undefined);
                                    ^
(additional stack frames may have been skipped...)

```

This seems like it’s getting a little more common these days, though that may be subjective. Is there a mechanism to temporarily up the stack limit?

---

<div class="post-metadata">

**Author:** ![VoilaNeighbor](https://ziggit.dev/user_avatar/ziggit.dev/voilaneighbor/32/745_2.png) [@VoilaNeighbor](https://ziggit.dev/u/VoilaNeighbor)\
**Post date:** [June 20, 2026, 3:49am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/2 "2026-06-20T03:49:52Z")

</div>

It’s less of a problem but more of everyday life for a programmer – error traces are never too beautiful…

But I do think std lib can mark pure-forwarding functions as `inline`. It’s good for reading the intention – this is just a forwarding function.

---

<div class="post-metadata">

**Author:** ![pzittlau](https://ziggit.dev/letter_avatar_proxy/v4/letter/p/ecae2f/32.png) [@pzittlau](https://ziggit.dev/u/pzittlau)\
**Post date:** [June 20, 2026, 4:00am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/3 "2026-06-20T04:00:13Z")

</div>

> [@VoilaNeighbor](#):
>
> But I do think std lib can mark pure-forwarding functions as `inline`. It’s good for reading the intention – this is just a forwarding function.

Though it makes other things harder. `inline` isn’t purely about semantics. It has a real effect on the performance and sometimes even the correctness of a program.

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [June 20, 2026, 4:26am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/4 "2026-06-20T04:26:07Z")

</div>

they are refering to functions that are similar to:

```zig
fn foo (a: u32) u32 {
   return bar(a, 5);
}

```

inlining such a function will almost certainly be faster, and likely have little affect on binary size.

std already inlines some such functions specifically to reduce stack/error traces!

```zig
fn wasm_freestanding_start() callconv(.c) void {
    // This is marked inline because for some reason LLVM in
    // release mode fails to inline it, and we want fewer call frames in stack traces.
    _ = @call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global });
}

```

Though that example inlines at the call site, not function definition.

---

<div class="post-metadata">

**Author:** ![pzittlau](https://ziggit.dev/letter_avatar_proxy/v4/letter/p/ecae2f/32.png) [@pzittlau](https://ziggit.dev/u/pzittlau)\
**Post date:** [June 20, 2026, 4:46am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/5 "2026-06-20T04:46:24Z")

</div>

> [@vulpesx](#):
>
> inlining such a function will almost certainly be faster, and likely have little affect on binary size.

On a single function call scope definitely. On a whole project bases I’m not convinced. There many code paths converge on different points in this “pipeline” to the thing, that actually does something. If you force inline all of them, the compiler _has_ to follow that, which will likely make the tree wider than before increasing both cache pressure and binary size.

Doing this just to save some screen and head space for some, undoubtedly often unnecessary call stacks, seems wrong to me. A better solution for me would be to be able to setup filters or “just” use a tool afterwards, that filters. Similarly to how when using debuggers one often tells it to skip frames of libraries(especially `std`).

---

<div class="post-metadata">

**Author:** ![floooh](https://ziggit.dev/user_avatar/ziggit.dev/floooh/32/2989_2.png) [@floooh](https://ziggit.dev/u/floooh)\
**Post date:** [June 20, 2026, 7:12am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/6 "2026-06-20T07:12:20Z")

</div>

> [@VoilaNeighbor](#):
>
> But I do think std lib can mark pure-forwarding functions as `inline`. It’s good for reading the intention – this is just a forwarding function.

I’ve had a feeling for a while that the stdlib is starting to suffer from a serious case of C++ -ism 😉 (e.g. a bit too much DRY, for instance look at the callchain of `putContext => getOrPutContext => getOrPutContextAdapted`, and then a similar chain of calls just for the grow-allocation, that looks way too granular to me…, and IME code like this also is the main reason when debug performance is a lot worse than optimized performance).

---

<div class="post-metadata">

**Author:** ![floooh](https://ziggit.dev/user_avatar/ziggit.dev/floooh/32/2989_2.png) [@floooh](https://ziggit.dev/u/floooh)\
**Post date:** [June 20, 2026, 7:20am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/7 "2026-06-20T07:20:26Z")

</div>

> [@pzittlau](#):
>
> If you force inline all of them, the compiler _has_ to follow that, which will likely make the tree wider than before increasing both cache pressure and binary size.

Those deep call chains of very simple functions will almost certainly be inlined and ‘collapsed’ into a single resulting call anyway in optimized mode, while in debug mode they just hurt performance (it’s fine when debug mode is 2x slower than optimized, but beyond that it can be problematic).

Personally I wouldn’t use inline either, but instead try to structure the code in a way that such deep call chains of slightly-more-specialized functions don’t happen. IME this often happens when there’s no strict separation of ‘public interface functions’ and ‘internal implementation functions’. E.g. the implementation of public functions should almost never call other public functions.

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [June 20, 2026, 11:29am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/8 "2026-06-20T11:29:17Z")

</div>

> [@pasta](#):
>
> Is there a mechanism to temporarily up the stack limit?

I wonder whether there should be a parameter similar to `-freference-trace[=num]` but for the number of default debug allocator stack frames.

---

<div class="post-metadata">

**Author:** ![VoilaNeighbor](https://ziggit.dev/user_avatar/ziggit.dev/voilaneighbor/32/745_2.png) [@VoilaNeighbor](https://ziggit.dev/u/VoilaNeighbor)\
**Post date:** [June 21, 2026, 8:57am UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/9 "2026-06-21T08:57:19Z")

</div>

That is a problem but maybe on the other end of the pole – too “less” C++.

Without implicit metaprogramming, we have to make things explicit including the dull forwarding code.

---

<div class="post-metadata">

**Author:** ![pasta](https://ziggit.dev/user_avatar/ziggit.dev/pasta/32/6273_2.png) [@pasta](https://ziggit.dev/u/pasta)\
**Post date:** [June 21, 2026, 3:54pm UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/10 "2026-06-21T15:54:40Z")

</div>

FWIW one thing that confused me here was the `return_address` - I had thought that address was the one being used for the “bottom” of the allocation stack. Is that not what’s intended? e.g. shouldn’t it be saying that the allocation at `hash_map.zig:1477` leaked, not the call down at `Allocator.zig:142`?

---

<div class="post-metadata">

**Author:** ![gooncreeper](https://ziggit.dev/user_avatar/ziggit.dev/gooncreeper/32/7984_2.png) [@gooncreeper](https://ziggit.dev/u/gooncreeper)\
**Post date:** [June 21, 2026, 8:30pm UTC](https://ziggit.dev/t/stack-frames-in-stdlib-getting-too-deep/16258/11 "2026-06-21T20:30:17Z")

</div>

> [@pasta](#):
>
> Is there a mechanism to temporarily up the stack limit?

You can create your own `DebugAllocator` instance and specify the stack trace length with `DebugAllocatorConfig.stack_trace_frames`:

```zig
test {
    var debug_gpa: std.heap.DebugAllocator(.{ .stack_trace_frames = 16 }) = .init;
    defer _ = debug_gpa.deinit();
    const gpa = debug_gpa.allocator();

    ...
}

```
