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

Update stepping through MulticastDelegate under the Debugger #108414

10 changes: 8 additions & 2 deletions src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1583,8 +1583,14 @@ internal static void ValidateObject(object obj, IntPtr pMD)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr GetStubContext();

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void MulticastDebuggerTraceHelper(object o, int count);
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void MulticastDebuggerTraceHelper(object o, int count)
{
MulticastDebuggerTraceHelperQCall(ObjectHandleOnStack.Create(ref o), count);
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint="StubHelpers_MulticastDebuggerTraceHelper")]
private static partial void MulticastDebuggerTraceHelperQCall(ObjectHandleOnStack obj, int count);

[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
Expand Down
112 changes: 103 additions & 9 deletions src/coreclr/debug/ee/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,8 @@ DebuggerController::DebuggerController(Thread * pThread, AppDomain * pAppDomain)
m_unwindFP(LEAF_MOST_FRAME),
m_eventQueuedCount(0),
m_deleted(false),
m_fEnableMethodEnter(false)
m_fEnableMethodEnter(false),
m_multicastDelegateHelper(false)
{
CONTRACTL
{
Expand Down Expand Up @@ -1133,6 +1134,8 @@ void DebuggerController::DisableAll()
DisableTraceCall();
if (m_fEnableMethodEnter)
DisableMethodEnter();
if (m_multicastDelegateHelper)
DisableMultiCastDelegate();
}
}

Expand Down Expand Up @@ -2279,6 +2282,7 @@ static bool _AddrIsJITHelper(PCODE addr)
// method & return true.
//
// Return true if we set a patch, else false
#ifndef DACCESS_COMPILE
bool DebuggerController::PatchTrace(TraceDestination *trace,
FramePointer fp,
bool fStopInUnmanaged)
Expand Down Expand Up @@ -2364,7 +2368,7 @@ bool DebuggerController::PatchTrace(TraceDestination *trace,

case TRACE_FRAME_PUSH:
LOG((LF_CORDB, LL_INFO10000,
"Setting frame patch at 0x%p(%p)\n", trace->GetAddress(), fp.GetSPValue()));
"Setting frame patch at %p(%p)\n", trace->GetAddress(), fp.GetSPValue()));

AddAndActivateNativePatchForAddress((CORDB_ADDRESS_TYPE *)trace->GetAddress(),
fp,
Expand All @@ -2374,13 +2378,12 @@ bool DebuggerController::PatchTrace(TraceDestination *trace,

case TRACE_MGR_PUSH:
LOG((LF_CORDB, LL_INFO10000,
"Setting frame patch (TRACE_MGR_PUSH) at 0x%p(%p)\n",
trace->GetAddress(), fp.GetSPValue()));

"Setting frame patch (TRACE_MGR_PUSH) at %p(%p)\n",
trace->GetAddress(), fp.GetSPValue()));
dcp = AddAndActivateNativePatchForAddress((CORDB_ADDRESS_TYPE *)trace->GetAddress(),
LEAF_MOST_FRAME, // But Mgr_push can't have fp affinity!
TRUE,
DPT_DEFAULT_TRACE_TYPE); // TRACE_OTHER
LEAF_MOST_FRAME, // But Mgr_push can't have fp affinity!
TRUE,
DPT_DEFAULT_TRACE_TYPE); // TRACE_OTHER
// Now copy over the trace field since TriggerPatch will expect this
// to be set for this case.
if (dcp != NULL)
Expand All @@ -2390,6 +2393,10 @@ bool DebuggerController::PatchTrace(TraceDestination *trace,

return true;

case TRACE_MULTICAST_DELEGATE_HELPER:
EnableMultiCastDelegate();
return true;

case TRACE_OTHER:
LOG((LF_CORDB, LL_INFO10000,
"Can't set a trace patch for TRACE_OTHER...\n"));
Expand All @@ -2400,6 +2407,7 @@ bool DebuggerController::PatchTrace(TraceDestination *trace,
return false;
}
}
#endif

