Skip to content

Commit ca5d25c

Browse files
authored
[cdac] Implement ISOSDacInterface::GetNestedExceptionData (#103668)
- Implement `GetExceptionInfo` in `Exception` contract - Implement `ISOSDacInterface::GetNestedExceptionData` in cDAC - Add `ObjectHandle` to cDAC types
1 parent 3ed9deb commit ca5d25c

File tree

14 files changed

+163
-37
lines changed

14 files changed

+163
-37
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Contract Thread
2+
3+
This contract is for getting information about exceptions in the process.
4+
5+
## APIs of contract
6+
7+
``` csharp
8+
TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException);
9+
```
10+
11+
## Version 1
12+
13+
Data descriptors used:
14+
- `ExceptionInfo`
15+
16+
``` csharp
17+
TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException)
18+
{
19+
if (exception == TargetPointer.Null)
20+
throw new InvalidArgumentException();
21+
22+
nextNestedException = target.ReadPointer(address + /* ExceptionInfo::PreviousNestedInfo offset*/);
23+
TargetPointer thrownObjHandle = target.ReadPointer(address + /* ExceptionInfo::ThrownObject offset */);
24+
return = thrownObjHandle != TargetPointer.Null
25+
? target.ReadPointer(thrownObjHandle)
26+
: TargetPointer.Null;
27+
}
28+
```

docs/design/datacontracts/Thread.md

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ record struct ThreadData (
4646
ThreadStoreData GetThreadStoreData();
4747
ThreadStoreCounts GetThreadCounts();
4848
ThreadData GetThreadData(TargetPointer threadPointer);
49-
TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException);
5049
TargetPointer GetManagedThreadObject(TargetPointer threadPointer);
5150
```
5251

@@ -116,26 +115,6 @@ ThreadData GetThreadData(TargetPointer threadPointer)
116115
);
117116
}
118117

119-
TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException)
120-
{
121-
if (nestedExceptionPointer == TargetPointer.Null)
122-
{
123-
throw new InvalidArgumentException();
124-
}
125-
if (Target.ReadGlobalInt32("FEATURE_EH_FUNCLETS"))
126-
{
127-
var exData = new ExceptionTrackerBase(Target, nestedExceptionPointer);
128-
nextNestedException = exData.m_pPrevNestedInfo;
129-
return Contracts.GCHandle.GetObject(exData.m_hThrowable);
130-
}
131-
else
132-
{
133-
var exData = new ExInfo(Target, nestedExceptionPointer);
134-
nextNestedException = exData.m_pPrevNestedInfo;
135-
return Contracts.GCHandle.GetObject(exData.m_hThrowable);
136-
}
137-
}
138-
139118
TargetPointer GetManagedThreadObject(TargetPointer threadPointer)
140119
{
141120
var runtimeThread = new Thread(Target, threadPointer);

src/coreclr/debug/daccess/dacimpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ class ClrDataAccess
12311231

12321232
HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData);
12331233
HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data);
1234+
HRESULT GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException);
12341235

12351236
BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
12361237
#ifndef TARGET_UNIX

src/coreclr/debug/daccess/request.cpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,7 +3239,6 @@ ClrDataAccess::GetUsefulGlobals(struct DacpUsefulGlobalsData *globalsData)
32393239
return hr;
32403240
}
32413241

3242-
32433242
HRESULT
32443243
ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException)
32453244
{
@@ -3248,26 +3247,52 @@ ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS
32483247

32493248
SOSDacEnter();
32503249

3251-
#ifdef FEATURE_EH_FUNCLETS
3252-
ExceptionTrackerBase *pExData = PTR_ExceptionTrackerBase(TO_TADDR(exception));
3253-
#else
3254-
ExInfo *pExData = PTR_ExInfo(TO_TADDR(exception));
3255-
#endif // FEATURE_EH_FUNCLETS
3256-
3257-
if (!pExData)
3250+
if (m_cdacSos != NULL)
32583251
{
3259-
hr = E_INVALIDARG;
3252+
// Try the cDAC first - it will return E_NOTIMPL if it doesn't support this method yet. Fall back to the DAC.
3253+
hr = m_cdacSos->GetNestedExceptionData(exception, exceptionObject, nextNestedException);
3254+
if (FAILED(hr))
3255+
{
3256+
hr = GetNestedExceptionDataImpl(exception, exceptionObject, nextNestedException);
3257+
}
3258+
#ifdef _DEBUG
3259+
else
3260+
{
3261+
// Assert that the data is the same as what we get from the DAC.
3262+
CLRDATA_ADDRESS exceptionObjectLocal;
3263+
CLRDATA_ADDRESS nextNestedExceptionLocal;
3264+
HRESULT hrLocal = GetNestedExceptionDataImpl(exception, &exceptionObjectLocal, &nextNestedExceptionLocal);
3265+
_ASSERTE(hr == hrLocal);
3266+
_ASSERTE(*exceptionObject == exceptionObjectLocal);
3267+
_ASSERTE(*nextNestedException == nextNestedExceptionLocal);
3268+
}
3269+
#endif
32603270
}
32613271
else
32623272
{
3263-
*exceptionObject = TO_CDADDR(*PTR_TADDR(pExData->m_hThrowable));
3264-
*nextNestedException = PTR_HOST_TO_TADDR(pExData->m_pPrevNestedInfo);
3273+
hr = GetNestedExceptionDataImpl(exception, exceptionObject, nextNestedException);
32653274
}
32663275

32673276
SOSDacLeave();
32683277
return hr;
32693278
}
32703279

3280+
HRESULT
3281+
ClrDataAccess::GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException)
3282+
{
3283+
#ifdef FEATURE_EH_FUNCLETS
3284+
ExceptionTrackerBase *pExData = PTR_ExceptionTrackerBase(TO_TADDR(exception));
3285+
#else
3286+
ExInfo *pExData = PTR_ExInfo(TO_TADDR(exception));
3287+
#endif // FEATURE_EH_FUNCLETS
3288+
3289+
if (!pExData)
3290+
return E_INVALIDARG;
3291+
3292+
*exceptionObject = TO_CDADDR(*PTR_TADDR(pExData->m_hThrowable));
3293+
*nextNestedException = PTR_HOST_TO_TADDR(pExData->m_pPrevNestedInfo);
3294+
return S_OK;
3295+
}
32713296

32723297
HRESULT
32733298
ClrDataAccess::GetDomainLocalModuleData(CLRDATA_ADDRESS addr, struct DacpDomainLocalModuleData *pLocalModuleData)

src/coreclr/debug/runtimeinfo/contracts.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// cdac-build-tool can take multiple "-c contract_file" arguments
1010
// so to conditionally include contracts, put additional contracts in a separate file
1111
{
12+
"Exception": 1,
1213
"Thread": 1,
1314
"SOSBreakingChangeVersion": 1 // example contract: "runtime exports an SOS breaking change version global"
1415
}
15-

src/coreclr/debug/runtimeinfo/datadescriptor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,10 @@ CDAC_TYPE_END(GCAllocContext)
144144
CDAC_TYPE_BEGIN(ExceptionInfo)
145145
CDAC_TYPE_INDETERMINATE(ExceptionInfo)
146146
#if FEATURE_EH_FUNCLETS
147+
CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExceptionTrackerBase, m_hThrowable))
147148
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExceptionTrackerBase, m_pPrevNestedInfo))
148149
#else
150+
CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExInfo, m_hThrowable))
149151
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo))
150152
#endif
151153
CDAC_TYPE_END(ExceptionInfo)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
7+
8+
internal interface IException : IContract
9+
{
10+
static string IContract.Name { get; } = nameof(Exception);
11+
static IContract IContract.Create(Target target, int version)
12+
{
13+
return version switch
14+
{
15+
1 => new Exception_1(target),
16+
_ => default(Exception),
17+
};
18+
}
19+
20+
public virtual TargetPointer GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) => throw new NotImplementedException();
21+
}
22+
23+
internal readonly struct Exception : IException
24+
{
25+
// Everything throws NotImplementedException
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
7+
8+
internal readonly struct Exception_1 : IException
9+
{
10+
private readonly Target _target;
11+
12+
internal Exception_1(Target target)
13+
{
14+
_target = target;
15+
}
16+
17+
TargetPointer IException.GetExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException)
18+
{
19+
Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd<Data.ExceptionInfo>(exception);
20+
nextNestedException = exceptionInfo.PreviousNestedInfo;
21+
return exceptionInfo.ThrownObject.Object;
22+
}
23+
}

src/native/managed/cdacreader/src/Contracts/Registry.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public Registry(Target target)
1818
_target = target;
1919
}
2020

21+
public IException Exception => GetContract<IException>();
2122
public IThread Thread => GetContract<IThread>();
2223

2324
private T GetContract<T>() where T : IContract

src/native/managed/cdacreader/src/Contracts/Thread.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)
127127
thread.Frame,
128128
firstNestedException,
129129
thread.TEB,
130-
thread.LastThrownObject,
130+
thread.LastThrownObject.Handle,
131131
GetThreadFromLink(thread.LinkNext));
132132
}
133133

src/native/managed/cdacreader/src/Data/ExceptionInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ public ExceptionInfo(Target target, TargetPointer address)
1313
Target.TypeInfo type = target.GetTypeInfo(DataType.ExceptionInfo);
1414

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

1820
public TargetPointer PreviousNestedInfo { get; init; }
21+
public ObjectHandle ThrownObject { get; init; }
1922
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.Diagnostics.DataContractReader.Data;
5+
6+
internal sealed class ObjectHandle : IData<ObjectHandle>
7+
{
8+
static ObjectHandle IData<ObjectHandle>.Create(Target target, TargetPointer address)
9+
=> new ObjectHandle(target, address);
10+
11+
public ObjectHandle(Target target, TargetPointer address)
12+
{
13+
Handle = address;
14+
if (address != TargetPointer.Null)
15+
Object = target.ReadPointer(address);
16+
}
17+
18+
public TargetPointer Handle { get; init; }
19+
public TargetPointer Object { get; init; } = TargetPointer.Null;
20+
}

src/native/managed/cdacreader/src/Data/Thread.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public Thread(Target target, TargetPointer address)
2727
TEB = type.Fields.TryGetValue(nameof(TEB), out Target.FieldInfo fieldInfo)
2828
? target.ReadPointer(address + (ulong)fieldInfo.Offset)
2929
: TargetPointer.Null;
30-
LastThrownObject = target.ReadPointer(address + (ulong)type.Fields[nameof(LastThrownObject)].Offset);
30+
LastThrownObject = target.ProcessedData.GetOrAdd<ObjectHandle>(
31+
target.ReadPointer(address + (ulong)type.Fields[nameof(LastThrownObject)].Offset));
3132
LinkNext = target.ReadPointer(address + (ulong)type.Fields[nameof(LinkNext)].Offset);
3233

3334
// Address of the exception tracker - how it should be read depends on EH funclets feature global value
@@ -41,7 +42,7 @@ public Thread(Target target, TargetPointer address)
4142
public RuntimeThreadLocals? RuntimeThreadLocals { get; init; }
4243
public TargetPointer Frame { get; init; }
4344
public TargetPointer TEB { get; init; }
44-
public TargetPointer LastThrownObject { get; init; }
45+
public ObjectHandle LastThrownObject { get; init; }
4546
public TargetPointer LinkNext { get; init; }
4647
public TargetPointer ExceptionTracker { get; init; }
4748
}

src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,24 @@ public int GetBreakingChangeVersion()
8989
public unsafe int GetMethodTableTransparencyData(ulong mt, void* data) => HResults.E_NOTIMPL;
9090
public unsafe int GetModule(ulong addr, void** mod) => HResults.E_NOTIMPL;
9191
public unsafe int GetModuleData(ulong moduleAddr, void* data) => HResults.E_NOTIMPL;
92-
public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException) => HResults.E_NOTIMPL;
92+
93+
public unsafe int GetNestedExceptionData(ulong exception, ulong* exceptionObject, ulong* nextNestedException)
94+
{
95+
try
96+
{
97+
Contracts.IException contract = _target.Contracts.Exception;
98+
TargetPointer exceptionObjectLocal = contract.GetExceptionInfo(exception, out TargetPointer nextNestedExceptionLocal);
99+
*exceptionObject = exceptionObjectLocal;
100+
*nextNestedException = nextNestedExceptionLocal;
101+
}
102+
catch (Exception ex)
103+
{
104+
return ex.HResult;
105+
}
106+
107+
return HResults.S_OK;
108+
}
109+
93110
public unsafe int GetObjectClassName(ulong obj, uint count, char* className, uint* pNeeded) => HResults.E_NOTIMPL;
94111
public unsafe int GetObjectData(ulong objAddr, void* data) => HResults.E_NOTIMPL;
95112
public unsafe int GetObjectStringData(ulong obj, uint count, char* stringData, uint* pNeeded) => HResults.E_NOTIMPL;

0 commit comments

Comments
 (0)