Skip to content

Commit

Permalink
[cdac] Implement ISOSDacInterface2::GetObjectExceptionData (#104343)
Browse files Browse the repository at this point in the history
- Make cDac implement `ISOSDacInterface2`
- Add `Exception` (managed type) to data descriptor
- Add `GetExceptionData` to `Exception` contract which gets all the data that SOS-DAC API uses
  • Loading branch information
elinor-fung committed Jul 9, 2024
1 parent 5795e8c commit e733c2f
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 17 deletions.
38 changes: 33 additions & 5 deletions docs/design/datacontracts/Exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(exceptionAddr + /* Exception::HResult offset */),
target.Read<int>(exceptionAddr + /* Exception::XCode offset */),
);
}
```
1 change: 1 addition & 0 deletions src/coreclr/debug/daccess/daccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1424,6 +1425,7 @@ class ClrDataAccess

CDAC m_cdac;
NonVMComHolder<ISOSDacInterface> m_cdacSos;
NonVMComHolder<ISOSDacInterface2> m_cdacSos2;
NonVMComHolder<ISOSDacInterface9> m_cdacSos9;

#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
Expand Down
41 changes: 36 additions & 5 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4647,8 +4647,42 @@ HRESULT ClrDataAccess::GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct Da

SOSDacEnter();

PTR_ExceptionObject pObj = dac_cast<PTR_ExceptionObject>(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<PTR_ExceptionObject>(TO_TADDR(objAddr));
data->Message = TO_CDADDR(dac_cast<TADDR>(pObj->GetMessage()));
data->InnerException = TO_CDADDR(dac_cast<TADDR>(pObj->GetInnerException()));
data->StackTrace = TO_CDADDR(dac_cast<TADDR>(pObj->GetStackTraceArrayObject()));
Expand All @@ -4657,10 +4691,7 @@ HRESULT ClrDataAccess::GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct Da
data->RemoteStackTraceString = TO_CDADDR(dac_cast<TADDR>(pObj->GetRemoteStackTraceString()));
data->HResult = pObj->GetHResult();
data->XCode = pObj->GetXCode();

SOSDacLeave();

return hr;
return S_OK;
}

HRESULT ClrDataAccess::IsRCWDCOMProxy(CLRDATA_ADDRESS rcwAddr, BOOL* isDCOMProxy)
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExceptionObject>::_message)
CDAC_TYPE_FIELD(Exception, /*pointer*/, _innerException, cdac_offsets<ExceptionObject>::_innerException)
CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTrace, cdac_offsets<ExceptionObject>::_stackTrace)
CDAC_TYPE_FIELD(Exception, /*pointer*/, _watsonBuckets, cdac_offsets<ExceptionObject>::_watsonBuckets)
CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTraceString, cdac_offsets<ExceptionObject>::_stackTraceString)
CDAC_TYPE_FIELD(Exception, /*pointer*/, _remoteStackTraceString, cdac_offsets<ExceptionObject>::_remoteStackTraceString)
CDAC_TYPE_FIELD(Exception, /*int32*/, _HResult, cdac_offsets<ExceptionObject>::_HResult)
CDAC_TYPE_FIELD(Exception, /*int32*/, _xcode, cdac_offsets<ExceptionObject>::_xcode)
CDAC_TYPE_END(Exception)

CDAC_TYPE_BEGIN(ExceptionInfo)
CDAC_TYPE_INDETERMINATE(ExceptionInfo)
#if FEATURE_EH_FUNCLETS
Expand All @@ -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)
Expand Down
17 changes: 16 additions & 1 deletion src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -2115,7 +2115,7 @@ class LoaderAllocatorObject : public Object
INT32 GetSlotsUsed();
void SetSlotsUsed(INT32 newSlotsUsed);
#endif // DACCESS_COMPILE

void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator)
{
LIMITED_METHOD_CONTRACT;
Expand Down Expand Up @@ -2355,6 +2355,21 @@ class ExceptionObject : public Object
void* _xptrs;
INT32 _xcode;
INT32 _HResult;

template<typename T> friend struct ::cdac_offsets;
};

template<>
struct cdac_offsets<ExceptionObject>
{
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
Expand Down
13 changes: 12 additions & 1 deletion src/native/managed/cdacreader/src/Contracts/Exception.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
20 changes: 17 additions & 3 deletions src/native/managed/cdacreader/src/Contracts/Exception_1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Data.ExceptionInfo>(exception);
nextNestedException = exceptionInfo.PreviousNestedInfo;
Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd<Data.ExceptionInfo>(exceptionInfoAddr);
nextNestedExceptionInfo = exceptionInfo.PreviousNestedInfo;
return exceptionInfo.ThrownObject.Object;
}

ExceptionData IException.GetExceptionData(TargetPointer exceptionAddr)
{
Data.Exception exception = _target.ProcessedData.GetOrAdd<Data.Exception>(exceptionAddr);
return new ExceptionData(
exception.Message,
exception.InnerException,
exception.StackTrace,
exception.WatsonBuckets,
exception.StackTraceString,
exception.RemoteStackTraceString,
exception.HResult,
exception.XCode);
}
}
33 changes: 33 additions & 0 deletions src/native/managed/cdacreader/src/Data/Exception.cs
Original file line number Diff line number Diff line change
@@ -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<Exception>
{
static Exception IData<Exception>.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<int>(address + (ulong)type.Fields["_HResult"].Offset);
XCode = target.Read<int>(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; }
}
1 change: 1 addition & 0 deletions src/native/managed/cdacreader/src/DataType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public enum DataType
Thread,
ThreadStore,
GCAllocContext,
Exception,
ExceptionInfo,
RuntimeThreadLocals,
Module,
Expand Down
24 changes: 24 additions & 0 deletions src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 27 additions & 2 deletions src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy;
/// corresponding error code.
/// </remarks>
[GeneratedComClass]
internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9
internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2, ISOSDacInterface9
{
private readonly Target _target;

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit e733c2f

Please sign in to comment.