1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
9 // Inline functions for rspriv.h
11 //*****************************************************************************
18 // Get the native pipeline object, which resides on the Win32EventThread.
20 INativeEventPipeline * CordbWin32EventThread::GetNativePipeline()
22 return m_pNativePipeline;
26 // True if we're interop-debugging, else false.
27 // Note, we include this even in Non-interop builds because there are runtime checks throughout the APIs
28 // that certain operations only succeed/fail in interop-debugging.
30 bool CordbProcess::IsInteropDebugging()
32 #ifdef FEATURE_INTEROP_DEBUGGING
33 return (m_state & PS_WIN32_ATTACHED) != 0;
36 #endif // FEATURE_INTEROP_DEBUGGING
40 //-----------------------------------------------------------------------------
41 // Get the ShimProcess object.
44 // ShimProcess object if available; else NULL.
47 // This shim has V2 emulation logic.
48 // If we have no ShimProcess object, then we're in a V3 codepath.
49 // @dbgtodo - eventually, remove all emulation and this function.
50 //-----------------------------------------------------------------------------
52 ShimProcess * CordbProcess::GetShim()
59 //---------------------------------------------------------------------------------------
60 // Helper to read a structure from the target
63 // T - type of structure to read.
64 // pRemotePtr - remote pointer into target (src).
65 // pLocalBuffer - local buffer to copy into (Dest).
68 // Returns S_OK on success, in the event of a short read returns ERROR_PARTIAL_COPY
71 // This just does a raw Byte copy, but does not do any Marshalling.
72 // This fails if any part of the buffer can't be read.
74 //---------------------------------------------------------------------------------------
76 HRESULT CordbProcess::SafeReadStruct(CORDB_ADDRESS pRemotePtr, T * pLocalBuffer)
81 TargetBuffer tb(pRemotePtr, sizeof(T));
82 SafeReadBuffer(tb, (PBYTE) pLocalBuffer);
84 EX_CATCH_HRESULT(hr) ;
88 //---------------------------------------------------------------------------------------
89 // Destructor for RSInitHolder. Will safely neuter and release the object.
90 template<class T> inline
91 RSInitHolder<T>::~RSInitHolder()
93 if (m_pObject != NULL)
95 CordbProcess * pProcess = m_pObject->GetProcess();
96 RSLockHolder lockHolder(pProcess->GetProcessLock());
100 // Can't explicitly call 'delete' because somebody may have taken a reference.
105 //---------------------------------------------------------------------------------------
106 // Helper to write a structure to the target
109 // T - type of structure to read.
110 // pRemotePtr - remote pointer into target (dest).
111 // pLocalBuffer - local buffer to write (Src).
114 // Returns S_OK on success, in the event of a short write returns ERROR_PARTIAL_COPY
117 // This just does a raw Byte copy into the Target, but does not do any Marshalling.
118 // This fails if any part of the buffer can't be written.
120 //---------------------------------------------------------------------------------------
121 template<typename T> inline
122 HRESULT CordbProcess::SafeWriteStruct(CORDB_ADDRESS pRemotePtr, const T* pLocalBuffer)
127 TargetBuffer tb(pRemotePtr, sizeof(T));
128 SafeWriteBuffer(tb, (BYTE *) (pLocalBuffer));
130 EX_CATCH_HRESULT(hr);
135 CordbModule *CordbJITILFrame::GetModule()
137 return (m_ilCode->GetModule());
141 CordbAppDomain *CordbJITILFrame::GetCurrentAppDomain()
143 return (m_nativeFrame->GetCurrentAppDomain());
146 //-----------------------------------------------------------------------------
147 // Called to notify that we must flush DAC
148 //-----------------------------------------------------------------------------
150 void CordbProcess::ForceDacFlush()
152 // We need to take the process lock here because otherwise we could race with the Arrowhead stackwalking
153 // APIs. The Arrowhead stackwalking APIs check the flush counter and refresh all the state if necessary.
154 // However, while one thread is refreshing the state of the stackwalker, another thread may come in
155 // and force a flush. That's why we need to take a process lock before we flush. We need to synchronize
156 // with other threads which are using DAC memory.
157 RSLockHolder lockHolder(GetProcessLock());
159 // For Mac debugging, it is not safe to call into the DAC once code:INativeEventPipeline::TerminateProcess
160 // is called. Also, we must check m_exiting under the process lock.
163 if (m_pDacPrimitives != NULL)
165 STRESS_LOG1(LF_CORDB, LL_INFO1000, "Flush() - old counter: %d", m_flushCounter);
170 m_pDacPrimitives->FlushCache();
172 EX_CATCH_HRESULT(hr);
173 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
180 CordbFunction *CordbJITILFrame::GetFunction()
182 return m_nativeFrame->m_nativeCode->GetFunction();
185 //-----------------------------------------------------------------------------
186 // Helpers to assert threading semantics.
187 //-----------------------------------------------------------------------------
188 inline bool IsWin32EventThread(CordbProcess * p)
191 return p->IsWin32EventThread();
194 inline bool IsRCEventThread(Cordb* p)
197 return (p->m_rcEventThread != NULL) && p->m_rcEventThread->IsRCEventThread();
202 //-----------------------------------------------------------------------------
203 // StopContinueHolder. Ensure that we're synced during a certain region.
204 //-----------------------------------------------------------------------------
205 inline HRESULT StopContinueHolder::Init(CordbProcess * p)
208 LOG((LF_CORDB, LL_INFO100000, "Doing RS internal Stop\n"));
209 HRESULT hr = p->StopInternal(INFINITE, VMPTR_AppDomain::NullPtr());
210 if ((hr == CORDBG_E_PROCESS_TERMINATED) || SUCCEEDED(hr))
212 // Better be synced after calling Stop!
213 _ASSERTE(p->GetSynchronized());
220 inline StopContinueHolder::~StopContinueHolder()
222 // If Init() failed to call Stop, then don't call continue
227 LOG((LF_CORDB, LL_INFO100000, "Doing RS internal Continue\n"));
228 hr = m_p->ContinueInternal(false);
229 SIMPLIFYING_ASSUMPTION(
230 (hr == CORDBG_E_PROCESS_TERMINATED) ||
231 (hr == CORDBG_E_PROCESS_DETACHED) ||
232 (hr == CORDBG_E_OBJECT_NEUTERED) ||
233 (hr == E_ACCESSDENIED) || //Sadly in rare cases we leak this error code instead of PROCESS_TERMINATED
234 //See Dev10 bug 872621
238 //-----------------------------------------------------------------------------
239 // Neutering on the base object
240 //-----------------------------------------------------------------------------
242 void CordbCommonBase::Neuter()
244 LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object neutered: this=%p, id=%p\n", this, m_id));
248 // Unsafe neuter for an object that's already dead. Only use this if you know exactly what you're doing.
249 // The point here is that we can mark the object neutered even though we may not hold the stop-go lock.
251 void CordbCommonBase::UnsafeNeuterDeadObject()
253 LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object neutered: this=%p, id=%p\n", this, m_id));
258 //-----------------------------------------------------------------------------
259 // Reference Counting
260 //-----------------------------------------------------------------------------
262 void CordbCommonBase::InternalAddRef()
264 CONSISTENCY_CHECK_MSGF((m_RefCount & CordbBase_InternalRefCountMask) != (CordbBase_InternalRefCountMax),
265 ("Internal AddRef overlow, External Count = %d,\n'%s' @ 0x%p",
266 (m_RefCount >> CordbBase_ExternalRefCountShift), this->DbgGetName(), this));
268 // Since the internal ref-count is the lower bits, and we know we'll never overflow ;)
269 // we can just do an interlocked increment on the whole 32 bits.
270 #ifdef TRACK_OUTSTANDING_OBJECTS
271 MixedRefCountUnsigned Count =
274 InterlockedIncrement64((MixedRefCountSigned*) &m_RefCount);
279 // For leak detection in debug builds, track all internal references.
280 InterlockedIncrement(&Cordb::s_DbgMemTotalOutstandingInternalRefs);
283 #ifdef TRACK_OUTSTANDING_OBJECTS
284 if ((Count & CordbBase_InternalRefCountMask) != 1)
291 for (i = 0; i < Cordb::s_DbgMemOutstandingObjectMax; i++)
293 if (Cordb::s_DbgMemOutstandingObjects[i] == NULL)
295 if (InterlockedCompareExchangeT(&(Cordb::s_DbgMemOutstandingObjects[i]), (LPVOID) this, NULL) == NULL)
304 i = Cordb::s_DbgMemOutstandingObjectMax + 1;
306 while ((i < MAX_TRACKED_OUTSTANDING_OBJECTS) &&
307 (InterlockedCompareExchange(&Cordb::s_DbgMemOutstandingObjectMax, i, i - 1) != (i - 1)));
309 if (i < MAX_TRACKED_OUTSTANDING_OBJECTS)
311 Cordb::s_DbgMemOutstandingObjects[i] = this;
317 // Derived versions of AddRef / Release will call these.
320 ULONG CordbCommonBase::BaseAddRef()
322 Volatile<MixedRefCountUnsigned> ref;
323 MixedRefCountUnsigned refNew;
324 ExternalRefCount cExternalCount;
326 // Compute what refNew ought to look like; and then If m_RefCount hasn't changed on us
327 // (via another thread), then stash the new one in.
332 cExternalCount = (ExternalRefCount) (ref >> CordbBase_ExternalRefCountShift);
334 if (cExternalCount == CordbBase_InternalRefCountMax)
336 CONSISTENCY_CHECK_MSGF(false, ("Overflow in External AddRef. Internal Count =%d,\n'%s' @ 0x%p",
337 (ref & CordbBase_InternalRefCountMask), this->DbgGetName(), this));
339 // Ignore any AddRefs beyond this... This will screw up Release(), but we're
340 // probably already so screwed it wouldn't matter.
341 return cExternalCount;
346 refNew = (((MixedRefCountUnsigned)cExternalCount) << CordbBase_ExternalRefCountShift) | (ref & CordbBase_InternalRefCountMask);
348 while ((MixedRefCountUnsigned)InterlockedCompareExchange64((MixedRefCountSigned*)&m_RefCount, refNew, ref) != ref);
350 return cExternalCount;
353 // Do an AddRef against the External count. This is a semantics issue.
354 // We use this when an internal component Addrefs out-parameters (which Cordbg will call Release on).
356 void CordbCommonBase::ExternalAddRef()
358 // Call on BaseAddRef() to avoid any asserts that prevent stuff from inside the RS from bumping
359 // up the external ref count.
364 void CordbCommonBase::InternalRelease()
366 CONSISTENCY_CHECK_MSGF((m_RefCount & CordbBase_InternalRefCountMask) != 0,
367 ("Internal Release underflow, External Count = %d,\n'%s' @ 0x%p",
368 (m_RefCount >> CordbBase_ExternalRefCountShift), this->DbgGetName(), this));
371 // For leak detection in debug builds, track all internal references.
372 InterlockedDecrement(&Cordb::s_DbgMemTotalOutstandingInternalRefs);
377 // The internal count is in the low 16 bits, and we know that we'll never underflow the internal
379 // Furthermore we know that ExternalRelease will prevent us from underflowing the external release count.
380 // Thus we can just do an simple decrement here, and compare against 0x00000000 (which is the value
381 // when both the Internal + External counts are at 0)
382 MixedRefCountSigned cRefCount = InterlockedDecrement64((MixedRefCountSigned*) &m_RefCount);
384 #ifdef TRACK_OUTSTANDING_OBJECTS
385 if ((cRefCount & CordbBase_InternalRefCountMask) == 0)
387 for (LONG i = 0; i < Cordb::s_DbgMemOutstandingObjectMax; i++)
389 if (Cordb::s_DbgMemOutstandingObjects[i] == this)
391 Cordb::s_DbgMemOutstandingObjects[i] = NULL;
399 if (cRefCount == 0x00000000)
405 // Do an external release.
407 ULONG CordbCommonBase::BaseRelease()
409 Volatile<MixedRefCountUnsigned> ref;
410 MixedRefCountUnsigned refNew;
411 ExternalRefCount cExternalCount;
413 // Compute what refNew ought to look like; and then If m_RefCount hasn't changed on us
414 // (via another thread), then stash the new one in.
419 cExternalCount = (ExternalRefCount) (ref >> CordbBase_ExternalRefCountShift);
421 if (cExternalCount == 0)
423 CONSISTENCY_CHECK_MSGF(false, ("Underflow in External Release. Internal Count = %d\n'%s' @ 0x%p",
424 (ref & CordbBase_InternalRefCountMask), this->DbgGetName(), this));
426 // Ignore any Releases beyond this... This will screw up Release(), but we're
427 // probably already so screwed it wouldn't matter.
428 // It's very important that we don't let the release count go negative (both
429 // Releases assumes this when deciding whether to delete)
435 refNew = (((MixedRefCountUnsigned) cExternalCount) << CordbBase_ExternalRefCountShift) | (ref & CordbBase_InternalRefCountMask);
437 while ((MixedRefCountUnsigned)InterlockedCompareExchange64((MixedRefCountSigned*)&m_RefCount, refNew, ref) != ref);
439 // If the external count just dropped to 0, then this object can be neutered.
440 if (cExternalCount == 0)
450 return cExternalCount;
455 inline ULONG CordbCommonBase::BaseAddRefEnforceExternal()
457 // External refs shouldn't be called while in the RS
459 DbgRSThread * pThread = DbgRSThread::GetThread();
460 CONSISTENCY_CHECK_MSGF(!pThread->IsInRS(),
461 ("External addref for pThis=0x%p, name='%s' called from within RS",
462 this, this->DbgGetName()
465 return (BaseAddRef());
469 inline ULONG CordbCommonBase::BaseReleaseEnforceExternal()
472 DbgRSThread * pThread = DbgRSThread::GetThread();
474 CONSISTENCY_CHECK_MSGF(!pThread->IsInRS(),
475 ("External release for pThis=0x%p, name='%s' called from within RS",
476 this, this->DbgGetName()
480 return (BaseRelease());
485 //-----------------------------------------------------------------------------
487 //-----------------------------------------------------------------------------
491 inline bool RSLock::HasLock()
493 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
494 return m_tidOwner == ::GetCurrentThreadId();
499 // Ctor+ Dtor are only used for asserts.
500 inline RSLock::RSLock()
502 m_eAttr = cLockUninit;
503 m_tidOwner = (DWORD)-1;
506 inline RSLock::~RSLock()
508 // If this lock is still ininitialized, then no body ever deleted the critical section
509 // for it and we're leaking.
510 CONSISTENCY_CHECK_MSGF(!IsInit(), ("Leaking Critical section for RS Lock '%s'", m_szTag));
515 // Initialize a lock.
516 inline void RSLock::Init(const char * szTag, int eAttr, ERSLockLevel level)
518 CONSISTENCY_CHECK_MSGF(!IsInit(), ("RSLock '%s' already inited", szTag));
525 // Must be either re-entrant xor flat. (not neither; not both)
526 _ASSERTE(IsReentrant() ^ ((m_eAttr & cLockFlat) == cLockFlat));
528 _ASSERTE((level >= 0) && (level <= RSLock::LL_MAX));
532 InitializeCriticalSection(&m_lock);
536 inline void RSLock::Destroy()
538 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
539 DeleteCriticalSection(&m_lock);
542 m_eAttr = cLockUninit; // No longer initialized.
547 inline void RSLock::Lock()
549 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
552 DbgRSThread * pThread = DbgRSThread::GetThread();
553 pThread->NotifyTakeLock(this);
556 EnterCriticalSection(&m_lock);
558 m_tidOwner = ::GetCurrentThreadId();
561 // Either count == 1 or we're re-entrant.
562 _ASSERTE((m_count == 1) || (m_eAttr == cLockReentrant));
566 inline void RSLock::Unlock()
568 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
573 _ASSERTE(m_count >= 0);
576 m_tidOwner = (DWORD)-1;
581 // NotifyReleaseLock needs to be called before we release the lock.
582 // Note that HasLock()==false at this point. NotifyReleaseLock relies on that.
583 DbgRSThread * pThread = DbgRSThread::GetThread();
584 pThread->NotifyReleaseLock(this);
587 LeaveCriticalSection(&m_lock);
591 inline T* CordbSafeHashTable<T>::GetBase(ULONG_PTR id, BOOL fFab)
593 return static_cast<T*>(UnsafeGetBase(id, fFab));
597 inline T* CordbSafeHashTable<T>::GetBaseOrThrow(ULONG_PTR id, BOOL fFab)
599 T* pResult = GetBase(id, fFab);
602 ThrowHR(E_INVALIDARG);
610 // Copy the contents of the hash to an strong-ref array
613 // pArray - array to allocate storage and copy to
619 // Array takes strong internal references.
620 // This can be useful for dancing around locks; eg: If we want to iterate on a hash
621 // and do an operation that requires a lock that can't be held when iterating.
622 // (Example: Neuter needs Big stop-go lock; Hash is protected by little Process-lock).
625 inline void CordbSafeHashTable<T>::CopyToArray(RSPtrArray<T> * pArray)
627 // Assumes caller has necessary locks to iterate
628 UINT32 count = GetCount();
629 pArray->AllocOrThrow(count);
635 T * pCordbBase = FindFirst(&find);
638 pArray->Assign(idx, pCordbBase);
640 pCordbBase = FindNext(&find);
644 _ASSERTE(pCordbBase == NULL);
647 // Empty the contents of the hash to an array. Array gets ownersship.
650 // pArray - array to allocate and get ownership
656 // Hashtable will be empty after this.
658 inline void CordbSafeHashTable<T>::TransferToArray(RSPtrArray<T> * pArray)
660 // Assumes caller has necessary locks
663 UINT32 count = GetCount();
666 pArray->AllocOrThrow(count);
670 T * pCordbBase = FindFirst(&find);
671 _ASSERTE(pCordbBase != NULL);
672 pArray->Assign(idx, pCordbBase);
675 // We're removing while iterating the collection.
676 // But we reset the iteration each time by calling FindFirst.
677 RemoveBase((ULONG_PTR)pCordbBase->m_id); // this will call release, adjust GetCount()
681 _ASSERTE(GetCount() == 0);
685 // Neuter all elements in the hash table and empty the hash.
688 // pLock - lock required to iterate through hash.
691 // Caller ensured it's safe to Neuter.
692 // Caller has locked the hash.
695 inline void CordbSafeHashTable<T>::NeuterAndClear(RSLock * pLock)
697 _ASSERTE(pLock->HasLock());
700 UINT32 count = GetCount();
705 T * pCordbBase = FindFirst(&find);
706 _ASSERTE(pCordbBase != NULL);
708 // Using this Validate to help track down bug DevDiv bugs 739406
709 pCordbBase->ValidateObject();
710 pCordbBase->Neuter();
713 // We're removing while iterating the collection.
714 // But we reset the iteration each time by calling FindFirst.
715 RemoveBase((ULONG_PTR)pCordbBase->m_id); // this will call release, adjust GetCount()
719 _ASSERTE(GetCount() == 0);
723 #endif // RSPRIV_INL_