1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

[cdac] Implement ISOSDacInterface2::GetObjectExceptionData (#104343)

- 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
This commit is contained in:
Elinor Fung 2024-07-09 14:15:09 -07:00 committed by GitHub
parent 5795e8c056
commit e733c2f227
Signed by: github
GPG key ID: B5690EEEBB952194
12 changed files with 218 additions and 17 deletions

View file

@ -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 */),
);
}
```

View file

@ -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);
}
}

View file

@ -1235,6 +1235,7 @@ public:
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 @@ public:
CDAC m_cdac;
NonVMComHolder<ISOSDacInterface> m_cdacSos;
NonVMComHolder<ISOSDacInterface2> m_cdacSos2;
NonVMComHolder<ISOSDacInterface9> m_cdacSos9;
#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS

View file

@ -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()));
@ -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)

View file

@ -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
@ -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)

View file

@ -2115,7 +2115,7 @@ public:
INT32 GetSlotsUsed();
void SetSlotsUsed(INT32 newSlotsUsed);
#endif // DACCESS_COMPILE
void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator)
{
LIMITED_METHOD_CONTRACT;
@ -2355,6 +2355,21 @@ private:
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

View file

@ -5,6 +5,16 @@ using System;
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 @@ internal interface IException : IContract
};
}
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

View file

@ -14,10 +14,24 @@ internal readonly struct Exception_1 : IException
_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);
}
}

View file

@ -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; }
}

View file

@ -23,6 +23,7 @@ public enum DataType
Thread,
ThreadStore,
GCAllocContext,
Exception,
ExceptionInfo,
RuntimeThreadLocals,
Module,

View file

@ -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

View file

@ -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;
@ -204,7 +204,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9
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 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9
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 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface9
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;