diff options
Diffstat (limited to 'src/debug/di/shimevents.cpp')
-rw-r--r-- | src/debug/di/shimevents.cpp | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/debug/di/shimevents.cpp b/src/debug/di/shimevents.cpp new file mode 100644 index 0000000000..e54b1bd7f2 --- /dev/null +++ b/src/debug/di/shimevents.cpp @@ -0,0 +1,292 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// File: ShimEvents.cpp +// + +// +// The V3 ICD debugging APIs have a lower abstraction level than V2. +// This provides V2 ICD debugging functionality on top of the V3 debugger object. +//***************************************************************************** + +#include "stdafx.h" + +#include "safewrap.h" +#include "check.h" + +#include <limits.h> +#include "shimpriv.h" + +//--------------------------------------------------------------------------------------- +// Need virtual dtor since this is a base class. +// Derived classes will do real work +//--------------------------------------------------------------------------------------- +ManagedEvent::~ManagedEvent() +{ +} + +#ifdef _DEBUG +//--------------------------------------------------------------------------------------- +// For debugging, get a pointer value that can identify the type of this event. +// +// Returns: +// persistent pointer value that can be used as cookie to identify this event type. +//--------------------------------------------------------------------------------------- +void * ManagedEvent::GetDebugCookie() +{ + // Return vtable, first void* in the structure. + return *(reinterpret_cast<void**> (this)); +} +#endif + +//--------------------------------------------------------------------------------------- +// Ctor for DispatchArgs +// +// Arguments: +// pCallback1 - 1st callback, for debug events in V1.0, V1.1 +// pCallback2 - 2nd callback, for debug events added in V2 +// +// Notes: +// We'll have a lot of derived classes of ManagedEvent, and so encapsulating the arguments +// for the Dispatch() function lets us juggle them around easily without hitting every signature. +//--------------------------------------------------------------------------------------- +ManagedEvent::DispatchArgs::DispatchArgs(ICorDebugManagedCallback * pCallback1, ICorDebugManagedCallback2 * pCallback2, ICorDebugManagedCallback3 * pCallback3) +{ + m_pCallback1 = pCallback1; + m_pCallback2 = pCallback2; + m_pCallback3 = pCallback3; +} + + +// trivial accessor to get Callback 1 +ICorDebugManagedCallback * ManagedEvent::DispatchArgs::GetCallback1() +{ + return m_pCallback1; +} + +// trivial accessor to get callback 2 +ICorDebugManagedCallback2 * ManagedEvent::DispatchArgs::GetCallback2() +{ + return m_pCallback2; +} + +// trivial accessor to get callback 3 +ICorDebugManagedCallback3 * ManagedEvent::DispatchArgs::GetCallback3() +{ + return m_pCallback3; +} + +// Returns OS Thread Id that this event occurred on, 0 if no thread affinity. +DWORD ManagedEvent::GetOSTid() +{ + return m_dwThreadId; +} + +//--------------------------------------------------------------------------------------- +// Constructore for events with thread affinity +// +// Arguments: +// pThread - thread that this event is associated with. +// +// Notes: +// Thread affinity is used with code:ManagedEventQueue::HasQueuedCallbacks +// This includes event callbacks that have a thread parameter +//--------------------------------------------------------------------------------------- +ManagedEvent::ManagedEvent(ICorDebugThread * pThread) +{ + m_dwThreadId = 0; + if (pThread != NULL) + { + pThread->GetID(&m_dwThreadId); + } + + m_pNext = NULL; +} + +//--------------------------------------------------------------------------------------- +// Constructor for events with no thread affinity +//--------------------------------------------------------------------------------------- +ManagedEvent::ManagedEvent() +{ + m_dwThreadId = 0; + m_pNext = NULL; +} + + + + + + +// Ctor +ManagedEventQueue::ManagedEventQueue() +{ + m_pFirstEvent = NULL; + m_pLastEvent = NULL; + m_pLock = NULL; +} + +//--------------------------------------------------------------------------------------- +// Initialize +// +// Arguments: +// pLock - lock that protects this event queue. This takes a weak ref to the lock, +// so caller ensures lock stays alive for lifespan of this object +// +// Notes: +// Event queue locks itself using this lock. +// Only call this once. +//--------------------------------------------------------------------------------------- +void ManagedEventQueue::Init(RSLock * pLock) +{ + _ASSERTE(m_pLock == NULL); + m_pLock = pLock; +} + +//--------------------------------------------------------------------------------------- +// Remove event from the top. +// +// Returns: +// Event that was just dequeued. +// +// Notes: +// Caller then takes ownership of Event and will call Delete on it. +// If IsEmpty() function returns NULL. +// +// It is an error to call Dequeue when the only elements in the queue are suspended. +// Suspending the queue implies there are going to be new events added which should come before +// the elements that are suspended. Trying to deqeue when there are only suspended elements +// left is error-prone - if it were allowed, the order may be non-deterministic. +// In practice we could probably ban calling Dequeue at all when any elements are suspended, +// but this seems overly restrictive - there is nothing wrong with allowing these "new" +// events to be dequeued since we know they come first (you can't nest suspensions). +//--------------------------------------------------------------------------------------- +ManagedEvent * ManagedEventQueue::Dequeue() +{ + RSLockHolder lockHolder(m_pLock); + if (m_pFirstEvent == NULL) + { + return NULL; + } + + ManagedEvent * pEvent = m_pFirstEvent; + m_pFirstEvent = m_pFirstEvent->m_pNext; + if (m_pFirstEvent == NULL) + { + m_pLastEvent = NULL; + } + + pEvent->m_pNext = NULL; + return pEvent; +} + +//--------------------------------------------------------------------------------------- +// Append the event to the end of the queue. +// Queue owns the event and will delete it (unless it's dequeued first). +// +// Note that this can be called when a suspended queue is active. Events are pushed onto +// the currently active queue (ahead of the suspended queue). +// +// Arguments: +// pEvent - event to queue. +// +//--------------------------------------------------------------------------------------- +void ManagedEventQueue::QueueEvent(ManagedEvent * pEvent) +{ + RSLockHolder lockHolder(m_pLock); + _ASSERTE(pEvent != NULL); + _ASSERTE(pEvent->m_pNext == NULL); + + if (m_pLastEvent == NULL) + { + _ASSERTE(m_pFirstEvent == NULL); + m_pFirstEvent = m_pLastEvent = pEvent; + } + else + { + m_pLastEvent->m_pNext = pEvent; + m_pLastEvent = pEvent; + } +} + + +//--------------------------------------------------------------------------------------- +// Returns true iff the event queue is empty (including any suspended queue elements) +//--------------------------------------------------------------------------------------- +bool ManagedEventQueue::IsEmpty() +{ + RSLockHolder lockHolder(m_pLock); + if (m_pFirstEvent != NULL) + { + _ASSERTE(m_pLastEvent != NULL); + return false; + } + + _ASSERTE(m_pLastEvent == NULL); + return true; +} + + +//--------------------------------------------------------------------------------------- +// Delete all events and empty the queue (including any suspended queue elements) +// +// Notes: +// This is like calling { while(!IsEmpty()) delete Dequeue(); } +//--------------------------------------------------------------------------------------- +void ManagedEventQueue::DeleteAll() +{ + RSLockHolder lockHolder(m_pLock); + + while (m_pFirstEvent != NULL) + { + // verify that the last event in the queue is actually the one stored as the last event + _ASSERTE( m_pFirstEvent->m_pNext != NULL || m_pFirstEvent == m_pLastEvent ); + + ManagedEvent * pNext = m_pFirstEvent->m_pNext; + delete m_pFirstEvent; + m_pFirstEvent = pNext; + } + m_pLastEvent = NULL; + + _ASSERTE(IsEmpty()); +}; + +//--------------------------------------------------------------------------------------- +// Worker to implement ICorDebugProcess::HasQueuedCallbacks for shim +//--------------------------------------------------------------------------------------- +BOOL ManagedEventQueue::HasQueuedCallbacks(ICorDebugThread * pThread) +{ + // This is from the public paths of ICorDebugProcess::HasQueuedCallbacks. + // In V2, this would fail in cases, notably including if the process is not synchronized. + // In arrowhead, it always succeeds. + + // No thread - look process wide. + if (pThread == NULL) + { + return !IsEmpty(); + } + + // If we have a thread, look for events with thread affinity. + DWORD dwThreadID = 0; + HRESULT hr = pThread->GetID(&dwThreadID); + (void)hr; //prevent "unused variable" error from GCC + SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); + + // Don't take lock until after we don't call any ICorDebug APIs. + RSLockHolder lockHolder(m_pLock); + + ManagedEvent * pCurrent = m_pFirstEvent; + while (pCurrent != NULL) + { + if (pCurrent->GetOSTid() == dwThreadID) + { + return true; + } + pCurrent = pCurrent->m_pNext; + } + return false; +} + + + + |