Remove target ifdefs from dbi - #130935
#130935Remove target ifdefs from dbi#130935rcj1 wants to merge 29 commits intodotnet:maindotnet/runtime:mainfrom rcj1:target-info-dbi-api-usagercj1/runtime:target-info-dbi-api-usageCopy head branch name to clipboard
Conversation
Introduces IDacDbiInterface::GetTargetInfo, which reports the target's processor architecture and OS family, along with the TargetInfo/TargetArchitecture/TargetOperatingSystem types. Includes the native DAC implementation and the managed cDAC implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new DBI API (GetTargetInfo) to query target architecture / OS (and pointer size on the native side) and replaces many TARGET_* conditionals in the RS/shim code with runtime target queries (or HOST_* where appropriate).
Changes:
- Add
IDacDbiInterface::GetTargetInfo(IDL + headers + DAC implementation) and a managed implementation in cDAC Legacy. - Replace target-ifdef-based logic in RS/shim with
GetTargetInfo-based branching (plus a fewTARGET_*→HOST_*fixes). - Remove the DAC-side platform mismatch check that compared
ICorDebugDataTarget::GetPlatformto the DAC build platform.
Show a summary per file
| File | Description |
|---|---|
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs | Adds managed GetTargetInfo and managed TargetInfo/enums. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs | Implements managed GetTargetInfo via IRuntimeInfo. |
| src/coreclr/inc/dacdbi.idl | Adds native TargetInfo struct and GetTargetInfo method to the IDL. |
| src/coreclr/debug/inc/dacdbiinterface.h | Adds TargetInfo (arch/os/pointerSize) and GetTargetInfo to the native interface definition. |
| src/coreclr/debug/di/valuehome.cpp | Uses target info to replace TARGET_64BIT conditionals for 8-byte register values; removes some target-ifdef blocks. |
| src/coreclr/debug/di/shimstackwalk.cpp | Uses target info instead of TARGET_ARM* ifdefs for SP adjustment. |
| src/coreclr/debug/di/shimremotedatatarget.cpp | Implements GetPlatform based on queried target info; switches an ARM64 proc-mem workaround to HOST_ARM64. |
| src/coreclr/debug/di/shimprocess.cpp | Wires the ShimProcess pointer into the data target instance. |
| src/coreclr/debug/di/shimpriv.h | Adds a forward declaration needed by new target-info usage. |
| src/coreclr/debug/di/shimlocaldatatarget.cpp | Converts several TARGET_* checks to HOST_* for local data target behavior. |
| src/coreclr/debug/di/shimdatatarget.h | Adds ShimProcess backpointer plumbing for shim data targets. |
| src/coreclr/debug/di/rstype.cpp | Makes CordbType::RequiresAlign8 unconditional (was FEATURE_64BIT_ALIGNMENT-guarded). |
| src/coreclr/debug/di/rsthread.cpp | Replaces many TARGET_* ifdefs with target-info-driven logic for frame matching, hijacks, FP regs, arg walking, etc. |
| src/coreclr/debug/di/rspriv.h | Adds declarations/fields for target info caching; adjusts some host/target ifdefs; tweaks alignment helper. |
| src/coreclr/debug/di/rsmain.cpp | Converts TARGET_UNIX to HOST_UNIX in privilege code. |
| src/coreclr/debug/di/process.cpp | Adds cached CordbProcess::GetTargetInfo that calls through to the DAC; converts several TARGET_* to HOST_*. |
| src/coreclr/debug/di/platformspecific.cpp | Switches platform-specific includes to HOST_*. |
| src/coreclr/debug/di/module.cpp | Converts a couple TARGET_UNIX checks to HOST_UNIX. |
| src/coreclr/debug/di/cordb.cpp | Converts TARGET_UNIX to HOST_UNIX for local-debugging support gating. |
| src/coreclr/debug/daccess/dacdbiimpl.h | Declares DacDbiInterfaceImpl::GetTargetInfo. |
| src/coreclr/debug/daccess/dacdbiimpl.cpp | Implements DacDbiInterfaceImpl::GetTargetInfo using target build macros and pointer-size macro. |
| src/coreclr/debug/daccess/daccess.cpp | Removes the DAC-side host/target platform mismatch verification block. |
Copilot's findings
- Files reviewed: 22/22 changed files
- Comments generated: 5
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/coreclr/debug/daccess/daccess.cpp:5124
- ClrDataAccess::Initialize removed the platform-mismatch validation (GetPlatform + CORDBG_E_INCOMPATIBLE_PLATFORMS). There doesn’t appear to be a replacement check in daccess/, so a mismatched DAC/target combination could proceed further and fail later with harder-to-diagnose memory read errors/crashes instead of a clear incompatibility HRESULT.
HRESULT
ClrDataAccess::Initialize(void)
{
HRESULT hr;
CLRDATA_ADDRESS base = { 0 };
// Get the current DLL base for mscorwks globals.
// In case of multiple-CLRs, there may be multiple dlls named "mscorwks".
// code:OpenVirtualProcess can take the base address (clrInstanceId) to select exactly
// which CLR to is being target. If so, m_globalBase will already be set.
//
if (m_globalBase == 0)
{
// Caller didn't specify which CLR to debug, we should be using a legacy data target.
if (m_pLegacyTarget == NULL)
{
DacError(E_INVALIDARG);
UNREACHABLE();
}
ReleaseHolder<ICLRRuntimeLocator> pRuntimeLocator(NULL);
if (m_pLegacyTarget->QueryInterface(__uuidof(ICLRRuntimeLocator), (void**)&pRuntimeLocator) != S_OK || pRuntimeLocator->GetRuntimeBase(&base) != S_OK)
{
IfFailRet(m_pLegacyTarget->GetImageBase(TARGET_MAIN_CLR_DLL_NAME_W, &base));
}
m_globalBase = TO_TADDR(base);
}
- Files reviewed: 22/22 changed files
- Comments generated: 1
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs:5764
- Consider initializing the output struct to a known default value before populating individual fields. As written, if an exception occurs after setting
Archbut beforeOS/PointerSize, callers could observe partially-updated data even when a failure HRESULT is returned.
if (pTargetInfo is null)
throw new ArgumentException("Output pointer cannot be null.", nameof(pTargetInfo));
Contracts.IRuntimeInfo runtimeInfo = _target.Contracts.RuntimeInfo;
pTargetInfo->Arch = runtimeInfo.GetTargetArchitecture() switch
- Files reviewed: 18/18 changed files
- Comments generated: 3
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/coreclr/debug/di/rspriv.h:11400
- AlignAddressForType now unconditionally calls CordbType::RequiresAlign8. On targets where FEATURE_64BIT_ALIGNMENT is not enabled, the DAC side (DacDbiInterfaceImpl::RequiresAlign8) throws E_NOTIMPL, causing IfFailThrow here to throw unexpectedly during varargs argument walking. This helper should be a no-op unless the target architecture actually uses FEATURE_64BIT_ALIGNMENT (currently ARM and WASM).
inline void AlignAddressForType(CordbType* pArgType, CORDB_ADDRESS& argBase)
{
BOOL align = FALSE;
IfFailThrow(pArgType->RequiresAlign8(&align));
- Files reviewed: 18/18 changed files
- Comments generated: 2
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/coreclr/debug/di/process.cpp:8174
GetTargetOpcodeSizedoesn’t handlekArchWasm, even thoughIDacDbiInterface::TargetArchitectureincludes it anddebug/inc/wasm/primitives.hdefines a 1-byte breakpoint instruction. As-is, any path that needs opcode sizing on Wasm will hit the default case and returnE_NOTIMPL.
switch (targetInfo.arch)
{
case IDacDbiInterface::kArchX86:
case IDacDbiInterface::kArchAMD64:
*pcbSize = 1;
break;
- Files reviewed: 18/18 changed files
- Comments generated: 0 new
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (2)
src/coreclr/debug/di/cordb.cpp:474
- Same issue as the corresponding
DbiGetThreadContextguard above: Unix builds should keep the local-debugging exclusion (previously viaSUPPORT_LOCAL_DEBUGGING) so this doesn't try to use PALSetThreadContextfor remote scenarios.
{
#if !defined(HOST_ARM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || defined(__ANDROID__)
_ASSERTE(!"Can't use local GetThreadContext remotely, this needed to go to datatarget");
return FALSE;
src/coreclr/debug/di/rspriv.h:11400
AlignAddressForTypeused to be a true no-op except on platforms that need 8-byte argument alignment (historically ARM32 viaFEATURE_64BIT_ALIGNMENT). After this change it calls into DAC for every non-x86 target, which adds avoidable overhead (and can now throw) on architectures where this alignment adjustment is unnecessary. Consider gating the alignment computation on the target architecture so the helper stays a no-op for the common case.
BOOL align = FALSE;
IfFailThrow(pArgType->RequiresAlign8(&align));
if (align)
argBase = ALIGN_ADDRESS(argBase, 8);
- Files reviewed: 18/18 changed files
- Comments generated: 1
| // if we aren't local debugging this isn't going to work | ||
| #if !defined(HOST_ARM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING | ||
| #if !defined(HOST_ARM) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || defined(__ANDROID__) | ||
| _ASSERTE(!"Can't use local GetThreadContext remotely, this needed to go to datatarget"); | ||
| return FALSE; |
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (3)
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs:5799
- GetTargetInfo writes to an output pointer but does not initialize the output struct on failure paths (exceptions are caught and converted to HRESULT). This can leave the caller with partially-initialized data if an exception occurs mid-way; initialize the output to default once the pointer is validated.
if (pTargetInfo is null)
throw new ArgumentException("Output pointer cannot be null.", nameof(pTargetInfo));
Contracts.IRuntimeInfo runtimeInfo = _target.Contracts.RuntimeInfo;
src/coreclr/debug/di/valuehome.cpp:212
- In release builds, the pointer-size invariant in the 8-byte case is only asserted, so if this path is ever hit on a non-64-bit target it will silently proceed and truncate/produce incorrect results. Consider turning the pointer-size check into a real failure (and keep the assert as a debug aid).
IfFailThrow(m_pFrame->GetProcess()->GetTargetInfo(&targetInfo));
_ASSERTE(targetInfo.pointerSize == 8);
extendedVal = (SSIZE_T) *(INT64*)newValue.StartAddress();
break;
src/coreclr/debug/di/valuehome.cpp:235
- In release builds, the pointer-size invariant in the 8-byte case is only asserted, so if this path is ever hit on a non-64-bit target it will silently proceed and truncate/produce incorrect results. Consider turning the pointer-size check into a real failure (and keep the assert as a debug aid).
IfFailThrow(m_pFrame->GetProcess()->GetTargetInfo(&targetInfo));
_ASSERTE(targetInfo.pointerSize == 8);
extendedVal = (SIZE_T) *(UINT64*)newValue.StartAddress();
break;
- Files reviewed: 18/18 changed files
- Comments generated: 0 new
noahfalk
left a comment
There was a problem hiding this comment.
The change looks reasonable to me - I think my broader concern now is timing and the risk to the .NET 11 release making changes of this scope that could impact non-cDAC scenarios. We are now less than 2 weeks from the RC1 snap. If there were bugs in here that our internal testing didn't catch we might not hear about it until the bits are shipped in mid-September at which point the bug fix bar + schedule constraints probably mean it stays broken at release and servicing fixes go out early 2027.
I'm going to propose that we should start holding these larger DBI refactors until we've set up a place where we can build and ship cDAC/DBI out-of-band from the runtime. This avoids the risk that we accidentally break our non-cDAC debugging scenarios and uncouples the non-contract portion of the work from the .NET 11 runtime schedule. We should probably discuss this a bit more as a team to decide how we proceed.
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs:5832
- In the DEBUG parity check, the legacy call always uses a non-null local buffer. If this method returns E_INVALIDARG due to a null pTargetInfo, hrLocal will be S_OK (because &targetInfoLocal is non-null) and Debug.ValidateHResult will assert even though both implementations would return E_INVALIDARG for a null output pointer.
Consider passing a null pointer through to the legacy implementation when pTargetInfo is null, while still using a local buffer for the successful case, so the parity check compares like-for-like HRESULTs.
if (_legacy is not null)
{
TargetInfo targetInfoLocal;
int hrLocal = _legacy.GetTargetInfo(&targetInfoLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
- Files reviewed: 18/18 changed files
- Comments generated: 0 new
Target ifdefs in DBI generally fall into four categories: