Add Process Info

This commit is contained in:
Gustavo Varo 2025-03-31 20:25:46 -04:00
parent 38f139b0a5
commit 13b27252c1
6 changed files with 189 additions and 0 deletions

View file

@ -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_process_info.cpp)
list(APPEND mi_sources ${PROJECT_SOURCE_DIR}/src/prim/windows/windbg/mimalloc_windbg_settings.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)

View file

@ -52,6 +52,17 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON
return hr;
}
// Query for the IDebugSystemObjects4 interface
hr = g_DebugClient->QueryInterface(__uuidof(IDebugSystemObjects4), (void**)&g_DebugSystemObjects);
if (FAILED(hr)) {
g_DebugSymbols->Release();
g_DebugControl->Release();
g_DebugClient->Release();
g_DataSpaces->Release();
return hr;
}
// Find mimalloc base address at startup
hr = FindMimallocBase();
if (FAILED(hr) || g_MiMallocBase == 0) {
@ -80,6 +91,7 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK DebugExtensionInitialize(PULON
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_extension_settings\">Show Extension Settings</link>\n");
g_DebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, "<link cmd=\"!mi_dump_process_info\">Dump Process Info</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");

View file

@ -12,6 +12,8 @@ extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_options(PDEBUG_CLIENT
HRESULT hr = S_OK;
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
ULONG64 optionsAddr = 0;
hr = GetSymbolOffset("mi_options", optionsAddr);
if (FAILED(hr) || optionsAddr == 0) {

View file

@ -0,0 +1,115 @@
/* ----------------------------------------------------------------------------
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"
/*
Command: !mi_dump_process_info
*/
extern "C" __declspec(dllexport) HRESULT CALLBACK mi_dump_process_info(PDEBUG_CLIENT client, PCSTR args) {
UNREFERENCED_PARAMETER(args);
HRESULT hr = S_OK;
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
// Get process handle
ULONG64 processHandle = 0;
hr = g_DebugSystemObjects->GetCurrentProcessHandle(&processHandle);
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to get Process Handle.\n");
return hr;
}
// Get process ID
ULONG processId = 0;
hr = g_DebugSystemObjects->GetCurrentProcessSystemId(&processId);
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to get Process ID.\n");
return hr;
}
// Get executable arguments
ULONG64 pebAddr = 0;
hr = g_DebugSystemObjects->GetCurrentProcessPeb(&pebAddr);
if (FAILED(hr) || pebAddr == 0) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to get PEB address.\n");
return hr;
}
// Get offset of ProcessParameters
ULONG processParametersOffset = 0;
hr = GetNtSymbolOffset("_PEB", "ProcessParameters", processParametersOffset);
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to get ProcessParameters offset.\n");
return hr;
}
ULONG64 processParametersAddr = 0;
hr = ReadMemory(pebAddr + processParametersOffset, &processParametersAddr, sizeof(ULONG64));
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read ProcessParameters address from PEB.\n");
return hr;
}
// Get offset of CommandLine
ULONG commandLineOffset = 0;
hr = GetNtSymbolOffset("_RTL_USER_PROCESS_PARAMETERS", "CommandLine", commandLineOffset);
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to get CommandLine offset.\n");
return hr;
}
UNICODE_STRING cmdLine {};
hr = ReadMemory(processParametersAddr + commandLineOffset, &cmdLine, sizeof(cmdLine));
if (FAILED(hr) || cmdLine.Buffer == 0 || cmdLine.Length == 0) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read CommandLine struct.\n");
return hr;
}
// Read the command line string
std::wstring commandLine;
hr = ReadWideString(cmdLine.Buffer, commandLine, cmdLine.Length / sizeof(WCHAR));
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read CommandLine string.\n");
return hr;
}
// Get Processor Type (x86, x64, ARM64, etc.)
ULONG processorType = 0;
hr = g_DebugControl->GetActualProcessorType(&processorType);
const char* arch = (processorType == IMAGE_FILE_MACHINE_AMD64) ? "x64"
: (processorType == IMAGE_FILE_MACHINE_I386) ? "x86"
: (processorType == IMAGE_FILE_MACHINE_ARM64) ? "ARM64"
: "Unknown";
// Read NUMA node count
ULONG64 numaNodeOffSet = 0;
hr = GetSymbolOffset("_mi_numa_node_count", numaNodeOffSet);
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to get NUMA node count address.\n");
return hr;
}
ULONG numaNodeCount = 0;
hr = ReadMemory(numaNodeOffSet, &numaNodeCount, sizeof(ULONG));
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read NUMA node count.\n");
return hr;
}
// Output debuggee process info
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "Debugging Process (From Debuggee Context):\n");
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " Process ID : %u (0x%X)\n", processId, processId);
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " Command Line : %s\n", commandLine.c_str());
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " Architecture : %s\n", arch);
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " NUMA Node : %u\n", numaNodeCount);
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, " Process Handle : %p\n", processHandle);
g_DebugControl->Output(DEBUG_OUTPUT_NORMAL, "\n");
return S_OK;
}

