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#include "common.h"
6#include "eventpipe.h"
7#include "eventpipeconfiguration.h"
8#include "eventpipebuffer.h"
9#include "eventpipebuffermanager.h"
10
11#ifdef FEATURE_PERFTRACING
12
13EventPipeBufferManager::EventPipeBufferManager()
14{
15 CONTRACTL
16 {
17 THROWS;
18 GC_NOTRIGGER;
19 MODE_ANY;
20 }
21 CONTRACTL_END;
22
23 m_pPerThreadBufferList = new SList<SListElem<EventPipeBufferList*>>();
24 m_sizeOfAllBuffers = 0;
25 m_lock.Init(LOCK_TYPE_DEFAULT);
26
27#ifdef _DEBUG
28 m_numBuffersAllocated = 0;
29 m_numBuffersStolen = 0;
30 m_numBuffersLeaked = 0;
31 m_numEventsStored = 0;
32 m_numEventsDropped = 0;
33 m_numEventsWritten = 0;
34#endif // _DEBUG
35}
36
37EventPipeBufferManager::~EventPipeBufferManager()
38{
39 CONTRACTL
40 {
41 NOTHROW;
42 GC_TRIGGERS;
43 MODE_ANY;
44 }
45 CONTRACTL_END;
46
47 if(m_pPerThreadBufferList != NULL)
48 {
49 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
50 while(pElem != NULL)
51 {
52 SListElem<EventPipeBufferList*> *pCurElem = pElem;
53
54 EventPipeBufferList *pThreadBufferList = pCurElem->GetValue();
55 if (!pThreadBufferList->OwnedByThread())
56 {
57 Thread *pThread = NULL;
58 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
59 {
60 if (pThread->GetEventPipeBufferList() == pThreadBufferList)
61 {
62 pThread->SetEventPipeBufferList(NULL);
63 break;
64 }
65 }
66
67 // We don't delete buffers themself because they can be in-use
68 delete(pThreadBufferList);
69 }
70
71 pElem = m_pPerThreadBufferList->GetNext(pElem);
72 delete(pCurElem);
73 }
74
75 delete(m_pPerThreadBufferList);
76 m_pPerThreadBufferList = NULL;
77 }
78}
79
80EventPipeBuffer* EventPipeBufferManager::AllocateBufferForThread(EventPipeSession &session, Thread *pThread, unsigned int requestSize)
81{
82 CONTRACTL
83 {
84 NOTHROW;
85 GC_NOTRIGGER;
86 MODE_ANY;
87 PRECONDITION(pThread != NULL);
88 PRECONDITION(requestSize > 0);
89 }
90 CONTRACTL_END;
91
92 // Allocating a buffer requires us to take the lock.
93 SpinLockHolder _slh(&m_lock);
94
95 // Determine if the requesting thread has at least one buffer.
96 // If not, we guarantee that each thread gets at least one (to prevent thrashing when the circular buffer size is too small).
97 bool allocateNewBuffer = false;
98 EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList();
99 if(pThreadBufferList == NULL)
100 {
101 pThreadBufferList = new (nothrow) EventPipeBufferList(this);
102 if (pThreadBufferList == NULL)
103 {
104 return NULL;
105 }
106
107 SListElem<EventPipeBufferList*> *pElem = new (nothrow) SListElem<EventPipeBufferList*>(pThreadBufferList);
108 if (pElem == NULL)
109 {
110 return NULL;
111 }
112
113 m_pPerThreadBufferList->InsertTail(pElem);
114 pThread->SetEventPipeBufferList(pThreadBufferList);
115 allocateNewBuffer = true;
116 }
117
118 // Determine if policy allows us to allocate another buffer, or if we need to steal one
119 // from another thread.
120 if(!allocateNewBuffer)
121 {
122 EventPipeConfiguration *pConfig = EventPipe::GetConfiguration();
123 if(pConfig == NULL)
124 {
125 return NULL;
126 }
127
128 size_t circularBufferSizeInBytes = pConfig->GetCircularBufferSize();
129 if(m_sizeOfAllBuffers < circularBufferSizeInBytes)
130 {
131 // We don't worry about the fact that a new buffer could put us over the circular buffer size.
132 // This is OK, and we won't do it again if we actually go over.
133 allocateNewBuffer = true;
134 }
135 }
136
137 // Only steal buffers from other threads if the session being written to is a
138 // file-based session. Streaming sessions will simply drop events.
139 // TODO: Add dropped events telemetry here.
140 EventPipeBuffer *pNewBuffer = NULL;
141 if(!allocateNewBuffer && (session.GetSessionType() == EventPipeSessionType::File))
142 {
143 // We can't allocate a new buffer.
144 // Find the oldest buffer, de-allocate it, and re-purpose it for this thread.
145
146 // Find the thread that contains the oldest stealable buffer, and get its list of buffers.
147 EventPipeBufferList *pListToStealFrom = FindThreadToStealFrom();
148 if(pListToStealFrom != NULL)
149 {
150 // Assert that the buffer we're stealing is not the only buffer in the list.
151 // This invariant is enforced by FindThreadToStealFrom.
152 _ASSERTE((pListToStealFrom->GetHead() != NULL) && (pListToStealFrom->GetHead()->GetNext() != NULL));
153
154 // Remove the oldest buffer from the list.
155 pNewBuffer = pListToStealFrom->GetAndRemoveHead();
156
157 // De-allocate the buffer. We do this because buffers are variable sized
158 // based on how much volume is coming from the thread.
159 DeAllocateBuffer(pNewBuffer);
160 pNewBuffer = NULL;
161
162 // Set that we want to allocate a new buffer.
163 allocateNewBuffer = true;
164
165#ifdef _DEBUG
166 m_numBuffersStolen++;
167#endif // _DEBUG
168
169 }
170 else
171 {
172 // This only happens when # of threads == # of buffers.
173 // We'll allocate one more buffer, and then this won't happen again.
174 allocateNewBuffer = true;
175 }
176 }
177
178 if(allocateNewBuffer)
179 {
180 // Pick a buffer size by multiplying the base buffer size by the number of buffers already allocated for this thread.
181 unsigned int sizeMultiplier = pThreadBufferList->GetCount() + 1;
182
183 // Pick the base buffer size based. Debug builds have a smaller size to stress the allocate/steal path more.
184 unsigned int baseBufferSize =
185#ifdef _DEBUG
186 30 * 1024; // 30K
187#else
188 100 * 1024; // 100K
189#endif
190 unsigned int bufferSize = baseBufferSize * sizeMultiplier;
191
192 // Make sure that buffer size >= request size so that the buffer size does not
193 // determine the max event size.
194 if(bufferSize < requestSize)
195 {
196 bufferSize = requestSize;
197 }
198
199 // Don't allow the buffer size to exceed 1MB.
200 const unsigned int maxBufferSize = 1024 * 1024;
201 if(bufferSize > maxBufferSize)
202 {
203 bufferSize = maxBufferSize;
204 }
205
206 // EX_TRY is used here as opposed to new (nothrow) because
207 // the constructor also allocates a private buffer, which
208 // could throw, and cannot be easily checked
209 EX_TRY
210 {
211 pNewBuffer = new EventPipeBuffer(bufferSize);
212 }
213 EX_CATCH
214 {
215 pNewBuffer = NULL;
216 }
217 EX_END_CATCH(SwallowAllExceptions);
218
219 if (pNewBuffer == NULL)
220 {
221 return NULL;
222 }
223
224 m_sizeOfAllBuffers += bufferSize;
225#ifdef _DEBUG
226 m_numBuffersAllocated++;
227#endif // _DEBUG
228 }
229
230 // Set the buffer on the thread.
231 if(pNewBuffer != NULL)
232 {
233 pThreadBufferList->InsertTail(pNewBuffer);
234 return pNewBuffer;
235 }
236
237 return NULL;
238}
239
240EventPipeBufferList* EventPipeBufferManager::FindThreadToStealFrom()
241{
242 CONTRACTL
243 {
244 NOTHROW;
245 GC_NOTRIGGER;
246 MODE_ANY;
247 PRECONDITION(m_lock.OwnedByCurrentThread());
248 }
249 CONTRACTL_END;
250
251 // Find the thread buffer list containing the buffer whose most recent event is the oldest as long as the buffer is not
252 // the current buffer for the thread (e.g. it's next pointer is non-NULL).
253 // This means that the thread must also have multiple buffers, so that we don't steal its only buffer.
254 EventPipeBufferList *pOldestContainingList = NULL;
255
256 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
257 while(pElem != NULL)
258 {
259 EventPipeBufferList *pCandidate = pElem->GetValue();
260
261 // The current candidate has more than one buffer (otherwise it is disqualified).
262 if(pCandidate->GetHead()->GetNext() != NULL)
263 {
264 // If we haven't seen any candidates, this one automatically becomes the oldest candidate.
265 if(pOldestContainingList == NULL)
266 {
267 pOldestContainingList = pCandidate;
268 }
269 // Otherwise, to replace the existing candidate, this candidate must have an older timestamp in its oldest buffer.
270 else if((pOldestContainingList->GetHead()->GetMostRecentTimeStamp().QuadPart) >
271 (pCandidate->GetHead()->GetMostRecentTimeStamp().QuadPart))
272 {
273 pOldestContainingList = pCandidate;
274 }
275 }
276
277 pElem = m_pPerThreadBufferList->GetNext(pElem);
278 }
279
280 return pOldestContainingList;
281}
282
283void EventPipeBufferManager::DeAllocateBuffer(EventPipeBuffer *pBuffer)
284{
285 CONTRACTL
286 {
287 NOTHROW;
288 GC_NOTRIGGER;
289 MODE_ANY;
290 }
291 CONTRACTL_END;
292
293 if(pBuffer != NULL)
294 {
295 m_sizeOfAllBuffers -= pBuffer->GetSize();
296 delete(pBuffer);
297#ifdef _DEBUG
298 m_numBuffersAllocated--;
299#endif // _DEBUG
300 }
301}
302
303bool EventPipeBufferManager::WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread, StackContents *pStack)
304{
305 CONTRACTL
306 {
307 NOTHROW;
308 GC_NOTRIGGER;
309 MODE_ANY;
310 // The input thread must match the current thread because no lock is taken on the buffer.
311 PRECONDITION(pThread == GetThread());
312 }
313 CONTRACTL_END;
314
315 _ASSERTE(pThread == GetThread());
316
317 // Check to see an event thread was specified. If not, then use the current thread.
318 if(pEventThread == NULL)
319 {
320 pEventThread = pThread;
321 }
322
323 // Before we pick a buffer, make sure the event is enabled.
324 if(!event.IsEnabled())
325 {
326 return false;
327 }
328
329 // The event is still enabled. Mark that the thread is now writing an event.
330 pThread->SetEventWriteInProgress(true);
331
332 // Check one more time to make sure that the event is still enabled.
333 // We do this because we might be trying to disable tracing and free buffers, so we
334 // must make sure that the event is enabled after we mark that we're writing to avoid
335 // races with the destructing thread.
336 if(!event.IsEnabled())
337 {
338 return false;
339 }
340
341 // See if the thread already has a buffer to try.
342 bool allocNewBuffer = false;
343 EventPipeBuffer *pBuffer = NULL;
344 EventPipeBufferList *pThreadBufferList = pThread->GetEventPipeBufferList();
345 if(pThreadBufferList == NULL)
346 {
347 allocNewBuffer = true;
348 }
349 else
350 {
351 // The thread already has a buffer list. Select the newest buffer and attempt to write into it.
352 pBuffer = pThreadBufferList->GetTail();
353 if(pBuffer == NULL)
354 {
355 // This should never happen. If the buffer list exists, it must contain at least one entry.
356 _ASSERT(!"Thread buffer list with zero entries encountered.");
357 return false;
358 }
359 else
360 {
361 // Attempt to write the event to the buffer. If this fails, we should allocate a new buffer.
362 allocNewBuffer = !pBuffer->WriteEvent(pEventThread, session, event, payload, pActivityId, pRelatedActivityId, pStack);
363 }
364 }
365
366 // Check to see if we need to allocate a new buffer, and if so, do it here.
367 if(allocNewBuffer)
368 {
369 // We previously switched to preemptive mode here, however, this is not safe and can cause deadlocks.
370 // When a GC is started, and background threads are created (for the first BGC), a thread creation event is fired.
371 // When control gets here the buffer is allocated, but then the thread hangs waiting for the GC to complete
372 // (it was marked as started before creating threads) so that it can switch back to cooperative mode.
373 // However, the GC is waiting on this call to return so that it can make forward progress. Thus it is not safe
374 // to switch to preemptive mode here.
375
376 unsigned int requestSize = sizeof(EventPipeEventInstance) + payload.GetSize();
377 pBuffer = AllocateBufferForThread(session, pThread, requestSize);
378 }
379
380 // Try to write the event after we allocated (or stole) a buffer.
381 // This is the first time if the thread had no buffers before the call to this function.
382 // This is the second time if this thread did have one or more buffers, but they were full.
383 if(allocNewBuffer && pBuffer != NULL)
384 {
385 allocNewBuffer = !pBuffer->WriteEvent(pEventThread, session, event, payload, pActivityId, pRelatedActivityId, pStack);
386 }
387
388 // Mark that the thread is no longer writing an event.
389 pThread->SetEventWriteInProgress(false);
390
391#ifdef _DEBUG
392 if(!allocNewBuffer)
393 {
394 InterlockedIncrement(&m_numEventsStored);
395 }
396 else
397 {
398 InterlockedIncrement(&m_numEventsDropped);
399 }
400#endif // _DEBUG
401 return !allocNewBuffer;
402}
403
404void EventPipeBufferManager::WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp)
405{
406 CONTRACTL
407 {
408 THROWS;
409 GC_NOTRIGGER;
410 MODE_ANY;
411 PRECONDITION(pFile != NULL);
412 }
413 CONTRACTL_END;
414
415 // TODO: Better version of merge sort.
416 // 1. Iterate through all of the threads, adding each buffer to a temporary list.
417 // 2. While iterating, get the lowest most recent timestamp. This is the timestamp that we want to process up to.
418 // 3. Process up to the lowest most recent timestamp for the set of buffers.
419 // 4. When we get NULLs from each of the buffers on PopNext(), we're done.
420 // 5. While iterating if PopNext() == NULL && Empty() == NULL, remove the buffer from the list. It's empty.
421 // 6. While iterating, grab the next lowest most recent timestamp.
422 // 7. Walk through the list again and look for any buffers that have a lower most recent timestamp than the next most recent timestamp.
423 // 8. If we find one, add it to the list and select its most recent timestamp as the lowest.
424 // 9. Process again (go to 3).
425 // 10. Continue until there are no more buffers to process.
426
427 // Take the lock before walking the buffer list.
428 SpinLockHolder _slh(&m_lock);
429
430 // Naively walk the circular buffer, writing the event stream in timestamp order.
431 while(true)
432 {
433 EventPipeEventInstance *pOldestInstance = NULL;
434 EventPipeBuffer *pOldestContainingBuffer = NULL;
435 EventPipeBufferList *pOldestContainingList = NULL;
436 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
437 while(pElem != NULL)
438 {
439 EventPipeBufferList *pBufferList = pElem->GetValue();
440
441 // Peek the next event out of the list.
442 EventPipeBuffer *pContainingBuffer = NULL;
443 EventPipeEventInstance *pNext = pBufferList->PeekNextEvent(stopTimeStamp, &pContainingBuffer);
444 if(pNext != NULL)
445 {
446 // If it's the oldest event we've seen, then save it.
447 if((pOldestInstance == NULL) ||
448 (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart))
449 {
450 pOldestInstance = pNext;
451 pOldestContainingBuffer = pContainingBuffer;
452 pOldestContainingList = pBufferList;
453 }
454 }
455
456 pElem = m_pPerThreadBufferList->GetNext(pElem);
457 }
458
459 if(pOldestInstance == NULL)
460 {
461 // We're done. There are no more events.
462 break;
463 }
464
465 // Write the oldest event.
466 pFile->WriteEvent(*pOldestInstance);
467#ifdef _DEBUG
468 m_numEventsWritten++;
469#endif // _DEBUG
470
471 // Pop the event from the buffer.
472 pOldestContainingList->PopNextEvent(stopTimeStamp);
473 }
474}
475
476EventPipeEventInstance* EventPipeBufferManager::GetNextEvent()
477{
478 CONTRACTL
479 {
480 NOTHROW;
481 GC_NOTRIGGER;
482 MODE_ANY;
483 }
484 CONTRACTL_END;
485
486 // Take the lock before walking the buffer list.
487 SpinLockHolder _slh(&m_lock);
488
489 // Naively walk the circular buffer, getting the event stream in timestamp order.
490 LARGE_INTEGER stopTimeStamp;
491 QueryPerformanceCounter(&stopTimeStamp);
492 while (true)
493 {
494 EventPipeEventInstance *pOldestInstance = NULL;
495 EventPipeBuffer *pOldestContainingBuffer = NULL;
496 EventPipeBufferList *pOldestContainingList = NULL;
497 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
498 while (pElem != NULL)
499 {
500 EventPipeBufferList *pBufferList = pElem->GetValue();
501
502 // Peek the next event out of the list.
503 EventPipeBuffer *pContainingBuffer = NULL;
504 EventPipeEventInstance *pNext = pBufferList->PeekNextEvent(stopTimeStamp, &pContainingBuffer);
505 if (pNext != NULL)
506 {
507 // If it's the oldest event we've seen, then save it.
508 if ((pOldestInstance == NULL) ||
509 (pOldestInstance->GetTimeStamp()->QuadPart > pNext->GetTimeStamp()->QuadPart))
510 {
511 pOldestInstance = pNext;
512 pOldestContainingBuffer = pContainingBuffer;
513 pOldestContainingList = pBufferList;
514 }
515 }
516
517 pElem = m_pPerThreadBufferList->GetNext(pElem);
518 }
519
520 if (pOldestInstance == NULL)
521 {
522 // We're done. There are no more events.
523 return NULL;
524 }
525
526 // Pop the event from the buffer.
527 pOldestContainingList->PopNextEvent(stopTimeStamp);
528
529 // Return the oldest event that hasn't yet been processed.
530 return pOldestInstance;
531 }
532}
533
534void EventPipeBufferManager::DeAllocateBuffers()
535{
536 CONTRACTL
537 {
538 NOTHROW;
539 GC_NOTRIGGER;
540 MODE_ANY;
541 }
542 CONTRACTL_END;
543
544 _ASSERTE(EnsureConsistency());
545
546 // Take the thread store lock because we're going to iterate through the thread list.
547 {
548 ThreadStoreLockHolder tsl;
549
550 // Take the buffer manager manipulation lock.
551 SpinLockHolder _slh(&m_lock);
552
553 Thread *pThread = NULL;
554 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
555 {
556 // Get the thread's buffer list.
557 EventPipeBufferList *pBufferList = pThread->GetEventPipeBufferList();
558 if(pBufferList != NULL)
559 {
560 // Attempt to free the buffer list.
561 // If the thread is using its buffer list skip it.
562 // This means we will leak a single buffer, but if tracing is re-enabled, that buffer can be used again.
563 if(!pThread->GetEventWriteInProgress())
564 {
565 EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead();
566 while(pBuffer != NULL)
567 {
568 DeAllocateBuffer(pBuffer);
569 pBuffer = pBufferList->GetAndRemoveHead();
570 }
571
572 // Remove the list entry from the per thread buffer list.
573 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
574 while(pElem != NULL)
575 {
576 EventPipeBufferList* pEntry = pElem->GetValue();
577 if(pEntry == pBufferList)
578 {
579 pElem = m_pPerThreadBufferList->FindAndRemove(pElem);
580
581 // In DEBUG, make sure that the element was found and removed.
582 _ASSERTE(pElem != NULL);
583
584 SListElem<EventPipeBufferList*> *pCurElem = pElem;
585 pElem = m_pPerThreadBufferList->GetNext(pElem);
586 delete(pCurElem);
587 }
588 else
589 {
590 pElem = m_pPerThreadBufferList->GetNext(pElem);
591 }
592 }
593
594 // Remove the list reference from the thread.
595 pThread->SetEventPipeBufferList(NULL);
596
597 // Now that all of the list elements have been freed, free the list itself.
598 delete(pBufferList);
599 pBufferList = NULL;
600 }
601#ifdef _DEBUG
602 else
603 {
604 // We can't deallocate the buffers.
605 m_numBuffersLeaked += pBufferList->GetCount();
606 }
607#endif // _DEBUG
608 }
609 }
610 }
611
612 // Now that we've walked through all of the threads, let's see if there are any other buffers
613 // that belonged to threads that died during tracing. We can free these now.
614
615 // Take the buffer manager manipulation lock
616 SpinLockHolder _slh(&m_lock);
617
618 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
619 while(pElem != NULL)
620 {
621 // Get the list and determine if we can free it.
622 EventPipeBufferList *pBufferList = pElem->GetValue();
623 if(!pBufferList->OwnedByThread())
624 {
625 // Iterate over all nodes in the list and de-allocate them.
626 EventPipeBuffer *pBuffer = pBufferList->GetAndRemoveHead();
627 while(pBuffer != NULL)
628 {
629 DeAllocateBuffer(pBuffer);
630 pBuffer = pBufferList->GetAndRemoveHead();
631 }
632
633 // Remove the buffer list from the per-thread buffer list.
634 pElem = m_pPerThreadBufferList->FindAndRemove(pElem);
635 _ASSERTE(pElem != NULL);
636
637 SListElem<EventPipeBufferList*> *pCurElem = pElem;
638 pElem = m_pPerThreadBufferList->GetNext(pElem);
639 delete(pCurElem);
640
641 // Now that all of the list elements have been freed, free the list itself.
642 delete(pBufferList);
643 pBufferList = NULL;
644 }
645 else
646 {
647 pElem = m_pPerThreadBufferList->GetNext(pElem);
648 }
649 }
650}
651
652#ifdef _DEBUG
653bool EventPipeBufferManager::EnsureConsistency()
654{
655 LIMITED_METHOD_CONTRACT;
656
657 SListElem<EventPipeBufferList*> *pElem = m_pPerThreadBufferList->GetHead();
658 while(pElem != NULL)
659 {
660 EventPipeBufferList *pBufferList = pElem->GetValue();
661
662 _ASSERTE(pBufferList->EnsureConsistency());
663
664 pElem = m_pPerThreadBufferList->GetNext(pElem);
665 }
666
667 return true;
668}
669#endif // _DEBUG
670
671EventPipeBufferList::EventPipeBufferList(EventPipeBufferManager *pManager)
672{
673 LIMITED_METHOD_CONTRACT;
674
675 m_pManager = pManager;
676 m_pHeadBuffer = NULL;
677 m_pTailBuffer = NULL;
678 m_bufferCount = 0;
679 m_pReadBuffer = NULL;
680 m_ownedByThread = true;
681
682#ifdef _DEBUG
683 m_pCreatingThread = GetThread();
684#endif // _DEBUG
685}
686
687EventPipeBuffer* EventPipeBufferList::GetHead()
688{
689 LIMITED_METHOD_CONTRACT;
690
691 return m_pHeadBuffer;
692}
693
694EventPipeBuffer* EventPipeBufferList::GetTail()
695{
696 LIMITED_METHOD_CONTRACT;
697
698 return m_pTailBuffer;
699}
700
701void EventPipeBufferList::InsertTail(EventPipeBuffer *pBuffer)
702{
703 CONTRACTL
704 {
705 NOTHROW;
706 GC_NOTRIGGER;
707 MODE_ANY;
708 PRECONDITION(pBuffer != NULL);
709 }
710 CONTRACTL_END;
711
712 _ASSERTE(EnsureConsistency());
713
714 // Ensure that the input buffer didn't come from another list that was improperly cleaned up.
715 _ASSERTE((pBuffer->GetNext() == NULL) && (pBuffer->GetPrevious() == NULL));
716
717 // First node in the list.
718 if(m_pTailBuffer == NULL)
719 {
720 m_pHeadBuffer = m_pTailBuffer = pBuffer;
721 }
722 else
723 {
724 // Set links between the old and new tail nodes.
725 m_pTailBuffer->SetNext(pBuffer);
726 pBuffer->SetPrevious(m_pTailBuffer);
727
728 // Set the new tail node.
729 m_pTailBuffer = pBuffer;
730 }
731
732 m_bufferCount++;
733
734 _ASSERTE(EnsureConsistency());
735}
736
737EventPipeBuffer* EventPipeBufferList::GetAndRemoveHead()
738{
739 CONTRACTL
740 {
741 NOTHROW;
742 GC_NOTRIGGER;
743 MODE_ANY;
744 }
745 CONTRACTL_END;
746
747 _ASSERTE(EnsureConsistency());
748
749 EventPipeBuffer *pRetBuffer = NULL;
750 if(m_pHeadBuffer != NULL)
751 {
752 // Save the head node.
753 pRetBuffer = m_pHeadBuffer;
754
755 // Set the new head node.
756 m_pHeadBuffer = m_pHeadBuffer->GetNext();
757
758 // Update the head node's previous pointer.
759 if(m_pHeadBuffer != NULL)
760 {
761 m_pHeadBuffer->SetPrevious(NULL);
762 }
763 else
764 {
765 // We just removed the last buffer from the list.
766 // Make sure both head and tail pointers are NULL.
767 m_pTailBuffer = NULL;
768 }
769
770 // Clear the next pointer of the old head node.
771 pRetBuffer->SetNext(NULL);
772
773 // Ensure that the old head node has no dangling references.
774 _ASSERTE((pRetBuffer->GetNext() == NULL) && (pRetBuffer->GetPrevious() == NULL));
775
776 // Decrement the count of buffers in the list.
777 m_bufferCount--;
778 }
779
780 _ASSERTE(EnsureConsistency());
781
782 return pRetBuffer;
783}
784
785unsigned int EventPipeBufferList::GetCount() const
786{
787 LIMITED_METHOD_CONTRACT;
788
789 return m_bufferCount;
790}
791
792EventPipeEventInstance* EventPipeBufferList::PeekNextEvent(LARGE_INTEGER beforeTimeStamp, EventPipeBuffer **pContainingBuffer)
793{
794 CONTRACTL
795 {
796 NOTHROW;
797 GC_NOTRIGGER;
798 MODE_ANY;
799 }
800 CONTRACTL_END;
801
802 // Get the current read buffer.
803 // If it's not set, start with the head buffer.
804 if(m_pReadBuffer == NULL)
805 {
806 m_pReadBuffer = m_pHeadBuffer;
807 }
808
809 // If the read buffer is still NULL, then this list contains no buffers.
810 if(m_pReadBuffer == NULL)
811 {
812 return NULL;
813 }
814
815 // Get the next event in the buffer.
816 EventPipeEventInstance *pNext = m_pReadBuffer->PeekNext(beforeTimeStamp);
817
818 // If the next event is NULL, then go to the next buffer.
819 if(pNext == NULL)
820 {
821 m_pReadBuffer = m_pReadBuffer->GetNext();
822 if(m_pReadBuffer != NULL)
823 {
824 pNext = m_pReadBuffer->PeekNext(beforeTimeStamp);
825 }
826 }
827
828 // Set the containing buffer.
829 if(pNext != NULL && pContainingBuffer != NULL)
830 {
831 *pContainingBuffer = m_pReadBuffer;
832 }
833
834 // Make sure pContainingBuffer is properly set.
835 _ASSERTE((pNext == NULL) || (pNext != NULL && pContainingBuffer == NULL) || (pNext != NULL && *pContainingBuffer == m_pReadBuffer));
836 return pNext;
837}
838
839EventPipeEventInstance* EventPipeBufferList::PopNextEvent(LARGE_INTEGER beforeTimeStamp)
840{
841 CONTRACTL
842 {
843 NOTHROW;
844 GC_NOTRIGGER;
845 MODE_ANY;
846 }
847 CONTRACTL_END;
848
849 // Get the next event.
850 EventPipeBuffer *pContainingBuffer = NULL;
851 EventPipeEventInstance *pNext = PeekNextEvent(beforeTimeStamp, &pContainingBuffer);
852
853 // Check to see if we need to clean-up the buffer that contained the previously popped event.
854 if(pContainingBuffer->GetPrevious() != NULL)
855 {
856 // Remove the previous node. The previous node should always be the head node.
857 EventPipeBuffer *pRemoved = GetAndRemoveHead();
858 _ASSERTE(pRemoved != pContainingBuffer);
859 _ASSERTE(pContainingBuffer == GetHead());
860
861 // De-allocate the buffer.
862 m_pManager->DeAllocateBuffer(pRemoved);
863 }
864
865 // If the event is non-NULL, pop it.
866 if(pNext != NULL && pContainingBuffer != NULL)
867 {
868 pContainingBuffer->PopNext(beforeTimeStamp);
869 }
870
871 return pNext;
872}
873
874bool EventPipeBufferList::OwnedByThread()
875{
876 LIMITED_METHOD_CONTRACT;
877 return m_ownedByThread;
878}
879
880void EventPipeBufferList::SetOwnedByThread(bool value)
881{
882 LIMITED_METHOD_CONTRACT;
883 m_ownedByThread = value;
884}
885
886#ifdef _DEBUG
887Thread* EventPipeBufferList::GetThread()
888{
889 LIMITED_METHOD_CONTRACT;
890
891 return m_pCreatingThread;
892}
893
894bool EventPipeBufferList::EnsureConsistency()
895{
896 CONTRACTL
897 {
898 NOTHROW;
899 GC_NOTRIGGER;
900 MODE_ANY;
901 }
902 CONTRACTL_END;
903
904 // Either the head and tail nodes are both NULL or both are non-NULL.
905 _ASSERTE((m_pHeadBuffer == NULL && m_pTailBuffer == NULL) || (m_pHeadBuffer != NULL && m_pTailBuffer != NULL));
906
907 // If the list is NULL, check the count and return.
908 if(m_pHeadBuffer == NULL)
909 {
910 _ASSERTE(m_bufferCount == 0);
911 return true;
912 }
913
914 // If the list is non-NULL, walk the list forward until we get to the end.
915 unsigned int nodeCount = (m_pHeadBuffer != NULL) ? 1 : 0;
916 EventPipeBuffer *pIter = m_pHeadBuffer;
917 while(pIter->GetNext() != NULL)
918 {
919 pIter = pIter->GetNext();
920 nodeCount++;
921
922 // Check for consistency of the buffer itself.
923 // NOTE: We can't check the last buffer because the owning thread could
924 // be writing to it, which could result in false asserts.
925 if(pIter->GetNext() != NULL)
926 {
927 _ASSERTE(pIter->EnsureConsistency());
928 }
929
930 // Check for cycles.
931 _ASSERTE(nodeCount <= m_bufferCount);
932 }
933
934 // When we're done with the walk, pIter must point to the tail node.
935 _ASSERTE(pIter == m_pTailBuffer);
936
937 // Node count must equal the buffer count.
938 _ASSERTE(nodeCount == m_bufferCount);
939
940 // Now, walk the list in reverse.
941 pIter = m_pTailBuffer;
942 nodeCount = (m_pTailBuffer != NULL) ? 1 : 0;
943 while(pIter->GetPrevious() != NULL)
944 {
945 pIter = pIter->GetPrevious();
946 nodeCount++;
947
948 // Check for cycles.
949 _ASSERTE(nodeCount <= m_bufferCount);
950 }
951
952 // When we're done with the reverse walk, pIter must point to the head node.
953 _ASSERTE(pIter == m_pHeadBuffer);
954
955 // Node count must equal the buffer count.
956 _ASSERTE(nodeCount == m_bufferCount);
957
958 // We're done.
959 return true;
960}
961#endif // _DEBUG
962
963#endif // FEATURE_PERFTRACING
964