diff --git a/src/spice2x/launcher/signal.cpp b/src/spice2x/launcher/signal.cpp index 764b269..4c73e8c 100644 --- a/src/spice2x/launcher/signal.cpp +++ b/src/spice2x/launcher/signal.cpp @@ -14,6 +14,7 @@ #include "util/detour.h" #include "util/libutils.h" #include "util/logging.h" +#include "util/memutils.h" #include "cfg/configurator.h" #include "logger.h" @@ -175,6 +176,9 @@ static LONG WINAPI TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *Exception log_warning("signal", "minidump creation function not available, skipping"); } + // dump memory information + memutils::show_available_memory(); + // this will stall all UI threads for this process show_popup_for_crash(); diff --git a/src/spice2x/util/memutils.cpp b/src/spice2x/util/memutils.cpp index 7ca92d0..679cc05 100644 --- a/src/spice2x/util/memutils.cpp +++ b/src/spice2x/util/memutils.cpp @@ -44,6 +44,61 @@ namespace memutils { return get_mem_counters().PrivateUsage; } + void show_available_memory() { + constexpr double MB = 1024.0 * 1024.0; + + auto mem_status = get_mem_status(); + + const auto pct = [](DWORDLONG used, DWORDLONG total) -> double { + return total ? (100.0 * static_cast(used) / static_cast(total)) : 0.0; + }; + + log_info("memutils", + "system physical memory: {:.1f} / {:.1f} MiB used ({:.1f}%)", + mem_total_used() / MB, mem_total() / MB, + pct(mem_total_used(), mem_total())); + + log_info("memutils", + "system page file: {:.1f} / {:.1f} MiB used ({:.1f}%, {:.1f} MiB commit headroom)", + vmem_total_used() / MB, vmem_total() / MB, + pct(vmem_total_used(), vmem_total()), + mem_status.ullAvailPageFile / MB); + + // process virtual address space - 32-bit processes die at ~2 GiB + // (or ~4 GiB with /LARGEADDRESSAWARE) regardless of how much RAM is free + const DWORDLONG va_used = mem_status.ullTotalVirtual - mem_status.ullAvailVirtual; + log_info("memutils", + "process virtual address space: {:.1f} / {:.1f} MiB used ({:.1f}%)", + va_used / MB, mem_status.ullTotalVirtual / MB, + pct(va_used, mem_status.ullTotalVirtual)); + + // largest contiguous free block in the process address space + // (large allocations can fail due to fragmentation even with plenty available) + MEMORY_BASIC_INFORMATION mbi{}; + SIZE_T largest_free = 0; + uintptr_t addr = 0; + const uintptr_t max_addr = static_cast(mem_status.ullTotalVirtual); + while (addr < max_addr && + VirtualQuery(reinterpret_cast(addr), &mbi, sizeof(mbi)) == sizeof(mbi)) { + if (mbi.State == MEM_FREE && mbi.RegionSize > largest_free) { + largest_free = mbi.RegionSize; + } + const uintptr_t next = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize; + if (next <= addr) { + break; + } + addr = next; + } + log_info("memutils", + "process largest free virtual block: {:.1f} MiB ({:.1f}% of total VA)", + largest_free / MB, + pct(largest_free, mem_status.ullTotalVirtual)); + + log_info("memutils", + "process working set: {:.1f} MiB, private bytes: {:.1f} MiB", + mem_used() / MB, vmem_used() / MB); + } + VProtectGuard::VProtectGuard(void *addr, size_t size, DWORD mode, bool reset) : addr(addr), reset(reset), size(size) { diff --git a/src/spice2x/util/memutils.h b/src/spice2x/util/memutils.h index 4c1a635..4dd980c 100644 --- a/src/spice2x/util/memutils.h +++ b/src/spice2x/util/memutils.h @@ -14,6 +14,8 @@ namespace memutils { DWORDLONG vmem_total_used(); DWORDLONG vmem_used(); + void show_available_memory(); + /* * Helper class to unprotect/reprotect memory safely. * It will free it's mode override on destruction.