mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
utils: dump memory info on crash (#708)
For diagnosing out of memory errors / malloc failures.
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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<double>(used) / static_cast<double>(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<uintptr_t>(mem_status.ullTotalVirtual);
|
||||
while (addr < max_addr &&
|
||||
VirtualQuery(reinterpret_cast<LPCVOID>(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<uintptr_t>(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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user