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);