//-----------------------------------------------------------------------------
// Checks if the patch matches the context + thread.
Expand Down Expand Up @@ -3879,6 +3887,73 @@ void DebuggerController::DispatchMethodEnter(void * pIP, FramePointer fp)

}

void DebuggerController::EnableMultiCastDelegate()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;

ControllerLockHolder chController;
if (!m_multicastDelegateHelper)
{
LOG((LF_CORDB, LL_INFO1000000, "DC::EnableMultiCastDel, this=%p, previously disabled\n", this));
m_multicastDelegateHelper = true;
g_multicastDelegateTraceActiveCount += 1;
}
else
{
LOG((LF_CORDB, LL_INFO1000000, "DC::EnableMultiCastDel, this=%p, already set\n", this));
}
}

void DebuggerController::DisableMultiCastDelegate()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;

ControllerLockHolder chController;
if (m_multicastDelegateHelper)
{
LOG((LF_CORDB, LL_INFO10000, "DC::DisableMultiCastDelegate, this=%p, previously set\n", this));
m_multicastDelegateHelper = false;
g_multicastDelegateTraceActiveCount -= 1;
}
else
{
LOG((LF_CORDB, LL_INFO10000, "DC::DisableMultiCastDelegate, this=%p, already disabled\n", this));
}
}

// Loop through controllers and dispatch TriggerMulticastDelegate
void DebuggerController::DispatchMulticastDelegate(DELEGATEREF pbDel, INT32 countDel)
{
LOG((LF_CORDB, LL_INFO10000, "DC::DispatchMulticastDelegate\n"));

Thread * pThread = g_pEEInterface->GetThread();
_ASSERTE(pThread != NULL);

ControllerLockHolder lockController;

DebuggerController *p = g_controllers;
while (p != NULL)
{
if (p->m_multicastDelegateHelper)
{
if ((p->GetThread() == NULL) || (p->GetThread() == pThread))
mikelle-rogers marked this conversation as resolved.
Show resolved Hide resolved
{
p->TriggerMulticastDelegate(pbDel, countDel);
}
}
p = p->m_next;
}
}
//
// AddProtection adds page protection to (at least) the given range of
// addresses
Expand Down Expand Up @@ -4023,6 +4098,10 @@ void DebuggerController::DispatchFuncEvalExit(Thread * thread)


}
void DebuggerController::TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateCount)
{
_ASSERTE(!"This code should be unreachable. If your controller enables MulticastDelegateHelper events,it should also override this callback to do something useful when the event arrives.");
}


#ifdef _DEBUG
Expand Down Expand Up @@ -6283,7 +6362,7 @@ void DebuggerStepper::TrapStepOut(ControllerStackInfo *info, bool fForceTraditio
&& g_pEEInterface->FollowTrace(&trace)
&& PatchTrace(&trace, info->m_activeFrame.fp,
true))
break;
continue;
}
else if (info->m_activeFrame.md != nullptr && info->m_activeFrame.md->IsILStub() &&
info->m_activeFrame.md->AsDynamicMethodDesc()->GetILStubType() == DynamicMethodDesc::StubTailCallCallTarget)
Expand Down Expand Up @@ -7553,6 +7632,21 @@ void DebuggerStepper::TriggerUnwind(Thread *thread,
m_reason = unwindReason;
}

void DebuggerStepper::TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateCount)
{
TraceDestination trace;
FramePointer fp = LEAF_MOST_FRAME;

PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetInvocationList();
DELEGATEREF pCurrentInvokeDel = (DELEGATEREF) pDelInvocationList->GetAt(delegateCount);

StubLinkStubManager::TraceDelegateObject((BYTE*)OBJECTREFToObject(pCurrentInvokeDel), &trace);

g_pEEInterface->FollowTrace(&trace);
//fStopInUnmanaged only matters for TRACE_UNMANAGED
PatchTrace(&trace, fp, /*fStopInUnmanaged*/false);
this->DisableMultiCastDelegate();
}

