From 817504f27d134f76290f199fe6d349b3d87a0fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Kn=C3=A1pek?= Date: Sun, 26 Jul 2026 06:45:00 +0200 Subject: [PATCH 1/2] Debugger pretty printer for MSVC. --- extra/.gitignore | 4 + extra/int128_printer_msvc.bat | 70 +++++++++++++ extra/int128_printer_msvc.cpp | 162 +++++++++++++++++++++++++++++++ extra/int128_printer_msvc.natvis | 9 ++ 4 files changed, 245 insertions(+) create mode 100644 extra/.gitignore create mode 100644 extra/int128_printer_msvc.bat create mode 100644 extra/int128_printer_msvc.cpp create mode 100644 extra/int128_printer_msvc.natvis diff --git a/extra/.gitignore b/extra/.gitignore new file mode 100644 index 00000000..08a6a6b2 --- /dev/null +++ b/extra/.gitignore @@ -0,0 +1,4 @@ +/int128_printer_msvc.dll +/int128_printer_msvc.exp +/int128_printer_msvc.lib +/int128_printer_msvc.obj diff --git a/extra/int128_printer_msvc.bat b/extra/int128_printer_msvc.bat new file mode 100644 index 00000000..bc4e4ffd --- /dev/null +++ b/extra/int128_printer_msvc.bat @@ -0,0 +1,70 @@ +@if "%~1"=="inner" goto :mk_inner +@cmd.exe /c ""%~f0" "inner" %*" +@if %errorlevel% neq 0 goto :mk_outer_bad +@goto :mk_outer_gud + +:mk_inner +@echo off +if exist "c:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" goto :mk_vs_enterprise +if exist "c:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat" goto :mk_vs_community +echo Visual Studio 2026 not found. +goto :mk_fail + +:mk_vs_enterprise +call "c:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 +@echo off +goto :mk_next + +:mk_vs_community +call "c:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +@echo off +goto :mk_next + +:mk_next +set INCLUDE=%INCLUDE%;%~dp0..\include +cl.exe ^ +/nologo ^ +/LD ^ +/std:c++latest ^ +/EHsc ^ +/O2 ^ +/Ob2 ^ +/GL ^ +/MT ^ +/Gy ^ +/Brepro ^ +int128_printer_msvc.cpp ^ +/link ^ +/LTCG ^ +/RELEASE ^ +/DYNAMICBASE ^ +/NXCOMPAT ^ +/SWAPRUN:CD ^ +/SWAPRUN:NET ^ +/OPT:REF ^ +/OPT:ICF ^ +/SUBSYSTEM:CONSOLE +if %errorlevel% neq 0 goto :mk_bad +goto :mk_gud + +:mk_fail +exit /b 1 +goto :mk_end + +:mk_bad +exit /b %errorlevel% +goto :mk_end + +:mk_gud +goto :mk_end + +:mk_outer_bad +@echo Bad. +@exit /b %errorlevel% +@goto :mk_end + +:mk_outer_gud +@echo Gud. +@goto :mk_end + +:mk_end diff --git a/extra/int128_printer_msvc.cpp b/extra/int128_printer_msvc.cpp new file mode 100644 index 00000000..03726c5b --- /dev/null +++ b/extra/int128_printer_msvc.cpp @@ -0,0 +1,162 @@ +#define WIN32_LEAN_AND_MEAN +#include + + +#include + + +#define my_min(a, b) ((b)<(a)?(b):(a)) + + +#define arch_k_arm64ec 1001 +#define arch_k_arm64 1002 +#define arch_k_arm32 1003 +#define arch_k_mips 1004 +#define arch_k_ppc 1005 +#define arch_k_alpha 1006 +#define arch_k_ia64 1007 +#define arch_k_x8664 1008 +#define arch_k_x8632 1009 + +#if 0 +#elif defined _MSC_VER && defined _M_X64 && defined _M_AMD64 && defined _M_ARM64EC +#define arch_v arch_k_arm64ec +#elif defined _MSC_VER && defined _M_ARM64 +#define arch_v arch_k_arm64 +#elif defined _MSC_VER && defined _M_ARM +#define arch_v arch_k_arm32 +#elif defined _MSC_VER && defined _M_MRX000 +#define arch_v arch_k_mips +#elif defined _MSC_VER && (defined _M_PPC || defined _M_MPPC) +#define arch_v arch_k_ppc +#elif defined _MSC_VER && defined _M_ALPHA +#define arch_v arch_k_alpha +#elif defined _MSC_VER && defined _M_IA64 +#define arch_v arch_k_ia64 +#elif defined _MSC_VER && defined _M_X64 && defined _M_AMD64 +#define arch_v arch_k_x8664 +#elif defined _MSC_VER && defined _M_IX86 && !defined _M_I86 && !defined M_I86 +#define arch_v arch_k_x8632 +#elif defined _MSC_VER && (defined _M_I86 || defined M_I86) +#define arch_v arch_k_x8616 +#else +#error unknown arch +#endif + +#define arch_is_arm64ec (arch_v == arch_k_arm64ec) +#define arch_is_arm64 (arch_v == arch_k_arm64 ) +#define arch_is_arm32 (arch_v == arch_k_arm32 ) +#define arch_is_mips (arch_v == arch_k_mips ) +#define arch_is_ppc (arch_v == arch_k_ppc ) +#define arch_is_alpha (arch_v == arch_k_alpha ) +#define arch_is_ia64 (arch_v == arch_k_ia64 ) +#define arch_is_x8664 (arch_v == arch_k_x8664 ) +#define arch_is_x8632 (arch_v == arch_k_x8632 ) + + +struct DEBUGHELPER_s; +typedef struct DEBUGHELPER_s DEBUGHELPER_t; + +struct DEBUGHELPER_s +{ + DWORD m_version; + HRESULT(WINAPI*m_pfn_ReadDebuggeeMemory)(DEBUGHELPER_t* const self, DWORD const addr, DWORD const want, void* const where, DWORD* const got); + DWORDLONG(WINAPI*m_pfn_GetRealAddress)(DEBUGHELPER_t* const self); + HRESULT(WINAPI*m_pfn_ReadDebuggeeMemoryEx)(DEBUGHELPER_t* const self, DWORDLONG const addr, DWORD const want, void* const where, DWORD* const got); + int(WINAPI*m_pfn_GetProcessorType)(DEBUGHELPER_t* const self); +}; +typedef struct DEBUGHELPER_s DEBUGHELPER_t; + +typedef HRESULT(WINAPI*customviewer_t)(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); + + +template +static inline HRESULT formatter_128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) +{ + DWORDLONG addr2; + HRESULT hr; + t integer; + DWORD got; + char* chr; + char txt_n[boost::int128::detail::mini_to_chars_buffer_size]; + size_t len; + size_t n; + size_t i; + + assert(helper); + assert(result || maximum == 0); + + #if arch_is_x8664 + assert(helper->m_version >= 0x00020000); + assert(helper->m_pfn_GetRealAddress); + assert(helper->m_pfn_ReadDebuggeeMemoryEx); + addr2 = helper->m_pfn_GetRealAddress(helper); + hr = helper->m_pfn_ReadDebuggeeMemoryEx(helper, addr2, sizeof(integer), &integer, &got); + #elif arch_is_x8632 + assert(helper->m_pfn_ReadDebuggeeMemory); + hr = helper->m_pfn_ReadDebuggeeMemory(helper, address, sizeof(integer), &integer, &got); + #else + #error unsupported arch + #endif + if(hr != S_OK) + { + return hr; + } + if(got != sizeof(integer)) + { + return E_FAIL; + } + chr = boost::int128::detail::mini_to_chars(txt_n, integer, 10, false); + if(!(chr >= &txt_n[0] && chr < &txt_n[0] + boost::int128::detail::mini_to_chars_buffer_size)) + { + return E_FAIL; + } + if(!(txt_n[boost::int128::detail::mini_to_chars_buffer_size - 1] == '\0')) + { + return E_FAIL; + } + len = ((size_t)((&txt_n[0] + boost::int128::detail::mini_to_chars_buffer_size) - chr)); + if(unicode) + { + n = my_min(len, maximum); + for(i = 0; i != n; ++i) + { + ((wchar_t*)(result))[i] = ((wchar_t)(chr[i])); + } + } + else + { + n = my_min(len, maximum); + for(i = 0; i != n; ++i) + { + result[i] = chr[i]; + } + } + return S_OK; +} + + +extern "C" __declspec(dllexport) HRESULT __stdcall formatter_u128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); +__declspec(dllexport) HRESULT __stdcall formatter_u128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) +{ + return formatter_128_dec(address, helper, base, unicode, result, maximum, reserved); +} + +extern "C" __declspec(dllexport) HRESULT __stdcall formatter_s128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); +__declspec(dllexport) HRESULT __stdcall formatter_s128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) +{ + return formatter_128_dec(address, helper, base, unicode, result, maximum, reserved); +} + +BOOL APIENTRY DllMain(HMODULE const hmodule, DWORD const reason, LPVOID const reserved) +{ + switch(reason) + { + case DLL_PROCESS_ATTACH: break; + case DLL_THREAD_ATTACH : break; + case DLL_THREAD_DETACH : break; + case DLL_PROCESS_DETACH: break; + default : break; + } + return TRUE; +} diff --git a/extra/int128_printer_msvc.natvis b/extra/int128_printer_msvc.natvis new file mode 100644 index 00000000..36d9391f --- /dev/null +++ b/extra/int128_printer_msvc.natvis @@ -0,0 +1,9 @@ + + + + + + + + + From 874e551cc56cc1573c47b52ec074178fc7a83aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Kn=C3=A1pek?= Date: Mon, 27 Jul 2026 00:45:00 +0200 Subject: [PATCH 2/2] Debugger pretty printer for MSVC. Add separate dec and hex views. --- extra/int128_printer_msvc.cpp | 61 ++++++++++++++++++++++++++------ extra/int128_printer_msvc.natvis | 8 +++-- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/extra/int128_printer_msvc.cpp b/extra/int128_printer_msvc.cpp index 03726c5b..daf0c316 100644 --- a/extra/int128_printer_msvc.cpp +++ b/extra/int128_printer_msvc.cpp @@ -1,6 +1,7 @@ #define WIN32_LEAN_AND_MEAN #include +#include #include @@ -70,8 +71,8 @@ typedef struct DEBUGHELPER_s DEBUGHELPER_t; typedef HRESULT(WINAPI*customviewer_t)(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); -template -static inline HRESULT formatter_128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) +template +static inline HRESULT formatter_128(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) { DWORDLONG addr2; HRESULT hr; @@ -80,6 +81,8 @@ static inline HRESULT formatter_128_dec(DWORD const address, DEBUGHELPER_t* cons char* chr; char txt_n[boost::int128::detail::mini_to_chars_buffer_size]; size_t len; + size_t rem; + size_t off; size_t n; size_t i; @@ -106,7 +109,7 @@ static inline HRESULT formatter_128_dec(DWORD const address, DEBUGHELPER_t* cons { return E_FAIL; } - chr = boost::int128::detail::mini_to_chars(txt_n, integer, 10, false); + chr = boost::int128::detail::mini_to_chars(txt_n, integer, numeric_base, false); if(!(chr >= &txt_n[0] && chr < &txt_n[0] + boost::int128::detail::mini_to_chars_buffer_size)) { return E_FAIL; @@ -118,18 +121,42 @@ static inline HRESULT formatter_128_dec(DWORD const address, DEBUGHELPER_t* cons len = ((size_t)((&txt_n[0] + boost::int128::detail::mini_to_chars_buffer_size) - chr)); if(unicode) { - n = my_min(len, maximum); + rem = maximum; + off = 0; + if(numeric_base == 16) + { + off = 2; + if(rem >= off) + { + ((wchar_t*)(result))[0] = L'0'; + ((wchar_t*)(result))[1] = L'x'; + rem -= off; + } + } + n = my_min(len, rem); for(i = 0; i != n; ++i) { - ((wchar_t*)(result))[i] = ((wchar_t)(chr[i])); + ((wchar_t*)(result))[off + i] = ((wchar_t)(chr[i])); } } else { - n = my_min(len, maximum); + rem = maximum; + off = 0; + if(numeric_base == 16) + { + off = 2; + if(rem >= off) + { + result[0] = '0'; + result[1] = 'x'; + rem -= off; + } + } + n = my_min(len, rem); for(i = 0; i != n; ++i) { - result[i] = chr[i]; + result[off + i] = chr[i]; } } return S_OK; @@ -137,15 +164,29 @@ static inline HRESULT formatter_128_dec(DWORD const address, DEBUGHELPER_t* cons extern "C" __declspec(dllexport) HRESULT __stdcall formatter_u128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); +extern "C" __declspec(dllexport) HRESULT __stdcall formatter_u128_hex(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); +extern "C" __declspec(dllexport) HRESULT __stdcall formatter_s128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); +extern "C" __declspec(dllexport) HRESULT __stdcall formatter_s128_hex(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); + + __declspec(dllexport) HRESULT __stdcall formatter_u128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) { - return formatter_128_dec(address, helper, base, unicode, result, maximum, reserved); + return formatter_128(address, helper, base, unicode, result, maximum, reserved); +} + +__declspec(dllexport) HRESULT __stdcall formatter_u128_hex(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) +{ + return formatter_128(address, helper, base, unicode, result, maximum, reserved); } -extern "C" __declspec(dllexport) HRESULT __stdcall formatter_s128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved); __declspec(dllexport) HRESULT __stdcall formatter_s128_dec(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) { - return formatter_128_dec(address, helper, base, unicode, result, maximum, reserved); + return formatter_128(address, helper, base, unicode, result, maximum, reserved); +} + +__declspec(dllexport) HRESULT __stdcall formatter_s128_hex(DWORD const address, DEBUGHELPER_t* const helper, int const base, BOOL const unicode, char* const result, size_t const maximum, DWORD const reserved) +{ + return formatter_128(address, helper, base, unicode, result, maximum, reserved); } BOOL APIENTRY DllMain(HMODULE const hmodule, DWORD const reason, LPVOID const reserved) diff --git a/extra/int128_printer_msvc.natvis b/extra/int128_printer_msvc.natvis index 36d9391f..2fe32f4a 100644 --- a/extra/int128_printer_msvc.natvis +++ b/extra/int128_printer_msvc.natvis @@ -1,9 +1,13 @@ - + + + - + + +