Skip to content
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
30 changes: 30 additions & 0 deletions docs/design/datacontracts/Debugger.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void SetSendExceptionsOutsideOfJMC(bool sendExceptionsOutsideOfJMC);
TargetPointer GetDebuggerControlBlockAddress();
void EnableGCNotificationEvents(bool fEnable);
HijackKind GetHijackKind(TargetCodePointer controlPC);
TargetPointer PrepareExceptionHijack(byte[] context, TargetPointer vmThread, byte[]? exceptionRecord, int reason, TargetPointer userData)
```

## Version 1
Expand Down Expand Up @@ -188,4 +189,33 @@ HijackKind GetHijackKind(TargetCodePointer controlPC)
}
return HijackKind.None;
}

private TargetPointer GetHijackAddress()
{
// Returns the start address of the unhandled-exception hijack function
// (index UnhandledExceptionHijackIndex == 0 in the RgHijackFunction array).
if (!TryGetDebuggerAddress(out TargetPointer debuggerAddress))
return TargetPointer.Null;

TargetPointer rgHijack = target.ReadPointer(
debuggerAddress + /* Debugger::RgHijackFunction offset */);
if (rgHijack == TargetPointer.Null)
return TargetPointer.Null;

uint maxHijackFunctions = target.ReadGlobal<uint>("MaxHijackFunctions");
if (UnhandledExceptionHijackIndex >= maxHijackFunctions)
return TargetPointer.Null;

uint stride = // Size of one MemoryRange entry
TargetPointer entryAddress = rgHijack + (ulong)(UnhandledExceptionHijackIndex * stride);
return target.ReadPointer(entryAddress + /* MemoryRange::StartAddress offset */);
}

TargetPointer PrepareExceptionHijack(byte[] context, TargetPointer vmThread, byte[]? exceptionRecord, int reason, TargetPointer userData)
{
// Finds hijack address via GetHijackAddress.
// Writes the exception record and context into the target stack as necessary.
// Places the arguments to ExceptionHijackWorker as dictated by the native ABI.
// Mutates stack pointer and context as necessary.
}
```
20 changes: 20 additions & 0 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ record struct ThreadData (
bool IsInteropDebuggingHijacked;
TargetPointer DebuggerFilterContext;
TargetPointer GCFrame;
bool IsExceptionInProgress;
TargetPointer OSExceptionRecord;
TargetPointer OSExceptionContextRecord;
);
```

Expand Down Expand Up @@ -103,6 +106,8 @@ The contract additionally depends on these data descriptors
| `ExceptionInfo` | `PreviousNestedInfo` | Pointer to previous nested exception info |
| `ExceptionInfo` | `ThrownObjectHandle` | Pointer to exception object handle |
| `ExceptionInfo` | `ExceptionWatsonBucketTrackerBuckets` | Pointer to Watson unhandled buckets on non-Unix |
| `ExceptionInfo` | `ExceptionRecord` | Pointer to the OS `EXCEPTION_RECORD` the OS dispatcher pushed for this exception |
| `ExceptionInfo` | `ContextRecord` | Pointer to the OS `CONTEXT` the OS dispatcher pushed for this exception |
| `GCAllocContext` | `Pointer` | GC allocation pointer |
| `GCAllocContext` | `Limit` | Allocation limit pointer |
| `GCAllocContext` | `AllocBytes` | Number of bytes allocated on SOH by this context |
Expand Down Expand Up @@ -228,6 +233,18 @@ ThreadData GetThreadData(TargetPointer address)
}

ulong threadLinkoffset = ... // offset from Thread data descriptor

// The OS-pushed EXCEPTION_RECORD / CONTEXT are reachable through the current
// exception tracker (ExInfo). When there is no exception in progress the tracker
// pointer is null.
bool isExceptionInProgress = exceptionTrackerAddr != TargetPointer.Null;
TargetPointer osExceptionRecord = isExceptionInProgress
? target.ReadPointer(exceptionTrackerAddr + /* ExceptionInfo::ExceptionRecord offset */)
: TargetPointer.Null;
TargetPointer osExceptionContextRecord = isExceptionInProgress
? target.ReadPointer(exceptionTrackerAddr + /* ExceptionInfo::ContextRecord offset */)
: TargetPointer.Null;

return new ThreadData(
Id: target.Read<uint>(address + /* Thread::Id offset */),
OSId: target.ReadNUInt(address + /* Thread::OSId offset */),
Expand All @@ -240,6 +257,9 @@ ThreadData GetThreadData(TargetPointer address)
FirstNestedException : firstNestedException,
NextThread: target.ReadPointer(address + /* Thread::LinkNext offset */) - threadLinkOffset;
GCFrame: target.ReadPointer(address + /* Thread::GCFrame offset */),
IsExceptionInProgress: isExceptionInProgress,
OSExceptionRecord: osExceptionRecord,
OSExceptionContextRecord: osExceptionContextRecord,
);
}

Expand Down
17 changes: 16 additions & 1 deletion src/coreclr/debug/daccess/cdac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@ namespace
return S_OK;
}

int WriteThreadContext(uint32_t threadId, uint32_t contextSize, const uint8_t* contextBuffer, void* context)
{
ICorDebugDataTarget* target = reinterpret_cast<ICorDebugDataTarget*>(context);
ICorDebugMutableDataTarget* mutableTarget = nullptr;
HRESULT hr = target->QueryInterface(__uuidof(ICorDebugMutableDataTarget), (void**)&mutableTarget);
if (FAILED(hr))
return hr;
hr = mutableTarget->SetThreadContext(threadId, contextSize, contextBuffer);
mutableTarget->Release();
if (FAILED(hr))
return hr;

return S_OK;
}

int AllocVirtualCallback(uint32_t size, uint64_t* allocatedAddress, void* context)
{
ICorDebugDataTarget* target = reinterpret_cast<ICorDebugDataTarget*>(context);
Expand Down Expand Up @@ -119,7 +134,7 @@ CDAC CDAC::Create(uint64_t descriptorAddr, ICorDebugDataTarget* target, IUnknown
target2->Release();

intptr_t handle;
if (init(descriptorAddr, &ReadFromTargetCallback, &WriteToTargetCallback, &ReadThreadContext, allocCallback, target, &handle) != 0)
if (init(descriptorAddr, &ReadFromTargetCallback, &WriteToTargetCallback, &ReadThreadContext, &WriteThreadContext, allocCallback, target, &handle) != 0)
{
::FreeLibrary(cdacLib);
return {};
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/cdacstress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,8 @@ void CdacStressPolicy::Initialize()
// Get the address of the contract descriptor in our own process
uint64_t descriptorAddr = reinterpret_cast<uint64_t>(&DotNetRuntimeContractDescriptor);

// Initialize the cDAC reader with in-process callbacks (no alloc_virtual for in-process stress)
if (init(descriptorAddr, &ReadFromTargetCallback, &WriteToTargetCallback, &ReadThreadContextCallback, nullptr, nullptr, &s_cdacHandle) != 0)
// Initialize the cDAC reader with in-process callbacks (no write_thread_context or alloc_virtual for in-process stress)
if (init(descriptorAddr, &ReadFromTargetCallback, &WriteToTargetCallback, &ReadThreadContextCallback, nullptr, nullptr, nullptr, &s_cdacHandle) != 0)
{
CDAC_ERR("cdac_reader_init failed (descriptorAddr=0x%llx).\n",
(unsigned long long)descriptorAddr);
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, ThrownObject, offsetof(ExInfo, m_excep
CDAC_TYPE_FIELD(ExceptionInfo, T_UINT32, ExceptionFlags, cdac_data<ExInfo>::ExceptionFlagsValue)
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, StackLowBound, cdac_data<ExInfo>::StackLowBound)
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, StackHighBound, cdac_data<ExInfo>::StackHighBound)
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, ExceptionRecord, cdac_data<ExInfo>::ExceptionRecord)
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, ContextRecord, cdac_data<ExInfo>::ContextRecord)
#ifndef TARGET_UNIX
CDAC_TYPE_FIELD(ExceptionInfo, T_POINTER, ExceptionWatsonBucketTrackerBuckets, cdac_data<ExInfo>::ExceptionWatsonBucketTrackerBuckets)
#endif // TARGET_UNIX
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/exinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ struct cdac_data<ExInfo>
static constexpr size_t StackHighBound = offsetof(ExInfo, m_ScannedStackRange)
+ offsetof(ExInfo::StackRange, m_sfHighBound);
static constexpr size_t ExceptionFlagsValue = offsetof(ExInfo, m_ExceptionFlags.m_flags);
static constexpr size_t ExceptionRecord = offsetof(ExInfo, m_ptrs)
+ offsetof(ExInfo::DAC_EXCEPTION_POINTERS, ExceptionRecord);
static constexpr size_t ContextRecord = offsetof(ExInfo, m_ptrs)
+ offsetof(ExInfo::DAC_EXCEPTION_POINTERS, ContextRecord);
#ifndef TARGET_UNIX
static constexpr size_t ExceptionWatsonBucketTrackerBuckets = offsetof(ExInfo, m_WatsonBucketTracker)
+ offsetof(EHWatsonBucketTracker, m_WatsonUnhandledInfo.m_pUnhandledBuckets);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public interface IDebugger : IContract
TargetPointer GetDebuggerControlBlockAddress() => throw new NotImplementedException();
void EnableGCNotificationEvents(bool fEnable) => throw new NotImplementedException();
HijackKind GetHijackKind(TargetCodePointer controlPC) => throw new NotImplementedException();
TargetPointer PrepareExceptionHijack(byte[] context, TargetPointer vmThread, byte[]? exceptionRecord, int reason, TargetPointer userData) => throw new NotImplementedException();
}

public readonly struct Debugger : IDebugger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ public record struct ThreadData(
TargetPointer ThreadHandle,
bool IsInteropDebuggingHijacked,
TargetPointer DebuggerFilterContext,
TargetPointer GCFrame);
TargetPointer GCFrame,
bool IsExceptionInProgress,
TargetPointer OSExceptionRecord,
TargetPointer OSExceptionContextRecord);

public interface IThread : IContract
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public abstract class Target
/// <returns>true if successful, false otherwise</returns>
public abstract bool TryGetThreadContext(ulong threadId, uint contextFlags, Span<byte> buffer);

/// <summary>
/// Sets the context of the given thread from the supplied buffer
/// </summary>
/// <param name="threadId">The identifier of the thread whose context is to be set. The identifier is defined by the operating system.</param>
/// <param name="context">Buffer containing the new thread context. The contents must be a platform context structure of the size expected by the target.</param>
/// <returns>true if successful, false otherwise</returns>
public abstract bool TrySetThreadContext(ulong threadId, ReadOnlySpan<byte> context);

/// <summary>
/// Reads a well-known global pointer value from the target process
/// </summary>
Expand Down
Loading
Loading