Adding Anomaly Detection

This commit is contained in:
Gustavo Varo 2025-03-10 18:10:02 -04:00
parent 3deac1bc60
commit adfbdac7ec
2 changed files with 54 additions and 4 deletions

View file

@ -43,6 +43,8 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT c
size_t totalCommittedPages = 0; size_t totalCommittedPages = 0;
size_t totalAbandonedPages = 0; size_t totalAbandonedPages = 0;
std::vector<float> potentialFragmentation;
std::vector<float> highAbandonedPagesRatio;
for (size_t i = 0; i < arenaCount; ++i) { for (size_t i = 0; i < arenaCount; ++i) {
ULONG64 arenaAddr = subprocMainAddr + offsetof(mi_subproc_t, arenas) + (i * sizeof(std::atomic<mi_arena_t*>)); ULONG64 arenaAddr = subprocMainAddr + offsetof(mi_subproc_t, arenas) + (i * sizeof(std::atomic<mi_arena_t*>));
ULONG64 arenaValueAddr = 0; ULONG64 arenaValueAddr = 0;
@ -83,6 +85,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT c
totalCommittedPages += committedPages; totalCommittedPages += committedPages;
// For abandoned pages, iterate over each bin (0 to MI_BIN_COUNT - 1). // For abandoned pages, iterate over each bin (0 to MI_BIN_COUNT - 1).
size_t abandonedPages = 0;
for (int bin = 0; bin < MI_BIN_COUNT; bin++) { for (int bin = 0; bin < MI_BIN_COUNT; bin++) {
ULONG64 itemAdr = reinterpret_cast<ULONG64>(arena.pages_abandoned[bin]); ULONG64 itemAdr = reinterpret_cast<ULONG64>(arena.pages_abandoned[bin]);
mi_bitmap_t abandonedBmp {}; mi_bitmap_t abandonedBmp {};
@ -92,9 +95,21 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT c
return hr; return hr;
} }
size_t abandonedPages = mi_bitmap_count(&abandonedBmp); abandonedPages = mi_bitmap_count(&abandonedBmp);
totalAbandonedPages += abandonedPages;
} }
totalAbandonedPages += abandonedPages;
// --- Anomaly Detection ---
float pctCommittedSlices = (arena.slice_count > 0) ? (committed * 100.0 / arena.slice_count) : 0.0;
potentialFragmentation.push_back(pctCommittedSlices);
float abandonedPagesRatio = 0.0;
if (committedPages > 0 && abandonedPages > 0) {
abandonedPagesRatio = (abandonedPages * 100.0) / committedPages;
}
highAbandonedPagesRatio.push_back(abandonedPagesRatio);
} }
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nTotal Arenas: %d\n", arenaCount); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nTotal Arenas: %d\n", arenaCount);
@ -111,6 +126,19 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT c
for (size_t i = 0; i < arenaCount; ++i) { for (size_t i = 0; i < arenaCount; ++i) {
ULONG64 arenaAddr = subprocMainAddr + offsetof(mi_subproc_t, arenas) + (i * sizeof(std::atomic<mi_arena_t*>)); ULONG64 arenaAddr = subprocMainAddr + offsetof(mi_subproc_t, arenas) + (i * sizeof(std::atomic<mi_arena_t*>));
PrintLink(arenaAddr, std::format("!mi_dump_arena 0x{:016X}", arenaAddr), std::format("Arena {}", i)); PrintLink(arenaAddr, std::format("!mi_dump_arena 0x{:016X}", arenaAddr), std::format("Arena {}", i));
if (potentialFragmentation[i] < MinCommittedSlicesPct) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, " Warning: Low committed slices percentage (%.2f%%). Potential fragmentation detected.\n", potentialFragmentation[i]);
}
if (highAbandonedPagesRatio[i] > MaxAbandonedPagesRatio) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, " Warning: High ratio of abandoned pages (%.2f%%). Possible memory leak or delayed decommitment.\n",
highAbandonedPagesRatio[i]);
}
if (potentialFragmentation[i] < MinCommittedSlicesPct || highAbandonedPagesRatio[i] > MaxAbandonedPagesRatio) {
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
}
} }
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
@ -191,6 +219,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT cl
totalCommittedPages += committedPages; totalCommittedPages += committedPages;
// For abandoned pages, iterate over each bin (0 to MI_BIN_COUNT - 1). // For abandoned pages, iterate over each bin (0 to MI_BIN_COUNT - 1).
size_t abandonedPages = 0;
for (int bin = 0; bin < MI_BIN_COUNT; bin++) { for (int bin = 0; bin < MI_BIN_COUNT; bin++) {
ULONG64 itemAdr = reinterpret_cast<ULONG64>(arena.pages_abandoned[bin]); ULONG64 itemAdr = reinterpret_cast<ULONG64>(arena.pages_abandoned[bin]);
mi_bitmap_t abandonedBmp {}; mi_bitmap_t abandonedBmp {};
@ -200,10 +229,11 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT cl
return hr; return hr;
} }
size_t abandonedPages = mi_bitmap_count(&abandonedBmp); abandonedPages = mi_bitmap_count(&abandonedBmp);
totalAbandonedPages += abandonedPages;
} }
totalAbandonedPages += abandonedPages;
// Summary that aids in detecting fragmentation or undercommitment. // Summary that aids in detecting fragmentation or undercommitment.
double pctCommitted = (totalSlices > 0) ? (totalCommittedSlices * 100.0 / totalSlices) : 0.0; double pctCommitted = (totalSlices > 0) ? (totalCommittedSlices * 100.0 / totalSlices) : 0.0;
const auto arenaStats = std::format(" Total Slices: {}\n Committed Slices: {} ({}%)", totalSlices, totalCommittedSlices, pctCommitted); const auto arenaStats = std::format(" Total Slices: {}\n Committed Slices: {} ({}%)", totalSlices, totalCommittedSlices, pctCommitted);
@ -213,5 +243,22 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT cl
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Page Statistics:\n%s\n", pageStats.c_str()); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Page Statistics:\n%s\n", pageStats.c_str());
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
// --- Anomaly Detection ---
float pctCommittedSlices = (arena.slice_count > 0) ? (committed * 100.0 / arena.slice_count) : 0.0;
float abandonedPagesRatio = 0.0;
if (committedPages > 0 && abandonedPages > 0) {
abandonedPagesRatio = (abandonedPages * 100.0) / committedPages;
}
if (pctCommittedSlices < MinCommittedSlicesPct) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, " Warning: Low committed slices percentage (%.2f%%). Potential fragmentation detected.\n", pctCommittedSlices);
}
if (abandonedPagesRatio > MaxAbandonedPagesRatio) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, " Warning: High ratio of abandoned pages (%.2f%%). Possible memory leak or delayed decommitment.\n", abandonedPagesRatio);
}
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
return S_OK; return S_OK;
} }

View file

@ -25,6 +25,9 @@ extern IDebugControl4* g_DebugControl;
extern IDebugSymbols3* g_DebugSymbols; extern IDebugSymbols3* g_DebugSymbols;
extern IDebugDataSpaces* g_DataSpaces; extern IDebugDataSpaces* g_DataSpaces;
constexpr double MinCommittedSlicesPct = 60.0; // if below threshold, potential fragmentation.
constexpr double MaxAbandonedPagesRatio = 0.4; // If abandoned pages exceed 40% of committed pages.
HRESULT FindMimallocBase(); HRESULT FindMimallocBase();
HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset); HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset);
HRESULT ReadMemory(const char* symbolName, void* outBuffer, size_t bufferSize); HRESULT ReadMemory(const char* symbolName, void* outBuffer, size_t bufferSize);