diff --git a/src/prim/windows/windbg/mimalloc_windbg.cpp b/src/prim/windows/windbg/mimalloc_windbg.cpp
index d419e173..16778884 100644
--- a/src/prim/windows/windbg/mimalloc_windbg.cpp
+++ b/src/prim/windows/windbg/mimalloc_windbg.cpp
@@ -67,7 +67,17 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON
nullptr);
// 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", g_MiMallocBase);
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "mimalloc.dll base address found: 0x%llx\n\n", g_MiMallocBase);
+
+ // show 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, "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");
return S_OK;
}
diff --git a/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp b/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp
index 6a8e638b..5c77e6d7 100644
--- a/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp
+++ b/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp
@@ -15,29 +15,26 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT c
ULONG64 subprocMainAddr = 0;
hr = GetSymbolOffset("subproc_main", subprocMainAddr);
if (FAILED(hr) || subprocMainAddr == 0) {
- g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Could not locate mi_subproc_t.\n");
+ g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Could not locate subproc_main.\n");
return E_FAIL;
}
- g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "mi_subproc_t found at 0x%llx\n\n", subprocMainAddr);
+ PrintLink(subprocMainAddr, std::format("dx -r1 (*((mimalloc!mi_subproc_s *)0x{:016X}))", subprocMainAddr), "subproc_main");
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 mi_subproc_t at 0x%llx.\n", subprocMainAddr);
+ g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read subproc_main at 0x%llx.\n", subprocMainAddr);
return hr;
}
// Print results
size_t arenaCount = subprocMain.arena_count.load();
- g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Arena count: %llu\n\n", arenaCount);
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Arena count: %llu\n", arenaCount);
for (size_t i = 0; i < arenaCount; ++i) {
ULONG64 arenaAddr = subprocMainAddr + offsetof(mi_subproc_t, arenas) + (i * sizeof(std::atomic));
-
- // Print clickable Arena Index link
- g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
- "[0x%p] [Arena %llu]\n", (void*)arenaAddr, (void*)arenaAddr, i);
+ PrintLink(arenaAddr, std::format("!mi_dump_arena 0x{:016X}", arenaAddr), std::format("Arena {}", i));
}
return S_OK;
@@ -55,21 +52,31 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT cl
return E_INVALIDARG;
}
- g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Dumping Arena at Address: 0x%016llx\n\n", arenaAddr);
+ ULONG64 arenaValueAddr = 0;
+ HRESULT hr = ReadMemory(arenaAddr, &arenaValueAddr, sizeof(ULONG64));
+ if (FAILED(hr) || arenaValueAddr == 0) {
+ g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read mi_arena_t pointer at 0x%016llx.\n", arenaAddr);
+ return hr;
+ }
- //// Read the `mi_arena_t` structure from the memory
- // mi_arena_t arena{};
- // HRESULT hr = ReadMemory(arenaAddr, &arena, sizeof(mi_arena_t));
- // if (FAILED(hr))
- //{
- // g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read mi_arena_t at 0x%016llx.\n", arenaAddr);
- // return hr;
- // }
+ PrintLink(arenaAddr, std::format("dx -r1 ((mimalloc!mi_arena_s *)0x{:016X})", arenaValueAddr), std::format("Dumping Arena at Address: 0x{:016X}\n", arenaAddr));
+
+ // Read the `mi_arena_t` structure from the memory
+ mi_arena_t arena {};
+ hr = ReadMemory(arenaValueAddr, &arena, sizeof(mi_arena_t));
+ if (FAILED(hr)) {
+ g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read mi_arena_t at 0x%016llx.\n", arenaAddr);
+ return hr;
+ }
+
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - Slice Count: %llu\n", arenaAddr + offsetof(mi_arena_t, slice_count), arena.slice_count);
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - Info Slices: %llu\n", arenaAddr + offsetof(mi_arena_t, info_slices), arena.info_slices);
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - NUMA Node: %d\n", arenaAddr + offsetof(mi_arena_t, numa_node), arena.numa_node);
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - Exclusive: %s\n", arenaAddr + offsetof(mi_arena_t, is_exclusive), arena.is_exclusive ? "Yes" : "No");
+ g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - Purge Expire: %d\n", arenaAddr + offsetof(mi_arena_t, purge_expire), arena.purge_expire.load());
+
+ // g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - Slices Free (Count): %d\n", arena.slices_free->chunk_count.load());
+ // g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "[0x%p] - Slices Committed (Max Accessed): %d\n", arena.slices_committed.load());
- // g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " - Slice Count: %llu\n", arena.slice_count);
- // g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " - Info Slices: %llu\n", arena.info_slices);
- // g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " - NUMA Node: %d\n", arena.numa_node);
- // g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " - Exclusive: %s\n", arena.is_exclusive ? "Yes" : "No");
- //
return S_OK;
}
diff --git a/src/prim/windows/windbg/mimalloc_windbg_options.cpp b/src/prim/windows/windbg/mimalloc_windbg_options.cpp
index 3dae8c9e..b6424113 100644
--- a/src/prim/windows/windbg/mimalloc_windbg_options.cpp
+++ b/src/prim/windows/windbg/mimalloc_windbg_options.cpp
@@ -12,5 +12,30 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_options(PDEBUG_CLIENT
HRESULT hr = S_OK;
+ ULONG64 optionsAddr = 0;
+ hr = GetSymbolOffset("options", optionsAddr);
+ if (FAILED(hr) || optionsAddr == 0) {
+ g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Could not locate optionsAddr.\n");
+ return E_FAIL;
+ }
+
+ const int optionsSize = static_cast(mi_option_e::_mi_option_last);
+ PrintLink(optionsAddr, std::format("dx -r1 (*((mimalloc!mi_option_desc_s (*)[{}])0x{:016X}))", optionsSize, optionsAddr),
+ std::format("Dumping Options at Address: 0x{:016X}\n", optionsAddr));
+
+ for (size_t i = 0; i < optionsSize; ++i) {
+ ULONG64 optionItemAddr = optionsAddr + (i * sizeof(mi_option_desc_t));
+
+ mi_option_desc_t optionDesc {};
+ hr = ReadMemory(optionItemAddr, &optionDesc, sizeof(mi_option_desc_t));
+ if (FAILED(hr)) {
+ g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read mi_option_desc_t at 0x%llx.\n", optionItemAddr);
+ return hr;
+ }
+
+ PrintLink(optionItemAddr, std::format("dx -r1 (*((mimalloc!mi_option_desc_s *)0x{:016X}))", optionItemAddr), std::to_string(i),
+ std::format(" {}: {}", optionDesc.name, optionDesc.value));
+ }
+
return S_OK;
}
\ 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 e9001197..5d60af5d 100644
--- a/src/prim/windows/windbg/mimalloc_windbg_utils.h
+++ b/src/prim/windows/windbg/mimalloc_windbg_utils.h
@@ -10,11 +10,13 @@ terms of the MIT license. A copy of the license can be found in the file
#include
#include
+#include
#include
#include
-#include "mimalloc.h"
-#include "mimalloc/internal.h"
+#include
+#include
+#include "../src/bitmap.h"
extern ULONG64 g_MiMallocBase;
@@ -29,4 +31,44 @@ 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);
+inline void PrintLink(ULONG64 addr, std::string cmd, std::string linkText, std::string extraText = "") {
+ g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,
+ std::format("[0x{:016X}] {} {}\n", addr, cmd, linkText, extraText).c_str());
+}
+
+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