2025-07-03 12:53:49 +04:00

77 lines
2.4 KiB
C

#include "private/gc_priv.h"
void GC_foreach_heap_section(void* user_data, GC_heap_section_proc callback)
{
GC_ASSERT(I_HOLD_LOCK());
if (callback == NULL)
return;
// GC memory is organized in heap sections, which are split in heap blocks.
// Each block has header (can get via HDR(ptr)) and it's size is aligned to HBLKSIZE
// Block headers are kept separately from memory their points to, and quickly address
// headers GC maintains 2-level cache structure which uses address as a hash key.
for (unsigned i = 0; i < GC_n_heap_sects; i++)
{
ptr_t sectionStart = GC_heap_sects[i].hs_start;
ptr_t sectionEnd = sectionStart + GC_heap_sects[i].hs_bytes;
// Merge in contiguous sections.
// A heap block might start in one heap section and extend
// into the next one.
while (i + 1 < GC_n_heap_sects && GC_heap_sects[i + 1].hs_start == sectionEnd)
{
++i;
sectionEnd = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes;
}
ptr_t blockStart = sectionStart;
while (blockStart < sectionEnd)
{
// This does lookup into 2 level tree data structure,
// which uses address as hash key to find block header.
hdr* hhdr = HDR(blockStart);
if (IS_FORWARDING_ADDR_OR_NIL(hhdr))
{
// This pointer has no header registered in headers cache.
// We skip one HBLKSIZE and attempt to get header for it.
// We don't report it, as we don't know is mapped or not.
blockStart = blockStart + HBLKSIZE;
}
else if (HBLK_IS_FREE(hhdr))
{
// We have a header, and the block is marked as free.
// Note: for "free" blocks "hb_sz" = the size in bytes of the whole block.
ptr_t blockEnd = blockStart + hhdr->hb_sz;
#if USE_MUNMAP
// Only report free block if it's mapped.
if ((hhdr->hb_flags & WAS_UNMAPPED) != 0)
{
blockStart = blockEnd;
continue;
}
#endif
callback(user_data, blockStart, blockEnd, GC_HEAP_SECTION_TYPE_FREE);
blockStart = blockEnd;
}
else
{
// This heap block is used, report it.
// Note: for used blocks "hb_sz" = size in bytes, of objects in the block.
ptr_t blockEnd = blockStart + HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr->hb_sz);
ptr_t usedBlocknEnd = blockStart + hhdr->hb_sz;
if (usedBlocknEnd > blockStart)
callback(user_data, blockStart, usedBlocknEnd, GC_HEAP_SECTION_TYPE_USED);
if (blockEnd > usedBlocknEnd)
callback(user_data, usedBlocknEnd, blockEnd, GC_HEAP_SECTION_TYPE_PADDING);
blockStart = blockEnd;
}
}
}
}