From 090ef45120db223d69ee2ce165cbb906294fa2be Mon Sep 17 00:00:00 2001 From: Gustavo Varo Date: Tue, 18 Mar 2025 10:33:33 -0400 Subject: [PATCH] Windbg Extension improvments - Add settings command - Fix help - Fix Arenas --- CMakeLists.txt | 1 + src/prim/windows/windbg/mimalloc_windbg.cpp | 1 + .../windows/windbg/mimalloc_windbg_arenas.cpp | 63 +++++++------- .../windows/windbg/mimalloc_windbg_help.cpp | 8 ++ .../windbg/mimalloc_windbg_settings.cpp | 83 +++++++++++++++++++ .../windows/windbg/mimalloc_windbg_utils.h | 4 +- 6 files changed, 124 insertions(+), 36 deletions(-) create mode 100644 src/prim/windows/windbg/mimalloc_windbg_settings.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e0d6496f..e4296bda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,6 +295,7 @@ if(WIN32 AND MI_WIN_DBG_EXTS) list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp) list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_help.cpp) list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_options.cpp) + list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_settings.cpp) list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_stats.cpp) list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_utils.cpp) list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg.cpp) diff --git a/src/prim/windows/windbg/mimalloc_windbg.cpp b/src/prim/windows/windbg/mimalloc_windbg.cpp index 231e40bd..de00af76 100644 --- a/src/prim/windows/windbg/mimalloc_windbg.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg.cpp @@ -79,6 +79,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Dump Options\n"); g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Dump Arenas\n"); g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Dump Statistics\n"); + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Show Extension Settings\n"); g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Show Help commands\n"); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); diff --git a/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp b/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp index 9ac5947d..e6010922 100644 --- a/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp @@ -7,6 +7,30 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc_windbg_utils.h" +void PrintArenaSummary(double totalSlices, double totalCommittedSlices, double totalCommittedPages, double totalAbandonedPages) { + double pctCommitted = (totalSlices > 0) ? (totalCommittedSlices * 100.0 / totalSlices) : 0.0; + const auto arenaStats = std::format(" Total Slices: {}\n Committed Slices: {} ({}%)", totalSlices, totalCommittedSlices, pctCommitted); + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Arena Statistics:\n%s\n", arenaStats.c_str()); + + const auto pageStats = std::format(" Committed Pages: {}\n Abandoned Pages: {}", totalCommittedPages, totalAbandonedPages); + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Page Statistics:\n%s\n", pageStats.c_str()); + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); +} + +void PrintAnomalyDetectionIfNeeded(double potentialFragmentation, double highAbandonedPagesRatio) { + if (potentialFragmentation < MinCommittedSlicesPct) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, " Warning: Low committed slices percentage (%.2f%%). Potential fragmentation detected.\n", potentialFragmentation); + } + + if (highAbandonedPagesRatio > MaxAbandonedPagesRatio) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, " Warning: High ratio of abandoned pages (%.2f%%). Possible memory leak or delayed decommitment.\n", highAbandonedPagesRatio); + } + + if (potentialFragmentation < MinCommittedSlicesPct || highAbandonedPagesRatio > MaxAbandonedPagesRatio) { + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + } +} + /* Command: !mi_dump_arenas - Arenas are a fundamental component of mimalloc’s memory management. @@ -115,30 +139,13 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT c g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nTotal Arenas: %d\n", arenaCount); // Summary that aids in detecting fragmentation or undercommitment. - double pctCommitted = (totalSlices > 0) ? (totalCommittedSlices * 100.0 / totalSlices) : 0.0; - const auto arenaStats = std::format(" Total Slices: {}\n Committed Slices: {} ({}%)", totalSlices, totalCommittedSlices, pctCommitted); - g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Arena Statistics:\n%s\n", arenaStats.c_str()); - - const auto pageStats = std::format(" Committed Pages: {}\n Abandoned Pages: {}", totalCommittedPages, totalAbandonedPages); - g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Page Statistics:\n%s\n", pageStats.c_str()); - g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + PrintArenaSummary(totalSlices, totalCommittedSlices, totalCommittedPages, totalAbandonedPages); for (size_t i = 0; i < arenaCount; ++i) { ULONG64 arenaAddr = subprocMainAddr + offsetof(mi_subproc_t, arenas) + (i * sizeof(std::atomic)); 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"); - } + PrintAnomalyDetectionIfNeeded(potentialFragmentation[i], highAbandonedPagesRatio[i]); } g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); @@ -235,13 +242,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT cl totalAbandonedPages += abandonedPages; // Summary that aids in detecting fragmentation or undercommitment. - double pctCommitted = (totalSlices > 0) ? (totalCommittedSlices * 100.0 / totalSlices) : 0.0; - const auto arenaStats = std::format(" Total Slices: {}\n Committed Slices: {} ({}%)", totalSlices, totalCommittedSlices, pctCommitted); - g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Arena Statistics:\n%s\n", arenaStats.c_str()); - - const auto pageStats = std::format(" Committed Pages: {}\n Abandoned Pages: {}", totalCommittedPages, totalAbandonedPages); - g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nAggregated Page Statistics:\n%s\n", pageStats.c_str()); - g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + PrintArenaSummary(totalSlices, totalCommittedSlices, totalCommittedPages, totalAbandonedPages); // --- Anomaly Detection --- float pctCommittedSlices = (arena.slice_count > 0) ? (committed * 100.0 / arena.slice_count) : 0.0; @@ -251,14 +252,8 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT cl 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); - } + PrintAnomalyDetectionIfNeeded(pctCommittedSlices, abandonedPagesRatio); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); return S_OK; -} +} \ No newline at end of file diff --git a/src/prim/windows/windbg/mimalloc_windbg_help.cpp b/src/prim/windows/windbg/mimalloc_windbg_help.cpp index fdda8491..8e091abf 100644 --- a/src/prim/windows/windbg/mimalloc_windbg_help.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg_help.cpp @@ -15,9 +15,17 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_show_help(PDEBUG_CLIENT cli HRESULT hr = S_OK; + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + // Print Help g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Hello from MiMalloc WinDbg Extension!\n"); g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Start here:\n"); + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Dump Options\n"); + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Dump Arenas\n"); + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Dump Statistics\n"); + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Show Extension Settings\n"); + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "Show Help commands\n"); + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); return S_OK; } \ No newline at end of file diff --git a/src/prim/windows/windbg/mimalloc_windbg_settings.cpp b/src/prim/windows/windbg/mimalloc_windbg_settings.cpp new file mode 100644 index 00000000..d6937fc7 --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_settings.cpp @@ -0,0 +1,83 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) Microsoft Research +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +#include "mimalloc_windbg_utils.h" + +/* +Command: !mi_show_extension_settings +*/ +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_show_extension_settings(PDEBUG_CLIENT client, PCSTR args) { + UNREFERENCED_PARAMETER(args); + + HRESULT hr = S_OK; + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, + std::format("{:<30} {:<10} {}\n", "MinCommittedSlicesPct", MinCommittedSlicesPct, "When below threshold, potential fragmentation.").c_str()); + + g_DebugControl->ControlledOutput( + DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, + std::format("{:<30} {:<10} {}\n", "MaxAbandonedPagesRatio", MaxAbandonedPagesRatio, "When abandoned pages exceeds ratio of committed pages, potential leak.").c_str()); + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + + g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, + "Use !mi_set_extension_setting <setting> <value> to set a new value for the setting.\n"); + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + + return S_OK; +} + +/* +Command: !mi_set_extension_setting +*/ +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_set_extension_setting(PDEBUG_CLIENT client, PCSTR args) { + if (!args || !*args) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Usage: !mi_set_extension_setting \n"); + return E_INVALIDARG; + } + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n"); + + char fieldName[64]; + char valueStr[64]; + + // Parse the input arguments + if (sscanf_s(args, "%63s %63s", fieldName, (unsigned)_countof(fieldName), valueStr, (unsigned)_countof(valueStr)) != 2) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Invalid input format. Expected: !mi_set_extension_setting \n"); + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + + // Update the setting based on the field name + if (strcmp(fieldName, "MinCommittedSlicesPct") == 0) { + double doubleValue = 0.0; + if (sscanf_s(valueStr, "%lf", &doubleValue) != 1 || doubleValue < 0 || doubleValue > 100) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Value must be an value between 0 and 100.\n"); + return E_INVALIDARG; + } + + MinCommittedSlicesPct = doubleValue; + } else if (strcmp(fieldName, "MaxAbandonedPagesRatio") == 0) { + double doubleValue = 0.0; + if (sscanf_s(valueStr, "%lf", &doubleValue) != 1 || doubleValue < 0.0 || doubleValue > 1.0) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Value must be a value between 0.0 and 1.0.\n"); + return E_INVALIDARG; + } + + MaxAbandonedPagesRatio = doubleValue; + } else { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Invalid field name. Allowed fields: MinCommittedSlicesPct, MaxAbandonedPagesRatio\n"); + return E_INVALIDARG; + } + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Successfully updated %s to %s\n", fieldName, valueStr); + return S_OK; +} diff --git a/src/prim/windows/windbg/mimalloc_windbg_utils.h b/src/prim/windows/windbg/mimalloc_windbg_utils.h index 5422d5d1..0ac65eee 100644 --- a/src/prim/windows/windbg/mimalloc_windbg_utils.h +++ b/src/prim/windows/windbg/mimalloc_windbg_utils.h @@ -25,8 +25,8 @@ extern IDebugControl4* g_DebugControl; extern IDebugSymbols3* g_DebugSymbols; 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. +static inline double MinCommittedSlicesPct = 60.0; // if below threshold, potential fragmentation. +static inline double MaxAbandonedPagesRatio = 0.4; // If abandoned pages exceed 40% of committed pages. HRESULT FindMimallocBase(); HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset);