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//*****************************************************************************
5// File: rspriv.inl
6//
7
8//
9// Inline functions for rspriv.h
10//
11//*****************************************************************************
12
13#ifndef RSPRIV_INL_
14#define RSPRIV_INL_
15
16#include "rspriv.h"
17
18// Get the native pipeline object, which resides on the Win32EventThread.
19inline
20INativeEventPipeline * CordbWin32EventThread::GetNativePipeline()
21{
22 return m_pNativePipeline;
23}
24
25
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.
29inline
30bool CordbProcess::IsInteropDebugging()
31{
32#ifdef FEATURE_INTEROP_DEBUGGING
33 return (m_state & PS_WIN32_ATTACHED) != 0;
34#else
35 return false;
36#endif // FEATURE_INTEROP_DEBUGGING
37}
38
39
40//-----------------------------------------------------------------------------
41// Get the ShimProcess object.
42//
43// Returns:
44// ShimProcess object if available; else NULL.
45//
46// Notes:
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//-----------------------------------------------------------------------------
51inline
52ShimProcess * CordbProcess::GetShim()
53{
54 return m_pShim;
55};
56
57
58
59//---------------------------------------------------------------------------------------
60// Helper to read a structure from the target
61//
62// Arguments:
63// T - type of structure to read.
64// pRemotePtr - remote pointer into target (src).
65// pLocalBuffer - local buffer to copy into (Dest).
66//
67// Return Value:
68// Returns S_OK on success, in the event of a short read returns ERROR_PARTIAL_COPY
69//
70// Notes:
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.
73//
74//---------------------------------------------------------------------------------------
75template<typename T>
76HRESULT CordbProcess::SafeReadStruct(CORDB_ADDRESS pRemotePtr, T * pLocalBuffer)
77{
78 HRESULT hr = S_OK;
79 EX_TRY
80 {
81 TargetBuffer tb(pRemotePtr, sizeof(T));
82 SafeReadBuffer(tb, (PBYTE) pLocalBuffer);
83 }
84 EX_CATCH_HRESULT(hr) ;
85 return hr;
86}
87
88//---------------------------------------------------------------------------------------
89// Destructor for RSInitHolder. Will safely neuter and release the object.
90template<class T> inline
91RSInitHolder<T>::~RSInitHolder()
92{
93 if (m_pObject != NULL)
94 {
95 CordbProcess * pProcess = m_pObject->GetProcess();
96 RSLockHolder lockHolder(pProcess->GetProcessLock());
97
98 m_pObject->Neuter();
99
100 // Can't explicitly call 'delete' because somebody may have taken a reference.
101 m_pObject.Clear();
102 }
103}
104
105//---------------------------------------------------------------------------------------
106// Helper to write a structure to the target
107//
108// Arguments:
109// T - type of structure to read.
110// pRemotePtr - remote pointer into target (dest).
111// pLocalBuffer - local buffer to write (Src).
112//
113// Return Value:
114// Returns S_OK on success, in the event of a short write returns ERROR_PARTIAL_COPY
115//
116// Notes:
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.
119//
120//---------------------------------------------------------------------------------------
121template<typename T> inline
122HRESULT CordbProcess::SafeWriteStruct(CORDB_ADDRESS pRemotePtr, const T* pLocalBuffer)
123{
124 HRESULT hr= S_OK;
125 EX_TRY
126 {
127 TargetBuffer tb(pRemotePtr, sizeof(T));
128 SafeWriteBuffer(tb, (BYTE *) (pLocalBuffer));
129 }
130 EX_CATCH_HRESULT(hr);
131 return hr;
132}
133
134inline
135CordbModule *CordbJITILFrame::GetModule()
136{
137 return (m_ilCode->GetModule());
138}
139
140inline
141CordbAppDomain *CordbJITILFrame::GetCurrentAppDomain()
142{
143 return (m_nativeFrame->GetCurrentAppDomain());
144}
145
146//-----------------------------------------------------------------------------
147// Called to notify that we must flush DAC
148//-----------------------------------------------------------------------------
149inline
150void CordbProcess::ForceDacFlush()
151{
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());
158
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.
161 if (!m_exiting)
162 {
163 if (m_pDacPrimitives != NULL)
164 {
165 STRESS_LOG1(LF_CORDB, LL_INFO1000, "Flush() - old counter: %d\n", m_flushCounter);
166 m_flushCounter++;
167 HRESULT hr = S_OK;
168 EX_TRY
169 {
170 m_pDacPrimitives->FlushCache();
171 }
172 EX_CATCH_HRESULT(hr);
173 SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr);
174 }
175 }
176}
177
178
179inline
180CordbFunction *CordbJITILFrame::GetFunction()
181{
182 return m_nativeFrame->m_nativeCode->GetFunction();
183}
184
185//-----------------------------------------------------------------------------
186// Helpers to assert threading semantics.
187//-----------------------------------------------------------------------------
188inline bool IsWin32EventThread(CordbProcess * p)
189{
190 _ASSERTE(p!= NULL);
191 return p->IsWin32EventThread();
192}
193
194inline bool IsRCEventThread(Cordb* p)
195{
196 _ASSERTE(p!= NULL);
197 return (p->m_rcEventThread != NULL) && p->m_rcEventThread->IsRCEventThread();
198}
199
200
201
202//-----------------------------------------------------------------------------
203// StopContinueHolder. Ensure that we're synced during a certain region.
204//-----------------------------------------------------------------------------
205inline HRESULT StopContinueHolder::Init(CordbProcess * p)
206{
207 _ASSERTE(p != NULL);
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))
211 {
212 // Better be synced after calling Stop!
213 _ASSERTE(p->GetSynchronized());
214 m_p = p;
215 }
216
217 return hr;
218};
219
220inline StopContinueHolder::~StopContinueHolder()
221{
222 // If Init() failed to call Stop, then don't call continue
223 if (m_p == NULL)
224 return;
225
226 HRESULT hr;
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
235 SUCCEEDED(hr));
236}
237
238//-----------------------------------------------------------------------------
239// Neutering on the base object
240//-----------------------------------------------------------------------------
241inline
242void CordbCommonBase::Neuter()
243{
244 LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object neutered: this=%p, id=%p\n", this, m_id));
245 m_fIsNeutered = 1;
246}
247
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.
250inline
251void CordbCommonBase::UnsafeNeuterDeadObject()
252{
253 LOG((LF_CORDB, LL_EVERYTHING, "Memory: CordbBase object neutered: this=%p, id=%p\n", this, m_id));
254 m_fIsNeutered = 1;
255}
256
257
258//-----------------------------------------------------------------------------
259// Reference Counting
260//-----------------------------------------------------------------------------
261inline
262void CordbCommonBase::InternalAddRef()
263{
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));
267
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 =
272#endif
273
274 InterlockedIncrement64((MixedRefCountSigned*) &m_RefCount);
275
276
277#ifdef _DEBUG_IMPL
278
279 // For leak detection in debug builds, track all internal references.
280 InterlockedIncrement(&Cordb::s_DbgMemTotalOutstandingInternalRefs);
281#endif
282
283#ifdef TRACK_OUTSTANDING_OBJECTS
284 if ((Count & CordbBase_InternalRefCountMask) != 1)
285 {
286 return;
287 }
288
289 LONG i;
290
291 for (i = 0; i < Cordb::s_DbgMemOutstandingObjectMax; i++)
292 {
293 if (Cordb::s_DbgMemOutstandingObjects[i] == NULL)
294 {
295 if (InterlockedCompareExchangeT(&(Cordb::s_DbgMemOutstandingObjects[i]), (LPVOID) this, NULL) == NULL)
296 {
297 return;
298 }
299 }
300 }
301
302 do
303 {
304 i = Cordb::s_DbgMemOutstandingObjectMax + 1;
305 }
306 while ((i < MAX_TRACKED_OUTSTANDING_OBJECTS) &&
307 (InterlockedCompareExchange(&Cordb::s_DbgMemOutstandingObjectMax, i, i - 1) != (i - 1)));
308
309 if (i < MAX_TRACKED_OUTSTANDING_OBJECTS)
310 {
311 Cordb::s_DbgMemOutstandingObjects[i] = this;
312 }
313#endif
314
315}
316
317// Derived versions of AddRef / Release will call these.
318// External AddRef.
319inline
320ULONG CordbCommonBase::BaseAddRef()
321{
322 Volatile<MixedRefCountUnsigned> ref;
323 MixedRefCountUnsigned refNew;
324 ExternalRefCount cExternalCount;
325
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.
328 do
329 {
330 ref = m_RefCount;
331
332 cExternalCount = (ExternalRefCount) (ref >> CordbBase_ExternalRefCountShift);
333
334 if (cExternalCount == CordbBase_InternalRefCountMax)
335 {
336 CONSISTENCY_CHECK_MSGF(false, ("Overflow in External AddRef. Internal Count =%d,\n'%s' @ 0x%p",
337 (ref & CordbBase_InternalRefCountMask), this->DbgGetName(), this));
338
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;
342 }
343
344 cExternalCount++;
345
346 refNew = (((MixedRefCountUnsigned)cExternalCount) << CordbBase_ExternalRefCountShift) | (ref & CordbBase_InternalRefCountMask);
347 }
348 while ((MixedRefCountUnsigned)InterlockedCompareExchange64((MixedRefCountSigned*)&m_RefCount, refNew, ref) != ref);
349
350 return cExternalCount;
351}
352
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).
355inline
356void CordbCommonBase::ExternalAddRef()
357{
358 // Call on BaseAddRef() to avoid any asserts that prevent stuff from inside the RS from bumping
359 // up the external ref count.
360 BaseAddRef();
361}
362
363inline
364void CordbCommonBase::InternalRelease()
365{
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));
369
370#ifdef _DEBUG_IMPL
371 // For leak detection in debug builds, track all internal references.
372 InterlockedDecrement(&Cordb::s_DbgMemTotalOutstandingInternalRefs);
373#endif
374
375
376
377 // The internal count is in the low 16 bits, and we know that we'll never underflow the internal
378 // release. ;)
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);
383
384#ifdef TRACK_OUTSTANDING_OBJECTS
385 if ((cRefCount & CordbBase_InternalRefCountMask) == 0)
386 {
387 for (LONG i = 0; i < Cordb::s_DbgMemOutstandingObjectMax; i++)
388 {
389 if (Cordb::s_DbgMemOutstandingObjects[i] == this)
390 {
391 Cordb::s_DbgMemOutstandingObjects[i] = NULL;
392 break;
393 }
394 }
395 }
396#endif
397
398
399 if (cRefCount == 0x00000000)
400 {
401 delete this;
402 }
403}
404
405// Do an external release.
406inline
407ULONG CordbCommonBase::BaseRelease()
408{
409 Volatile<MixedRefCountUnsigned> ref;
410 MixedRefCountUnsigned refNew;
411 ExternalRefCount cExternalCount;
412
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.
415 do
416 {
417 ref = m_RefCount;
418
419 cExternalCount = (ExternalRefCount) (ref >> CordbBase_ExternalRefCountShift);
420
421 if (cExternalCount == 0)
422 {
423 CONSISTENCY_CHECK_MSGF(false, ("Underflow in External Release. Internal Count = %d\n'%s' @ 0x%p",
424 (ref & CordbBase_InternalRefCountMask), this->DbgGetName(), this));
425
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)
430 return 0;
431 }
432
433 cExternalCount--;
434
435 refNew = (((MixedRefCountUnsigned) cExternalCount) << CordbBase_ExternalRefCountShift) | (ref & CordbBase_InternalRefCountMask);
436 }
437 while ((MixedRefCountUnsigned)InterlockedCompareExchange64((MixedRefCountSigned*)&m_RefCount, refNew, ref) != ref);
438
439 // If the external count just dropped to 0, then this object can be neutered.
440 if (cExternalCount == 0)
441 {
442 m_fNeuterAtWill = 1;
443 }
444
445 if (refNew == 0)
446 {
447 delete this;
448 return 0;
449 }
450 return cExternalCount;
451
452}
453
454
455inline ULONG CordbCommonBase::BaseAddRefEnforceExternal()
456{
457 // External refs shouldn't be called while in the RS
458#ifdef RSCONTRACTS
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()
463 ));
464#endif
465 return (BaseAddRef());
466
467}
468
469inline ULONG CordbCommonBase::BaseReleaseEnforceExternal()
470{
471#ifdef RSCONTRACTS
472 DbgRSThread * pThread = DbgRSThread::GetThread();
473
474 CONSISTENCY_CHECK_MSGF(!pThread->IsInRS(),
475 ("External release for pThis=0x%p, name='%s' called from within RS",
476 this, this->DbgGetName()
477 ));
478#endif
479
480 return (BaseRelease());
481}
482
483
484
485//-----------------------------------------------------------------------------
486// Locks
487//-----------------------------------------------------------------------------
488
489// Base class
490#ifdef _DEBUG
491inline bool RSLock::HasLock()
492{
493 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
494 return m_tidOwner == ::GetCurrentThreadId();
495}
496#endif
497
498#ifdef _DEBUG
499// Ctor+ Dtor are only used for asserts.
500inline RSLock::RSLock()
501{
502 m_eAttr = cLockUninit;
503 m_tidOwner = (DWORD)-1;
504};
505
506inline RSLock::~RSLock()
507{
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));
511}
512#endif
513
514
515// Initialize a lock.
516inline void RSLock::Init(const char * szTag, int eAttr, ERSLockLevel level)
517{
518 CONSISTENCY_CHECK_MSGF(!IsInit(), ("RSLock '%s' already inited", szTag));
519#ifdef _DEBUG
520 m_szTag = szTag;
521 m_eAttr = eAttr;
522 m_count = 0;
523 m_level = level;
524
525 // Must be either re-entrant xor flat. (not neither; not both)
526 _ASSERTE(IsReentrant() ^ ((m_eAttr & cLockFlat) == cLockFlat));
527#endif
528 _ASSERTE((level >= 0) && (level <= RSLock::LL_MAX));
529
530 _ASSERTE(IsInit());
531
532 InitializeCriticalSection(&m_lock);
533}
534
535// Cleanup a lock.
536inline void RSLock::Destroy()
537{
538 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
539 DeleteCriticalSection(&m_lock);
540
541#ifdef _DEBUG
542 m_eAttr = cLockUninit; // No longer initialized.
543 _ASSERTE(!IsInit());
544#endif
545}
546
547inline void RSLock::Lock()
548{
549 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
550
551#ifdef RSCONTRACTS
552 DbgRSThread * pThread = DbgRSThread::GetThread();
553 pThread->NotifyTakeLock(this);
554#endif
555
556 EnterCriticalSection(&m_lock);
557#ifdef _DEBUG
558 m_tidOwner = ::GetCurrentThreadId();
559 m_count++;
560
561 // Either count == 1 or we're re-entrant.
562 _ASSERTE((m_count == 1) || (m_eAttr == cLockReentrant));
563#endif
564}
565
566inline void RSLock::Unlock()
567{
568 CONSISTENCY_CHECK_MSGF(IsInit(), ("RSLock '%s' not inited", m_szTag));
569
570#ifdef _DEBUG
571 _ASSERTE(HasLock());
572 m_count--;
573 _ASSERTE(m_count >= 0);
574 if (m_count == 0)
575 {
576 m_tidOwner = (DWORD)-1;
577 }
578#endif
579
580#ifdef RSCONTRACTS
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);
585#endif
586
587 LeaveCriticalSection(&m_lock);
588}
589
590template <class T>
591inline T* CordbSafeHashTable<T>::GetBase(ULONG_PTR id, BOOL fFab)
592{
593 return static_cast<T*>(UnsafeGetBase(id, fFab));
594}
595
596template <class T>
597inline T* CordbSafeHashTable<T>::GetBaseOrThrow(ULONG_PTR id, BOOL fFab)
598{
599 T* pResult = GetBase(id, fFab);
600 if (pResult == NULL)
601 {
602 ThrowHR(E_INVALIDARG);
603 }
604 else
605 {
606 return pResult;
607 }
608}
609
610// Copy the contents of the hash to an strong-ref array
611//
612// Arguments:
613// pArray - array to allocate storage and copy to
614//
615// Assumptions:
616// Caller locks.
617//
618// Notes:
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).
623//
624template <class T>
625inline void CordbSafeHashTable<T>::CopyToArray(RSPtrArray<T> * pArray)
626{
627 // Assumes caller has necessary locks to iterate
628 UINT32 count = GetCount();
629 pArray->AllocOrThrow(count);
630
631
632 HASHFIND find;
633 UINT32 idx = 0;
634
635 T * pCordbBase = FindFirst(&find);
636 while(idx < count)
637 {
638 pArray->Assign(idx, pCordbBase);
639 idx++;
640 pCordbBase = FindNext(&find);
641 }
642
643 // Assert is at end.
644 _ASSERTE(pCordbBase == NULL);
645}
646
647// Empty the contents of the hash to an array. Array gets ownersship.
648//
649// Arguments:
650// pArray - array to allocate and get ownership
651//
652// Assumptions:
653// Caller locks.
654//
655// Notes:
656// Hashtable will be empty after this.
657template <class T>
658inline void CordbSafeHashTable<T>::TransferToArray(RSPtrArray<T> * pArray)
659{
660 // Assumes caller has necessary locks
661
662 HASHFIND find;
663 UINT32 count = GetCount();
664 UINT32 idx = 0;
665
666 pArray->AllocOrThrow(count);
667
668 while(idx < count)
669 {
670 T * pCordbBase = FindFirst(&find);
671 _ASSERTE(pCordbBase != NULL);
672 pArray->Assign(idx, pCordbBase);
673
674 idx++;
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()
678 }
679
680 // Assert is at end.
681 _ASSERTE(GetCount() == 0);
682}
683
684//
685// Neuter all elements in the hash table and empty the hash.
686//
687// Arguments:
688// pLock - lock required to iterate through hash.
689//
690// Assumptions:
691// Caller ensured it's safe to Neuter.
692// Caller has locked the hash.
693//
694template <class T>
695inline void CordbSafeHashTable<T>::NeuterAndClear(RSLock * pLock)
696{
697 _ASSERTE(pLock->HasLock());
698
699 HASHFIND find;
700 UINT32 count = GetCount();
701 UINT32 idx = 0;
702
703 while(idx < count)
704 {
705 T * pCordbBase = FindFirst(&find);
706 _ASSERTE(pCordbBase != NULL);
707
708 // Using this Validate to help track down bug DevDiv bugs 739406
709 pCordbBase->ValidateObject();
710 pCordbBase->Neuter();
711 idx++;
712
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()
716 }
717
718 // Assert is at end.
719 _ASSERTE(GetCount() == 0);
720}
721
722
723#endif // RSPRIV_INL_
724