diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c292523..e0d6496f 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_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() diff --git a/src/prim/windows/windbg/mimalloc_windbg.cpp b/src/prim/windows/windbg/mimalloc_windbg.cpp index a205ef3e..231e40bd 100644 --- a/src/prim/windows/windbg/mimalloc_windbg.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg.cpp @@ -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, "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 Help commands\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; } \ No newline at end of file diff --git a/src/prim/windows/windbg/mimalloc_windbg_heap.cpp b/src/prim/windows/windbg/mimalloc_windbg_heap.cpp new file mode 100644 index 00000000..f08ce7a4 --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_heap.cpp @@ -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 +#include +#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 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 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; +} diff --git a/src/prim/windows/windbg/mimalloc_windbg_options.cpp b/src/prim/windows/windbg/mimalloc_windbg_options.cpp index b6424113..0ef41729 100644 --- a/src/prim/windows/windbg/mimalloc_windbg_options.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg_options.cpp @@ -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; diff --git a/src/prim/windows/windbg/mimalloc_windbg_stats.cpp b/src/prim/windows/windbg/mimalloc_windbg_stats.cpp new file mode 100644 index 00000000..d4126fc4 --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_stats.cpp @@ -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 +#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}{2} {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("{2:>10} {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}{2} {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(" normal {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(" huge {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; +} \ No newline at end of file diff --git a/src/prim/windows/windbg/mimalloc_windbg_utils.cpp b/src/prim/windows/windbg/mimalloc_windbg_utils.cpp index 06de0e1b..9145cf01 100644 --- a/src/prim/windows/windbg/mimalloc_windbg_utils.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg_utils.cpp @@ -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 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 suffixes = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"}; + double size = static_cast(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 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]); } \ No newline at end of file diff --git a/src/prim/windows/windbg/mimalloc_windbg_utils.h b/src/prim/windows/windbg/mimalloc_windbg_utils.h index c688d7ce..5422d5d1 100644 --- a/src/prim/windows/windbg/mimalloc_windbg_utils.h +++ b/src/prim/windows/windbg/mimalloc_windbg_utils.h @@ -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 \ No newline at end of file