Add arena debug visualization tool

This commit is contained in:
2025-08-15 11:25:10 -04:00
parent 28fa96011f
commit f1794bcb3e
6 changed files with 576 additions and 24 deletions

View File

@@ -4,6 +4,7 @@
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <new>
@@ -369,6 +370,181 @@ public:
return current_block_ ? current_block_->block_count : 0;
}
/**
* @brief Debug function to find all intra-arena pointers.
*
* Scans all used memory in the arena for 64-bit aligned values that could be
* pointers to locations within the arena itself. This is useful for
* understanding memory references and potential data structures.
*
* @return Vector of PointerInfo structs containing source and target
* addresses
*/
struct PointerInfo {
const void *source_addr; ///< Address where the pointer was found
size_t source_block_number; ///< Block number containing the source
size_t source_offset; ///< Offset within the source block
const void *target_addr; ///< Address the pointer points to
size_t target_block_number; ///< Block number containing the target
size_t target_offset; ///< Offset within the target block
PointerInfo(const void *src, size_t src_block, size_t src_offset,
const void *target, size_t target_block, size_t target_offset)
: source_addr(src), source_block_number(src_block),
source_offset(src_offset), target_addr(target),
target_block_number(target_block), target_offset(target_offset) {}
};
std::vector<PointerInfo> find_intra_arena_pointers() const {
std::vector<PointerInfo> pointers;
if (!current_block_) {
return pointers;
}
// Build list of blocks from current to first
std::vector<Block *> blocks;
Block *block = current_block_;
while (block) {
blocks.push_back(block);
block = block->prev;
}
// Helper function to check if a pointer value points within the used area
// of any block
auto is_intra_arena_pointer = [&blocks,
this](uint64_t pointer_value) -> bool {
for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
Block *b = blocks[block_idx];
uintptr_t block_start = reinterpret_cast<uintptr_t>(b->data());
// Calculate used bytes in this specific block
size_t block_used;
if (block_idx == 0) {
// Current block - use current_offset_
block_used = current_offset_;
} else {
// Previous blocks are fully used
block_used = b->size;
}
uintptr_t block_used_end = block_start + block_used;
// Check if pointer falls within the used area of this block
if (pointer_value >= block_start && pointer_value < block_used_end) {
return true;
}
}
return false;
};
// Scan each block for pointers
for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
Block *b = blocks[block_idx];
const char *data = b->data();
// Calculate used bytes in this specific block
size_t block_used;
if (block_idx == 0) {
// Current block - use current_offset_
block_used = current_offset_;
} else {
// Previous blocks are fully used
block_used = b->size;
}
// Scan for 64-bit aligned pointers
for (size_t offset = 0; offset + sizeof(uint64_t) <= block_used;
offset += sizeof(uint64_t)) {
uint64_t potential_pointer;
std::memcpy(&potential_pointer, data + offset,
sizeof(potential_pointer));
// Check if this value points within the used area of any block
if (is_intra_arena_pointer(potential_pointer)) {
// Find target location within arena
auto target_location = find_address_location(
reinterpret_cast<const void *>(potential_pointer));
pointers.emplace_back(
data + offset, // source address
blocks.size() - block_idx, // source block number (1-based)
offset, // source offset in block
reinterpret_cast<const void *>(
potential_pointer), // target address
target_location.found ? target_location.block_number
: 0, // target block number
target_location.found ? target_location.offset_in_block
: 0 // target offset
);
}
}
}
return pointers;
}
/**
* @brief Find which block and offset a given address belongs to.
*
* @param addr The address to locate within the arena
* @return PointerInfo with block number and offset, or invalid info if not
* found
*/
struct AddressLocation {
size_t block_number;
size_t offset_in_block;
bool found;
AddressLocation() : block_number(0), offset_in_block(0), found(false) {}
AddressLocation(size_t block, size_t offset)
: block_number(block), offset_in_block(offset), found(true) {}
};
AddressLocation find_address_location(const void *addr) const {
if (!current_block_ || !addr) {
return AddressLocation();
}
uintptr_t target_addr = reinterpret_cast<uintptr_t>(addr);
// Build list of blocks from current to first
std::vector<Block *> blocks;
Block *block = current_block_;
while (block) {
blocks.push_back(block);
block = block->prev;
}
// Check each block to see if the address falls within its used area
for (size_t block_idx = 0; block_idx < blocks.size(); ++block_idx) {
Block *b = blocks[block_idx];
uintptr_t block_start = reinterpret_cast<uintptr_t>(b->data());
// Calculate used bytes in this specific block
size_t block_used;
if (block_idx == 0) {
// Current block - use current_offset_
block_used = current_offset_;
} else {
// Previous blocks are fully used
block_used = b->size;
}
uintptr_t block_used_end = block_start + block_used;
// Check if address falls within the used area of this block
if (target_addr >= block_start && target_addr < block_used_end) {
return AddressLocation(
blocks.size() - block_idx, // block number (1-based)
target_addr - block_start // offset within block
);
}
}
return AddressLocation(); // Not found
}
/**
* @brief Debug function to visualize the arena's layout and contents.
*