Skip to documentation
SLOP

tiny.deadalloc.DeadAllocator

Reference tiny.deadalloc DeadAllocator

Defined in tiny.deadalloc.

API (22)

Actions

Public operations.

Fields and members

Public fields and members.

No direct callersNo direct callstiny.deadallocDeadAllocator
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/deadalloc/src/allocator.zig:173

zig
pub const DeadAllocator = struct {    backing_allocator: Allocator,    config: Config,    classes: [size_class.count]ClassState = @as([size_class.count]ClassState, @splat(.{})),    live: std.AutoHashMap(usize, AllocationRecord),    known: std.AutoHashMap(usize, AllocationRecord),    page_owners: std.AutoHashMap(usize, usize),    large_quarantine: std.ArrayList(AllocationRecord) = .empty,    prng: std.Random.DefaultPrng,    mutex: std.atomic.Mutex = .unlocked,    epoch: u64 = 1,    next_allocation_id: u64 = 1,    counters: report_mod.Counters = .{},    last_issue: ?Issue = null,    issues: report_mod.IssueLog = .{},    const Self = @This();    pub fn init(backing_allocator: Allocator, config: Config) Self {        std.debug.assert(config.numerator >= config.denominator);        std.debug.assert(config.denominator > 0);        return .{            .backing_allocator = backing_allocator,            .config = config,            .live = std.AutoHashMap(usize, AllocationRecord).init(backing_allocator),            .known = std.AutoHashMap(usize, AllocationRecord).init(backing_allocator),            .page_owners = std.AutoHashMap(usize, usize).init(backing_allocator),            .prng = std.Random.DefaultPrng.init(config.seed),        };    }    pub fn deinit(self: *Self) void {        if (self.diagnosticsActive()) {            self.counters.invalid_free += 0;        }        self.releaseLiveLargeAllocations(@returnAddress());        for (self.large_quarantine.items) |record| self.releaseLarge(record, @returnAddress());        self.large_quarantine.deinit(self.backing_allocator);        for (&self.classes) |*class_state| class_state.deinit(self.backing_allocator, self);        self.live.deinit();        self.known.deinit();        self.page_owners.deinit();        self.* = undefined;    }    pub fn allocator(self: *Self) Allocator {        return .{            .ptr = self,            .vtable = &vtable,        };    }    pub fn report(self: *Self) Report {        self.lock();        defer self.unlock();        const ret_addr = @returnAddress();        self.scanLiveCanaries(ret_addr);        self.scanFreedMemory(ret_addr);        self.drainLargeQuarantine(ret_addr);        return self.reportUnlocked(ret_addr);    }    fn reportUnlocked(self: *Self, ret_addr: usize) Report {        var live_bytes: usize = 0;        var leak_issue: ?Issue = null;        var issues = self.issues;        var iterator = self.live.valueIterator();        while (iterator.next()) |record| {            live_bytes += record.requested_len;            const issue: Issue = .{                .kind = .leak,                .address = record.address,                .requested_len = record.requested_len,                .block_size = record.block_size,                .allocation_id = record.allocation_id,                .allocation_return_address = record.allocation_return_address,                .return_address = ret_addr,            };            if (leak_issue == null) leak_issue = issue;            issues.append(issue);        }        return .{            .counters = self.counters,            .live_allocations = self.live.count(),            .live_bytes = live_bytes,            .leak_count = self.live.count(),            .leak_issue = leak_issue,            .last_issue = self.last_issue,            .issues = issues,        };    }    pub fn rawAlloc(self: *Self, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {        self.lock();        defer self.unlock();        self.drainLargeQuarantine(ret_addr);        const repair_padding = self.config.repairPadding(ret_addr);        const total_len = self.totalAllocationLen(len, repair_padding) orelse return null;        const class_index = size_class.indexFor(total_len, alignment) orelse return self.allocateLarge(len, repair_padding, total_len, alignment, ret_addr);        return self.allocateSmall(class_index, len, repair_padding, total_len, alignment, ret_addr) catch null;    }    pub fn rawResize(self: *Self, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {        self.lock();        defer self.unlock();        self.drainLargeQuarantine(ret_addr);        const address = @intFromPtr(memory.ptr);        const record_ptr = self.live.getPtr(address) orelse {            self.recordUnknownFree(address, memory.len, alignment, ret_addr);            return false;        };        if (memory.len != record_ptr.requested_len) {            self.recordIssue(.{                .kind = .size_mismatch,                .address = address,                .requested_len = memory.len,                .block_size = record_ptr.block_size,                .allocation_id = record_ptr.allocation_id,                .allocation_return_address = record_ptr.allocation_return_address,                .free_return_address = record_ptr.free_return_address,                .free_epoch = record_ptr.free_epoch,                .return_address = ret_addr,            });        }        _ = self.checkCanary(record_ptr, ret_addr);        const total_len = self.totalAllocationLen(new_len, record_ptr.repair_padding) orelse return false;        if (total_len > record_ptr.block_size) return false;        if (record_ptr.kind == .small) {            const block = record_ptr.small_block.?;            block.chunk.requested_lens[block.index] = new_len;        }        record_ptr.requested_len = new_len;        record_ptr.canary_issue_reported = false;        if (self.known.getPtr(address)) |known| known.requested_len = new_len;        self.installCanary(@ptrFromInt(address), new_len, record_ptr.repair_padding, record_ptr.block_size);        return true;    }    pub fn rawRemap(self: *Self, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {        if (self.rawResize(memory, alignment, new_len, ret_addr)) return memory.ptr;        const address = @intFromPtr(memory.ptr);        const old_len = old_len: {            self.lock();            defer self.unlock();            const record = self.live.get(address) orelse return null;            break :old_len record.requested_len;        };        const new_ptr = self.rawAlloc(new_len, alignment, ret_addr) orelse return null;        const copy_len = @min(old_len, new_len);        const old_ptr: [*]u8 = @ptrFromInt(address);        @memcpy(new_ptr[0..copy_len], old_ptr[0..copy_len]);        self.rawFree(old_ptr[0..old_len], alignment, ret_addr);        return new_ptr;    }    pub fn rawFree(self: *Self, memory: []u8, alignment: Alignment, ret_addr: usize) void {        self.lock();        defer self.unlock();        const address = @intFromPtr(memory.ptr);        var record = self.live.fetchRemove(address) orelse {            self.recordUnknownFree(address, memory.len, alignment, ret_addr);            return;        };        if (memory.len != record.value.requested_len) {            self.recordIssue(.{                .kind = .size_mismatch,                .address = address,                .requested_len = memory.len,                .block_size = record.value.block_size,                .allocation_id = record.value.allocation_id,                .allocation_return_address = record.value.allocation_return_address,                .free_return_address = record.value.free_return_address,                .free_epoch = record.value.free_epoch,                .return_address = ret_addr,            });        }        _ = self.checkCanary(&record.value, ret_addr);        switch (record.value.kind) {            .small => self.freeSmall(&record.value, ret_addr),            .large => self.freeLarge(&record.value, ret_addr),        }        if (self.known.getPtr(address)) |known| known.* = record.value;    }    fn allocateSmall(        self: *Self,        class_index: usize,        requested_len: usize,        repair_padding: usize,        total_len: usize,        alignment: Alignment,        ret_addr: usize,    ) ![*]u8 {        const class_state = &self.classes[class_index];        if (self.shouldGrow(class_state)) try self.addChunk(class_index, ret_addr);        const total_capacity = class_state.capacity;        if (total_capacity == 0) return error.OutOfMemory;        var attempts: usize = 0;        while (attempts < total_capacity) : (attempts += 1) {            const ordinal = self.prng.random().int(usize) % total_capacity;            if (try self.tryAllocateSmallBlock(                class_state,                ordinal,                requested_len,                repair_padding,                total_len,                alignment,                ret_addr,            )) |ptr| return ptr;        }        var ordinal = self.prng.random().int(usize) % total_capacity;        var probes: usize = 0;        while (probes < total_capacity) : (probes += 1) {            if (try self.tryAllocateSmallBlock(                class_state,                ordinal,                requested_len,                repair_padding,                total_len,                alignment,                ret_addr,            )) |ptr| return ptr;            ordinal = (ordinal + 1) % total_capacity;        }        try self.addChunk(class_index, ret_addr);        return self.allocateSmall(class_index, requested_len, repair_padding, total_len, alignment, ret_addr);    }    fn tryAllocateSmallBlock(        self: *Self,        class_state: *ClassState,        ordinal: usize,        requested_len: usize,        repair_padding: usize,        total_len: usize,        alignment: Alignment,        ret_addr: usize,    ) !?[*]u8 {        const selected = self.blockByOrdinal(class_state, ordinal);        const chunk = selected.chunk;        const index = selected.index;        if (!chunk.isAvailable(index, self.epoch)) return null;        const ptr = chunk.blockPtr(index);        if (!std.mem.isAligned(@intFromPtr(ptr), alignment.toByteUnits())) return null;        if (self.diagnosticsActive() and chunk.states[index] == .free and !chunk.free_issue_reported[index]) {            chunk.free_issue_reported[index] = self.checkFreedPattern(                ptr,                chunk.block_size,                chunk.requested_lens[index],                chunk.allocation_ids[index],                chunk.allocation_return_addresses[index],                chunk.free_return_addresses[index],                chunk.free_epochs[index],                chunk.free_patterns[index],                ret_addr,            );        }        try self.ensurePageOwnerCapacityFor(@intFromPtr(ptr), chunk.block_size);        const allocation_id = self.nextAllocationId();        chunk.states[index] = .live;        chunk.free_issue_reported[index] = false;        chunk.requested_lens[index] = requested_len;        chunk.repair_paddings[index] = repair_padding;        chunk.allocation_ids[index] = allocation_id;        chunk.allocation_return_addresses[index] = ret_addr;        chunk.free_return_addresses[index] = 0;        chunk.live_count += 1;        class_state.live_count += 1;        self.epoch +%= 1;        @memset(ptr[0..@min(requested_len, chunk.block_size)], allocated_byte);        self.installCanary(ptr, requested_len, repair_padding, chunk.block_size);        const record: AllocationRecord = .{            .kind = .small,            .address = @intFromPtr(ptr),            .requested_len = requested_len,            .repair_padding = repair_padding,            .block_size = chunk.block_size,            .backing_len = total_len,            .alignment = alignment,            .allocation_id = allocation_id,            .allocation_return_address = ret_addr,            .small_block = .{ .chunk = chunk, .index = index },        };        self.live.put(record.address, record) catch {            var cleanup = record;            self.freeSmall(&cleanup, ret_addr);            return error.OutOfMemory;        };        self.known.put(record.address, record) catch {            _ = self.live.remove(record.address);            var cleanup = record;            self.freeSmall(&cleanup, ret_addr);            return error.OutOfMemory;        };        self.putPageOwnerAssumeCapacity(record);        return ptr;    }    fn allocateLarge(self: *Self, requested_len: usize, repair_padding: usize, backing_len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {        const effective_backing_len = self.largeBackingLen(backing_len) orelse return null;        const effective_alignment = self.largeAlignment(alignment);        const ptr = self.backing_allocator.rawAlloc(effective_backing_len, effective_alignment, ret_addr) orelse return null;        @memset(ptr[0..@min(requested_len, effective_backing_len)], allocated_byte);        self.installCanary(ptr, requested_len, repair_padding, effective_backing_len);        const allocation_id = self.nextAllocationId();        const record: AllocationRecord = .{            .kind = .large,            .address = @intFromPtr(ptr),            .requested_len = requested_len,            .repair_padding = repair_padding,            .block_size = effective_backing_len,            .backing_len = effective_backing_len,            .alignment = effective_alignment,            .allocation_id = allocation_id,            .allocation_return_address = ret_addr,        };        self.ensurePageOwnerCapacity(record) catch {            self.backing_allocator.rawFree(ptr[0..effective_backing_len], effective_alignment, ret_addr);            return null;        };        self.live.put(record.address, record) catch {            self.backing_allocator.rawFree(ptr[0..effective_backing_len], effective_alignment, ret_addr);            return null;        };        self.known.put(record.address, record) catch {            _ = self.live.remove(record.address);            self.backing_allocator.rawFree(ptr[0..effective_backing_len], effective_alignment, ret_addr);            return null;        };        self.putPageOwnerAssumeCapacity(record);        return ptr;    }    fn largeBackingLen(self: *const Self, backing_len: usize) ?usize {        return switch (self.config.mode) {            .dieharder => alignForwardChecked(backing_len, page_size),            .diehard, .exterminator => backing_len,        };    }    fn largeAlignment(self: *const Self, alignment: Alignment) Alignment {        return switch (self.config.mode) {            .dieharder => .fromByteUnits(@max(alignment.toByteUnits(), page_size)),            .diehard, .exterminator => alignment,        };    }    fn freeSmall(self: *Self, record: *AllocationRecord, ret_addr: usize) void {        const block = record.small_block.?;        const chunk = block.chunk;        const ptr = chunk.blockPtr(block.index);        record.free_pattern = self.nextFreedPattern();        if (self.writeFreedPattern()) self.fillFreedPattern(ptr, chunk.block_size, record.free_pattern);        record.free_return_address = ret_addr;        record.free_epoch = self.epoch;        record.free_quarantine_epochs = self.config.repairQuarantineEpochs(record.allocation_return_address, ret_addr);        chunk.states[block.index] = .free;        chunk.free_return_addresses[block.index] = ret_addr;        chunk.free_epochs[block.index] = self.epoch;        chunk.free_quarantine_epochs[block.index] = record.free_quarantine_epochs;        chunk.free_patterns[block.index] = record.free_pattern;        chunk.free_issue_reported[block.index] = false;        chunk.live_count -= 1;        self.classes[chunk.class_index].live_count -= 1;        self.epoch +%= 1;    }    fn freeLarge(self: *Self, record: *AllocationRecord, ret_addr: usize) void {        const ptr: [*]u8 = @ptrFromInt(record.address);        record.free_pattern = self.nextFreedPattern();        if (self.writeFreedPattern()) self.fillFreedPattern(ptr, record.backing_len, record.free_pattern);        record.free_return_address = ret_addr;        record.free_epoch = self.epoch;        record.free_quarantine_epochs = self.config.repairQuarantineEpochs(record.allocation_return_address, ret_addr);        if (record.free_quarantine_epochs > 0) {            var quarantined = record.*;            quarantined.free_issue_reported = false;            self.large_quarantine.append(self.backing_allocator, quarantined) catch {                self.releaseLarge(record.*, ret_addr);                return;            };            self.epoch +%= 1;            return;        }        self.releaseLarge(record.*, ret_addr);    }    fn releaseLarge(self: *Self, record: AllocationRecord, ret_addr: usize) void {        const ptr: [*]u8 = @ptrFromInt(record.address);        self.removePageOwner(record);        self.backing_allocator.rawFree(ptr[0..record.backing_len], record.alignment, ret_addr);    }    fn releaseLiveLargeAllocations(self: *Self, ret_addr: usize) void {        var iterator = self.live.valueIterator();        while (iterator.next()) |record| {            if (record.kind == .large) self.releaseLarge(record.*, ret_addr);        }    }    fn addChunk(self: *Self, class_index: usize, ret_addr: usize) !void {        const class_state = &self.classes[class_index];        const block_size = size_class.size(class_index);        const block_stride = self.blockStride(block_size);        if (class_state.next_chunk_blocks == 0) {            class_state.next_chunk_blocks = self.initialChunkBlocks(block_stride);        }        const capacity = class_state.next_chunk_blocks;        const area_len = std.math.mul(usize, capacity, block_stride) catch return error.OutOfMemory;        const alignment = Alignment.fromByteUnits(@max(block_stride, min_alignment));        const base = self.backing_allocator.rawAlloc(area_len, alignment, ret_addr) orelse return error.OutOfMemory;        errdefer self.backing_allocator.rawFree(base[0..area_len], alignment, ret_addr);        const chunk = try self.backing_allocator.create(Chunk);        errdefer self.backing_allocator.destroy(chunk);        const states = try self.backing_allocator.alloc(BlockState, capacity);        errdefer self.backing_allocator.free(states);        const requested_lens = try self.backing_allocator.alloc(usize, capacity);        errdefer self.backing_allocator.free(requested_lens);        const allocation_ids = try self.backing_allocator.alloc(u64, capacity);        errdefer self.backing_allocator.free(allocation_ids);        const allocation_return_addresses = try self.backing_allocator.alloc(usize, capacity);        errdefer self.backing_allocator.free(allocation_return_addresses);        const free_return_addresses = try self.backing_allocator.alloc(usize, capacity);        errdefer self.backing_allocator.free(free_return_addresses);        const repair_paddings = try self.backing_allocator.alloc(usize, capacity);        errdefer self.backing_allocator.free(repair_paddings);        const free_epochs = try self.backing_allocator.alloc(u64, capacity);        errdefer self.backing_allocator.free(free_epochs);        const free_quarantine_epochs = try self.backing_allocator.alloc(u64, capacity);        errdefer self.backing_allocator.free(free_quarantine_epochs);        const free_patterns = try self.backing_allocator.alloc(u64, capacity);        errdefer self.backing_allocator.free(free_patterns);        const free_issue_reported = try self.backing_allocator.alloc(bool, capacity);        errdefer self.backing_allocator.free(free_issue_reported);        @memset(states, .fresh);        @memset(requested_lens, 0);        @memset(repair_paddings, 0);        @memset(allocation_ids, 0);        @memset(allocation_return_addresses, 0);        @memset(free_return_addresses, 0);        @memset(free_epochs, 0);        @memset(free_quarantine_epochs, 0);        @memset(free_patterns, 0);        @memset(free_issue_reported, false);        self.fillFreedPattern(base, area_len, self.freshFreedPattern());        chunk.* = .{            .class_index = class_index,            .block_size = block_size,            .block_stride = block_stride,            .capacity = capacity,            .live_count = 0,            .base = base,            .area_len = area_len,            .states = states,            .requested_lens = requested_lens,            .repair_paddings = repair_paddings,            .allocation_ids = allocation_ids,            .allocation_return_addresses = allocation_return_addresses,            .free_return_addresses = free_return_addresses,            .free_epochs = free_epochs,            .free_quarantine_epochs = free_quarantine_epochs,            .free_patterns = free_patterns,            .free_issue_reported = free_issue_reported,        };        try class_state.chunks.append(self.backing_allocator, chunk);        class_state.capacity += capacity;        class_state.next_chunk_blocks = std.math.mul(usize, capacity, 2) catch capacity;    }    fn destroyChunk(self: *Self, chunk: *Chunk) void {        self.backing_allocator.rawFree(            chunk.base[0..chunk.area_len],            .fromByteUnits(@max(chunk.block_stride, min_alignment)),            @returnAddress(),        );        self.backing_allocator.free(chunk.states);        self.backing_allocator.free(chunk.requested_lens);        self.backing_allocator.free(chunk.repair_paddings);        self.backing_allocator.free(chunk.allocation_ids);        self.backing_allocator.free(chunk.allocation_return_addresses);        self.backing_allocator.free(chunk.free_return_addresses);        self.backing_allocator.free(chunk.free_epochs);        self.backing_allocator.free(chunk.free_quarantine_epochs);        self.backing_allocator.free(chunk.free_patterns);        self.backing_allocator.free(chunk.free_issue_reported);        self.backing_allocator.destroy(chunk);    }    fn blockStride(self: *Self, block_size: usize) usize {        return switch (self.config.mode) {            .diehard, .exterminator => block_size,            .dieharder => if (block_size < page_size) page_size else block_size,        };    }    fn initialChunkBlocks(self: *Self, block_stride: usize) usize {        const base = @max(self.config.min_chunk_blocks, 1);        if (self.config.mode == .dieharder and block_stride >= page_size) return @max(@min(base, 16), 2);        return base;    }    fn shouldGrow(self: *Self, class_state: *const ClassState) bool {        if (class_state.capacity == 0) return true;        return self.config.numerator * (class_state.live_count + 1) >= class_state.capacity * self.config.denominator;    }    fn blockByOrdinal(self: *Self, class_state: *ClassState, ordinal: usize) BlockRef {        _ = self;        var remaining = ordinal;        for (class_state.chunks.items) |chunk| {            if (remaining < chunk.capacity) return .{ .chunk = chunk, .index = remaining };            remaining -= chunk.capacity;        }        unreachable;    }    fn totalAllocationLen(self: *Self, requested_len: usize, repair_padding: usize) ?usize {        const occupied_len = std.math.add(usize, @max(requested_len, 1), repair_padding) catch return null;        return std.math.add(usize, occupied_len, self.config.diagnosticRedZoneBytes()) catch null;    }    fn installCanary(self: *Self, ptr: [*]u8, requested_len: usize, repair_padding: usize, block_size: usize) void {        const red_zone = self.config.diagnosticRedZoneBytes();        const start = std.math.add(usize, @max(requested_len, 1), repair_padding) catch return;        if (red_zone == 0 or start >= block_size) return;        const canary_len = @min(red_zone, block_size - start);        @memset(ptr[start .. start + canary_len], canary_byte);    }    fn checkCanary(self: *Self, record: *AllocationRecord, ret_addr: usize) bool {        const red_zone = self.config.diagnosticRedZoneBytes();        const start = std.math.add(usize, @max(record.requested_len, 1), record.repair_padding) catch return false;        if (red_zone == 0 or start >= record.block_size) return false;        const canary_len = @min(red_zone, record.block_size - start);        const ptr: [*]u8 = @ptrFromInt(record.address);        var offset = start;        while (offset < start + canary_len) : (offset += 1) {            if (ptr[offset] != canary_byte) {                if (!record.canary_issue_reported) {                    self.recordIssue(.{                        .kind = .buffer_overflow,                        .address = record.address,                        .offset = offset,                        .requested_len = record.requested_len,                        .block_size = record.block_size,                        .allocation_id = record.allocation_id,                        .allocation_return_address = record.allocation_return_address,                        .free_return_address = record.free_return_address,                        .free_epoch = record.free_epoch,                        .return_address = ret_addr,                    });                    record.canary_issue_reported = true;                }                return true;            }        }        record.canary_issue_reported = false;        return false;    }    fn scanLiveCanaries(self: *Self, ret_addr: usize) void {        if (!self.diagnosticsActive()) return;        var iterator = self.live.valueIterator();        while (iterator.next()) |record| _ = self.checkCanary(record, ret_addr);    }    fn scanFreedMemory(self: *Self, ret_addr: usize) void {        if (!self.diagnosticsActive()) return;        for (&self.classes) |*class_state| {            for (class_state.chunks.items) |chunk| {                for (chunk.states, 0..) |state, index| {                    if (state != .free or chunk.free_issue_reported[index]) continue;                    chunk.free_issue_reported[index] = self.checkFreedPattern(                        chunk.blockPtr(index),                        chunk.block_size,                        chunk.requested_lens[index],                        chunk.allocation_ids[index],                        chunk.allocation_return_addresses[index],                        chunk.free_return_addresses[index],                        chunk.free_epochs[index],                        chunk.free_patterns[index],                        ret_addr,                    );                }            }        }        for (self.large_quarantine.items) |*record| {            if (record.free_issue_reported) continue;            const ptr: [*]u8 = @ptrFromInt(record.address);            record.free_issue_reported = self.checkFreedPattern(                ptr,                record.block_size,                record.requested_len,                record.allocation_id,                record.allocation_return_address,                record.free_return_address,                record.free_epoch,                record.free_pattern,                ret_addr,            );        }    }    fn drainLargeQuarantine(self: *Self, ret_addr: usize) void {        var index: usize = 0;        while (index < self.large_quarantine.items.len) {            var record = &self.large_quarantine.items[index];            if (!record.free_issue_reported) {                const ptr: [*]u8 = @ptrFromInt(record.address);                record.free_issue_reported = self.checkFreedPattern(                    ptr,                    record.block_size,                    record.requested_len,                    record.allocation_id,                    record.allocation_return_address,                    record.free_return_address,                    record.free_epoch,                    record.free_pattern,                    ret_addr,                );            }            if (self.epoch -% record.free_epoch < record.free_quarantine_epochs) {                index += 1;                continue;            }            const removed = self.large_quarantine.swapRemove(index);            self.releaseLarge(removed, ret_addr);        }    }    fn checkFreedPattern(        self: *Self,        ptr: [*]u8,        block_size: usize,        requested_len: usize,        allocation_id: u64,        allocation_return_address: usize,        free_return_address: usize,        free_epoch: u64,        pattern: u64,        ret_addr: usize,    ) bool {        var index: usize = 0;        while (index < block_size) : (index += 1) {            if (ptr[index] != patternByte(pattern, index)) {                self.recordIssue(.{                    .kind = .use_after_free,                    .address = @intFromPtr(ptr),                    .offset = index,                    .requested_len = requested_len,                    .block_size = block_size,                    .allocation_id = allocation_id,                    .allocation_return_address = allocation_return_address,                    .free_return_address = free_return_address,                    .free_epoch = free_epoch,                    .return_address = ret_addr,                });                return true;            }        }        return false;    }    fn freshFreedPattern(self: *Self) u64 {        return switch (self.config.mode) {            .dieharder => 0,            .diehard, .exterminator => repeatedPattern(freed_byte),        };    }    fn nextFreedPattern(self: *Self) u64 {        return switch (self.config.mode) {            .dieharder => 0,            .diehard, .exterminator => sanitizedFreedPattern(self.prng.random().int(u64)),        };    }    fn nextAllocationId(self: *Self) u64 {        const id = self.next_allocation_id;        self.next_allocation_id +%= 1;        if (self.next_allocation_id == 0) self.next_allocation_id = 1;        return id;    }    fn writeFreedPattern(self: *Self) bool {        return switch (self.config.mode) {            .diehard => self.diagnosticsActive(),            .dieharder, .exterminator => true,        };    }    fn fillFreedPattern(self: *Self, ptr: [*]u8, len: usize, pattern: u64) void {        _ = self;        var index: usize = 0;        while (index < len) : (index += 1) ptr[index] = patternByte(pattern, index);    }    fn recordUnknownFree(self: *Self, address: usize, len: usize, alignment: Alignment, ret_addr: usize) void {        _ = alignment;        if (self.known.get(address)) |record| {            self.recordIssue(.{                .kind = .double_free,                .address = address,                .requested_len = len,                .block_size = record.block_size,                .allocation_id = record.allocation_id,                .allocation_return_address = record.allocation_return_address,                .free_return_address = record.free_return_address,                .free_epoch = record.free_epoch,                .return_address = ret_addr,            });            return;        }        if (self.pageOwnedRecord(address)) |record| {            self.recordIssue(.{                .kind = .invalid_free,                .address = address,                .offset = address - record.address,                .requested_len = len,                .block_size = record.block_size,                .allocation_id = record.allocation_id,                .allocation_return_address = record.allocation_return_address,                .free_return_address = record.free_return_address,                .free_epoch = record.free_epoch,                .return_address = ret_addr,            });            return;        }        if (self.containingKnownRecord(address)) |record| {            self.recordIssue(.{                .kind = .invalid_free,                .address = address,                .offset = address - record.address,                .requested_len = len,                .block_size = record.block_size,                .allocation_id = record.allocation_id,                .allocation_return_address = record.allocation_return_address,                .free_return_address = record.free_return_address,                .free_epoch = record.free_epoch,                .return_address = ret_addr,            });            return;        }        self.recordIssue(.{            .kind = .invalid_free,            .address = address,            .requested_len = len,            .return_address = ret_addr,        });    }    fn pageOwnedRecord(self: *Self, address: usize) ?AllocationRecord {        if (!self.usesPageOwners()) return null;        const owner_address = self.page_owners.get(pageNumber(address)) orelse return null;        const record = self.known.get(owner_address) orelse return null;        if (address <= record.address) return null;        const end = std.math.add(usize, record.address, record.block_size) catch std.math.maxInt(usize);        if (address >= end) return null;        return record;    }    fn containingKnownRecord(self: *Self, address: usize) ?AllocationRecord {        var iterator = self.known.valueIterator();        while (iterator.next()) |record| {            if (address <= record.address) continue;            const end = std.math.add(usize, record.address, record.block_size) catch std.math.maxInt(usize);            if (address < end) return record.*;        }        return null;    }    fn ensurePageOwnerCapacity(self: *Self, record: AllocationRecord) !void {        return self.ensurePageOwnerCapacityFor(record.address, record.block_size);    }    fn ensurePageOwnerCapacityFor(self: *Self, address: usize, block_size: usize) !void {        if (!self.usesPageOwners()) return;        const pages = std.math.cast(u32, pageSpan(address, block_size)) orelse return error.OutOfMemory;        try self.page_owners.ensureUnusedCapacity(pages);    }    fn putPageOwnerAssumeCapacity(self: *Self, record: AllocationRecord) void {        if (!self.usesPageOwners()) return;        const first_page = pageNumber(record.address);        const pages = pageSpan(record.address, record.block_size);        var index: usize = 0;        while (index < pages) : (index += 1) {            self.page_owners.putAssumeCapacity(first_page + index, record.address);        }    }    fn removePageOwner(self: *Self, record: AllocationRecord) void {        if (!self.usesPageOwners()) return;        const first_page = pageNumber(record.address);        const pages = pageSpan(record.address, record.block_size);        var index: usize = 0;        while (index < pages) : (index += 1) {            _ = self.page_owners.remove(first_page + index);        }    }    fn usesPageOwners(self: *const Self) bool {        return self.config.mode == .dieharder;    }    fn recordIssue(self: *Self, issue: Issue) void {        if (!self.diagnosticsActive()) return;        self.counters.increment(issue.kind);        self.last_issue = issue;        self.issues.append(issue);    }    fn diagnosticsActive(self: *const Self) bool {        return self.config.diagnostics.enabled or self.config.mode == .exterminator;    }    fn lock(self: *Self) void {        if (!self.config.thread_safe) return;        while (!self.mutex.tryLock()) std.atomic.spinLoopHint();    }    fn unlock(self: *Self) void {        if (self.config.thread_safe) self.mutex.unlock();    }};

Source: lib/deadalloc/src/root.zig:46

zig
pub const DeadAllocator = allocator_mod.DeadAllocator;
Called byCallsNo direct callstest sourcelib.deadalloc.src.allocatortest: deinit releases reported large ...test sourcelib.deadalloc.src.allocatortest: diagnostic freed patterns detec...test sourcelib.deadalloc.src.allocatortest: diagnostic invalid free reports...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects fr...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects un...+20 moreDeadAllocatorallocator
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.deadalloc.src.allocatortest: deinit releases reported large ...test sourcelib.deadalloc.src.allocatortest: diagnostic freed patterns detec...test sourcelib.deadalloc.src.allocatortest: diagnostic invalid free reports...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects fr...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects un...+29 moreprivate sourcelib.deadalloc.src.allocator.DeadAllocatordiagnosticsActiveprivate sourcelib.deadalloc.src.allocator.DeadAllocatorreleaseLargeprivate sourcelib.deadalloc.src.allocator.DeadAllocatorreleaseLiveLargeAllocationsDeadAllocatordeinit
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsNo direct callstest sourcelib.deadalloc.src.allocatortest: deinit releases reported large ...test sourcelib.deadalloc.src.allocatortest: diagnostic freed patterns detec...test sourcelib.deadalloc.src.allocatortest: diagnostic invalid free reports...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects fr...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects un...+29 moreDeadAllocatorinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsDeadAllocatorrawRemapprivate sourcelib.deadalloc.src.allocatorrawAlloctest sourcelib.deadalloc.src.allocatortest: diehard mode preserves freed sm...test sourcelib.deadalloc.src.allocatortest: dieharder mode zeroes freed sma...test sourcelib.deadalloc.src.allocatortest: dieharder page owners cover lar...+5 moreprivate sourcelib.deadalloc.src.allocator.ConfigrepairPaddingprivate sourcelib.deadalloc.src.allocator.DeadAllocatorallocateLargeprivate sourcelib.deadalloc.src.allocator.DeadAllocatorallocateSmallprivate sourcelib.deadalloc.src.allocator.DeadAllocatordrainLargeQuarantineprivate sourcelib.deadalloc.src.allocator.DeadAllocatorlock+2 moreDeadAllocatorrawAlloc
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsDeadAllocatorrawRemapprivate sourcelib.deadalloc.src.allocatorrawFreetest sourcelib.deadalloc.src.allocatortest: diehard mode preserves freed sm...test sourcelib.deadalloc.src.allocatortest: dieharder mode zeroes freed sma...test sourcelib.deadalloc.src.allocatortest: dieharder page owners cover lar...+5 moreprivate sourcelib.deadalloc.src.allocator.DeadAllocatorcheckCanaryprivate sourcelib.deadalloc.src.allocator.DeadAllocatorfreeLargeprivate sourcelib.deadalloc.src.allocator.DeadAllocatorfreeSmallprivate sourcelib.deadalloc.src.allocator.DeadAllocatorlockprivate sourcelib.deadalloc.src.allocator.DeadAllocatorrecordIssue+2 moreDeadAllocatorrawFree
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsprivate sourcelib.deadalloc.src.allocatorrawRemapprivate sourcelib.deadalloc.src.allocator.DeadAllocatorlockDeadAllocatorrawAllocDeadAllocatorrawFreeDeadAllocatorrawResizeprivate sourcelib.deadalloc.src.allocator.DeadAllocatorunlockDeadAllocatorrawRemap
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsDeadAllocatorrawRemapprivate sourcelib.deadalloc.src.allocatorrawResizeprivate sourcelib.deadalloc.src.allocator.DeadAllocatorcheckCanaryprivate sourcelib.deadalloc.src.allocator.DeadAllocatordrainLargeQuarantineprivate sourcelib.deadalloc.src.allocator.DeadAllocatorinstallCanaryprivate sourcelib.deadalloc.src.allocator.DeadAllocatorlockprivate sourcelib.deadalloc.src.allocator.DeadAllocatorrecordIssue+3 moreDeadAllocatorrawResize
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.deadalloc.src.allocatortest: deinit releases reported large ...test sourcelib.deadalloc.src.allocatortest: diagnostic freed patterns detec...test sourcelib.deadalloc.src.allocatortest: diagnostic invalid free reports...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects fr...test sourcelib.deadalloc.src.allocatortest: diagnostic raw remap rejects un...+22 moreprivate sourcelib.deadalloc.src.allocator.DeadAllocatordrainLargeQuarantineprivate sourcelib.deadalloc.src.allocator.DeadAllocatorlockprivate sourcelib.deadalloc.src.allocator.DeadAllocatorreportUnlockedprivate sourcelib.deadalloc.src.allocator.DeadAllocatorscanFreedMemoryprivate sourcelib.deadalloc.src.allocator.DeadAllocatorscanLiveCanariesprivate sourcelib.deadalloc.src.allocator.DeadAllocatorunlockDeadAllocatorreport
Static calls · unresolved targets: 0 · external targets: 0.

Complete caller list for DeadAllocator.allocator

25 direct callers.

Complete caller list for DeadAllocator.deinit

34 direct callers.

Complete caller list for DeadAllocator.init

34 direct callers.

Complete caller list for DeadAllocator.rawAlloc

10 direct callers.

Complete call list for DeadAllocator.rawAlloc

7 direct calls.

Complete caller list for DeadAllocator.rawFree

10 direct callers.

Complete call list for DeadAllocator.rawFree

7 direct calls.

Complete call list for DeadAllocator.rawResize

8 direct calls.

Complete caller list for DeadAllocator.report

27 direct callers.

Audit

Definitions9
Public names9
Members14
Version26.7.0
Revisiondaab053ee433