Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -101,6 +107,7 @@ target_link_libraries(
TBB::tbb
TBB::tbbmalloc
xbyak::xbyak
Boost::stacktrace_windbg
)

if (MSVC)
Expand Down
23 changes: 21 additions & 2 deletions src/patches/formcaching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<std::uintptr_t>(formPointer), reinterpret_cast<std::uintptr_t>(formPointerInGlobalFormTable));
formPointer = nullptr;
wrongPtrInCache = true;
}

return formPointer;
}
}
Expand All @@ -70,7 +89,7 @@ namespace patches

GlobalFormTableLock->UnlockForRead();

if (formPointer)
if (formPointer || wrongPtrInCache)
UpdateFormCache(FormId, formPointer, false);

return formPointer;
Expand Down
251 changes: 235 additions & 16 deletions src/patches/memorymanager.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include "version.h"

#include "offsets.h"
#include <boost/stacktrace.hpp>
#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")

namespace
{
Expand Down Expand Up @@ -78,34 +82,234 @@ namespace
}
}

namespace MemoryManagerStats
{

struct PointerStats
{
bool allocated = false;
};

tbb::concurrent_hash_map<uintptr_t, PointerStats*> allocatedPointers;

RE::TESForm* ctd;

inline void StackDump()
{
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<uintptr_t>(mem);

if (allocatedPointers.find(accessor, addr))
{
PointerStats* stats = accessor->second;
if (stats->allocated)
{
logger::trace("MemoryManagerStats::Allocate error 101: already allocated for {}.", addr);
return false;
}
else
{
stats->allocated = true;
//logger::trace("MemoryManagerStats::Allocate success: allocated for {}.", addr);
return true;
}
}
else
{
PointerStats* stats = new PointerStats();
stats->allocated = true;
allocatedPointers.emplace(addr, stats);
//logger::trace("MemoryManagerStats::Allocate success: allocated for {}.", addr);
return true;
}
}

bool Deallocate(void* mem)
{
decltype(allocatedPointers)::accessor accessor;

uintptr_t addr = reinterpret_cast<uintptr_t>(mem);

if (allocatedPointers.find(accessor, addr))
{
PointerStats* stats = accessor->second;
if (!stats->allocated)
{
logger::error("MemoryManagerStats::Deallocate error 202: already deallocated for {}.", addr);
StackDump();
return false;

}
else {
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;
}
}

bool Reallocate(void* oldmem, void* newmem)
{
decltype(allocatedPointers)::accessor accessor;

uintptr_t old_addr = reinterpret_cast<uintptr_t>(oldmem);
uintptr_t new_addr = reinterpret_cast<uintptr_t>(newmem);

if (old_addr == new_addr) {
return true;
}

bool oldSuccess = true;
bool newSuccess = true;

if (allocatedPointers.find(accessor, old_addr))
{
PointerStats* stats = accessor->second;
if (!stats->allocated)
{
logger::trace("MemoryManagerStats::Reallocate old_addr error 302: already deallocated for {}.", old_addr);
oldSuccess = false;
}
else
{
//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;
if (stats->allocated)
{
logger::trace("MemoryManagerStats::Reallocate new_addr error 301: already allocated for {}.", new_addr);
newSuccess = false;
}
else
{
//logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated for {}.", new_addr);
stats->allocated = true;
}
}
else
{
PointerStats* stats = new PointerStats();
stats->allocated = true;
allocatedPointers.emplace(new_addr, stats);
//logger::trace("MemoryManagerStats::Reallocate new_addr success: allocated for {}.", new_addr);
}

return oldSuccess && newSuccess;
}
}

namespace MemoryManager
{
void* Allocate(RE::MemoryManager*, std::size_t a_size, std::uint32_t a_alignment, bool a_alignmentRequired)
{
void* ret = g_trash;
if (a_size > 0)
return a_alignmentRequired ?
scalable_aligned_malloc(a_size, a_alignment) :
scalable_malloc(a_size);
else
return g_trash;
{
ret = a_alignmentRequired ?
scalable_aligned_malloc(a_size, a_alignment) :
scalable_malloc(a_size);
if (ret == nullptr)
{
logger::trace("MemoryManager::Allocate error {} ", errno);
}
else {
MemoryManagerStats::Allocate(ret);
}
}
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)
{
if (MemoryManagerStats::Deallocate(a_mem))
{
a_alignmentRequired ?
scalable_aligned_free(a_mem) :
scalable_free(a_mem);
}
}
}

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 ?
{
ret = a_alignmentRequired ?
scalable_aligned_realloc(a_oldMem, a_newSize, a_alignment) :
scalable_realloc(a_oldMem, a_newSize);

if (ret == nullptr)
{
logger::trace("MemoryManager::Reallocate error {} ", errno);
}
else {
MemoryManagerStats::Reallocate(a_oldMem, ret);
}
}
return ret;
}

void ReplaceAllocRoutines()
Expand Down Expand Up @@ -158,9 +362,19 @@ 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;
if (a_size > 0)
{
ret = scalable_aligned_malloc(a_size, a_alignment);
if (ret == nullptr)
{
logger::trace("ScrapHeap::Allocate error {} ", errno);
}
else {
MemoryManagerStats::Allocate(ret);
}
}
return ret;
}

RE::ScrapHeap* Ctor(RE::ScrapHeap* a_this)
Expand All @@ -172,8 +386,13 @@ 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)
{
if (MemoryManagerStats::Deallocate(a_mem))
{
scalable_aligned_free(a_mem);
}
}
}

void WriteHooks()
Expand Down
Loading