Files
weaseldb/tools/debug_arena.cpp

390 lines
12 KiB
C++

#include "commit_request.hpp"
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <unordered_set>
#include <vector>
struct ArenaDebugger {
const CommitRequest &commit_request;
const ArenaAllocator &arena;
std::unordered_set<const void *> referenced_addresses;
explicit ArenaDebugger(const CommitRequest &cr)
: commit_request(cr), arena(cr.arena()) {}
void analyze_references() {
// Track all string_view data pointers from the parsed commit request
if (commit_request.request_id().has_value()) {
add_reference(commit_request.request_id()->data(),
commit_request.request_id()->size());
}
add_reference(commit_request.leader_id().data(),
commit_request.leader_id().size());
for (const auto &precond : commit_request.preconditions()) {
add_reference(precond.begin.data(), precond.begin.size());
add_reference(precond.end.data(), precond.end.size());
}
for (const auto &op : commit_request.operations()) {
add_reference(op.param1.data(), op.param1.size());
add_reference(op.param2.data(), op.param2.size());
}
}
void add_reference(const char *ptr, size_t size) {
if (ptr && size > 0) {
referenced_addresses.insert(ptr);
// Also add end pointer to mark the range
referenced_addresses.insert(ptr + size - 1);
}
}
void visualize_arena() {
std::cout << "=== Arena Visualization Debug Tool ===" << std::endl;
std::cout << "Analyzing commit request and arena memory layout"
<< std::endl;
std::cout << std::endl;
// First, analyze what's referenced
analyze_references();
// Print basic arena statistics
std::cout << "Arena Statistics:" << std::endl;
std::cout << "- Total allocated: " << arena.total_allocated() << " bytes"
<< std::endl;
std::cout << "- Currently used: " << arena.used_bytes() << " bytes"
<< std::endl;
std::cout << "- Number of blocks: " << arena.num_blocks() << " blocks"
<< std::endl;
std::cout << "- Referenced addresses: " << referenced_addresses.size()
<< std::endl;
std::cout << std::endl;
// Use the arena's debug_dump with content visualization
std::cout << "Raw Arena Memory Layout:" << std::endl;
arena.debug_dump(std::cout, true, true, 1024);
std::cout << std::endl;
std::cout << "=== Pointer Analysis ===" << std::endl;
// Scan for potential pointers in arena memory using the arena's built-in
// method
scan_arena_pointers();
std::cout << std::endl;
std::cout << "=== Referenced Memory Regions ===" << std::endl;
visualize_referenced_data();
}
private:
void scan_arena_pointers() {
std::cout << "Scanning all used arena memory for 64-bit aligned pointers..."
<< std::endl;
// Use the arena's comprehensive pointer scanning method
auto pointers = arena.find_intra_arena_pointers();
std::cout << "Arena memory scan complete:" << std::endl;
std::cout << "- Total scanned: " << arena.used_bytes() << " bytes across "
<< arena.num_blocks() << " blocks" << std::endl;
std::cout << "- Intra-arena pointers found: " << pointers.size()
<< std::endl;
if (pointers.empty()) {
std::cout << "No intra-arena pointers detected." << std::endl;
return;
}
std::cout << std::endl;
std::cout << "Detected pointers:" << std::endl;
for (size_t i = 0; i < pointers.size(); ++i) {
const auto &ptr_info = pointers[i];
std::cout << "Pointer #" << (i + 1) << ":" << std::endl;
std::cout << " Source: " << ptr_info.source_addr << " (Block #"
<< ptr_info.source_block_number << ", offset +0x" << std::hex
<< ptr_info.source_offset << std::dec << ")" << std::endl;
std::cout << " Target: " << ptr_info.target_addr << " (Block #"
<< ptr_info.target_block_number << ", offset +0x" << std::hex
<< ptr_info.target_offset << std::dec << ")" << std::endl;
// Try to identify what this pointer might be pointing to
identify_pointer_target(ptr_info.target_addr);
std::cout << std::endl;
}
}
void identify_pointer_target(const void *target_addr) {
// Check if this target address matches any of our known string data
std::cout << " Points to: ";
bool found_match = false;
// Check request_id
if (commit_request.request_id().has_value()) {
const auto &req_id = *commit_request.request_id();
if (target_addr >= req_id.data() &&
target_addr < req_id.data() + req_id.size()) {
std::cout << "request_id string";
found_match = true;
}
}
// Check leader_id
if (!found_match) {
const auto &leader_id = commit_request.leader_id();
if (target_addr >= leader_id.data() &&
target_addr < leader_id.data() + leader_id.size()) {
std::cout << "leader_id string";
found_match = true;
}
}
// Check preconditions
if (!found_match) {
for (size_t i = 0; i < commit_request.preconditions().size(); ++i) {
const auto &precond = commit_request.preconditions()[i];
if (!precond.begin.empty() && target_addr >= precond.begin.data() &&
target_addr < precond.begin.data() + precond.begin.size()) {
std::cout << "precondition[" << i << "].begin string";
found_match = true;
break;
}
if (!precond.end.empty() && target_addr >= precond.end.data() &&
target_addr < precond.end.data() + precond.end.size()) {
std::cout << "precondition[" << i << "].end string";
found_match = true;
break;
}
}
}
// Check operations
if (!found_match) {
for (size_t i = 0; i < commit_request.operations().size(); ++i) {
const auto &op = commit_request.operations()[i];
if (!op.param1.empty() && target_addr >= op.param1.data() &&
target_addr < op.param1.data() + op.param1.size()) {
std::cout << "operation[" << i << "].param1 string";
found_match = true;
break;
}
if (!op.param2.empty() && target_addr >= op.param2.data() &&
target_addr < op.param2.data() + op.param2.size()) {
std::cout << "operation[" << i << "].param2 string";
found_match = true;
break;
}
}
}
if (!found_match) {
std::cout << "unknown arena data";
}
std::cout << std::endl;
}
std::string_view find_string_view_for_data(const char *data) {
if (commit_request.request_id().has_value() &&
commit_request.request_id()->data() == data) {
return *commit_request.request_id();
}
if (commit_request.leader_id().data() == data) {
return commit_request.leader_id();
}
for (const auto &precond : commit_request.preconditions()) {
if (precond.begin.data() == data)
return precond.begin;
if (precond.end.data() == data)
return precond.end;
}
for (const auto &op : commit_request.operations()) {
if (op.param1.data() == data)
return op.param1;
if (op.param2.data() == data)
return op.param2;
}
return {};
}
void visualize_referenced_data() {
std::cout << "Visualizing parsed commit request data references:"
<< std::endl;
std::cout << std::endl;
// Show request_id
if (commit_request.request_id().has_value()) {
std::cout << "request_id: ";
visualize_string_data(*commit_request.request_id());
}
// Show leader_id
std::cout << "leader_id: ";
visualize_string_data(commit_request.leader_id());
// Show read_version
std::cout << "read_version: " << commit_request.read_version() << std::endl;
// Show preconditions
std::cout << "preconditions (" << commit_request.preconditions().size()
<< "):" << std::endl;
for (size_t i = 0; i < commit_request.preconditions().size(); ++i) {
const auto &precond = commit_request.preconditions()[i];
std::cout << " [" << i
<< "] type: " << precondition_type_to_string(precond.type)
<< ", version: " << precond.version << std::endl;
std::cout << " begin: ";
visualize_string_data(precond.begin, " ");
std::cout << " end: ";
visualize_string_data(precond.end, " ");
}
// Show operations
std::cout << "operations (" << commit_request.operations().size()
<< "):" << std::endl;
for (size_t i = 0; i < commit_request.operations().size(); ++i) {
const auto &op = commit_request.operations()[i];
std::cout << " [" << i << "] type: " << operation_type_to_string(op.type)
<< std::endl;
std::cout << " param1: ";
visualize_string_data(op.param1, " ");
std::cout << " param2: ";
visualize_string_data(op.param2, " ");
}
}
void visualize_string_data(std::string_view sv,
const std::string &indent = "") {
if (sv.empty()) {
std::cout << "(empty)" << std::endl;
return;
}
const char *data = sv.data();
size_t size = sv.size();
std::cout << "\"" << sv << "\" @ " << static_cast<const void *>(data)
<< " [" << size << " bytes]";
if (referenced_addresses.count(data)) {
std::cout << " (REFERENCED)";
}
std::cout << std::endl;
// Show hex dump of the string data
if (size > 0 && size <= 64) { // Only show hex for reasonable sizes
std::cout << indent << "Hex: ";
for (size_t i = 0; i < size; ++i) {
unsigned char byte = static_cast<unsigned char>(data[i]);
std::cout << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<unsigned int>(byte) << std::dec;
if ((i + 1) % 4 == 0 && i < size - 1)
std::cout << " ";
}
std::cout << std::endl;
}
}
const char *precondition_type_to_string(Precondition::Type type) {
switch (type) {
case Precondition::Type::PointRead:
return "point_read";
case Precondition::Type::RangeRead:
return "range_read";
default:
return "unknown";
}
}
const char *operation_type_to_string(Operation::Type type) {
switch (type) {
case Operation::Type::Write:
return "write";
case Operation::Type::Delete:
return "delete";
case Operation::Type::RangeDelete:
return "range_delete";
default:
return "unknown";
}
}
};
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <json_file>" << std::endl;
std::cerr << "Debug tool to visualize arena memory layout from commit "
"request JSON"
<< std::endl;
return 1;
}
const char *filename = argv[1];
// Read JSON file
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Could not open file '" << filename << "'" << std::endl;
return 1;
}
std::ostringstream ss;
ss << file.rdbuf();
std::string json_content = ss.str();
file.close();
if (json_content.empty()) {
std::cerr << "Error: File is empty or could not be read" << std::endl;
return 1;
}
std::cout << "Reading commit request from: " << filename << std::endl;
std::cout << "JSON size: " << json_content.size() << " bytes" << std::endl;
std::cout << std::endl;
// Parse the commit request
CommitRequest commit_request;
// Make a mutable copy for parsing (weaseljson requires mutable data)
std::vector<char> mutable_json(json_content.begin(), json_content.end());
mutable_json.push_back('\0'); // Null terminate for safety
bool parse_success =
commit_request.parse_json(mutable_json.data(), mutable_json.size() - 1);
if (!parse_success || !commit_request.is_parse_complete()) {
std::cerr << "Error: Failed to parse JSON" << std::endl;
if (commit_request.has_parse_error()) {
std::cerr << "Parse error: " << commit_request.get_parse_error()
<< std::endl;
}
return 1;
}
std::cout << "Successfully parsed commit request!" << std::endl;
std::cout << std::endl;
// Create debugger and visualize
ArenaDebugger debugger(commit_request);
debugger.visualize_arena();
return 0;
}