// Prepare for sending an event.
// This is called 1:1 w/ SendEvent, but this method can be called in a GC_TRIGGERABLE context
Expand Down
8 changes: 7 additions & 1 deletion src/coreclr/debug/ee/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,7 @@ class DebuggerController
// pIP is the ip right after the prolog of the method we've entered.
// fp is the frame pointer for that method.
static void DispatchMethodEnter(void * pIP, FramePointer fp);
static void DispatchMulticastDelegate(DELEGATEREF pbDel, INT32 countDel);


// Delete any patches that exist for a specific module and optionally a specific AppDomain.
Expand Down Expand Up @@ -1299,6 +1300,9 @@ class DebuggerController
void EnableMethodEnter();
void DisableMethodEnter();

void EnableMultiCastDelegate();
void DisableMultiCastDelegate();

void DisableAll();

virtual DEBUGGER_CONTROLLER_TYPE GetDCType( void )
Expand Down Expand Up @@ -1398,6 +1402,7 @@ class DebuggerController
const BYTE * ip,
FramePointer fp);

virtual void TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateCount);

// Send the managed debug event.
// This is called after TriggerPatch/TriggerSingleStep actually trigger.
Expand Down Expand Up @@ -1437,6 +1442,7 @@ class DebuggerController
int m_eventQueuedCount;
bool m_deleted;
bool m_fEnableMethodEnter;
bool m_multicastDelegateHelper;

#endif // !DACCESS_COMPILE
};
Expand Down Expand Up @@ -1638,7 +1644,7 @@ class DebuggerStepper : public DebuggerController


virtual void TriggerMethodEnter(Thread * thread, DebuggerJitInfo * dji, const BYTE * ip, FramePointer fp);

void TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateCount);

void ResetRange();

Expand Down
9 changes: 8 additions & 1 deletion src/coreclr/debug/ee/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "../../vm/dwreport.h"
#include "../../vm/eepolicy.h"
#include "../../vm/excep.h"

#if defined(FEATURE_DBGIPC_TRANSPORT_VM)
#include "dbgtransportsession.h"
#endif // FEATURE_DBGIPC_TRANSPORT_VM
Expand Down Expand Up @@ -12588,7 +12589,7 @@ bool Debugger::IsThreadAtSafePlace(Thread *thread)
// any thread handling a SO is not at a safe place.
// NOTE: don't check for thread->IsExceptionInProgress(), SO has special handling
// that directly sets the last thrown object without ever creating a tracker.
// (Tracker is what thread->IsExceptionInProgress() checks for)
// (Tracker is what thread->IsExceptionInProgress() checks for)
if (g_pEEInterface->GetThreadException(thread) == CLRException::GetPreallocatedStackOverflowExceptionHandle())
{
return false;
Expand Down Expand Up @@ -16775,6 +16776,12 @@ BOOL Debugger::IsOutOfProcessSetContextEnabled()
}
#endif // OUT_OF_PROCESS_SETTHREADCONTEXT
#endif // DACCESS_COMPILE
#ifndef DACCESS_COMPILE
void Debugger::MulticastTraceNextStep(DELEGATEREF pbDel, INT32 count)
{
DebuggerController::DispatchMulticastDelegate(pbDel, count);
}
#endif //DACCESS_COMPILE

#endif //DEBUGGING_SUPPORTED

3 changes: 3 additions & 0 deletions src/coreclr/debug/ee/debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -2610,6 +2610,9 @@ class Debugger : public DebugInterface
bool ThisIsHelperThread(void);

HRESULT ReDaclEvents(PSECURITY_DESCRIPTOR securityDescriptor);
#ifndef DACCESS_COMPILE
void MulticastTraceNextStep(DELEGATEREF pbDel, INT32 count);
#endif

#ifdef DACCESS_COMPILE
virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
Expand Down
40 changes: 16 additions & 24 deletions src/coreclr/vm/comdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2137,18 +2137,30 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat
dwReturnValNum = pCode->NewLocal(sig.GetRetTypeHandleNT());

ILCodeLabel *nextDelegate = pCode->NewCodeLabel();
ILCodeLabel *checkCount = pCode->NewCodeLabel();

