Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cdac] Implement ISOSDacInterface::GetNestedExceptionData #103668

Merged
merged 3 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/design/datacontracts/Exception.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Contract Thread

This contract is for getting information about exceptions in the process.

## APIs of contract

``` csharp
TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException);
```

## Version 1

Data descriptors used:
- `ExceptionInfo`

``` csharp
TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException)
{
if (exception == TargetPointer.Null)
throw new InvalidArgumentException();

nextNestedException = target.ReadPointer(address + /* ExceptionInfo::PreviousNestedInfo offset*/);
TargetPointer thrownObjHandle = target.ReadPointer(address + /* ExceptionInfo::ThrownObject offset */);
return = thrownObjHandle != TargetPointer.Null
? target.ReadPointer(thrownObjHandle)
: TargetPointer.Null;
}
```
21 changes: 0 additions & 21 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ record struct ThreadData (
ThreadStoreData GetThreadStoreData();
ThreadStoreCounts GetThreadCounts();
ThreadData GetThreadData(TargetPointer threadPointer);
TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException);
TargetPointer GetManagedThreadObject(TargetPointer threadPointer);
```

Expand Down Expand Up @@ -116,26 +115,6 @@ ThreadData GetThreadData(TargetPointer threadPointer)
);
}

TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException)
{
if (nestedExceptionPointer == TargetPointer.Null)
{
throw new InvalidArgumentException();
}
if (Target.ReadGlobalInt32("FEATURE_EH_FUNCLETS"))
{
var exData = new ExceptionTrackerBase(Target, nestedExceptionPointer);
nextNestedException = exData.m_pPrevNestedInfo;
return Contracts.GCHandle.GetObject(exData.m_hThrowable);
}
else
{
var exData = new ExInfo(Target, nestedExceptionPointer);
nextNestedException = exData.m_pPrevNestedInfo;
return Contracts.GCHandle.GetObject(exData.m_hThrowable);
}
}

TargetPointer GetManagedThreadObject(TargetPointer threadPointer)
{
var runtimeThread = new Thread(Target, threadPointer);
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,7 @@ class ClrDataAccess

HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData);
HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data);
HRESULT GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException);

BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
#ifndef TARGET_UNIX
Expand Down
47 changes: 36 additions & 11 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3233,7 +3233,6 @@ ClrDataAccess::GetUsefulGlobals(struct DacpUsefulGlobalsData *globalsData)
return hr;
}


HRESULT
ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException)
{
Expand All @@ -3242,26 +3241,52 @@ ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS

SOSDacEnter();

#ifdef FEATURE_EH_FUNCLETS
ExceptionTrackerBase *pExData = PTR_ExceptionTrackerBase(TO_TADDR(exception));
#else
ExInfo *pExData = PTR_ExInfo(TO_TADDR(exception));
#endif // FEATURE_EH_FUNCLETS

if (!pExData)
if (m_cdacSos != NULL)
{
hr = E_INVALIDARG;
// Try the cDAC first - it will return E_NOTIMPL if it doesn't support this method yet. Fall back to the DAC.
hr = m_cdacSos->GetNestedExceptionData(exception, exceptionObject, nextNestedException);
if (FAILED(hr))
{
hr = GetNestedExceptionDataImpl(exception, exceptionObject, nextNestedException);
}
#ifdef _DEBUG
else
{
// Assert that the data is the same as what we get from the DAC.
CLRDATA_ADDRESS exceptionObjectLocal;
CLRDATA_ADDRESS nextNestedExceptionLocal;
HRESULT hrLocal = GetNestedExceptionDataImpl(exception, &exceptionObjectLocal, &nextNestedExceptionLocal);
_ASSERTE(hr == hrLocal);
_ASSERTE(*exceptionObject == exceptionObjectLocal);
_ASSERTE(*nextNestedException == nextNestedExceptionLocal);
}
#endif
}
else
{
*exceptionObject = TO_CDADDR(*PTR_TADDR(pExData->m_hThrowable));
*nextNestedException = PTR_HOST_TO_TADDR(pExData->m_pPrevNestedInfo);
hr = GetNestedExceptionDataImpl(exception, exceptionObject, nextNestedException);
}

SOSDacLeave();
return hr;
}

HRESULT
ClrDataAccess::GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException)
{
#ifdef FEATURE_EH_FUNCLETS
ExceptionTrackerBase *pExData = PTR_ExceptionTrackerBase(TO_TADDR(exception));
#else
ExInfo *pExData = PTR_ExInfo(TO_TADDR(exception));
#endif // FEATURE_EH_FUNCLETS

if (!pExData)
return E_INVALIDARG;

*exceptionObject = TO_CDADDR(*PTR_TADDR(pExData->m_hThrowable));
*nextNestedException = PTR_HOST_TO_TADDR(pExData->m_pPrevNestedInfo);
return S_OK;
}

HRESULT
ClrDataAccess::GetDomainLocalModuleData(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *pLocalModuleData)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/runtimeinfo/contracts.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// cdac-build-tool can take multiple "-c contract_file" arguments
// so to conditionally include contracts, put additional contracts in a separate file
{
"Exception": 1,
"Thread": 1,
"SOSBreakingChangeVersion": 1 // example contract: "runtime exports an SOS breaking change version global"
}

2 changes: 2 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ CDAC_TYPE_END(GCAllocContext)
CDAC_TYPE_BEGIN(ExceptionInfo)
CDAC_TYPE_INDETERMINATE(ExceptionInfo)
#if FEATURE_EH_FUNCLETS
CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExceptionTrackerBase, m_hThrowable))
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExceptionTrackerBase, m_pPrevNestedInfo))
#else
CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExInfo, m_hThrowable))
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo))
#endif
CDAC_TYPE_END(ExceptionInfo)
Expand Down
26 changes: 26 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Exception.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal interface IException : IContract
{
static string IContract.Name { get; } = nameof(Exception);
static IContract IContract.Create(Target target, int version)
{
return version switch
{
1 => new Exception_1(target),
_ => default(Exception),
};
}

public virtual TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) => throw new NotImplementedException();
}

internal readonly struct Exception : IException
{
// Everything throws NotImplementedException
}
23 changes: 23 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Exception_1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal readonly struct Exception_1 : IException
{
private readonly Target _target;

internal Exception_1(Target target)
{
_target = target;
}

TargetPointer IException.GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException)
{
Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd<Data.ExceptionInfo>(exception);
nextNestedException = exceptionInfo.PreviousNestedInfo;
return exceptionInfo.ThrownObject.Object;
}
}
1 change: 1 addition & 0 deletions src/native/managed/cdacreader/src/Contracts/Registry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public Registry(Target target)
_target = target;
}

public IException Exception => GetContract<IException>();
public IThread Thread => GetContract<IThread>();

private T GetContract<T>() where T : IContract
Expand Down
2 changes: 1 addition & 1 deletion src/native/managed/cdacreader/src/Contracts/Thread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)
thread.Frame,
firstNestedException,
thread.TEB,
thread.LastThrownObject,
thread.LastThrownObject.Handle,
GetThreadFromLink(thread.LinkNext));
}

Expand Down
3 changes: 3 additions & 0 deletions src/native/managed/cdacreader/src/Data/ExceptionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ public ExceptionInfo(Target target, TargetPointer address)
Target.TypeInfo type = target.GetTypeInfo(DataType.ExceptionInfo);

PreviousNestedInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(PreviousNestedInfo)].Offset);
ThrownObject = target.ProcessedData.GetOrAdd<ObjectHandle>(
target.ReadPointer(address + (ulong)type.Fields[nameof(ThrownObject)].Offset));
}

public TargetPointer PreviousNestedInfo { get; init; }
public ObjectHandle ThrownObject { get; init; }
}
20 changes: 20 additions & 0 deletions src/native/managed/cdacreader/src/Data/ObjectHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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 ObjectHandle : IData<ObjectHandle>
{
static ObjectHandle IData<ObjectHandle>.Create(Target target, TargetPointer address)
=> new ObjectHandle(target, address);

public ObjectHandle(Target target, TargetPointer address)
{
Handle = address;
if (address != TargetPointer.Null)
Object = target.ReadPointer(address);
}

public TargetPointer Handle { get; init; }
public TargetPointer Object { get; init; } = TargetPointer.Null;
}
5 changes: 3 additions & 2 deletions src/native/managed/cdacreader/src/Data/Thread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public Thread(Target target, TargetPointer address)
TEB = type.Fields.TryGetValue(nameof(TEB), out Target.FieldInfo fieldInfo)
? target.ReadPointer(address + (ulong)fieldInfo.Offset)
: TargetPointer.Null;
LastThrownObject = target.ReadPointer(address + (ulong)type.Fields[nameof(LastThrownObject)].Offset);
LastThrownObject = target.ProcessedData.GetOrAdd<ObjectHandle>(
target.ReadPointer(address + (ulong)type.Fields[nameof(LastThrownObject)].Offset));
LinkNext = target.ReadPointer(address + (ulong)type.Fields[nameof(LinkNext)].Offset);

// Address of the exception tracker - how it should be read depends on EH funclets feature global value
Expand All @@ -41,7 +42,7 @@ public Thread(Target target, TargetPointer address)
public GCAllocContext? AllocContext { get; init; }
public TargetPointer Frame { get; init; }
public TargetPointer TEB { get; init; }
public TargetPointer LastThrownObject { get; init; }
public ObjectHandle LastThrownObject { get; init; }
public TargetPointer LinkNext { get; init; }
public TargetPointer ExceptionTracker { get; init; }
}
19 changes: 18 additions & 1 deletion src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,24 @@ public int GetBreakingChangeVersion()
public unsafe int GetMethodTableTransparencyData(ulong mt, void* data) => HResults.E_NOTIMPL;
public unsafe int GetModule(ulong addr, void** mod) => HResults.E_NOTIMPL;
public unsafe int GetModuleData(ulong moduleAddr, void* data) => HResults.E_NOTIMPL;
public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException) => HResults.E_NOTIMPL;

public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException)
{
try
{
Contracts.IException contract = _target.Contracts.Exception;
TargetPointer exceptionObjectLocal = contract.GetExceptionInfo(exception, out TargetPointer nextNestedExceptionLocal);
*exceptionObject = exceptionObjectLocal;
*nextNestedException = nextNestedExceptionLocal;
}
catch (Exception ex)
{
return ex.HResult;
}

return HResults.S_OK;
}

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 GetObjectStringData(ulong obj, uint count, char* stringData, uint* pNeeded) => HResults.E_NOTIMPL;
Expand Down
Loading