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