View file

@ -14,6 +14,7 @@ IDebugClient* g_DebugClient = nullptr;
IDebugControl4* g_DebugControl = nullptr;
IDebugSymbols3* g_DebugSymbols = nullptr;
IDebugDataSpaces* g_DataSpaces = nullptr;
IDebugSystemObjects4* g_DebugSystemObjects = nullptr;
// Function to find mimalloc.dll base address at startup
HRESULT FindMimallocBase() {
@ -45,6 +46,37 @@ HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset) {
return S_OK;
}
// Function to get the offset of a nt symbol in the debuggee process
HRESULT GetNtSymbolOffset(const char* typeName, const char* fieldName, ULONG& outOffset) {
// Ensure debug interfaces are valid
if (!g_DebugSymbols || g_MiMallocBase == 0) {
return E_FAIL;
}
ULONG index = 0;
ULONG64 base = 0;
HRESULT hr = g_DebugSymbols->GetModuleByModuleName("ntdll", 0, &index, &base);
if (FAILED(hr) || index == 0 || base == 0) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to locate module name for ntdll");
return E_FAIL;
}
ULONG typeId = 0;
hr = g_DebugSymbols->GetTypeId(base, typeName, &typeId);
if (FAILED(hr) || typeId == 0) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to locate type id for: %s\n", typeName);
return E_FAIL;
}
hr = g_DebugSymbols->GetFieldTypeAndOffset(base, typeId, fieldName, nullptr, &outOffset);
if (FAILED(hr) || outOffset == 0) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to locate symbol: %s\n", fieldName);
return E_FAIL;
}
return S_OK;
}
// Function to read memory from the debuggee process
HRESULT ReadMemory(const char* symbolName, void* outBuffer, size_t bufferSize) {
if (!g_DataSpaces) {
@ -122,6 +154,24 @@ HRESULT ReadString(const char* symbolName, std::string& outBuffer) {
return S_OK;
}
HRESULT ReadWideString(ULONG64 address, std::wstring& outString, size_t maxLength) {
if (!g_DataSpaces) {
return E_FAIL;
}
WCHAR buffer[1025] = {0}; // max 1024 chars + null terminator
size_t readLength = min(maxLength, 1024);
HRESULT hr = g_DataSpaces->ReadVirtual(address, buffer, (ULONG)(readLength * sizeof(WCHAR)), nullptr);
if (FAILED(hr)) {
g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "ERROR: Failed to read string from address 0x%llx.\n", address);
return hr;
}
outString = buffer;
return S_OK;
}
// Helper function to count the number of set bits in a mi_bitmap_t.
// This implementation assumes that the bitmap is organized into chunks,
// and that each chunk contains MI_BCHUNK_FIELDS of type mi_bfield_t (usually a 64-bit word).

View file

@ -24,15 +24,18 @@ extern IDebugClient* g_DebugClient;
extern IDebugControl4* g_DebugControl;
extern IDebugSymbols3* g_DebugSymbols;
extern IDebugDataSpaces* g_DataSpaces;
extern IDebugSystemObjects4* g_DebugSystemObjects;
static inline double MinCommittedSlicesPct = 60.0; // if below threshold, potential fragmentation.
static inline double MaxAbandonedPagesRatio = 0.4; // If abandoned pages exceed 40% of committed pages.
HRESULT FindMimallocBase();
HRESULT GetSymbolOffset(const char* symbolName, ULONG64& outOffset);
HRESULT GetNtSymbolOffset(const char* typeName, const char* fieldName, ULONG& 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);
HRESULT ReadWideString(ULONG64 address, std::wstring& outString, size_t maxLength = 1024);
size_t mi_bitmap_count(mi_bitmap_t* bmp);
std::string FormatSize(std::size_t bytes);
std::string FormatNumber(double num);
@ -42,6 +45,12 @@ inline void PrintLink(ULONG64 addr, std::string cmd, std::string linkText, std::
std::format("[0x{:016X}] <link cmd=\"{}\">{}</link> {}\n", addr, cmd, linkText, extraText).c_str());
}
struct UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
ULONG64 Buffer;
};
#if defined(_MSC_VER)
#include <intrin.h>
static inline int popcount64(uint64_t x) {