Skip to content
DebugAllocatorLiveCount

DebugAllocatorLiveCount

Description

Number of outstanding allocations (alloc minus free), read from the embedded live map.

Parameters

Name Direction Description
self in DebugAllocator instance, or NULL.

Success

Returns the live-allocation count. No state is touched.

Failure

Returns 0 when self is NULL.

Usage example (Cross-references)

Usage examples (Cross-references)
    // ---------------------------------------------------------------------------
    
    size DebugAllocatorLiveCount(const DebugAllocator *self) {
        if (!self)
            return 0;
    
        DebugAllocator alloc    = DebugAllocatorInit();
        size           baseline = DebugAllocatorLiveCount(&alloc);
    
        SymbolResolver res;
    
        // A populated cache with two opened ELFs sits strictly above baseline.
        ok = ok && DebugAllocatorLiveCount(&alloc) > baseline;
    
        SymbolResolverDeinit(&res);
        SymbolResolverDeinit(&res);
        // Correct teardown (main + sidecar ElfDeinit + Vec free) returns to 0.
        ok = ok && DebugAllocatorLiveCount(&alloc) == baseline;
    
        DebugAllocatorDeinit(&alloc);
        // reclaim the source so only the vec's deep copies remain live.
        HttpHeaderDeinit(&hh);
        ok = ok && (DebugAllocatorLiveCount(&dbg) > 0);
    
        // VecDeinit runs http_header_deinit on the surviving copy.
        // VecDeinit runs http_header_deinit on the surviving copy.
        VecDeinit(&headers);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        Zstr        next = HttpRequestParse(&req, raw);
        bool        ok   = (next != raw) && (StrLen(&req.url) > 0);
        ok               = ok && (DebugAllocatorLiveCount(&dbg) > 0);
    
        HttpRequestDeinit(&req);
    
        HttpRequestDeinit(&req);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        StrDeinit(&second);
        HttpResponseDeinit(&response);
        bool ok = (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        StrDeinit(&path);
        HttpResponseDeinit(&response);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        ResolvedSymbol ra;
        bool           ok    = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &ra) && ra.module_path;
        size           live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol rb;
        ResolvedSymbol rb;
        ok         = ok && SymbolResolverResolve(&res, (void *)&sr1_marker_b, &rb) && rb.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ok = ok && live2 == live1;                                   // no re-open
        ResolvedSymbol rc;
        bool           ok    = SymbolResolverResolve(&res, (void *)&sr1_marker_a, &rc) && rc.module_path;
        size           live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol rd;
        ResolvedSymbol rd;
        ok         = ok && SymbolResolverResolve(&res, (void *)&sr_cache_data_marker, &rd) && rd.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ok = ok && live2 == live1;                                   // fallback hit, no re-open
        u64 other  = 0;
        ok         = ok && find_other_module_addr(ra.module_path, &other);
        size live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol ro;
        ResolvedSymbol ro;
        ok         = ok && SymbolResolverResolve(&res, (void *)other, &ro) && ro.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ok = ok && ZstrCompare(ro.module_path, ra.module_path) != 0; // distinct module
        ResolvedSymbol r1;
        ok         = ok && SymbolResolverResolve(&res, (void *)other, &r1) && r1.module_path;
        size live1 = DebugAllocatorLiveCount(&alloc);
    
        ResolvedSymbol r2;
        ResolvedSymbol r2;
        ok         = ok && SymbolResolverResolve(&res, (void *)other, &r2) && r2.module_path;
        size live2 = DebugAllocatorLiveCount(&alloc);
    
        ok = ok && live2 == live1;                                   // hit on entry[1], no re-open
    bool test_sr_deinit_frees_everything(void) {
        DebugAllocator alloc    = DebugAllocatorInit();
        size           baseline = DebugAllocatorLiveCount(&alloc);
    
        SymbolResolver res;
    
        SymbolResolverDeinit(&res);
        ok = ok && DebugAllocatorLiveCount(&alloc) == baseline;
    
        DebugAllocatorDeinit(&alloc);
        DebugAllocator alloc    = DebugAllocatorInit();
        Allocator     *a        = ALLOCATOR_OF(&alloc);
        size           baseline = DebugAllocatorLiveCount(&alloc);
    
        DnsResolver r;
    
        // Sanity: population actually allocated something.
        bool grew = DebugAllocatorLiveCount(&alloc) > baseline;
    
        DnsResolverDeinit(&r);
        DnsResolverDeinit(&r);
    
        bool back = DebugAllocatorLiveCount(&alloc) == baseline;
        bool ok   = grew && back && r.allocator == NULL;
    // Convenience: a test passes iff `ok` held AND the DebugAllocator has no
    // outstanding allocations after the test released everything it owns.
    #define LEAK_CLEAN(dbg) (DebugAllocatorLiveCount(&(dbg)) == 0 && DebugAllocatorLiveBytes(&(dbg)) == 0)
    
    // Leak-only config: live-count tracking, NO per-alloc stack-trace / canary /
        bool result = GraphReserve(&graph, 8);
    
        size before = DebugAllocatorLiveCount(&dbg);
    
        g_fail_copy           = true;
        g_fail_copy           = false;
    
        size after = DebugAllocatorLiveCount(&dbg);
    
        result = result && (failed_id == 0);
        result      = result && (GraphCommitChanges(&graph) == 1);
    
        size before = DebugAllocatorLiveCount(&dbg);
    
        g_fail_copy           = true;
        g_fail_copy           = false;
    
        size after = DebugAllocatorLiveCount(&dbg);
    
        result = result && (failed_id == 0);
    
        DnsResponseDeinit(&resp);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        DnsResponseDeinit(&resp);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        DnsResponseDeinit(&resp);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    static bool dbg_no_leak(DebugAllocator *dbg, Str *out) {
        StrDeinit(out);
        bool ok = (DebugAllocatorLiveCount(dbg) == 0);
        DebugAllocatorDeinit(dbg);
        return ok;
        if (s)
            AllocatorFree(&dbg.base, s);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        bool ok = (BitVecToInteger(&bv) == 1) && (BitVecLen(&bv) == 4); // min-width clamp
        BitVecDeinit(&bv);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        bool ok = (BitVecToInteger(&bv) == 1) && (BitVecLen(&bv) == 3);
        BitVecDeinit(&bv);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        bool ok = (BitVecLen(&bv) == 5) && (BitVecToInteger(&bv) == 13);
        BitVecDeinit(&bv);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        StrDeinit(&t);
        IntDeinit(&v);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        StrDeinit(&t);
        FloatDeinit(&f);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        StrDeinit(&t);
        FloatDeinit(&f);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        bool ok = p1 && p2;
        ok      = ok && DebugAllocatorLiveCount(&dbg) == 2;
        ok      = ok && DebugAllocatorLiveBytes(&dbg) == (64 + 128);
    
        AllocatorFree(adbg, p1);
        ok = ok && DebugAllocatorLiveCount(&dbg) == 1;
        ok = ok && DebugAllocatorLiveBytes(&dbg) == 128;
    
        AllocatorFree(adbg, p2);
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        ok = ok && DebugAllocatorLiveBytes(&dbg) == 0;
        ok = ok && DebugAllocatorOverflows(&dbg) == 0;
    
        void *p  = AllocatorAlloc(adbg, 0, false);
        bool  ok = (p == NULL) && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        AllocatorFree(adbg, NULL); // no-op, must not crash
        void *p  = AllocatorAlloc(adbg, 32, false);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        if (p)
            AllocatorFree(adbg, p);
        (void)AllocatorAlloc(adbg, 48, true);
    
        bool ok = DebugAllocatorLiveCount(&dbg) == 2;
        ok      = ok && DebugAllocatorLiveBytes(&dbg) == (32 + 48);
        }
        ok = ok && (DebugAllocatorFreedCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        u8 *grown = (u8 *)AllocatorRealloc(adbg, p, 200);
        ok        = ok && (grown != NULL) && (grown[0] == 'h') && (grown[15] == '!');
        ok        = ok && (DebugAllocatorLiveCount(&dbg) == 1);
        ok        = ok && (DebugAllocatorLiveBytes(&dbg) == 200);
    
        void *p   = AllocatorAlloc(adbg, 24, false);
        bool  ok  = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        void *nil = AllocatorRealloc(adbg, p, 0);
        ok        = ok && (nil == NULL) && (DebugAllocatorLiveCount(&dbg) == 0);
        bool  ok  = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        void *nil = AllocatorRealloc(adbg, p, 0);
        ok        = ok && (nil == NULL) && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
    
        void *p  = AllocatorRealloc(adbg, NULL, 32);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        if (p)
            AllocatorFree(adbg, p);
    
        void *p  = AllocatorAlloc(adbg, 64, true);
        bool  ok = (p != NULL) && (DebugAllocatorLiveCount(&dbg) == 1);
        ok       = ok && (DebugAllocatorLiveBytes(&dbg) == 64);
        ok       = ok && (((u64)p & 0xfff) == 0); // page-aligned
    
        AllocatorFree(adbg, p);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
    
        ok = ok && (DebugAllocatorOverflows(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        ok       = ok && (AllocatorFailedAllocations(adbg) == 1);
        // No live record / bytes for a failed alloc.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
            ok    = ok && (ps[i] != NULL);
        }
        ok = ok && (DebugAllocatorLiveCount(&dbg) == N);
    
        // Every pointer must resolve to its own live record (hash + probe
            ps[i] = NULL;
        }
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
        ok   = ok && (r == 0);
        // The allocation is untouched -- still one live record of 64 bytes.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 1);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 64);
        ok         = ok && (grown[50] == 0x5A);
        ok         = ok && (grown[63] == 0x6B);
        ok         = ok && (DebugAllocatorLiveCount(&dbg) == 1);
        ok         = ok && (DebugAllocatorLiveBytes(&dbg) == 200);
        for (u32 i = 0; i < 8; i++)
            ok = ok && (shrunk[i] == (u8)(i + 1));
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 1);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 8);
        // Crux: the fail path must have freed every allocation, including the
        // taken data buffer and the per-stream arrays.
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
    
        DebugAllocatorDeinit(&dbg);
    
        // Everything PdbDeinit owns -- including the name pool -- must be gone.
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
    
        DebugAllocatorDeinit(&dbg);
        // Each piece is long enough to force its own heap buffer.
        Str  s      = StrInitFromZstr("alphaaa,betaaaa,gammaaa,deltaaa", &dbg);
        size before = DebugAllocatorLiveCount(&dbg);
    
        Strs parts = StrSplit(&s, ",");
        // Sanity: the split really produced multiple heap-backed elements,
        // otherwise the leak would be unobservable.
        bool produced = VecLen(&parts) == 4 && DebugAllocatorLiveCount(&dbg) > before;
    
        VecDeinit(&parts); // invokes str_deinit per element on real code.
    
        VecDeinit(&parts); // invokes str_deinit per element on real code.
        size after = DebugAllocatorLiveCount(&dbg);
    
        // Real code: every element buffer (+ backing array) freed -> back to
        StrDeinit(&s);
    
        bool result = (DebugAllocatorLiveCount(&dbg) == 0);
        if (!result) {
            WriteFmt("    FAIL: Expected 0 live allocations after StrDeinit\n");
        DnsResponseDeinit(&resp);
    
        bool match = (ok == false) && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return match;
        DnsResponseDeinit(&resp);
    
        bool match = (ok == false) && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return match;
    // A test passes iff `ok` held AND the DebugAllocator has no outstanding
    // allocations after the test released everything it owns.
    #define LEAK_CLEAN(dbg) (DebugAllocatorLiveCount(&(dbg)) == 0 && DebugAllocatorLiveBytes(&(dbg)) == 0)
    
    typedef Graph(int) IntGraph;
    #include "../Util/TestRunner.h"
    
    #define LEAK_CLEAN(dbg) (DebugAllocatorLiveCount(&(dbg)) == 0 && DebugAllocatorLiveBytes(&(dbg)) == 0)
    
    // Leak-only config: live-count tracking with NO per-alloc stack-trace capture,
        // Real code frees the temp clone inside rotate; with the Deinit
        // removed it survives as a live allocation after bv is torn down.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
            bool inited = StrTryInitFromCstr(&entry.module_path, pe_path, ZstrLen(pe_path), base);
            if (inited) {
                size before = DebugAllocatorLiveCount(&dbg);
                bool opened = entry_open(&entry, base);
                // find_pdb fails -> entry_open returns false; pdb_path must
                    if (entry.pe_open)
                        PeDeinit(&entry.pe);
                    size after = DebugAllocatorLiveCount(&dbg);
                    result     = (after <= before);
                }
        bool result = false;
        if (wrote) {
            size before = DebugAllocatorLiveCount(&dbg);
    
            PdbCache  cache = PdbCacheInit(base);
            u32       off   = 0;
            bool      r     = PdbCacheResolve(&cache, (Zstr)fx.pe_path, mbase, mbase + 0x1100, &name, &off);
            bool      grew  = VecLen(&cache.entries) == 1 && DebugAllocatorLiveCount(&dbg) > before;
    
            PdbCacheDeinit(&cache);
    
            PdbCacheDeinit(&cache);
            size after = DebugAllocatorLiveCount(&dbg);
    
            // After Deinit every cached allocation is released.
        Allocator     *base = ALLOCATOR_OF(&dbg);
    
        size before = DebugAllocatorLiveCount(&dbg);
    
        ProcMaps maps;
        // The load must have taken at least one live allocation (raw buffer
        // and/or entries vector); otherwise the assertion below is vacuous.
        size during = DebugAllocatorLiveCount(&dbg);
        bool ok     = during > before;
    
        // After Deinit every load allocation is gone again.
        size after = DebugAllocatorLiveCount(&dbg);
        ok         = ok && after == before;
    
        // Live allocations are non-zero here (clones + table storage).
        bool result = (DebugAllocatorLiveCount(&dbg) > 0);
    
        // Exercise every removal path against the deep-copy map.
    
        // After deinit, every clone and the table storage must be freed.
        result = result && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        // Real code frees the temp clone inside rotate; with the Deinit
        // removed it survives as a live allocation after bv is torn down.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        // The rendered bv_str borrows bv's DebugAllocator. Real code frees it;
        // the mutant leaks it, so the live count stays non-zero after teardown.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        // bv_str (the render of bv) borrows bv's DebugAllocator. Real code
        // frees it; the mutant leaks it.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DefaultAllocatorDeinit(&palloc);
        // After releasing every Int the test created, nothing the function
        // allocated internally should remain outstanding.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        ok                  = ok && bl_write_file(dsym_path, bl_dsym_buf, dsym_size);
    
        size baseline = DebugAllocatorLiveCount(&dbg);
    
        MachoCache cache    = MachoCacheInit(base);
        bool       resolved = MachoCacheResolve(&cache, bin_path, 0, low + 0x14, &name, &offset);
        ok                  = ok && resolved && name && ZstrCompare(name, fn_name) == 0 && offset == 0x14;
        ok                  = ok && DebugAllocatorLiveCount(&dbg) > baseline;
    
        MachoCacheDeinit(&cache);
    
        MachoCacheDeinit(&cache);
        ok = ok && DebugAllocatorLiveCount(&dbg) == baseline;
    
        FileRemove(bin_path);
        bool      ok       = mc_write_file(bin_path, mc_bin_buf, bin_size);
    
        size baseline = DebugAllocatorLiveCount(&dbg);
    
        MachoCache cache      = MachoCacheInit(base);
    
        // Cache now holds allocations; live count must have grown.
        ok = ok && DebugAllocatorLiveCount(&dbg) > baseline;
    
        MachoCacheDeinit(&cache);
    
        MachoCacheDeinit(&cache);
        ok = ok && DebugAllocatorLiveCount(&dbg) == baseline;
    
        FileRemove(bin_path);
        ok              = ok && mc_write_file(bin_b, mc_dsym_buf, sz_b);
    
        size baseline = DebugAllocatorLiveCount(&dbg);
    
        MachoCache cache = MachoCacheInit(base);
        ok             = ok && MachoCacheResolve(&cache, bin_b, 0, 0x100000110ull, &name, &off);
        ok             = ok && VecLen(&cache.entries) == 2;
        size after_two = DebugAllocatorLiveCount(&dbg);
    
        // Re-resolve module B: its entry lives at index 1, so the find loop
        ok = ok && MachoCacheResolve(&cache, bin_b, 0, 0x100000110ull, &name, &off);
        ok = ok && VecLen(&cache.entries) == 2;
        ok = ok && DebugAllocatorLiveCount(&dbg) == after_two;
    
        // Re-resolve module A (index 0) too: still a hit, no growth.
        ok = ok && MachoCacheResolve(&cache, bin_a, 0, 0x100000110ull, &name, &off);
        ok = ok && VecLen(&cache.entries) == 2;
        ok = ok && DebugAllocatorLiveCount(&dbg) == after_two;
    
        // Two entries are live; MachoCacheDeinit must free BOTH and return to
        // entry 0 and leaks entry 1.
        MachoCacheDeinit(&cache);
        ok = ok && DebugAllocatorLiveCount(&dbg) == baseline;
    
        FileRemove(bin_a);
        ok                  = ok && mc_write_file(dsym_path, mc_dsym_buf, dsym_size);
    
        size baseline = DebugAllocatorLiveCount(&dbg);
    
        MachoCache cache    = MachoCacheInit(base);
        bool       resolved = MachoCacheResolve(&cache, bin_path, 0, 0x100000208ull, &name, &offset);
        ok                  = ok && resolved && name && ZstrCompare(name, "dsym_only_fn") == 0 && offset == 0x8;
        ok                  = ok && DebugAllocatorLiveCount(&dbg) > baseline;
    
        MachoCacheDeinit(&cache);
    
        MachoCacheDeinit(&cache);
        ok = ok && DebugAllocatorLiveCount(&dbg) == baseline;
    
        FileRemove(bin_path);
        ok                  = ok && mc_write_file(dsym_path, mc_dsym_buf, dsym_size);
    
        size baseline = DebugAllocatorLiveCount(&dbg);
    
        MachoCache cache = MachoCacheInit(base);
    
        MachoCacheDeinit(&cache);
        ok = ok && DebugAllocatorLiveCount(&dbg) == baseline;
    
        FileRemove(bin_path);
        ok                  = ok && mc_write_file(dsym_path, mc_dsym_buf, dsym_size);
    
        size baseline = DebugAllocatorLiveCount(&dbg);
    
        MachoCache cache  = MachoCacheInit(base);
        bool resolved = MachoCacheResolve(&cache, bin_path, 0, low + 0x20, &name, &offset);
        ok            = ok && resolved && name && ZstrCompare(name, fn_name) == 0 && offset == 0x20;
        ok            = ok && DebugAllocatorLiveCount(&dbg) > baseline;
    
        MachoCacheDeinit(&cache);
    
        MachoCacheDeinit(&cache);
        ok = ok && DebugAllocatorLiveCount(&dbg) == baseline;
    
        FileRemove(bin_path);
        IntDeinit(&inverse);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        IntDeinit(&check);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        IntDeinit(&check);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        IntDeinit(&r);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        IntDeinit(&next);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        IntDeinit(&value);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        IntDeinit(&parsed);
    
        ok = ok && DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
        DebugAllocator alloc    = DebugAllocatorInit();
        Allocator     *a        = ALLOCATOR_OF(&alloc);
        size           baseline = DebugAllocatorLiveCount(&alloc);
    
        DnsAddrs out = VecInitT(out, a);
        VecDeinit(&out);
    
        bool back = DebugAllocatorLiveCount(&alloc) == baseline;
    
        DebugAllocatorDeinit(&alloc);
        DebugAllocator alloc    = DebugAllocatorInit();
        Allocator     *a        = ALLOCATOR_OF(&alloc);
        size           baseline = DebugAllocatorLiveCount(&alloc);
    
        HostsTable t = parse_hosts_crafted(
        free_hosts(&t);
    
        bool back = DebugAllocatorLiveCount(&alloc) == baseline;
    
        DebugAllocatorDeinit(&alloc);
    
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        IntDeinit(&value);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        FloatDeinit(&value);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    #define LEAK_WRITE_EPILOGUE()                                                                                          \
        StrDeinit(&out);                                                                                                   \
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);                           \
        DebugAllocatorDeinit(&dbg);                                                                                        \
        return ok
        ok = ok && StrAppendFmt(&out, "{}", s) && (StrLen(&out) > 0);
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        StrDeinit(&s);
        ok = ok && StrAppendFmt(&out, "{}", v) && (StrLen(&out) > 0);
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        IntDeinit(&v);
        ok = ok && StrAppendFmt(&out, "{}", v) && (StrLen(&out) > 0);
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        FloatDeinit(&v);
        ok = ok && StrAppendFmt(&out, "{}", v) && (StrLen(&out) > 0);
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        BitVecDeinit(&v);
        bool           ok  = StrAppendFmt(&out, "{x}", s) && (StrLen(&out) > 0);
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        StrDeinit(&s);
        ok = ok && StrAppendFmt(&out, "{}", f) && (StrLen(&out) > 0);
        StrDeinit(&out);
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        FloatDeinit(&f);
        Zstr           p   = input;
        StrReadFmt(p, fmt, s); // reader frees s on the unterminated/error branch
        bool ok = (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        Zstr           p   = "aaaaaaaaaaaaaaaaaaaaaaaa\\q"; // 24 'a' + invalid \q
        StrReadFmt(p, "{}", s);
        bool ok = (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        StrReadFmt(p, "{}", bv);
        BitVecDeinit(&bv);
        bool ok = (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        StrReadFmt(p, "{}", fv);
        FloatDeinit(&fv);
        bool ok = (DebugAllocatorLiveCount(&dbg) == 0) && (DebugAllocatorLiveBytes(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        // With four registered specs (plus the auto --help spec) left_col is
        // populated; the 414 loop must free each. A removed Deinit leaks them.
        bool ok = (rc == ARG_RUN_HELP) && (DebugAllocatorLiveCount(&dbg) == 0);
    
        DebugAllocatorDeinit(&dbg);
        ElfDeinit(&elf);
    
        bool ok = !built && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
        ElfDeinit(&elf);
    
        bool ok = !built && (DebugAllocatorLiveCount(&dbg) == 0);
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        // The open must have failed AND left no live allocations behind.
        bool ok = !opened && DebugAllocatorLiveCount(&dbg) == 0;
    
        DebugAllocatorDeinit(&dbg);
        }
    
        size       before = DebugAllocatorLiveCount(&dbg);
        DwarfLines lines;
        bool       built = DwarfLinesBuildFromElf(&lines, &elf, base);
        // pool freed by DwarfLinesDeinit -> back to baseline. Mutant (no
        // cu_strings_deinit): the per-CU scratch vectors leak -> after > before.
        size after = DebugAllocatorLiveCount(&dbg);
        ok         = ok && (after == before);
        }
    
        size       before = DebugAllocatorLiveCount(&dbg);
        DwarfLines lines;
        bool       built = DwarfLinesBuildFromElf(&lines, &elf, base);
            ok                      = ok && e && e->file && ZstrCompare(e->file, "source.c") == 0;
            // After the build there must be live allocations to release.
            ok = ok && (DebugAllocatorLiveCount(&dbg) > before);
            DwarfLinesDeinit(&lines);
        }
        // Real Deinit frees entries + string_pool -> baseline. Mutant leaves
        // the string pool buffer live -> after > before.
        size after = DebugAllocatorLiveCount(&dbg);
        ok         = ok && (after == before);
        MapDeinit(&cfg);
    
        ok = DebugAllocatorLiveCount(&dbg) == 0;
        DebugAllocatorDeinit(&dbg);
        return ok;
    
        // The stripped-away old value buffer must already be freed.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        // The parser's local key copy must already be freed.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        // The parser's local value copy must already be freed.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
    
        // Every transient lookup key must already be freed.
        ok = ok && (DebugAllocatorLiveCount(&dbg) == 0);
        ok = ok && (DebugAllocatorLiveBytes(&dbg) == 0);
Last updated on