summaryrefslogtreecommitdiff
path: root/src/debug/di/shimevents.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/di/shimevents.cpp')
-rw-r--r--src/debug/di/shimevents.cpp292
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;
+}
+
+
+
+