diff --git a/docs/design/datacontracts/Exception.md b/docs/design/datacontracts/Exception.md index 0c37889355455..61455b180d76c 100644 --- a/docs/design/datacontracts/Exception.md +++ b/docs/design/datacontracts/Exception.md @@ -4,25 +4,53 @@ This contract is for getting information about exceptions in the process. ## APIs of contract +```csharp +record struct ExceptionData( + TargetPointer Message, + TargetPointer InnerException, + TargetPointer StackTrace, + TargetPointer WatsonBuckets, + TargetPointer StackTraceString, + TargetPointer RemoteStackTraceString, + int HResult, + int XCode); +``` + ``` csharp -TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException); +TargetPointer GetNestedExceptionInfo(TargetPointer exceptionInfoAddr, out TargetPointer nextNestedExceptionInfo); +ExceptionData GetExceptionData(TargetPointer exceptionAddr) ``` ## Version 1 Data descriptors used: - `ExceptionInfo` +- `Exception` ``` csharp -TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) +TargetPointer GetNestedExceptionInfo(TargetPointer exceptionInfoAddr, out TargetPointer nextNestedExceptionInfo) { - if (exception == TargetPointer.Null) + if (exceptionInfo == TargetPointer.Null) throw new InvalidArgumentException(); - nextNestedException = target.ReadPointer(address + /* ExceptionInfo::PreviousNestedInfo offset*/); - TargetPointer thrownObjHandle = target.ReadPointer(address + /* ExceptionInfo::ThrownObject offset */); + nextNestedException = target.ReadPointer(exceptionInfo + /* ExceptionInfo::PreviousNestedInfo offset*/); + TargetPointer thrownObjHandle = target.ReadPointer(exceptionInfo + /* ExceptionInfo::ThrownObject offset */); return = thrownObjHandle != TargetPointer.Null ? target.ReadPointer(thrownObjHandle) : TargetPointer.Null; } + +ExceptionData GetExceptionData(TargetPointer exceptionAddr) +{ + return new ExceptionData( + target.ReadPointer(exceptionAddr + /* Exception::Message offset */), + target.ReadPointer(exceptionAddr + /* Exception::InnerException offset */), + target.ReadPointer(exceptionAddr + /* Exception::StackTrace offset */), + target.ReadPointer(exceptionAddr + /* Exception::WatsonBuckets offset */), + target.ReadPointer(exceptionAddr + /* Exception::StackTraceString offset */), + target.ReadPointer(exceptionAddr + /* Exception::RemoteStackTraceString offset */), + target.Read(exceptionAddr + /* Exception::HResult offset */), + target.Read(exceptionAddr + /* Exception::XCode offset */), + ); +} ``` diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 6dd0f52fa2e55..5b6b4f03ef3a1 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -5512,6 +5512,7 @@ ClrDataAccess::Initialize(void) // Get SOS interfaces from the cDAC if available. IUnknown* unk = m_cdac.SosInterface(); (void)unk->QueryInterface(__uuidof(ISOSDacInterface), (void**)&m_cdacSos); + (void)unk->QueryInterface(__uuidof(ISOSDacInterface2), (void**)&m_cdacSos2); (void)unk->QueryInterface(__uuidof(ISOSDacInterface9), (void**)&m_cdacSos9); } } diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 2d2aad5bd1f96..f15fb1aafdb33 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1235,6 +1235,7 @@ class ClrDataAccess HRESULT GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException); HRESULT GetMethodTableDataImpl(CLRDATA_ADDRESS mt, struct DacpMethodTableData *data); HRESULT GetMethodTableForEEClassImpl (CLRDATA_ADDRESS eeClassReallyMT, CLRDATA_ADDRESS *value); + HRESULT GetObjectExceptionDataImpl(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data); BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord); #ifndef TARGET_UNIX @@ -1424,6 +1425,7 @@ class ClrDataAccess CDAC m_cdac; NonVMComHolder m_cdacSos; + NonVMComHolder m_cdacSos2; NonVMComHolder m_cdacSos9; #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 291d048eed7ed..2dc737db2e700 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -4647,8 +4647,42 @@ HRESULT ClrDataAccess::GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct Da SOSDacEnter(); - PTR_ExceptionObject pObj = dac_cast(TO_TADDR(objAddr)); + if (m_cdacSos2 != NULL) + { + hr = m_cdacSos2->GetObjectExceptionData(objAddr, data); + if (FAILED(hr)) + { + hr = GetObjectExceptionDataImpl(objAddr, data); + } +#ifdef _DEBUG + else + { + DacpExceptionObjectData dataLocal; + HRESULT hrLocal = GetObjectExceptionDataImpl(objAddr, &dataLocal); + _ASSERTE(hr == hrLocal); + _ASSERTE(data->Message == dataLocal.Message); + _ASSERTE(data->InnerException == dataLocal.InnerException); + _ASSERTE(data->StackTrace == dataLocal.StackTrace); + _ASSERTE(data->WatsonBuckets == dataLocal.WatsonBuckets); + _ASSERTE(data->StackTraceString == dataLocal.StackTraceString); + _ASSERTE(data->RemoteStackTraceString == dataLocal.RemoteStackTraceString); + _ASSERTE(data->HResult == dataLocal.HResult); + _ASSERTE(data->XCode == dataLocal.XCode); + } +#endif + } + else + { + hr = GetObjectExceptionDataImpl(objAddr, data); + } + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetObjectExceptionDataImpl(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data) +{ + PTR_ExceptionObject pObj = dac_cast(TO_TADDR(objAddr)); data->Message = TO_CDADDR(dac_cast(pObj->GetMessage())); data->InnerException = TO_CDADDR(dac_cast(pObj->GetInnerException())); data->StackTrace = TO_CDADDR(dac_cast(pObj->GetStackTraceArrayObject())); @@ -4657,10 +4691,7 @@ HRESULT ClrDataAccess::GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct Da data->RemoteStackTraceString = TO_CDADDR(dac_cast(pObj->GetRemoteStackTraceString())); data->HResult = pObj->GetHResult(); data->XCode = pObj->GetXCode(); - - SOSDacLeave(); - - return hr; + return S_OK; } HRESULT ClrDataAccess::IsRCWDCOMProxy(CLRDATA_ADDRESS rcwAddr, BOOL* isDCOMProxy) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index d1b5443e497b5..a0c71736fd6f9 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -141,6 +141,21 @@ CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Limit, offsetof(gc_alloc_context, alloc_limit)) CDAC_TYPE_END(GCAllocContext) +// Exception + +// Use exact managed type field names for the descriptor as field names often can't change due to binary serialization or implicit diagnostic contracts +CDAC_TYPE_BEGIN(Exception) +CDAC_TYPE_INDETERMINATE(Exception) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _message, cdac_offsets::_message) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _innerException, cdac_offsets::_innerException) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTrace, cdac_offsets::_stackTrace) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _watsonBuckets, cdac_offsets::_watsonBuckets) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTraceString, cdac_offsets::_stackTraceString) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _remoteStackTraceString, cdac_offsets::_remoteStackTraceString) +CDAC_TYPE_FIELD(Exception, /*int32*/, _HResult, cdac_offsets::_HResult) +CDAC_TYPE_FIELD(Exception, /*int32*/, _xcode, cdac_offsets::_xcode) +CDAC_TYPE_END(Exception) + CDAC_TYPE_BEGIN(ExceptionInfo) CDAC_TYPE_INDETERMINATE(ExceptionInfo) #if FEATURE_EH_FUNCLETS @@ -152,6 +167,7 @@ CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(Ex #endif CDAC_TYPE_END(ExceptionInfo) + CDAC_TYPE_BEGIN(GCHandle) CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE)) CDAC_TYPE_END(GCHandle) diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 0c17bcbaa9b0a..4b9df2e18275f 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -2115,7 +2115,7 @@ class LoaderAllocatorObject : public Object INT32 GetSlotsUsed(); void SetSlotsUsed(INT32 newSlotsUsed); #endif // DACCESS_COMPILE - + void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator) { LIMITED_METHOD_CONTRACT; @@ -2355,6 +2355,21 @@ class ExceptionObject : public Object void* _xptrs; INT32 _xcode; INT32 _HResult; + + template friend struct ::cdac_offsets; +}; + +template<> +struct cdac_offsets +{ + static constexpr size_t _message = offsetof(ExceptionObject, _message); + static constexpr size_t _innerException = offsetof(ExceptionObject, _innerException); + static constexpr size_t _stackTrace = offsetof(ExceptionObject, _stackTrace); + static constexpr size_t _watsonBuckets = offsetof(ExceptionObject, _watsonBuckets); + static constexpr size_t _stackTraceString = offsetof(ExceptionObject, _stackTraceString); + static constexpr size_t _remoteStackTraceString = offsetof(ExceptionObject, _remoteStackTraceString); + static constexpr size_t _HResult = offsetof(ExceptionObject, _HResult); + static constexpr size_t _xcode = offsetof(ExceptionObject, _xcode); }; // Defined in Contracts.cs diff --git a/src/native/managed/cdacreader/src/Contracts/Exception.cs b/src/native/managed/cdacreader/src/Contracts/Exception.cs index c06a6984db898..2ad3d10faeb65 100644 --- a/src/native/managed/cdacreader/src/Contracts/Exception.cs +++ b/src/native/managed/cdacreader/src/Contracts/Exception.cs @@ -5,6 +5,16 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; +internal record struct ExceptionData( + TargetPointer Message, + TargetPointer InnerException, + TargetPointer StackTrace, + TargetPointer WatsonBuckets, + TargetPointer StackTraceString, + TargetPointer RemoteStackTraceString, + int HResult, + int XCode); + internal interface IException : IContract { static string IContract.Name { get; } = nameof(Exception); @@ -17,7 +27,8 @@ static IContract IContract.Create(Target target, int version) }; } - public virtual TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) => throw new NotImplementedException(); + public virtual TargetPointer GetNestedExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) => throw new NotImplementedException(); + public virtual ExceptionData GetExceptionData(TargetPointer managedException) => throw new NotImplementedException(); } internal readonly struct Exception : IException diff --git a/src/native/managed/cdacreader/src/Contracts/Exception_1.cs b/src/native/managed/cdacreader/src/Contracts/Exception_1.cs index 57249576719ee..faadb768cc8d8 100644 --- a/src/native/managed/cdacreader/src/Contracts/Exception_1.cs +++ b/src/native/managed/cdacreader/src/Contracts/Exception_1.cs @@ -14,10 +14,24 @@ internal Exception_1(Target target) _target = target; } - TargetPointer IException.GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) + TargetPointer IException.GetNestedExceptionInfo(TargetPointer exceptionInfoAddr, out TargetPointer nextNestedExceptionInfo) { - Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd(exception); - nextNestedException = exceptionInfo.PreviousNestedInfo; + Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd(exceptionInfoAddr); + nextNestedExceptionInfo = exceptionInfo.PreviousNestedInfo; return exceptionInfo.ThrownObject.Object; } + + ExceptionData IException.GetExceptionData(TargetPointer exceptionAddr) + { + Data.Exception exception = _target.ProcessedData.GetOrAdd(exceptionAddr); + return new ExceptionData( + exception.Message, + exception.InnerException, + exception.StackTrace, + exception.WatsonBuckets, + exception.StackTraceString, + exception.RemoteStackTraceString, + exception.HResult, + exception.XCode); + } } diff --git a/src/native/managed/cdacreader/src/Data/Exception.cs b/src/native/managed/cdacreader/src/Data/Exception.cs new file mode 100644 index 0000000000000..8f08dc08810e6 --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/Exception.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class Exception : IData +{ + static Exception IData.Create(Target target, TargetPointer address) + => new Exception(target, address); + + public Exception(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.Exception); + + Message = target.ReadPointer(address + (ulong)type.Fields["_message"].Offset); + InnerException = target.ReadPointer(address + (ulong)type.Fields["_innerException"].Offset); + StackTrace = target.ReadPointer(address + (ulong)type.Fields["_stackTrace"].Offset); + WatsonBuckets = target.ReadPointer(address + (ulong)type.Fields["_watsonBuckets"].Offset); + StackTraceString = target.ReadPointer(address + (ulong)type.Fields["_stackTraceString"].Offset); + RemoteStackTraceString = target.ReadPointer(address + (ulong)type.Fields["_remoteStackTraceString"].Offset); + HResult = target.Read(address + (ulong)type.Fields["_HResult"].Offset); + XCode = target.Read(address + (ulong)type.Fields["_xcode"].Offset); + } + + public TargetPointer Message { get; init; } + public TargetPointer InnerException { get; init; } + public TargetPointer StackTrace { get; init; } + public TargetPointer WatsonBuckets { get; init; } + public TargetPointer StackTraceString { get; init; } + public TargetPointer RemoteStackTraceString { get; init; } + public int HResult { get; init; } + public int XCode { get; init; } +} diff --git a/src/native/managed/cdacreader/src/DataType.cs b/src/native/managed/cdacreader/src/DataType.cs index 88e973c82ee14..3f20bdf7b095b 100644 --- a/src/native/managed/cdacreader/src/DataType.cs +++ b/src/native/managed/cdacreader/src/DataType.cs @@ -23,6 +23,7 @@ public enum DataType Thread, ThreadStore, GCAllocContext, + Exception, ExceptionInfo, RuntimeThreadLocals, Module, diff --git a/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs b/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs index 02b77265554ba..ef4952f64f61c 100644 --- a/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs @@ -334,6 +334,30 @@ internal unsafe partial interface ISOSDacInterface int GetFailedAssemblyDisplayName(ulong assembly, uint count, char* name, uint* pNeeded); }; +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value +internal struct DacpExceptionObjectData +{ + public ulong Message; + public ulong InnerException; + public ulong StackTrace; + public ulong WatsonBuckets; + public ulong StackTraceString; + public ulong RemoteStackTraceString; + public int HResult; + public int XCode; +} +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value + +[GeneratedComInterface] +[Guid("A16026EC-96F4-40BA-87FB-5575986FB7AF")] +internal unsafe partial interface ISOSDacInterface2 +{ + [PreserveSig] + int GetObjectExceptionData(ulong objectAddress, DacpExceptionObjectData* data); + [PreserveSig] + int IsRCWDCOMProxy(ulong rcwAddress, int* inDCOMProxy); +} + [GeneratedComInterface] [Guid("4eca42d8-7e7b-4c8a-a116-7bfbf6929267")] internal partial interface ISOSDacInterface9 diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 3567f3b8571f3..5ebbbd7a7471e 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -17,7 +17,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy; /// corresponding error code. /// [GeneratedComClass] -internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9 +internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2, ISOSDacInterface9 { private readonly Target _target; @@ -204,7 +204,7 @@ public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject try { Contracts.IException contract = _target.Contracts.Exception; - TargetPointer exceptionObjectLocal = contract.GetExceptionInfo(exception, out TargetPointer nextNestedExceptionLocal); + TargetPointer exceptionObjectLocal = contract.GetNestedExceptionInfo(exception, out TargetPointer nextNestedExceptionLocal); *exceptionObject = exceptionObjectLocal; *nextNestedException = nextNestedExceptionLocal; } @@ -218,6 +218,30 @@ public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject public unsafe int GetObjectClassName(ulong obj, uint count, char* className, uint* pNeeded) => HResults.E_NOTIMPL; public unsafe int GetObjectData(ulong objAddr, void* data) => HResults.E_NOTIMPL; + + public unsafe int GetObjectExceptionData(ulong objectAddress, DacpExceptionObjectData* data) + { + try + { + Contracts.IException contract = _target.Contracts.Exception; + Contracts.ExceptionData exceptionData = contract.GetExceptionData(objectAddress); + data->Message = exceptionData.Message; + data->InnerException = exceptionData.InnerException; + data->StackTrace = exceptionData.StackTrace; + data->WatsonBuckets = exceptionData.WatsonBuckets; + data->StackTraceString = exceptionData.StackTraceString; + data->RemoteStackTraceString = exceptionData.RemoteStackTraceString; + data->HResult = exceptionData.HResult; + data->XCode = exceptionData.XCode; + } + catch (Exception ex) + { + return ex.HResult; + } + + return HResults.S_OK; + } + public unsafe int GetObjectStringData(ulong obj, uint count, char* stringData, uint* pNeeded) => HResults.E_NOTIMPL; public unsafe int GetOOMData(ulong oomAddr, void* data) => HResults.E_NOTIMPL; public unsafe int GetOOMStaticData(void* data) => HResults.E_NOTIMPL; @@ -301,6 +325,7 @@ public unsafe int GetThreadStoreData(DacpThreadStoreData* data) public unsafe int GetTLSIndex(uint* pIndex) => HResults.E_NOTIMPL; public unsafe int GetUsefulGlobals(void* data) => HResults.E_NOTIMPL; public unsafe int GetWorkRequestData(ulong addrWorkRequest, void* data) => HResults.E_NOTIMPL; + public unsafe int IsRCWDCOMProxy(ulong rcwAddress, int* inDCOMProxy) => HResults.E_NOTIMPL; public unsafe int TraverseEHInfo(ulong ip, void* pCallback, void* token) => HResults.E_NOTIMPL; public unsafe int TraverseLoaderHeap(ulong loaderHeapAddr, void* pCallback) => HResults.E_NOTIMPL; public unsafe int TraverseModuleMap(int mmt, ulong moduleAddr, void* pCallback, void* token) => HResults.E_NOTIMPL;