// ============================================================================ // 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 #include #include #include #include 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(); 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("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("config", dynamic_cast(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; }