GroveEngine/tests/profile_memory_leak.cpp
StillHammer 9105610b29 feat: Add integration tests 8-10 & fix CTest configuration
Added three new integration test scenarios:
- Test 08: Config Hot-Reload (dynamic configuration updates)
- Test 09: Module Dependencies (dependency injection & cascade reload)
- Test 10: Multi-Version Coexistence (canary deployment & progressive migration)

Fixes:
- Fixed CTest working directory for all tests (add WORKING_DIRECTORY)
- Fixed module paths to use relative paths (./ prefix)
- Fixed IModule.h comments for clarity

New test modules:
- ConfigurableModule (for config reload testing)
- BaseModule, DependentModule, IndependentModule (for dependency testing)
- GameLogicModuleV1/V2/V3 (for multi-version testing)

Test coverage now includes 10 comprehensive integration scenarios covering
hot-reload, chaos testing, stress testing, race conditions, memory leaks,
error recovery, limits, config reload, dependencies, and multi-versioning.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 07:34:15 +08:00

143 lines
6.1 KiB
C++

// ============================================================================
// profile_memory_leak.cpp - Detailed memory profiling for leak detection
// ============================================================================
#include "grove/ModuleLoader.h"
#include "grove/SequentialModuleSystem.h"
#include "grove/JsonDataNode.h"
#include "helpers/SystemUtils.h"
#include <spdlog/spdlog.h>
#include <iostream>
#include <iomanip>
#include <filesystem>
#include <thread>
using namespace grove;
namespace fs = std::filesystem;
void printMemory(const std::string& label) {
size_t mem = getCurrentMemoryUsage();
float mb = mem / (1024.0f * 1024.0f);
std::cout << std::setw(40) << std::left << label << ": "
<< std::fixed << std::setprecision(3) << mb << " MB\n";
}
int main() {
std::cout << "================================================================================\n";
std::cout << "MEMORY LEAK PROFILER - Detailed Analysis\n";
std::cout << "================================================================================\n\n";
fs::path modulePath = "build/tests/libLeakTestModule.so";
if (!fs::exists(modulePath)) {
std::cerr << "❌ Module not found: " << modulePath << "\n";
return 1;
}
// Disable verbose logging
spdlog::set_level(spdlog::level::err);
printMemory("1. Initial baseline");
// Create loader and system
ModuleLoader loader;
printMemory("2. After ModuleLoader creation");
auto moduleSystem = std::make_unique<SequentialModuleSystem>();
moduleSystem->setLogLevel(spdlog::level::err);
printMemory("3. After ModuleSystem creation");
// Initial load
{
auto module = loader.load(modulePath, "LeakTestModule", false);
printMemory("4. After initial load");
nlohmann::json configJson = nlohmann::json::object();
auto config = std::make_unique<JsonDataNode>("config", configJson);
module->setConfiguration(*config, nullptr, nullptr);
printMemory("5. After setConfiguration");
moduleSystem->registerModule("LeakTestModule", std::move(module));
printMemory("6. After registerModule");
}
std::cout << "\n--- Starting 10 reload cycles ---\n\n";
size_t baselineAfterFirstLoad = getCurrentMemoryUsage();
for (int i = 1; i <= 10; i++) {
std::cout << "=== Cycle " << i << " ===\n";
size_t memBefore = getCurrentMemoryUsage();
// Extract module
auto module = moduleSystem->extractModule();
size_t memAfterExtract = getCurrentMemoryUsage();
auto state = module->getState();
size_t memAfterGetState = getCurrentMemoryUsage();
auto config = std::make_unique<JsonDataNode>("config",
dynamic_cast<const JsonDataNode&>(module->getConfiguration()).getJsonData());
size_t memAfterGetConfig = getCurrentMemoryUsage();
// Destroy old module
module.reset();
size_t memAfterReset = getCurrentMemoryUsage();
// Reload
auto newModule = loader.load(modulePath, "LeakTestModule", true);
size_t memAfterLoad = getCurrentMemoryUsage();
// Restore
newModule->setConfiguration(*config, nullptr, nullptr);
size_t memAfterSetConfig = getCurrentMemoryUsage();
newModule->setState(*state);
size_t memAfterSetState = getCurrentMemoryUsage();
// Register
moduleSystem->registerModule("LeakTestModule", std::move(newModule));
size_t memAfterRegister = getCurrentMemoryUsage();
// Process once
moduleSystem->processModules(0.016f);
size_t memAfterProcess = getCurrentMemoryUsage();
// Analysis
auto toKB = [](size_t delta) { return delta / 1024.0f; };
std::cout << " extractModule: " << std::setw(8) << toKB(memAfterExtract - memBefore) << " KB\n";
std::cout << " getState: " << std::setw(8) << toKB(memAfterGetState - memAfterExtract) << " KB\n";
std::cout << " getConfiguration: " << std::setw(8) << toKB(memAfterGetConfig - memAfterGetState) << " KB\n";
std::cout << " module.reset: " << std::setw(8) << toKB(memAfterReset - memAfterGetConfig) << " KB\n";
std::cout << " loader.load: " << std::setw(8) << toKB(memAfterLoad - memAfterReset) << " KB\n";
std::cout << " setConfiguration: " << std::setw(8) << toKB(memAfterSetConfig - memAfterLoad) << " KB\n";
std::cout << " setState: " << std::setw(8) << toKB(memAfterSetState - memAfterSetConfig) << " KB\n";
std::cout << " registerModule: " << std::setw(8) << toKB(memAfterRegister - memAfterSetState) << " KB\n";
std::cout << " processModules: " << std::setw(8) << toKB(memAfterProcess - memAfterRegister) << " KB\n";
std::cout << " TOTAL THIS CYCLE: " << std::setw(8) << toKB(memAfterProcess - memBefore) << " KB\n";
size_t totalGrowth = memAfterProcess - baselineAfterFirstLoad;
std::cout << " Cumulative growth: " << std::setw(8) << toKB(totalGrowth) << " KB\n";
std::cout << "\n";
// Small delay to let system settle
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
size_t finalMem = getCurrentMemoryUsage();
float totalGrowthKB = (finalMem - baselineAfterFirstLoad) / 1024.0f;
float perCycleKB = totalGrowthKB / 10.0f;
std::cout << "\n================================================================================\n";
std::cout << "SUMMARY\n";
std::cout << "================================================================================\n";
std::cout << "Baseline after first load: " << (baselineAfterFirstLoad / 1024.0f / 1024.0f) << " MB\n";
std::cout << "Final memory: " << (finalMem / 1024.0f / 1024.0f) << " MB\n";
std::cout << "Total growth (10 cycles): " << totalGrowthKB << " KB\n";
std::cout << "Average per cycle: " << perCycleKB << " KB\n";
std::cout << "================================================================================\n";
return 0;
}