From 6fff658c9cd28e4c8c893596a34a1910099c4d70 Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Wed, 16 May 2018 11:31:34 -0700 Subject: [PATCH] Implement ICorDebugManagedCallback4::DataBreakpoint --- src/debug/di/divalue.cpp | 11 +-- src/debug/di/process.cpp | 157 +----------------------------------- src/debug/di/rsmain.cpp | 11 +++ src/debug/di/rspriv.h | 2 - src/debug/di/shimcallback.cpp | 30 +++++++ src/debug/di/shimpriv.h | 3 + src/debug/ee/controller.cpp | 74 ++++++++--------- src/debug/ee/controller.h | 68 ++-------------- src/debug/ee/debugger.cpp | 2 +- src/inc/cordebug.idl | 5 +- src/pal/prebuilt/idl/cordebug_i.cpp | 2 +- src/pal/prebuilt/inc/cordebug.h | 20 +++-- 12 files changed, 107 insertions(+), 278 deletions(-) diff --git a/src/debug/di/divalue.cpp b/src/debug/di/divalue.cpp index 0ecb30c..e8975ec 100644 --- a/src/debug/di/divalue.cpp +++ b/src/debug/di/divalue.cpp @@ -377,18 +377,9 @@ ULONG32 CordbValue::GetSizeForType(CordbType * pType, BoxedValue boxing) HRESULT CordbValue::CreateBreakpoint(ICorDebugValueBreakpoint **ppBreakpoint) { - //PUBLIC_API_ENTRY(this); - //FAIL_IF_NEUTERED(this); - //ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); VALIDATE_POINTER_TO_OBJECT(ppBreakpoint, ICorDebugValueBreakpoint **); - //ICorDebugHandleValue** ppHandleValue = nullptr; - //if (SUCCEEDED(InternalCreateHandle((CorDebugHandleType)3, ppHandleValue))) - //{ - return GetProcess()->CreateBreakpoint((CORDB_ADDRESS)this->m_id, ppBreakpoint); - //} - - //return E_NOTIMPL; + return E_NOTIMPL; } // CordbValue::CreateBreakpoint // gets the exact type of a value diff --git a/src/debug/di/process.cpp b/src/debug/di/process.cpp index 4642937..3b5f5fb 100644 --- a/src/debug/di/process.cpp +++ b/src/debug/di/process.cpp @@ -2559,143 +2559,6 @@ COM_METHOD CordbProcess::GetContainerObject(CORDB_ADDRESS interiorPointer, ICorD return hr; } -COM_METHOD CordbProcess::CreateBreakpoint(CORDB_ADDRESS address, ICorDebugValueBreakpoint **ppBreakpoint) -{ - HRESULT hr = S_OK; - PUBLIC_API_ENTRY(this); - FAIL_IF_NEUTERED(this); - ATT_REQUIRE_STOPPED_MAY_FAIL(this); - - RSLockHolder lockHolder(GetProcessLock()); - HASHFIND hashFind; - CordbThread * pThread; - - for (pThread = m_userThreads.FindFirst(&hashFind); - pThread != NULL; - pThread = m_userThreads.FindNext(&hashFind)) - { - HRESULT hr = S_OK; - HANDLE hTemp; - EX_TRY - { - pThread->InternalGetHandle(&hTemp); // throws on error - } - EX_CATCH_HRESULT(hr); - - DWORD dwDesiredAccess = THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT | THREAD_SET_INFORMATION | THREAD_SUSPEND_RESUME | THREAD_TERMINATE; - - HANDLE hThread; - hr = ::DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(), &hThread, dwDesiredAccess, FALSE, 0); - - if (SUCCEEDED(hr)) - { - DWORD dwSuspendCount = ::SuspendThread(hThread); - - CONTEXT context; - context.ContextFlags = CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS; - - BOOL result = ::GetThreadContext(hThread, &context); - - if (result == TRUE) - { -#ifdef DBG_TARGET_X86 - if (m_dataBreakpoints.GetCount() == 0) - { - context.Dr0 = (uint32_t)address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len0 = 2; - pdr7->Rwe0 = 0x01; - pdr7->L0 = 0x01; - } - else if (m_dataBreakpoints.GetCount() == 1) - { - context.Dr1 = (uint32_t)address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len1 = 2; - pdr7->Rwe1 = 0x01; - pdr7->L1 = 0x01; - } - else if (m_dataBreakpoints.GetCount() == 2) - { - context.Dr2 = (uint32_t)address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len2 = 2; - pdr7->Rwe2 = 0x01; - pdr7->L2 = 0x01; - } - else if (m_dataBreakpoints.GetCount() == 3) - { - context.Dr3 = (uint32_t)address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len3 = 2; - pdr7->Rwe3 = 0x01; - pdr7->L3 = 0x01; - } - else - { - return E_FAIL; - } -#endif -#ifdef DBG_TARGET_AMD64 - if (m_dataBreakpoints.GetCount() == 0) - { - context.Dr0 = address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len0 = 2; - pdr7->Rwe0 = 0x01; - pdr7->L0 = 0x01; - } - else if (m_dataBreakpoints.GetCount() == 1) - { - context.Dr1 = address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len1 = 2; - pdr7->Rwe1 = 0x01; - pdr7->L1 = 0x01; - } - else if (m_dataBreakpoints.GetCount() == 2) - { - context.Dr2 = address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len2 = 2; - pdr7->Rwe2 = 0x01; - pdr7->L2 = 0x01; - } - else if (m_dataBreakpoints.GetCount() == 3) - { - context.Dr3 = address; - PDR7 pdr7 = (PDR7)&(context.Dr7); - pdr7->Len3 = 2; - pdr7->Rwe3 = 0x01; - pdr7->L3 = 0x01; - } - else - { - return E_FAIL; - } -#endif - } - result = ::SetThreadContext(hThread, &context); - ::ResumeThread(hThread); - } - } - - EX_TRY - { - RSInitHolder pValueBreakpoint(new CordbValueBreakpoint(m_dataBreakpoints.GetCount(), nullptr, this)); - - if (pValueBreakpoint) - { - hr = pValueBreakpoint->QueryInterface(IID_ICorDebugValueBreakpoint, (void**)ppBreakpoint); - } - - pValueBreakpoint.TransferOwnershipToHash(&m_dataBreakpoints); - } - EX_CATCH_HRESULT(hr); - - return hr; -} - #ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL COM_METHOD CordbProcess::InvokePauseCallback() @@ -5067,23 +4930,9 @@ void CordbProcess::RawDispatchEvent( case DB_IPCE_DATA_BREAKPOINT: { - _ASSERTE(pThread != NULL); - _ASSERTE(pAppDomain != NULL); - - HASHFIND hashFind; - CordbValueBreakpoint* pBreakpoint; - - for (pBreakpoint = m_dataBreakpoints.FindFirst(&hashFind); - pBreakpoint != NULL; - pBreakpoint = m_dataBreakpoints.FindNext(&hashFind)) - { - if (pBreakpoint->GetIndex() == pEvent->DataBreakpointData.index) - { - PUBLIC_CALLBACK_IN_THIS_SCOPE2(this, pLockHolder, pEvent, "thread=0x%p, bp=0x%p", pThread, pBreakpoint); - pCallback1->Breakpoint(pAppDomain, pThread, CordbBreakpointToInterface(pBreakpoint)); - break; - } - } + PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); + pCallback4->DataBreakpoint(static_cast(this)); + break; } break; case DB_IPCE_USER_BREAKPOINT: diff --git a/src/debug/di/rsmain.cpp b/src/debug/di/rsmain.cpp index 42cf8f8..254a2b7 100644 --- a/src/debug/di/rsmain.cpp +++ b/src/debug/di/rsmain.cpp @@ -870,6 +870,7 @@ namespace virtual ULONG STDMETHODCALLTYPE Release(); COM_METHOD BeforeGarbageCollection(ICorDebugProcess* pProcess); COM_METHOD AfterGarbageCollection(ICorDebugProcess* pProcess); + COM_METHOD DataBreakpoint(ICorDebugProcess* pProcess); private: // not implemented DefaultManagedCallback4(const DefaultManagedCallback4&); @@ -941,6 +942,16 @@ namespace pProcess->Continue(false); return S_OK; } + + HRESULT + DefaultManagedCallback4::DataBreakpoint(ICorDebugProcess* pProcess) + { + // + // Just ignore and continue the process. + // + pProcess->Continue(false); + return S_OK; + } } /* ------------------------------------------------------------------------- * diff --git a/src/debug/di/rspriv.h b/src/debug/di/rspriv.h index d5a8e60..1e01c5c 100644 --- a/src/debug/di/rspriv.h +++ b/src/debug/di/rspriv.h @@ -3167,8 +3167,6 @@ public: //----------------------------------------------------------- COM_METHOD GetContainerObject(CORDB_ADDRESS interiorPointer, ICorDebugObjectValue** ppContainerObject); - COM_METHOD CreateBreakpoint(CORDB_ADDRESS address, ICorDebugValueBreakpoint **ppBreakpoint); - #ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL // --------------------------------------------------------------- // ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly diff --git a/src/debug/di/shimcallback.cpp b/src/debug/di/shimcallback.cpp index 05fea35..078643f 100644 --- a/src/debug/di/shimcallback.cpp +++ b/src/debug/di/shimcallback.cpp @@ -1379,6 +1379,36 @@ HRESULT ShimProxyCallback::AfterGarbageCollection(ICorDebugProcess* pProcess) return S_OK; } +// Implementation of ICorDebugManagedCallback4::DataBreakpoint +// Arguments: +// input: +// pProcess - process in which the notification occurred +// Return value: S_OK +HRESULT ShimProxyCallback::DataBreakpoint(ICorDebugProcess* pProcess) +{ + m_pShim->PreDispatchEvent(); + class DataBreakpointEvent : public ManagedEvent + { + // callbacks parameters. These are strong references + RSExtSmartPtr m_pProcess; + + public: + // Ctor + DataBreakpointEvent(ICorDebugProcess* pProcess) : + ManagedEvent() + { + this->m_pProcess.Assign(pProcess); + } + + HRESULT Dispatch(DispatchArgs args) + { + return args.GetCallback4()->DataBreakpoint(m_pProcess); + } + }; // end class AfterGarbageCollectionEvent + + m_pShim->GetManagedEventQueue()->QueueEvent(new DataBreakpointEvent(pProcess)); + return S_OK; +} diff --git a/src/debug/di/shimpriv.h b/src/debug/di/shimpriv.h index c936b98..503ee3e 100644 --- a/src/debug/di/shimpriv.h +++ b/src/debug/di/shimpriv.h @@ -220,6 +220,9 @@ public: // Implementation of ICorDebugManagedCallback4::AfterGarbageCollection COM_METHOD ShimProxyCallback::AfterGarbageCollection(ICorDebugProcess* pProcess); + + // Implementation of ICorDebugManagedCallback4::DataBreakpoint + COM_METHOD ShimProxyCallback::DataBreakpoint(ICorDebugProcess* pProcess); }; diff --git a/src/debug/ee/controller.cpp b/src/debug/ee/controller.cpp index 6c0914f..bee9739 100644 --- a/src/debug/ee/controller.cpp +++ b/src/debug/ee/controller.cpp @@ -45,8 +45,7 @@ DebuggerController *DebuggerController::g_controllers = NULL; DebuggerControllerPage *DebuggerController::g_protections = NULL; CrstStatic DebuggerController::g_criticalSection; int DebuggerController::g_cTotalMethodEnter = 0; -DebuggerDataBreakpoint *DebuggerDataBreakpoint::g_DataBreakpoints[4]; -unsigned int DebuggerDataBreakpoint::g_DataBreakpointCount = 0; +DebuggerDataBreakpoint DebuggerDataBreakpoint::g_DebuggerDataBreakpoint; // Is this patch at a position at which it's safe to take a stack? bool DebuggerControllerPatch::IsSafeForStackTrace() @@ -2728,17 +2727,10 @@ DPOSS_ACTION DebuggerController::ScanForTriggers(CORDB_ADDRESS_TYPE *address, } if (stWhat & ST_SINGLE_STEP && - tpr != TPR_TRIGGER_ONLY_THIS && DebuggerDataBreakpoint::g_DataBreakpointCount > 0) + tpr != TPR_TRIGGER_ONLY_THIS && + DebuggerDataBreakpoint::g_DebuggerDataBreakpoint.TriggerDataBreakpoint(thread, context)) { - for (unsigned int i = 0; i < DebuggerDataBreakpoint::g_DataBreakpointCount; i++) - { - LOG((LF_CORDB, LL_INFO10000, "DC::SFT: Trigger data breakpoint controller\n")); - - if (DebuggerDataBreakpoint::g_DataBreakpoints[i]->TriggerDataBreakpoint(thread, context)) - { - pDcq->dcqEnqueue(DebuggerDataBreakpoint::g_DataBreakpoints[i], FALSE); - } - } + pDcq->dcqEnqueue(&DebuggerDataBreakpoint::g_DebuggerDataBreakpoint, FALSE); } if (stWhat & ST_SINGLE_STEP && @@ -3024,35 +3016,35 @@ DPOSS_ACTION DebuggerController::DispatchPatchOrSingleStep(Thread *thread, CONTE reabort = thread->m_StateNC & Thread::TSNC_DebuggerReAbort; SENDIPCEVENT_END; - CONTEXT c; - c.ContextFlags = CONTEXT_DEBUG_REGISTERS; - thread->GetThreadContext(&c); - - context->Dr7 = c.Dr7; - context->Dr0 = c.Dr0; - context->Dr1 = c.Dr1; - context->Dr2 = c.Dr2; - context->Dr3 = c.Dr3; - - if (context->Dr0 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 0) - { - DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); - } - - if (context->Dr1 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 1) - { - DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); - } - - if (context->Dr2 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 2) - { - DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); - } - - if (context->Dr3 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 3) - { - DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); - } + //CONTEXT c; + //c.ContextFlags = CONTEXT_DEBUG_REGISTERS; + //thread->GetThreadContext(&c); + + //context->Dr7 = c.Dr7; + //context->Dr0 = c.Dr0; + //context->Dr1 = c.Dr1; + //context->Dr2 = c.Dr2; + //context->Dr3 = c.Dr3; + + //if (context->Dr0 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 0) + //{ + // DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); + //} + + //if (context->Dr1 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 1) + //{ + // DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); + //} + + //if (context->Dr2 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 2) + //{ + // DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); + //} + + //if (context->Dr3 != NULL && DebuggerDataBreakpoint::g_DataBreakpointCount == 3) + //{ + // DebuggerDataBreakpoint::CreateDebuggerDataBreakpoint(thread, thread->GetDomain(), context); + //} if (!atSafePlace) g_pDebugger->DecThreadsAtUnsafePlaces(); diff --git a/src/debug/ee/controller.h b/src/debug/ee/controller.h index fc21cd4..e2680f1 100644 --- a/src/debug/ee/controller.h +++ b/src/debug/ee/controller.h @@ -1774,19 +1774,9 @@ private: class DebuggerDataBreakpoint : public DebuggerController { public: - static void CreateDebuggerDataBreakpoint(Thread* pThread, AppDomain* pAppDomain, CONTEXT* context) + DebuggerDataBreakpoint() : DebuggerController(NULL, NULL) { - _ASSERTE(g_DataBreakpointCount < 5); - - DebuggerDataBreakpoint* newDataBp = new DebuggerDataBreakpoint(g_DataBreakpointCount, pThread, pAppDomain, context); - g_DataBreakpoints[g_DataBreakpointCount++] = newDataBp; - } - - DebuggerDataBreakpoint(unsigned int index, Thread* pThread, AppDomain* pAppDomain, CONTEXT* context) : DebuggerController(pThread, pAppDomain) - { - LOG((LF_CORDB, LL_INFO10000, "D::DDBP: New Data Breakpoint set for 0x%x\n", context->Dr0)); - memcpy(&m_Context, context, sizeof(CONTEXT)); - m_Index = index; + LOG((LF_CORDB, LL_INFO10000, "D:DDBP: Data Breakpoint event created\n")); } virtual DEBUGGER_CONTROLLER_TYPE GetDCType(void) @@ -1827,47 +1817,10 @@ public: bool hitDataBp = false; PDR6 pdr6 = (PDR6)&(pContext->Dr6); - PDR7 pdr7 = (PDR7)&(pContext->Dr7); - - if (m_Index == 0) - { - if (pdr6->B0) - { - if (pContext->Dr0 == m_Context.Dr0 && pdr7->L0 != 0) - { - hitDataBp = true; - } - } - } - else if (m_Index == 1) - { - if (pdr6->B1) - { - if (pContext->Dr1 == m_Context.Dr1 && pdr7->L1 != 0) - { - hitDataBp = true; - } - } - } - else if (m_Index == 2) - { - if (pdr6->B2) - { - if (pContext->Dr2 == m_Context.Dr2 && pdr7->L2 != 0) - { - hitDataBp = true; - } - } - } - else if (m_Index == 3) + + if (pdr6->B0 || pdr6->B1 || pdr6->B2 || pdr6->B3) { - if (pdr6->B3) - { - if (pContext->Dr3 == m_Context.Dr3 && pdr7->L3 != 0) - { - hitDataBp = true; - } - } + hitDataBp = true; } if (hitDataBp) @@ -1882,16 +1835,7 @@ public: return hitDataBp; } - unsigned int GetIndex() - { - return m_Index; - } - - static DebuggerDataBreakpoint* g_DataBreakpoints[4]; - static unsigned int g_DataBreakpointCount; -private: - CONTEXT m_Context; - unsigned int m_Index; + static DebuggerDataBreakpoint g_DebuggerDataBreakpoint; }; diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp index f168819..7cd88de 100644 --- a/src/debug/ee/debugger.cpp +++ b/src/debug/ee/debugger.cpp @@ -6103,7 +6103,7 @@ void Debugger::SendDataBreakpoint(Thread *thread, CONTEXT *context, DB_IPCE_DATA_BREAKPOINT, thread, thread->GetDomain()); - ipce->DataBreakpointData.index = breakpoint->GetIndex(); + //ipce->DataBreakpointData.index = breakpoint->GetIndex(); _ASSERTE(breakpoint->m_pAppDomain == ipce->vmAppDomain.GetRawPtr()); m_pRCThread->SendIPCEvent(); diff --git a/src/inc/cordebug.idl b/src/inc/cordebug.idl index 7f13bf6..61fe60d 100644 --- a/src/inc/cordebug.idl +++ b/src/inc/cordebug.idl @@ -1359,8 +1359,9 @@ interface ICorDebugManagedCallback3 : IUnknown interface ICorDebugManagedCallback4 : IUnknown { - HRESULT BeforeGarbageCollection(ICorDebugProcess* pProcess); - HRESULT AfterGarbageCollection(ICorDebugProcess* pProcess); + HRESULT BeforeGarbageCollection([in] ICorDebugProcess* pProcess); + HRESULT AfterGarbageCollection([in] ICorDebugProcess* pProcess); + HRESULT DataBreakpoint([in] ICorDebugProcess* pProcess); } diff --git a/src/pal/prebuilt/idl/cordebug_i.cpp b/src/pal/prebuilt/idl/cordebug_i.cpp index 15e25c8..e30649c 100644 --- a/src/pal/prebuilt/idl/cordebug_i.cpp +++ b/src/pal/prebuilt/idl/cordebug_i.cpp @@ -8,7 +8,7 @@ /* File created by MIDL compiler version 8.01.0622 */ /* at Mon Jan 18 19:14:07 2038 */ -/* Compiler settings for F:/Dev/coreclr/src/inc/cordebug.idl: +/* Compiler settings for D:/dotnet/coreclr/src/inc/cordebug.idl: Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data diff --git a/src/pal/prebuilt/inc/cordebug.h b/src/pal/prebuilt/inc/cordebug.h index c8ad161..4ed5bde 100644 --- a/src/pal/prebuilt/inc/cordebug.h +++ b/src/pal/prebuilt/inc/cordebug.h @@ -6,7 +6,7 @@ /* File created by MIDL compiler version 8.01.0622 */ /* at Mon Jan 18 19:14:07 2038 */ -/* Compiler settings for F:/Dev/coreclr/src/inc/cordebug.idl: +/* Compiler settings for D:/dotnet/coreclr/src/inc/cordebug.idl: Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data @@ -3781,10 +3781,13 @@ EXTERN_C const IID IID_ICorDebugManagedCallback4; { public: virtual HRESULT STDMETHODCALLTYPE BeforeGarbageCollection( - ICorDebugProcess *pProcess) = 0; + /* [in] */ ICorDebugProcess *pProcess) = 0; virtual HRESULT STDMETHODCALLTYPE AfterGarbageCollection( - ICorDebugProcess *pProcess) = 0; + /* [in] */ ICorDebugProcess *pProcess) = 0; + + virtual HRESULT STDMETHODCALLTYPE DataBreakpoint( + /* [in] */ ICorDebugProcess *pProcess) = 0; }; @@ -3809,11 +3812,15 @@ EXTERN_C const IID IID_ICorDebugManagedCallback4; HRESULT ( STDMETHODCALLTYPE *BeforeGarbageCollection )( ICorDebugManagedCallback4 * This, - ICorDebugProcess *pProcess); + /* [in] */ ICorDebugProcess *pProcess); HRESULT ( STDMETHODCALLTYPE *AfterGarbageCollection )( ICorDebugManagedCallback4 * This, - ICorDebugProcess *pProcess); + /* [in] */ ICorDebugProcess *pProcess); + + HRESULT ( STDMETHODCALLTYPE *DataBreakpoint )( + ICorDebugManagedCallback4 * This, + /* [in] */ ICorDebugProcess *pProcess); END_INTERFACE } ICorDebugManagedCallback4Vtbl; @@ -3844,6 +3851,9 @@ EXTERN_C const IID IID_ICorDebugManagedCallback4; #define ICorDebugManagedCallback4_AfterGarbageCollection(This,pProcess) \ ( (This)->lpVtbl -> AfterGarbageCollection(This,pProcess) ) +#define ICorDebugManagedCallback4_DataBreakpoint(This,pProcess) \ + ( (This)->lpVtbl -> DataBreakpoint(This,pProcess) ) + #endif /* COBJMACROS */ -- 2.7.4