// initialize counter
pCode->EmitLDC(0);
pCode->EmitSTLOC(dwLoopCounterNum);

// Make the shape of the loop similar to what C# compiler emits
mikelle-rogers marked this conversation as resolved.
Show resolved Hide resolved
pCode->EmitBR(checkCount);

//Label_nextDelegate:
pCode->EmitLabel(nextDelegate);

#ifdef DEBUGGING_SUPPORTED
ILCodeLabel *invokeTraceHelper = pCode->NewCodeLabel();
ILCodeLabel *debuggerCheckEnd = pCode->NewCodeLabel();

// Call MulticastDebuggerTraceHelper only if we have a controller subscribing to the event
pCode->EmitLDC((DWORD_PTR)&g_multicastDelegateTraceActiveCount);
pCode->EmitCONV_I();
pCode->EmitLDIND_I4();
// g_multicastDelegateTraceActiveCount != 0
pCode->EmitLDC(0);
pCode->EmitCEQ();
pCode->EmitBRFALSE(invokeTraceHelper);

pCode->EmitLabel(debuggerCheckEnd);
#endif // DEBUGGING_SUPPORTED

// Load next delegate from array using LoopCounter as index
pCode->EmitLoadThis();
pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST)));
Expand All @@ -2172,26 +2184,6 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat
pCode->EmitADD();
pCode->EmitSTLOC(dwLoopCounterNum);

//Label_checkCount
pCode->EmitLabel(checkCount);

#ifdef DEBUGGING_SUPPORTED
ILCodeLabel *invokeTraceHelper = pCode->NewCodeLabel();
ILCodeLabel *debuggerCheckEnd = pCode->NewCodeLabel();

// Call MulticastDebuggerTraceHelper only if any debugger is attached
pCode->EmitLDC((DWORD_PTR)&g_CORDebuggerControlFlags);
pCode->EmitCONV_I();
pCode->EmitLDIND_I4();

// (g_CORDebuggerControlFlags & DBCF_ATTACHED) != 0
pCode->EmitLDC(DBCF_ATTACHED);
pCode->EmitAND();
pCode->EmitBRTRUE(invokeTraceHelper);

pCode->EmitLabel(debuggerCheckEnd);
#endif // DEBUGGING_SUPPORTED

// compare LoopCounter with InvocationCount. If less then branch to nextDelegate
pCode->EmitLDLOC(dwLoopCounterNum);
pCode->EmitLoadThis();
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/dbginterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ class DebugInterface
#ifndef DACCESS_COMPILE
virtual HRESULT DeoptimizeMethod(Module* pModule, mdMethodDef methodDef) = 0;
virtual HRESULT IsMethodDeoptimized(Module *pModule, mdMethodDef methodDef, BOOL *pResult) = 0;
virtual void MulticastTraceNextStep(DELEGATEREF pbDel, INT32 count) = 0;
#endif //DACCESS_COMPILE
};

Expand Down
1 change: 0 additions & 1 deletion src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ FCFuncStart(gStubHelperFuncs)
FCFuncElement("CalcVaListSize", StubHelpers::CalcVaListSize)
FCFuncElement("LogPinnedArgument", StubHelpers::LogPinnedArgument)
FCFuncElement("GetStubContext", StubHelpers::GetStubContext)
FCFuncElement("MulticastDebuggerTraceHelper", StubHelpers::MulticastDebuggerTraceHelper)
FCFuncElement("NextCallReturnAddress", StubHelpers::NextCallReturnAddress)
FCFuncEnd()

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ static const Entry s_QCall[] =
DllImportEntry(StubHelpers_MarshalToUnmanagedVaList)
DllImportEntry(StubHelpers_ValidateObject)
DllImportEntry(StubHelpers_ValidateByref)
DllImportEntry(StubHelpers_MulticastDebuggerTraceHelper)
#ifdef PROFILING_SUPPORTED
DllImportEntry(StubHelpers_ProfilerBeginTransitionCallback)
DllImportEntry(StubHelpers_ProfilerEndTransitionCallback)
Expand Down
Loading