#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); 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); if (blockEnd > usedBlocknEnd) callback(user_data, usedBlocknEnd, blockEnd); blockStart = blockEnd; } } } } void HeapSectionCountIncrementer(void* context, GC_PTR start, GC_PTR end) { GC_word* countPtr = (GC_word*)context; (*countPtr)++; } GC_word GC_get_heap_section_count() { GC_word count = 0; GC_foreach_heap_section(&count, HeapSectionCountIncrementer); return count; }