mimalloc/src/prim/windows/windbg/mimalloc_windbg_utils.cpp
2025-04-01 08:41:05 -04:00

216 lines
No EOL
7.1 KiB
C++

/* ----------------------------------------------------------------------------
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;
IDebugSystemObjects4* g_DebugSystemObjects = 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);
}
// Function to get the offset of a symbol in the debuggee process
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!<symbolName>"
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;
}
// 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, ULONG 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;
}
// Function to read memory from a specific address
HRESULT ReadMemory(ULONG64 address, void* outBuffer, ULONG 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;
}
// Function to read a string from the debuggee process
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;
ULONG 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;
}
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).
// It uses the popcount64 function (which uses __popcnt64 on MSVC) to count bits.
size_t mi_bitmap_count(mi_bitmap_t* bmp) {
size_t chunkCount = bmp->chunk_count.load();
size_t totalCount = 0;
for (size_t i = 0; i < chunkCount; i++) {
for (size_t j = 0; j < MI_BCHUNK_FIELDS; j++) {
mi_bfield_t field = bmp->chunks[i].bfields[j];
totalCount += (size_t)popcount64(field);
}
}
return totalCount;
}
std::string FormatSize(int64_t value) {
const char* suffixes[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
constexpr int maxIndex = static_cast<int>(std::size(suffixes)) - 1;
int index = 0;
double size = static_cast<double>(value);
while (size >= 1024.0 && index < maxIndex) {
size /= 1024.0;
++index;
}
return std::format("{:.2f} {}", size, suffixes[index]);
}
std::string FormatNumber(int64_t value) {
const char* suffixes[] = {"", "K", "M", "B", "T"};
constexpr int maxIndex = static_cast<int>(std::size(suffixes)) - 1;
int index = 0;
double num = static_cast<double>(value);
while (num >= 1000.0 && index < maxIndex) {
num /= 1000.0;
++index;
}
return std::format("{:.1f}{}", num, suffixes[index]);
}