mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-06 23:39:31 +03:00
Windbg Extension improvments
- Add settings command - Fix help - Fix Arenas
This commit is contained in:
parent
f9bdcef9c5
commit
090ef45120
6 changed files with 124 additions and 36 deletions
|
@ -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)
|
||||
|
|
|
@ -79,6 +79,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON
|
|||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_dump_options\">Dump Options</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_dump_arenas\">Dump Arenas</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_dump_stats\">Dump Statistics</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_show_extension_settings\">Show Extension Settings</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_show_help\">Show Help commands</link>\n");
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
|
|
|
@ -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<mi_arena_t*>));
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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, "<link cmd=\"!mi_dump_options\">Dump Options</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_dump_arenas\">Dump Arenas</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_dump_stats\">Dump Statistics</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_show_extension_settings\">Show Extension Settings</link>\n");
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_show_help\">Show Help commands</link>\n");
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
83
src/prim/windows/windbg/mimalloc_windbg_settings.cpp
Normal file
83
src/prim/windows/windbg/mimalloc_windbg_settings.cpp
Normal file
|
@ -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 <b>!mi_set_extension_setting <setting> <value></b> 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 <setting> <value>\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 <setting> <value>\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;
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue