Skip to content

Commit

Permalink
Update stepping through MulticastDelegate under the Debugger (#108414)
Browse files Browse the repository at this point in the history
* macOS stepping with multicast delegates

* remove one call of  MulticastDebuggerTraceHelper

* change int to INT_PTR

* change to QCall and optimize

* styling and cleanup
  • Loading branch information
mikelle-rogers authored Oct 9, 2024
1 parent 10760a2 commit abde3f9
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 146 deletions.
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))
{
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
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 @@ -386,7 +386,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 @@ -466,6 +466,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

0 comments on commit abde3f9

Please sign in to comment.