mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-06 07:29:30 +03:00
Add mi_dump_stats
This commit is contained in:
parent
5a6d3412b0
commit
f9bdcef9c5
7 changed files with 412 additions and 53 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_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)
|
||||
endif()
|
||||
|
|
|
@ -69,15 +69,16 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON
|
|||
// Print the mimalloc base address, this indicates extension was load successfully
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "mimalloc.dll base address found: 0x%llx\n\n", g_MiMallocBase);
|
||||
|
||||
// show version
|
||||
// show extension version
|
||||
const int vermajor = MI_MALLOC_VERSION / 100;
|
||||
const int verminor = (MI_MALLOC_VERSION % 100) / 10;
|
||||
const int verpatch = (MI_MALLOC_VERSION % 10);
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "mimalloc %s\n", std::format("v{}.{}.{} (built on {}, {})\n", vermajor, verminor, verpatch, __DATE__, __TIME__).c_str());
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "mimalloc extension %s\n", std::format("v{}.{}.{} (built on {}, {})\n", vermajor, verminor, verpatch, __DATE__, __TIME__).c_str());
|
||||
|
||||
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_help\">Show Help commands</link>\n");
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
|
@ -106,18 +107,4 @@ extern "C" __declspec(dllexport) void CALLBACK DebugExtensionUninitialize() {
|
|||
g_DebugClient->Release();
|
||||
g_DebugClient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Help command: !mi_help
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_help(PDEBUG_CLIENT Client, PCSTR args) {
|
||||
UNREFERENCED_PARAMETER(args);
|
||||
|
||||
// Print Help
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Hello from MiMalloc WinDbg Extension!\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_stats(PDEBUG_CLIENT client, PCSTR args) {
|
||||
return S_OK;
|
||||
}
|
153
src/prim/windows/windbg/mimalloc_windbg_heap.cpp
Normal file
153
src/prim/windows/windbg/mimalloc_windbg_heap.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
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_heap.h"
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include "mimalloc_internal.h"
|
||||
#include "mimalloc_windbg_utils.h"
|
||||
|
||||
/*
|
||||
Command: !mi_dump_current_thread_heap
|
||||
|
||||
- Retrieves the global _mi_heap_default symbol.
|
||||
- Reads the mi_heap_t structure from the debuggee memory.
|
||||
- Outputs key details of the current thread's heap (thread ID, page count, segment count).
|
||||
- Provides a rich link to trigger a detailed dump of the heap.
|
||||
- This command is critical for diagnosing per-thread memory allocation behavior.
|
||||
*/
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_current_thread_heap(PDEBUG_CLIENT client, PCSTR args) {
|
||||
UNREFERENCED_PARAMETER(args);
|
||||
UNREFERENCED_PARAMETER(client);
|
||||
|
||||
ULONG64 heapAddress = 0;
|
||||
HRESULT hr = GetSymbolOffset("_mi_heap_default", heapAddress);
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to locate _mi_heap_default\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
mi_heap_t heap = {0};
|
||||
hr = ReadMemory(heapAddress, &heap, sizeof(heap));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to read heap structure at 0x%llx\n", heapAddress);
|
||||
return hr;
|
||||
}
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Heap for thread %u:\n Page Count: %u, Segment Count: %u\n", heap.thread_id, heap.page_count, heap.segment_count);
|
||||
|
||||
char link[128];
|
||||
snprintf(link, sizeof(link), "!mi.DumpHeap 0x%llx", heapAddress);
|
||||
PrintLink(link, "Click here to dump detailed heap info", heapAddress);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Command: !mi_dump_all_thread_heaps
|
||||
|
||||
- Retrieves the _mi_heaps array and the heap count (_mi_heap_count).
|
||||
- Iterates over each thread's heap, reading its mi_heap_t structure.
|
||||
- Outputs a summary for each heap along with rich links for detailed inspection.
|
||||
- Useful for understanding the distribution of memory usage across threads.
|
||||
*/
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_all_thread_heaps(PDEBUG_CLIENT client, PCSTR args) {
|
||||
UNREFERENCED_PARAMETER(args);
|
||||
UNREFERENCED_PARAMETER(client);
|
||||
|
||||
ULONG64 heapsAddress = 0;
|
||||
HRESULT hr = GetSymbolOffset("_mi_heaps", heapsAddress);
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to locate _mi_heaps\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
ULONG64 heapCountAddress = 0;
|
||||
hr = GetSymbolOffset("_mi_heap_count", heapCountAddress);
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to locate _mi_heap_count\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
size_t heapCount = 0;
|
||||
hr = ReadMemory(heapCountAddress, &heapCount, sizeof(heapCount));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to read heap count\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
std::vector<mi_heap_t*> heaps(heapCount);
|
||||
hr = ReadMemory(heapsAddress, heaps.data(), heapCount * sizeof(mi_heap_t*));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to read heaps array\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < heapCount; i++) {
|
||||
ULONG64 heapAddr = (ULONG64)heaps[i];
|
||||
mi_heap_t heap = {0};
|
||||
hr = ReadMemory(heapAddr, &heap, sizeof(heap));
|
||||
if (FAILED(hr))
|
||||
continue;
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\nHeap for thread %u:\n Page Count: %u, Segment Count: %u\n", heap.thread_id, heap.page_count, heap.segment_count);
|
||||
|
||||
char link[128];
|
||||
snprintf(link, sizeof(link), "!mi.DumpHeap 0x%llx", heapAddr);
|
||||
PrintLink(link, "Click here for detailed heap info", heapAddr);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Command: !mi_dump_aggregated_thread_heap_stats
|
||||
|
||||
- Aggregates statistics from all thread heaps.
|
||||
- Sums the page count and segment count for all heaps.
|
||||
- Outputs an aggregated view that helps identify global memory usage patterns and potential imbalances.
|
||||
*/
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_aggregated_thread_heap_stats(PDEBUG_CLIENT client, PCSTR args) {
|
||||
UNREFERENCED_PARAMETER(args);
|
||||
UNREFERENCED_PARAMETER(client);
|
||||
|
||||
ULONG64 heapsAddress = 0;
|
||||
HRESULT hr = GetSymbolOffset("_mi_heaps", heapsAddress);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
ULONG64 heapCountAddress = 0;
|
||||
hr = GetSymbolOffset("_mi_heap_count", heapCountAddress);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
size_t heapCount = 0;
|
||||
hr = ReadMemory(heapCountAddress, &heapCount, sizeof(heapCount));
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
std::vector<mi_heap_t*> heaps(heapCount);
|
||||
hr = ReadMemory(heapsAddress, heaps.data(), heapCount * sizeof(mi_heap_t*));
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
size_t totalPages = 0;
|
||||
size_t totalSegments = 0;
|
||||
for (size_t i = 0; i < heapCount; i++) {
|
||||
ULONG64 heapAddr = (ULONG64)heaps[i];
|
||||
mi_heap_t heap = {0};
|
||||
hr = ReadMemory(heapAddr, &heap, sizeof(heap));
|
||||
if (FAILED(hr))
|
||||
continue;
|
||||
totalPages += heap.page_count;
|
||||
totalSegments += heap.segment_count;
|
||||
}
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Aggregated Heap Statistics:\n Total Heaps: %zu\n Total Pages: %zu, Total Segments: %zu\n", heapCount, totalPages, totalSegments);
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -13,7 +13,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_options(PDEBUG_CLIENT
|
|||
HRESULT hr = S_OK;
|
||||
|
||||
ULONG64 optionsAddr = 0;
|
||||
hr = GetSymbolOffset("options", optionsAddr);
|
||||
hr = GetSymbolOffset("mi_options", optionsAddr);
|
||||
if (FAILED(hr) || optionsAddr == 0) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Could not locate optionsAddr.\n");
|
||||
return E_FAIL;
|
||||
|
|
226
src/prim/windows/windbg/mimalloc_windbg_stats.cpp
Normal file
226
src/prim/windows/windbg/mimalloc_windbg_stats.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
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 <cassert>
|
||||
#include "mimalloc_windbg_utils.h"
|
||||
|
||||
HRESULT PrintItemStats(ULONG64 statsAddr, std::ptrdiff_t fieldOffset, std::string_view fieldName, std::string_view name, bool formatAsSize) {
|
||||
HRESULT hr = S_OK;
|
||||
ULONG64 itemAddr = statsAddr + fieldOffset;
|
||||
mi_stat_count_s item {};
|
||||
hr = ReadMemory(itemAddr, &item, sizeof(mi_stat_count_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, std::format("ERROR: Failed to read {0} at 0x{1:016X}.\n", fieldName, itemAddr).c_str());
|
||||
return hr;
|
||||
}
|
||||
|
||||
std::string peak = "";
|
||||
std::string total = "";
|
||||
std::string current = "";
|
||||
|
||||
if (formatAsSize) {
|
||||
peak = FormatSize(item.peak);
|
||||
total = FormatSize(item.total);
|
||||
current = FormatSize(item.current);
|
||||
} else {
|
||||
peak = FormatNumber(item.peak);
|
||||
total = FormatNumber(item.total);
|
||||
current = FormatNumber(item.current);
|
||||
}
|
||||
|
||||
const std::string pad = name.size() < 10 ? std::string(10 - name.size(), ' ') : "";
|
||||
g_DebugControl->ControlledOutput(
|
||||
DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format("{0}<link cmd=\"dx -r1 (*((mimalloc!mi_stat_count_s *)0x{1:016X}))\">{2}</link> {3:>20} {4:>20} {5:>20}\n", pad, itemAddr, name, peak, total, current).c_str());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Command: !mi_dump_stats
|
||||
-
|
||||
*/
|
||||
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_stats(PDEBUG_CLIENT client, PCSTR args) {
|
||||
UNREFERENCED_PARAMETER(args);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
ULONG64 subprocMainAddr = 0;
|
||||
hr = GetSymbolOffset("subproc_main", subprocMainAddr);
|
||||
if (FAILED(hr) || subprocMainAddr == 0) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Could not locate subproc_main.\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
mi_subproc_t subprocMain {};
|
||||
hr = ReadMemory(subprocMainAddr, &subprocMain, sizeof(mi_subproc_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read subproc_main at 0x%llx.\n", subprocMainAddr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Read _mi_heap_empty
|
||||
ULONG64 miHeapEmptyAddr = 0;
|
||||
hr = GetSymbolOffset("_mi_heap_empty", miHeapEmptyAddr);
|
||||
if (FAILED(hr) || subprocMainAddr == 0) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Could not locate _mi_heap_empty.\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
mi_heap_t miHeapEmpty {};
|
||||
hr = ReadMemory(miHeapEmptyAddr, &miHeapEmpty, sizeof(mi_heap_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read mi_heap_t at 0x%llx.\n", miHeapEmptyAddr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
ULONG64 statsAddr = subprocMainAddr + offsetof(mi_subproc_t, stats);
|
||||
ULONG64 mallocBinsAddr = statsAddr + offsetof(mi_stats_t, malloc_bins);
|
||||
|
||||
// Print Heap Header
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format("<link cmd=\"dx -r1 (*((mimalloc!mi_stat_count_s (*)[{0}])0x{1:016X}))\">{2:>10}</link> {3:>20} {4:>20} {5:>20} {6:>20} {7:>20}\n",
|
||||
MI_BIN_HUGE + 1, mallocBinsAddr, "Heap Stats", "Peak", "Total", "Current", "Block Size", "Total#")
|
||||
.c_str());
|
||||
|
||||
for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
|
||||
ULONG64 binAddr = mallocBinsAddr + (i * sizeof(mi_stat_count_t));
|
||||
mi_stat_count_t malloc_bin {};
|
||||
hr = ReadMemory(binAddr, &malloc_bin, sizeof(mi_stat_count_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read malloc_bins at 0x%llx.\n", binAddr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (malloc_bin.total > 0) {
|
||||
ULONG64 pagesAddr = miHeapEmptyAddr + offsetof(mi_heap_t, pages) + (i * sizeof(mi_page_queue_t));
|
||||
mi_page_queue_t page {};
|
||||
hr = ReadMemory(pagesAddr, &page, sizeof(mi_page_queue_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read page_bins at 0x%llx.\n", pagesAddr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
int64_t unit = page.block_size;
|
||||
|
||||
g_DebugControl->ControlledOutput(
|
||||
DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format("bin {0}<link cmd=\"dx -r1 (*((mimalloc!mi_stat_count_s *)0x{1:016X}))\">{2}</link> {3:>20} {4:>20} {5:>20} {6:>20} {7:>20}\n",
|
||||
std::string(6 - std::to_string(i).size(), ' '), binAddr, i, FormatSize(malloc_bin.peak * unit), FormatSize(malloc_bin.total * unit),
|
||||
FormatSize(malloc_bin.current * unit), FormatSize(unit), FormatNumber(malloc_bin.total))
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format("{0:>10} {1:>20} {2:>20} {3:>20}\n", "Heap Stats", "Peak", "Total", "Current").c_str());
|
||||
|
||||
ULONG64 mallocNormalAddr = statsAddr + offsetof(mi_stats_t, malloc_normal);
|
||||
mi_stat_count_s mallocNormal {};
|
||||
hr = ReadMemory(mallocNormalAddr, &mallocNormal, sizeof(mi_stat_count_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read malloc_normal at 0x%llx.\n", mallocNormalAddr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format(" <link cmd=\"dx -r1 (*((mimalloc!mi_stat_count_s *)0x{0:016X}))\">normal</link> {1:>20} {2:>20} {3:>20}\n", mallocNormalAddr,
|
||||
FormatSize(mallocNormal.peak), FormatSize(mallocNormal.total), FormatSize(mallocNormal.current))
|
||||
.c_str());
|
||||
|
||||
ULONG64 mallocHugeAddr = statsAddr + offsetof(mi_stats_t, malloc_huge);
|
||||
mi_stat_count_s mallocHuge {};
|
||||
hr = ReadMemory(mallocHugeAddr, &mallocHuge, sizeof(mi_stat_count_t));
|
||||
if (FAILED(hr)) {
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read malloc_huge at 0x%llx.\n", mallocHugeAddr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format(" <link cmd=\"dx -r1 (*((mimalloc!mi_stat_count_s *)0x{0:016X}))\">huge</link> {1:>20} {2:>20} {3:>20}\n", mallocHugeAddr,
|
||||
FormatSize(mallocHuge.peak), FormatSize(mallocHuge.total), FormatSize(mallocHuge.current))
|
||||
.c_str());
|
||||
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
std::format(" total {0:>20} {1:>20} {2:>20}\n", FormatSize(mallocNormal.peak + mallocHuge.peak),
|
||||
FormatSize(mallocNormal.total + mallocHuge.total), FormatSize(mallocNormal.current + mallocHuge.current))
|
||||
.c_str());
|
||||
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, malloc_requested), "malloc_requested", "requested", true);
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
// Reserved
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, reserved), "reserved", "reserved", true);
|
||||
|
||||
// Committed
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, committed), "committed", "committed", true);
|
||||
|
||||
// Reset
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, reset), "reset", "reset", true);
|
||||
|
||||
// Purged
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, purged), "purged", "purged", true);
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
// pages
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages), "pages", "pages", false);
|
||||
|
||||
// abandoned
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages_abandoned), "pages_abandoned", "abandoned", false);
|
||||
|
||||
// pages_reclaim_on_alloc
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages_reclaim_on_alloc), "pages_reclaim_on_alloc", "reclaim A", false);
|
||||
|
||||
// pages_reclaim_on_free
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages_reclaim_on_free), "pages_reclaim_on_free", "reclaim F", false);
|
||||
|
||||
// pages_reabandon_full
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages_reabandon_full), "pages_reabandon_full", "reabandon", false);
|
||||
|
||||
// pages_unabandon_busy_wait
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages_unabandon_busy_wait), "pages_unabandon_busy_wait", "waits", false);
|
||||
|
||||
// pages_retire
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, pages_retire), "pages_retire", "retire", false);
|
||||
|
||||
// arena_count
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, arena_count), "arena_count", "arenas", false);
|
||||
|
||||
// arena_rollback_count
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, arena_rollback_count), "arena_rollback_count", "rollback", false);
|
||||
|
||||
// mmap_calls
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, mmap_calls), "mmap_calls", "mmaps", false);
|
||||
|
||||
// commit_calls
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, commit_calls), "commit_calls", "commits", false);
|
||||
|
||||
// reset_calls
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, reset_calls), "reset_calls", "resets", false);
|
||||
|
||||
// purge_calls
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, purge_calls), "purge_calls", "purges", false);
|
||||
|
||||
// malloc_guarded_count
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, malloc_guarded_count), "malloc_guarded_count", "guarded", false);
|
||||
|
||||
// threads
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, threads), "threads", "threads", false);
|
||||
|
||||
// page_searches
|
||||
PrintItemStats(statsAddr, offsetof(mi_stats_t, page_searches), "page_searches", "searches", false);
|
||||
|
||||
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -6,6 +6,7 @@ terms of the MIT license. A copy of the license can be found in the file
|
|||
-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "mimalloc_windbg_utils.h"
|
||||
#include <array>
|
||||
|
||||
ULONG64 g_MiMallocBase = 0;
|
||||
|
||||
|
@ -135,4 +136,29 @@ size_t mi_bitmap_count(mi_bitmap_t* bmp) {
|
|||
}
|
||||
}
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
std::string FormatSize(std::size_t bytes) {
|
||||
constexpr std::array<const char*, 6> suffixes = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
|
||||
double size = static_cast<double>(bytes);
|
||||
int index = 0;
|
||||
|
||||
while (size >= 1024.0 && index < suffixes.size() - 1) {
|
||||
size /= 1024.0;
|
||||
++index;
|
||||
}
|
||||
|
||||
return std::format("{:.2f} {}", size, suffixes[index]);
|
||||
}
|
||||
|
||||
std::string FormatNumber(double num) {
|
||||
constexpr std::array<const char*, 5> suffixes = {"", "K", "M", "B", "T"};
|
||||
int index = 0;
|
||||
|
||||
while (num >= 1000.0 && index < suffixes.size() - 1) {
|
||||
num /= 1000.0;
|
||||
++index;
|
||||
}
|
||||
|
||||
return std::format("{:.1f}{}", num, suffixes[index]);
|
||||
}
|
|
@ -34,6 +34,8 @@ HRESULT ReadMemory(const char* symbolName, void* outBuffer, size_t bufferSize);
|
|||
HRESULT ReadMemory(ULONG64 address, void* outBuffer, size_t bufferSize);
|
||||
HRESULT ReadString(const char* symbolName, std::string& outBuffer);
|
||||
size_t mi_bitmap_count(mi_bitmap_t* bmp);
|
||||
std::string FormatSize(std::size_t bytes);
|
||||
std::string FormatNumber(double num);
|
||||
|
||||
inline void PrintLink(ULONG64 addr, std::string cmd, std::string linkText, std::string extraText = "") {
|
||||
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
|
||||
|
@ -57,40 +59,4 @@ static inline int popcount64(uint64_t x) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// TODO Remove the code below once it is avaialble in the mimalloc header
|
||||
typedef struct mi_arena_s {
|
||||
mi_memid_t memid; // memid of the memory area
|
||||
mi_subproc_t* subproc; // subprocess this arena belongs to (`this 'in' this->subproc->arenas`)
|
||||
|
||||
size_t slice_count; // total size of the area in arena slices (of `MI_ARENA_SLICE_SIZE`)
|
||||
size_t info_slices; // initial slices reserved for the arena bitmaps
|
||||
int numa_node; // associated NUMA node
|
||||
bool is_exclusive; // only allow allocations if specifically for this arena
|
||||
_Atomic(mi_msecs_t) purge_expire; // expiration time when slices can be purged from `slices_purge`.
|
||||
|
||||
mi_bbitmap_t* slices_free; // is the slice free? (a binned bitmap with size classes)
|
||||
mi_bitmap_t* slices_committed; // is the slice committed? (i.e. accessible)
|
||||
mi_bitmap_t* slices_dirty; // is the slice potentially non-zero?
|
||||
mi_bitmap_t* slices_purge; // slices that can be purged
|
||||
mi_bitmap_t* pages; // all registered pages (abandoned and owned)
|
||||
mi_bitmap_t* pages_abandoned[MI_BIN_COUNT]; // abandoned pages per size bin (a set bit means the start of the page)
|
||||
// the full queue contains abandoned full pages
|
||||
// followed by the bitmaps (whose sizes depend on the arena size)
|
||||
// note: when adding bitmaps revise `mi_arena_info_slices_needed`
|
||||
} mi_arena_t;
|
||||
|
||||
typedef enum mi_init_e {
|
||||
UNINIT, // not yet initialized
|
||||
DEFAULTED, // not found in the environment, use default value
|
||||
INITIALIZED // found in environment or set explicitly
|
||||
} mi_init_t;
|
||||
|
||||
typedef struct mi_option_desc_s {
|
||||
long value; // the value
|
||||
mi_init_t init; // is it initialized yet? (from the environment)
|
||||
mi_option_t option; // for debugging: the option index should match the option
|
||||
const char* name; // option name without `mimalloc_` prefix
|
||||
const char* legacy_name; // potential legacy option name
|
||||
} mi_option_desc_t;
|
||||
|
||||
#endif // MIMALLOC_WINDBG_UTILS_H
|
Loading…
Add table
Reference in a new issue