diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..ea62c8ca --- /dev/null +++ b/.clang-format @@ -0,0 +1,110 @@ +--- +BasedOnStyle: Microsoft +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 180 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH] +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: "^<.*" + Priority: 2 + - Regex: ".*" + Priority: 3 +IncludeIsMainRegex: "(Test)?$" +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 100 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp20 +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dba9ba1..64e0301d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,10 @@ set(mi_sources src/prim/prim.c) if(WIN32 AND MI_WIN_DBG_EXTS) - list(APPEND mi_sources src/prim/windows/windbg/mimalloc_dbg.cpp) + list(APPEND mi_sources src/prim/windows/windbg/mimalloc_windbg.cpp) + list(APPEND mi_sources src/prim/windows/windbg/mimalloc_windbg_arenas.cpp) + list(APPEND mi_sources src/prim/windows/windbg/mimalloc_windbg_options.cpp) + list(APPEND mi_sources src/prim/windows/windbg/mimalloc_windbg_utils.cpp) endif() set(mi_cflags "") diff --git a/ide/vs2022/mimalloc-lib.vcxproj b/ide/vs2022/mimalloc-lib.vcxproj index b5bc9677..c3eb5ca2 100644 --- a/ide/vs2022/mimalloc-lib.vcxproj +++ b/ide/vs2022/mimalloc-lib.vcxproj @@ -476,7 +476,9 @@ true - + + + @@ -493,7 +495,7 @@ - + diff --git a/ide/vs2022/mimalloc-lib.vcxproj.filters b/ide/vs2022/mimalloc-lib.vcxproj.filters index c2d7db7b..dfab6c95 100644 --- a/ide/vs2022/mimalloc-lib.vcxproj.filters +++ b/ide/vs2022/mimalloc-lib.vcxproj.filters @@ -61,7 +61,13 @@ Sources - + + Sources + + + Sources + + Sources @@ -99,6 +105,9 @@ Headers + + Headers + diff --git a/ide/vs2022/mimalloc-override-dll.vcxproj b/ide/vs2022/mimalloc-override-dll.vcxproj index 3904e344..feb5f4f2 100644 --- a/ide/vs2022/mimalloc-override-dll.vcxproj +++ b/ide/vs2022/mimalloc-override-dll.vcxproj @@ -206,6 +206,7 @@ false CompileAsCpp /Zc:__cplusplus %(AdditionalOptions) + stdcpp20 $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;dbgeng.lib;%(AdditionalDependencies) @@ -441,7 +442,7 @@ - + @@ -507,7 +508,9 @@ true - + + + diff --git a/ide/vs2022/mimalloc-override-dll.vcxproj.filters b/ide/vs2022/mimalloc-override-dll.vcxproj.filters index e09e6bb1..06ebb379 100644 --- a/ide/vs2022/mimalloc-override-dll.vcxproj.filters +++ b/ide/vs2022/mimalloc-override-dll.vcxproj.filters @@ -61,7 +61,13 @@ Sources - + + Sources + + + Sources + + Sources @@ -99,6 +105,9 @@ Headers + + Headers + diff --git a/src/prim/windows/windbg/mimalloc_dbg.cpp b/src/prim/windows/windbg/mimalloc_windbg.cpp similarity index 65% rename from src/prim/windows/windbg/mimalloc_dbg.cpp rename to src/prim/windows/windbg/mimalloc_windbg.cpp index 5bc76372..57d8d712 100644 --- a/src/prim/windows/windbg/mimalloc_dbg.cpp +++ b/src/prim/windows/windbg/mimalloc_windbg.cpp @@ -5,74 +5,46 @@ 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 -#include -#include -#include -#include - -#include "mimalloc.h" -#include "mimalloc/internal.h" - -ULONG64 g_MiMallocBase = 0; -IDebugClient* g_DebugClient = nullptr; -IDebugControl* g_DebugControl = nullptr; -IDebugSymbols3* g_DebugSymbols = nullptr; -IDebugDataSpaces* g_DataSpaces = nullptr; - -// Function to find mimalloc.dll base address at startup -HRESULT FindMimallocBase() -{ - if (g_DebugSymbols == nullptr) - { - return E_FAIL; - } - - return g_DebugSymbols->GetModuleByModuleName("mimalloc", 0, NULL, &g_MiMallocBase); -} +#include "mimalloc_windbg_utils.h" // Entry point for the extension -extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULONG version, PULONG flags) -{ +extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULONG version, PULONG flags) { UNREFERENCED_PARAMETER(flags); // Ensure Version is valid - if (!version) - { + if (!version) { return E_INVALIDARG; } // Set the version *version = DEBUG_EXTENSION_VERSION(1, 0); + // Create the debug interfaces HRESULT hr = DebugCreate(__uuidof(IDebugClient), (void**)&g_DebugClient); - if (FAILED(hr)) - { + if (FAILED(hr)) { return hr; } // Query for the IDebugControl interface - hr = g_DebugClient->QueryInterface(__uuidof(IDebugControl), (void**)&g_DebugControl); - if (FAILED(hr)) - { + hr = g_DebugClient->QueryInterface(__uuidof(IDebugControl4), (void**)&g_DebugControl); + if (FAILED(hr)) { g_DebugClient->Release(); return hr; } + // Query for the IDebugSymbols3 interface hr = g_DebugClient->QueryInterface(__uuidof(IDebugSymbols3), (void**)&g_DebugSymbols); - if (FAILED(hr)) - { + if (FAILED(hr)) { g_DebugControl->Release(); g_DebugClient->Release(); return hr; } + // Query for the IDebugDataSpaces interface hr = g_DebugClient->QueryInterface(__uuidof(IDebugDataSpaces), (void**)&g_DataSpaces); - if (FAILED(hr)) - { + if (FAILED(hr)) { g_DebugSymbols->Release(); g_DebugControl->Release(); g_DebugClient->Release(); @@ -82,11 +54,11 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON // Find mimalloc base address at startup hr = FindMimallocBase(); - if (FAILED(hr) || g_MiMallocBase == 0) - { - return E_FAIL; // Prevent extension from loading + if (FAILED(hr) || g_MiMallocBase == 0) { + return E_FAIL; // Prevent extension from loading } + // Register output callbacks mi_register_output( [](const char* msg, void* arg) { g_DebugControl->Output(DEBUG_OUTPUT_ERROR, msg); @@ -94,43 +66,38 @@ 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); return S_OK; } // Notifies the extension that a debug event has occurred -extern "C" __declspec(dllexport) void CALLBACK DebugExtensionNotify(ULONG notify, ULONG64 argument) -{ +extern "C" __declspec(dllexport) void CALLBACK DebugExtensionNotify(ULONG notify, ULONG64 argument) { UNREFERENCED_PARAMETER(notify); UNREFERENCED_PARAMETER(argument); } // Uninitializes the extension -extern "C" __declspec(dllexport) void CALLBACK DebugExtensionUninitialize() -{ - if (g_DebugSymbols) - { +extern "C" __declspec(dllexport) void CALLBACK DebugExtensionUninitialize() { + if (g_DebugSymbols) { g_DebugSymbols->Release(); g_DebugSymbols = nullptr; } - if (g_DebugControl) - { + if (g_DebugControl) { g_DebugControl->Release(); g_DebugControl = nullptr; } - if (g_DebugClient) - { + if (g_DebugClient) { g_DebugClient->Release(); g_DebugClient = nullptr; } } -// Sample command: !mi_help -extern "C" __declspec(dllexport) HRESULT CALLBACK mi_help(PDEBUG_CLIENT Client, PCSTR args) -{ +// Help command: !mi_help +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_help(PDEBUG_CLIENT Client, PCSTR args) { UNREFERENCED_PARAMETER(args); // Print Help @@ -139,8 +106,11 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_help(PDEBUG_CLIENT Client, return S_OK; } -extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT client, PCSTR args) -{ - mi_debug_show_arenas(); +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_stats(PDEBUG_CLIENT client, PCSTR args) { + return S_OK; +} + +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_options(PDEBUG_CLIENT client, PCSTR args) { + mi_options_print(); return S_OK; } \ No newline at end of file diff --git a/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp b/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp new file mode 100644 index 00000000..6a8e638b --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_arenas.cpp @@ -0,0 +1,75 @@ +/* ---------------------------------------------------------------------------- +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" + +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arenas(PDEBUG_CLIENT client, PCSTR args) { + UNREFERENCED_PARAMETER(args); + + HRESULT hr = S_OK; + + 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"); + return E_FAIL; + } + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "mi_subproc_t found at 0x%llx\n\n", subprocMainAddr); + + 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); + return hr; + } + + // Print results + size_t arenaCount = subprocMain.arena_count.load(); + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Arena count: %llu\n\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); + } + + return S_OK; +} + +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_arena(PDEBUG_CLIENT client, PCSTR args) { + if (!args || !*args) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Usage: !mi_dump_arena \n"); + return E_INVALIDARG; + } + + ULONG64 arenaAddr = 0; + if (sscanf_s(args, "%llx", &arenaAddr) != 1 || arenaAddr == 0) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Invalid arena address provided: %s\n", args); + return E_INVALIDARG; + } + + g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Dumping Arena at Address: 0x%016llx\n\n", arenaAddr); + + //// 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; + // } + + // 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 new file mode 100644 index 00000000..3dae8c9e --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_options.cpp @@ -0,0 +1,16 @@ +/* ---------------------------------------------------------------------------- +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" + +extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_options(PDEBUG_CLIENT client, PCSTR args) { + UNREFERENCED_PARAMETER(args); + + HRESULT hr = S_OK; + + 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 new file mode 100644 index 00000000..f4098b7b --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_utils.cpp @@ -0,0 +1,118 @@ +/* ---------------------------------------------------------------------------- +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" + +ULONG64 g_MiMallocBase = 0; + +IDebugClient* g_DebugClient = nullptr; +IDebugControl4* g_DebugControl = nullptr; +IDebugSymbols3* g_DebugSymbols = nullptr; +IDebugDataSpaces* g_DataSpaces = nullptr; + +// Function to find mimalloc.dll base address at startup +HRESULT FindMimallocBase() { + if (g_DebugSymbols == nullptr) { + return E_FAIL; + } + + return g_DebugSymbols->GetModuleByModuleName("mimalloc", 0, NULL, &g_MiMallocBase); +} + +HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset) { + // Ensure debug interfaces are valid + if (!g_DebugSymbols || g_MiMallocBase == 0) { + return E_FAIL; + } + + // Construct the full symbol name: "mimalloc!" + std::string fullSymbol = "mimalloc!"; + fullSymbol += symbolName; + + // Retrieve the memory offset of the symbol in the debuggee process + HRESULT hr = g_DebugSymbols->GetOffsetByName(fullSymbol.c_str(), &outOffset); + if (FAILED(hr) || outOffset == 0) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to locate symbol: %s\n", fullSymbol.c_str()); + return E_FAIL; + } + + return S_OK; +} + +HRESULT ReadMemory(const char* symbolName, void* outBuffer, size_t bufferSize) { + if (!g_DataSpaces) { + return E_FAIL; + } + + // Step 1: Get the memory address of the symbol + ULONG64 symbolOffset = 0; + HRESULT hr = GetSymbolOffset(symbolName, symbolOffset); + if (FAILED(hr)) { + return hr; + } + + // Step 2: Read memory from the debuggee + hr = g_DataSpaces->ReadVirtual(symbolOffset, outBuffer, bufferSize, nullptr); + if (FAILED(hr)) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read memory for symbol: %s\n", symbolName); + return hr; + } + + return S_OK; +} + +HRESULT ReadMemory(ULONG64 address, void* outBuffer, size_t bufferSize) { + if (!g_DataSpaces) { + return E_FAIL; + } + + // Read memory directly from the given address + HRESULT hr = g_DataSpaces->ReadVirtual(address, outBuffer, bufferSize, nullptr); + if (FAILED(hr)) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read memory at address: 0x%llx\n", address); + return hr; + } + + return S_OK; +} + +HRESULT ReadString(const char* symbolName, std::string& outBuffer) { + if (!g_DataSpaces) { + return E_FAIL; + } + + // Step 1: Get the memory address of the symbol (pointer to string) + ULONG64 stringPtr = 0; + HRESULT hr = ReadMemory(symbolName, &stringPtr, sizeof(ULONG64)); + if (FAILED(hr) || stringPtr == 0) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Symbol %s is NULL or invalid.\n", symbolName); + return E_FAIL; + } + + // Step 2: Read the actual string data from the debuggee memory + char tempChar = 0; + size_t length = 0; + + do { + hr = g_DataSpaces->ReadVirtual(stringPtr + length, &tempChar, sizeof(char), nullptr); + if (FAILED(hr)) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read string from address 0x%llx.\n", stringPtr); + return hr; + } + + length++; + } while (tempChar != '\0'); + + outBuffer.resize(length); + hr = g_DataSpaces->ReadVirtual(stringPtr, &outBuffer[0], length, nullptr); + if (FAILED(hr)) { + g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read string from address 0x%llx.\n", stringPtr); + return hr; + } + + 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 new file mode 100644 index 00000000..e9001197 --- /dev/null +++ b/src/prim/windows/windbg/mimalloc_windbg_utils.h @@ -0,0 +1,32 @@ +/* ---------------------------------------------------------------------------- +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. +-----------------------------------------------------------------------------*/ + +#ifndef MIMALLOC_WINDBG_UTILS_H +#define MIMALLOC_WINDBG_UTILS_H + +#include +#include +#include +#include + +#include "mimalloc.h" +#include "mimalloc/internal.h" + +extern ULONG64 g_MiMallocBase; + +extern IDebugClient* g_DebugClient; +extern IDebugControl4* g_DebugControl; +extern IDebugSymbols3* g_DebugSymbols; +extern IDebugDataSpaces* g_DataSpaces; + +HRESULT FindMimallocBase(); +HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset); +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); + +#endif // MIMALLOC_WINDBG_UTILS_H \ No newline at end of file