From 0e670642a4677dfc8568fa47625f92c07fc61bc1 Mon Sep 17 00:00:00 2001 From: Andrej E Baranov Date: Tue, 3 Jun 2025 16:59:36 +0700 Subject: [PATCH 1/3] debug --- src/patches/formcaching.cpp | 23 +++++++- src/patches/memorymanager.cpp | 78 ++++++++++++++++++++----- src/patches/treelodreferencecaching.cpp | 33 ++++++++++- 3 files changed, 117 insertions(+), 17 deletions(-) diff --git a/src/patches/formcaching.cpp b/src/patches/formcaching.cpp index 1816845..d541aca 100644 --- a/src/patches/formcaching.cpp +++ b/src/patches/formcaching.cpp @@ -38,6 +38,7 @@ namespace patches RE::TESForm* hk_GetFormByID(std::uint32_t FormId) { RE::TESForm* formPointer = nullptr; + bool wrongPtrInCache = false; const std::uint8_t masterId = (FormId & 0xFF000000) >> 24; const std::uint32_t baseId = (FormId & 0x00FFFFFF); @@ -50,10 +51,28 @@ namespace patches formPointer = accessor->second; if (formPointer->GetFormID() != FormId) { - logger::trace("debug hk_GetFormByID FormId = {:08X}, but formPointer->GetFormID() = {:08X}"sv, FormId, formPointer->GetFormID()); + logger::trace("debug hk_GetFormByID FormId = {:08X}, but formPointer->GetFormID() = {:08X}:{}"sv, FormId, formPointer->GetFormID(), RE::FormTypeToString(formPointer->GetFormType())); + formPointer = nullptr; + wrongPtrInCache = true; } else { + // TODO: temporary Debug + GlobalFormTableLock->LockForRead(); + RE::TESForm* formPointerInGlobalFormTable = nullptr; + if (*GlobalFormTable) + { + auto iter = (*GlobalFormTable)->find(FormId); + formPointerInGlobalFormTable = (iter != (*GlobalFormTable)->end()) ? iter->second : nullptr; + } + GlobalFormTableLock->UnlockForRead(); + if (formPointerInGlobalFormTable != formPointer) + { + logger::trace("debug hk_GetFormByID FormId = {:08X} found in cache, but Ptr in Cache {:08X} and GlobalFormTable {:08X} not same"sv, FormId, reinterpret_cast(formPointer), reinterpret_cast(formPointerInGlobalFormTable)); + formPointer = nullptr; + wrongPtrInCache = true; + } + return formPointer; } } @@ -70,7 +89,7 @@ namespace patches GlobalFormTableLock->UnlockForRead(); - if (formPointer) + if (formPointer || wrongPtrInCache) UpdateFormCache(FormId, formPointer, false); return formPointer; diff --git a/src/patches/memorymanager.cpp b/src/patches/memorymanager.cpp index 035c7e4..07a6227 100644 --- a/src/patches/memorymanager.cpp +++ b/src/patches/memorymanager.cpp @@ -82,30 +82,58 @@ namespace { void* Allocate(RE::MemoryManager*, std::size_t a_size, std::uint32_t a_alignment, bool a_alignmentRequired) { + void* ret = g_trash; + errno = 0; if (a_size > 0) - return a_alignmentRequired ? + ret = a_alignmentRequired ? scalable_aligned_malloc(a_size, a_alignment) : scalable_malloc(a_size); - else - return g_trash; + if (errno || ret == nullptr) + { + logger::trace("MemoryManager::Allocate error {} ", errno); + } + return ret; } void Deallocate(RE::MemoryManager*, void* a_mem, bool a_alignmentRequired) { - if (a_mem != g_trash) - a_alignmentRequired ? - scalable_aligned_free(a_mem) : - scalable_free(a_mem); + if (a_mem != g_trash && a_mem != nullptr) + { + errno = 0; + if (scalable_msize(a_mem) != 0) + { + a_alignmentRequired ? + scalable_aligned_free(a_mem) : + scalable_free(a_mem); + } + else + { + logger::trace("MemoryManager::Deallocate trying to free already free pointer"); + } + if (errno) + { + logger::trace("MemoryManager::Deallocate error {} ", errno); + } + } } void* Reallocate(RE::MemoryManager* a_self, void* a_oldMem, std::size_t a_newSize, std::uint32_t a_alignment, bool a_alignmentRequired) { + void* ret = g_trash; if (a_oldMem == g_trash) - return Allocate(a_self, a_newSize, a_alignment, a_alignmentRequired); + ret = Allocate(a_self, a_newSize, a_alignment, a_alignmentRequired); else - return a_alignmentRequired ? + { + errno = 0; + ret = a_alignmentRequired ? scalable_aligned_realloc(a_oldMem, a_newSize, a_alignment) : scalable_realloc(a_oldMem, a_newSize); + if (errno || ret == nullptr) + { + logger::trace("MemoryManager::Reallocate error {} ", errno); + } + } + return ret; } void ReplaceAllocRoutines() @@ -158,9 +186,17 @@ namespace { void* Allocate(RE::ScrapHeap*, std::size_t a_size, std::size_t a_alignment) { - return a_size > 0 ? - scalable_aligned_malloc(a_size, a_alignment) : - g_trash; + void* ret = g_trash; + errno = 0; + if (a_size > 0) + { + ret = scalable_aligned_malloc(a_size, a_alignment); + } + if (errno || ret == nullptr) + { + logger::trace("ScrapHeap::Allocate error {} ", errno); + } + return ret; } RE::ScrapHeap* Ctor(RE::ScrapHeap* a_this) @@ -172,8 +208,22 @@ namespace void Deallocate(RE::ScrapHeap*, void* a_mem) { - if (a_mem != g_trash) - scalable_aligned_free(a_mem); + if (a_mem != g_trash && a_mem != nullptr) + { + errno = 0; + if (scalable_msize(a_mem) != 0) + { + scalable_aligned_free(a_mem); + } + else + { + logger::trace("ScrapHeap::Deallocate trying to free already free pointer"); + } + if (errno) + { + logger::trace("ScrapHeap::Deallocate error {} ", errno); + } + } } void WriteHooks() diff --git a/src/patches/treelodreferencecaching.cpp b/src/patches/treelodreferencecaching.cpp index 10cc317..52e82b0 100644 --- a/src/patches/treelodreferencecaching.cpp +++ b/src/patches/treelodreferencecaching.cpp @@ -31,8 +31,10 @@ namespace patches decltype(referencesFormCache)::accessor accessor; if (referencesFormCache.find(accessor, maskedFormId)) + { refrObject = accessor->second; - else + } + if (refrObject == nullptr) { // Find first valid tree object by ESP/ESM load order auto dataHandler = RE::TESDataHandler::GetSingleton(); @@ -59,6 +61,35 @@ namespace patches // Insert even if it's a null pointer referencesFormCache.insert(std::make_pair(maskedFormId, refrObject)); } + else + { + RE::TESObjectREFR* refrObject2 = nullptr; + // Find first valid tree object by ESP/ESM load order + auto dataHandler = RE::TESDataHandler::GetSingleton(); + for (std::uint32_t i = 0; i < dataHandler->compiledFileCollection.files.size(); i++) + { + RE::TESForm* form = LookupFormByID((i << 24) | maskedFormId); + if (form) + refrObject2 = form->AsReference(); + if (refrObject2) + { + auto baseObj = refrObject2->GetBaseObject(); + if (baseObj) + { + using STATFlags = RE::TESObjectSTAT::RecordFlags; + // Checks if the form type is TREE (TESObjectTREE) or if it has the kHasTreeLOD flag (TESObjectSTAT) + if (baseObj->formFlags & STATFlags::kHasTreeLOD || baseObj->Is(RE::FormType::Tree)) + break; + } + } + + refrObject2 = nullptr; + } + if (refrObject2 != refrObject) + { + logger::trace("debug hk_UpdateBlockVisibility maskedFormId = {:08X} found in cache, but Ptr in Cache {:08X} and refrObject2 {:08X} not same"sv, maskedFormId, reinterpret_cast(refrObject), reinterpret_cast(refrObject2)); + } + } bool fullyHidden = false; float alpha = 1.0f; From f0e8865595cbf3c1b50f6a09bf1c5a93d4f92616 Mon Sep 17 00:00:00 2001 From: Andrej E Baranov Date: Fri, 25 Jul 2025 19:55:45 +0700 Subject: [PATCH 2/3] debug --- src/patches/memorymanager.cpp | 163 +++++++++++++++++++++++++++++++--- 1 file changed, 152 insertions(+), 11 deletions(-) diff --git a/src/patches/memorymanager.cpp b/src/patches/memorymanager.cpp index 07a6227..770834e 100644 --- a/src/patches/memorymanager.cpp +++ b/src/patches/memorymanager.cpp @@ -78,19 +78,148 @@ namespace } } + namespace MemoryManagerStats + { + + struct PointerStats + { + int allocated = 0; + }; + + tbb::concurrent_hash_map allocatedPointers; + + RE::TESForm* ctd; + + //std::mutex memory_mutex; + + void Allocate(void* mem) + { + //std::lock_guard guard(memory_mutex); + + decltype(allocatedPointers)::accessor accessor; + + uintptr_t addr = reinterpret_cast(mem); + + if (allocatedPointers.find(accessor, addr)) + { + PointerStats* stats = accessor->second; + stats->allocated++; + if (stats->allocated > 1) + { + logger::trace("MemoryManagerStats::Allocate error 101: already allocated {} times for {}.", stats->allocated, addr); + } + else { + //logger::trace("MemoryManagerStats::Allocate success: allocated {} times for {}.", stats->allocated, addr); + } + } + else + { + PointerStats* stats = new PointerStats(); + stats->allocated = 1; + allocatedPointers.emplace(addr, stats); + //logger::trace("MemoryManagerStats::Allocate success: allocated {} times for {}.", stats->allocated, addr); + } + } + + void Deallocate(void* mem) + { + //std::lock_guard guard(memory_mutex); + decltype(allocatedPointers)::accessor accessor; + + uintptr_t addr = reinterpret_cast(mem); + + if (allocatedPointers.find(accessor, addr)) + { + PointerStats* stats = accessor->second; + stats->allocated--; + if (stats->allocated < 0) + { + logger::trace("MemoryManagerStats::Deallocate error 202: already deallocated {} times for {}.", (stats->allocated * -1), addr); + std::this_thread::sleep_for(std::chrono::seconds(10000)); + + } + else { + //logger::trace("MemoryManagerStats::Deallocate success: allocated {} times for {}.", stats->allocated, addr); + } + } + else + { + logger::trace("MemoryManagerStats::Deallocate error 203: not allocated for {}.", addr); + } + } + + void Reallocate(void* oldmem, void* newmem) + { + //std::lock_guard guard(memory_mutex); + decltype(allocatedPointers)::accessor accessor; + + uintptr_t old_addr = reinterpret_cast(oldmem); + uintptr_t new_addr = reinterpret_cast(newmem); + + if (old_addr == new_addr) { + return; + } + + if (allocatedPointers.find(accessor, old_addr)) + { + PointerStats* stats = accessor->second; + stats->allocated--; + if (stats->allocated < 0) + { + logger::trace("MemoryManagerStats::Reallocate old_addr error 302: already deallocated {} times for {}.", (stats->allocated * -1), old_addr); + } + else + { + //logger::trace("MemoryManagerStats::Reallocate old_addr success: allocated {} times for {}.", stats->allocated, old_addr); + } + } + else + { + logger::trace("MemoryManagerStats::Reallocate old_addr error 303: not allocated for {}.", old_addr); + } + + if (allocatedPointers.find(accessor, new_addr)) + { + PointerStats* stats = accessor->second; + stats->allocated++; + if (stats->allocated > 1) + { + logger::trace("MemoryManagerStats::Reallocate new_addr error 301: already allocated {} times for {}.", stats->allocated, new_addr); + } + else + { + //logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated {} times for {}.", stats->allocated, new_addr); + } + } + else + { + PointerStats* stats = new PointerStats(); + stats->allocated = 1; + allocatedPointers.emplace(new_addr, stats); + //logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated {} times for {}.", stats->allocated, new_addr); + } + } + } + namespace MemoryManager { void* Allocate(RE::MemoryManager*, std::size_t a_size, std::uint32_t a_alignment, bool a_alignmentRequired) { void* ret = g_trash; - errno = 0; if (a_size > 0) - ret = a_alignmentRequired ? - scalable_aligned_malloc(a_size, a_alignment) : - scalable_malloc(a_size); - if (errno || ret == nullptr) { - logger::trace("MemoryManager::Allocate error {} ", errno); + errno = 0; + ret = a_alignmentRequired ? + scalable_aligned_malloc(a_size, a_alignment) : + scalable_malloc(a_size); + if (errno || ret == nullptr) + { + logger::trace("MemoryManager::Allocate error {} ", errno); + } + else + { + MemoryManagerStats::Allocate(ret); + } } return ret; } @@ -102,9 +231,12 @@ namespace errno = 0; if (scalable_msize(a_mem) != 0) { + MemoryManagerStats::Deallocate(a_mem); + a_alignmentRequired ? scalable_aligned_free(a_mem) : scalable_free(a_mem); + } else { @@ -132,6 +264,9 @@ namespace { logger::trace("MemoryManager::Reallocate error {} ", errno); } + else { + MemoryManagerStats::Reallocate(a_oldMem, ret); + } } return ret; } @@ -187,14 +322,18 @@ namespace void* Allocate(RE::ScrapHeap*, std::size_t a_size, std::size_t a_alignment) { void* ret = g_trash; - errno = 0; if (a_size > 0) { + errno = 0; ret = scalable_aligned_malloc(a_size, a_alignment); - } - if (errno || ret == nullptr) - { - logger::trace("ScrapHeap::Allocate error {} ", errno); + if (errno || ret == nullptr) + { + logger::trace("ScrapHeap::Allocate error {} ", errno); + } + else + { + MemoryManagerStats::Allocate(ret); + } } return ret; } @@ -214,6 +353,8 @@ namespace if (scalable_msize(a_mem) != 0) { scalable_aligned_free(a_mem); + + MemoryManagerStats::Deallocate(a_mem); } else { From ec6ddfea0898ebf743c5057ad2958247325181b1 Mon Sep 17 00:00:00 2001 From: Andrej E Baranov Date: Sat, 26 Jul 2025 18:03:48 +0700 Subject: [PATCH 3/3] memory already deallocated debug --- CMakeLists.txt | 7 ++ src/patches/memorymanager.cpp | 166 ++++++++++++++++++++-------------- vcpkg.json | 3 +- 3 files changed, 106 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 462a693..7337e8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,12 @@ find_package(TBB CONFIG ) find_package(xbyak REQUIRED CONFIG) +find_package(Boost + CONFIG + REQUIRED + COMPONENTS + stacktrace_windbg +) include(cmake/sourcelist.cmake) @@ -101,6 +107,7 @@ target_link_libraries( TBB::tbb TBB::tbbmalloc xbyak::xbyak + Boost::stacktrace_windbg ) if (MSVC) diff --git a/src/patches/memorymanager.cpp b/src/patches/memorymanager.cpp index 770834e..34c9396 100644 --- a/src/patches/memorymanager.cpp +++ b/src/patches/memorymanager.cpp @@ -1,6 +1,10 @@ #include "version.h" #include "offsets.h" +#include +#include +#include +#pragma comment(lib, "dbghelp.lib") namespace { @@ -83,19 +87,57 @@ namespace struct PointerStats { - int allocated = 0; + bool allocated = false; }; tbb::concurrent_hash_map allocatedPointers; RE::TESForm* ctd; - //std::mutex memory_mutex; - - void Allocate(void* mem) + inline void StackDump() { - //std::lock_guard guard(memory_mutex); + auto st = boost::stacktrace::stacktrace(); + HANDLE process = GetCurrentProcess(); + SymInitialize(process, NULL, TRUE); + + for (size_t i = 0; i < st.size(); i++) + { + auto frame = st[i]; + const void* address = frame.address(); + DWORD64 displacement = 0; + + // Get module info + IMAGEHLP_MODULE64 moduleInfo; + ZeroMemory(&moduleInfo, sizeof(moduleInfo)); + moduleInfo.SizeOfStruct = sizeof(moduleInfo); + BOOL hasModule = SymGetModuleInfo64(process, (DWORD64)address, &moduleInfo); + + // Get symbol info + char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + BOOL hasSymbol = SymFromAddr(process, (DWORD64)address, &displacement, symbol); + + // Get instruction address + CONTEXT context; + RtlCaptureContext(&context); + + char* dllName = nullptr; + if (hasModule) + + { + dllName = strrchr(moduleInfo.ImageName, '\\'); + } + + logger::error("Stack trace: [{}] DLL: {} | Offset: {:X} | Instruction: {:X} | Function: {}", i, (hasModule ? (dllName ? dllName + 1 : moduleInfo.ImageName) : "unknown"), displacement, context.Rip, (hasSymbol ? symbol->Name : "unknown")); + } + + SymCleanup(process); + } + bool Allocate(void* mem) + { decltype(allocatedPointers)::accessor accessor; uintptr_t addr = reinterpret_cast(mem); @@ -103,27 +145,30 @@ namespace if (allocatedPointers.find(accessor, addr)) { PointerStats* stats = accessor->second; - stats->allocated++; - if (stats->allocated > 1) + if (stats->allocated) { - logger::trace("MemoryManagerStats::Allocate error 101: already allocated {} times for {}.", stats->allocated, addr); + logger::trace("MemoryManagerStats::Allocate error 101: already allocated for {}.", addr); + return false; } - else { - //logger::trace("MemoryManagerStats::Allocate success: allocated {} times for {}.", stats->allocated, addr); + else + { + stats->allocated = true; + //logger::trace("MemoryManagerStats::Allocate success: allocated for {}.", addr); + return true; } } else { PointerStats* stats = new PointerStats(); - stats->allocated = 1; + stats->allocated = true; allocatedPointers.emplace(addr, stats); - //logger::trace("MemoryManagerStats::Allocate success: allocated {} times for {}.", stats->allocated, addr); + //logger::trace("MemoryManagerStats::Allocate success: allocated for {}.", addr); + return true; } } - void Deallocate(void* mem) + bool Deallocate(void* mem) { - //std::lock_guard guard(memory_mutex); decltype(allocatedPointers)::accessor accessor; uintptr_t addr = reinterpret_cast(mem); @@ -131,73 +176,83 @@ namespace if (allocatedPointers.find(accessor, addr)) { PointerStats* stats = accessor->second; - stats->allocated--; - if (stats->allocated < 0) + if (!stats->allocated) { - logger::trace("MemoryManagerStats::Deallocate error 202: already deallocated {} times for {}.", (stats->allocated * -1), addr); - std::this_thread::sleep_for(std::chrono::seconds(10000)); + logger::error("MemoryManagerStats::Deallocate error 202: already deallocated for {}.", addr); + StackDump(); + return false; } else { - //logger::trace("MemoryManagerStats::Deallocate success: allocated {} times for {}.", stats->allocated, addr); + stats->allocated = false; + //logger::trace("MemoryManagerStats::Deallocate success: allocated for {}.", addr); + return true; } } else { logger::trace("MemoryManagerStats::Deallocate error 203: not allocated for {}.", addr); + return false; } } - void Reallocate(void* oldmem, void* newmem) + bool Reallocate(void* oldmem, void* newmem) { - //std::lock_guard guard(memory_mutex); decltype(allocatedPointers)::accessor accessor; uintptr_t old_addr = reinterpret_cast(oldmem); uintptr_t new_addr = reinterpret_cast(newmem); if (old_addr == new_addr) { - return; + return true; } + bool oldSuccess = true; + bool newSuccess = true; + if (allocatedPointers.find(accessor, old_addr)) { PointerStats* stats = accessor->second; - stats->allocated--; - if (stats->allocated < 0) + if (!stats->allocated) { - logger::trace("MemoryManagerStats::Reallocate old_addr error 302: already deallocated {} times for {}.", (stats->allocated * -1), old_addr); + logger::trace("MemoryManagerStats::Reallocate old_addr error 302: already deallocated for {}.", old_addr); + oldSuccess = false; } else { - //logger::trace("MemoryManagerStats::Reallocate old_addr success: allocated {} times for {}.", stats->allocated, old_addr); + //logger::trace("MemoryManagerStats::Reallocate old_addr success: allocated for {}.", old_addr); + stats->allocated = false; } } else { logger::trace("MemoryManagerStats::Reallocate old_addr error 303: not allocated for {}.", old_addr); + oldSuccess = false; } if (allocatedPointers.find(accessor, new_addr)) { PointerStats* stats = accessor->second; - stats->allocated++; - if (stats->allocated > 1) + if (stats->allocated) { - logger::trace("MemoryManagerStats::Reallocate new_addr error 301: already allocated {} times for {}.", stats->allocated, new_addr); + logger::trace("MemoryManagerStats::Reallocate new_addr error 301: already allocated for {}.", new_addr); + newSuccess = false; } else { - //logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated {} times for {}.", stats->allocated, new_addr); + //logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated for {}.", new_addr); + stats->allocated = true; } } else { PointerStats* stats = new PointerStats(); - stats->allocated = 1; + stats->allocated = true; allocatedPointers.emplace(new_addr, stats); - //logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated {} times for {}.", stats->allocated, new_addr); + //logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated for {}.", new_addr); } + + return oldSuccess && newSuccess; } } @@ -208,16 +263,14 @@ namespace void* ret = g_trash; if (a_size > 0) { - errno = 0; ret = a_alignmentRequired ? - scalable_aligned_malloc(a_size, a_alignment) : - scalable_malloc(a_size); - if (errno || ret == nullptr) + scalable_aligned_malloc(a_size, a_alignment) : + scalable_malloc(a_size); + if (ret == nullptr) { logger::trace("MemoryManager::Allocate error {} ", errno); } - else - { + else { MemoryManagerStats::Allocate(ret); } } @@ -228,23 +281,11 @@ namespace { if (a_mem != g_trash && a_mem != nullptr) { - errno = 0; - if (scalable_msize(a_mem) != 0) + if (MemoryManagerStats::Deallocate(a_mem)) { - MemoryManagerStats::Deallocate(a_mem); - a_alignmentRequired ? scalable_aligned_free(a_mem) : scalable_free(a_mem); - - } - else - { - logger::trace("MemoryManager::Deallocate trying to free already free pointer"); - } - if (errno) - { - logger::trace("MemoryManager::Deallocate error {} ", errno); } } } @@ -256,11 +297,11 @@ namespace ret = Allocate(a_self, a_newSize, a_alignment, a_alignmentRequired); else { - errno = 0; ret = a_alignmentRequired ? scalable_aligned_realloc(a_oldMem, a_newSize, a_alignment) : scalable_realloc(a_oldMem, a_newSize); - if (errno || ret == nullptr) + + if (ret == nullptr) { logger::trace("MemoryManager::Reallocate error {} ", errno); } @@ -324,14 +365,12 @@ namespace void* ret = g_trash; if (a_size > 0) { - errno = 0; ret = scalable_aligned_malloc(a_size, a_alignment); - if (errno || ret == nullptr) + if (ret == nullptr) { logger::trace("ScrapHeap::Allocate error {} ", errno); } - else - { + else { MemoryManagerStats::Allocate(ret); } } @@ -349,20 +388,9 @@ namespace { if (a_mem != g_trash && a_mem != nullptr) { - errno = 0; - if (scalable_msize(a_mem) != 0) + if (MemoryManagerStats::Deallocate(a_mem)) { scalable_aligned_free(a_mem); - - MemoryManagerStats::Deallocate(a_mem); - } - else - { - logger::trace("ScrapHeap::Deallocate trying to free already free pointer"); - } - if (errno) - { - logger::trace("ScrapHeap::Deallocate error {} ", errno); } } } diff --git a/vcpkg.json b/vcpkg.json index 2a81169..8efadf5 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -12,7 +12,8 @@ "spdlog", "tbb", "xbyak", - "rsm-binary-io" + "rsm-binary-io", + "boost-stacktrace" ], "builtin-baseline": "d9ccd77bb554e67137f3f754a2e2f11f4188c82c", "overrides": [