| 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 | #ifndef __EVENTPIPE_BUFFERMANAGER_H__ |
| 6 | #define __EVENTPIPE_BUFFERMANAGER_H__ |
| 7 | |
| 8 | #ifdef FEATURE_PERFTRACING |
| 9 | |
| 10 | #include "eventpipe.h" |
| 11 | #include "eventpipefile.h" |
| 12 | #include "eventpipebuffer.h" |
| 13 | #include "eventpipesession.h" |
| 14 | #include "spinlock.h" |
| 15 | |
| 16 | class EventPipeBufferList; |
| 17 | |
| 18 | class EventPipeBufferManager |
| 19 | { |
| 20 | |
| 21 | // Declare friends. |
| 22 | friend class EventPipeBufferList; |
| 23 | |
| 24 | private: |
| 25 | |
| 26 | // A list of linked-lists of buffer objects. |
| 27 | // Each entry in this list represents a set of buffers owned by a single thread. |
| 28 | // The actual Thread object has a pointer to the object contained in this list. This ensures that |
| 29 | // each thread can access its own list, while at the same time, ensuring that when |
| 30 | // a thread is destroyed, we keep the buffers around without having to perform any |
| 31 | // migration or book-keeping. |
| 32 | SList<SListElem<EventPipeBufferList*>> *m_pPerThreadBufferList; |
| 33 | |
| 34 | // The total allocation size of buffers under management. |
| 35 | size_t m_sizeOfAllBuffers; |
| 36 | |
| 37 | // Lock to protect access to the per-thread buffer list and total allocation size. |
| 38 | SpinLock m_lock; |
| 39 | |
| 40 | #ifdef _DEBUG |
| 41 | // For debugging purposes. |
| 42 | unsigned int m_numBuffersAllocated; |
| 43 | unsigned int ; |
| 44 | unsigned int m_numBuffersLeaked; |
| 45 | Volatile<LONG> m_numEventsStored; |
| 46 | Volatile<LONG> m_numEventsDropped; |
| 47 | LONG m_numEventsWritten; |
| 48 | #endif // _DEBUG |
| 49 | |
| 50 | // Allocate a new buffer for the specified thread. |
| 51 | // This function will store the buffer in the thread's buffer list for future use and also return it here. |
| 52 | // A NULL return value means that a buffer could not be allocated. |
| 53 | EventPipeBuffer* AllocateBufferForThread(EventPipeSession &session, Thread *pThread, unsigned int requestSize); |
| 54 | |
| 55 | // Add a buffer to the thread buffer list. |
| 56 | void AddBufferToThreadBufferList(EventPipeBufferList *pThreadBuffers, EventPipeBuffer *pBuffer); |
| 57 | |
| 58 | // Find the thread that owns the oldest buffer that is eligible to be stolen. |
| 59 | EventPipeBufferList* FindThreadToStealFrom(); |
| 60 | |
| 61 | // De-allocates the input buffer. |
| 62 | void DeAllocateBuffer(EventPipeBuffer *pBuffer); |
| 63 | |
| 64 | public: |
| 65 | |
| 66 | EventPipeBufferManager(); |
| 67 | ~EventPipeBufferManager(); |
| 68 | |
| 69 | // Write an event to the input thread's current event buffer. |
| 70 | // An optional eventThread can be provided for sample profiler events. |
| 71 | // This is because the thread that writes the events is not the same as the "event thread". |
| 72 | // An optional stack trace can be provided for sample profiler events. |
| 73 | // Otherwise, if a stack trace is needed, one will be automatically collected. |
| 74 | bool WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, Thread *pEventThread = NULL, StackContents *pStack = NULL); |
| 75 | |
| 76 | // Write the contents of the managed buffers to the specified file. |
| 77 | // The stopTimeStamp is used to determine when tracing was stopped to ensure that we |
| 78 | // skip any events that might be partially written due to races when tracing is stopped. |
| 79 | void WriteAllBuffersToFile(EventPipeFile *pFile, LARGE_INTEGER stopTimeStamp); |
| 80 | |
| 81 | // Attempt to de-allocate resources as best we can. It is possible for some buffers to leak because |
| 82 | // threads can be in the middle of a write operation and get blocked, and we may not get an opportunity |
| 83 | // to free their buffer for a very long time. |
| 84 | void DeAllocateBuffers(); |
| 85 | |
| 86 | // Get next event. This is used to dispatch events to EventListener. |
| 87 | EventPipeEventInstance* GetNextEvent(); |
| 88 | |
| 89 | #ifdef _DEBUG |
| 90 | bool EnsureConsistency(); |
| 91 | #endif // _DEBUG |
| 92 | }; |
| 93 | |
| 94 | // Represents a list of buffers associated with a specific thread. |
| 95 | class EventPipeBufferList |
| 96 | { |
| 97 | private: |
| 98 | |
| 99 | // The buffer manager that owns this list. |
| 100 | EventPipeBufferManager *m_pManager; |
| 101 | |
| 102 | // Buffers are stored in an intrusive linked-list from oldest to newest. |
| 103 | // Head is the oldest buffer. Tail is the newest (and currently used) buffer. |
| 104 | EventPipeBuffer *m_pHeadBuffer; |
| 105 | EventPipeBuffer *m_pTailBuffer; |
| 106 | |
| 107 | // The number of buffers in the list. |
| 108 | unsigned int m_bufferCount; |
| 109 | |
| 110 | // The current read buffer (used when processing events on tracing stop). |
| 111 | EventPipeBuffer *m_pReadBuffer; |
| 112 | |
| 113 | // True if this thread is owned by a thread. |
| 114 | // If it is false, then this buffer can be de-allocated after it is drained. |
| 115 | Volatile<bool> m_ownedByThread; |
| 116 | |
| 117 | #ifdef _DEBUG |
| 118 | // For diagnostics, keep the thread pointer. |
| 119 | Thread *m_pCreatingThread; |
| 120 | #endif // _DEBUG |
| 121 | |
| 122 | public: |
| 123 | |
| 124 | EventPipeBufferList(EventPipeBufferManager *pManager); |
| 125 | |
| 126 | // Get the head node of the list. |
| 127 | EventPipeBuffer* GetHead(); |
| 128 | |
| 129 | // Get the tail node of the list. |
| 130 | EventPipeBuffer* GetTail(); |
| 131 | |
| 132 | // Insert a new buffer at the tail of the list. |
| 133 | void InsertTail(EventPipeBuffer *pBuffer); |
| 134 | |
| 135 | // Remove the head node of the list. |
| 136 | EventPipeBuffer* GetAndRemoveHead(); |
| 137 | |
| 138 | // Get the count of buffers in the list. |
| 139 | unsigned int GetCount() const; |
| 140 | |
| 141 | // Get the next event as long as it is before the specified timestamp. |
| 142 | EventPipeEventInstance* PeekNextEvent(LARGE_INTEGER beforeTimeStamp, EventPipeBuffer **pContainingBuffer); |
| 143 | |
| 144 | // Get the next event as long as it is before the specified timestamp, and also mark it as read. |
| 145 | EventPipeEventInstance* PopNextEvent(LARGE_INTEGER beforeTimeStamp); |
| 146 | |
| 147 | // True if a thread owns this list. |
| 148 | bool OwnedByThread(); |
| 149 | |
| 150 | // Set whether or not this list is owned by a thread. |
| 151 | // If it is not owned by a thread, then it can be de-allocated |
| 152 | // after the buffer is drained. |
| 153 | // The default value is true. |
| 154 | void SetOwnedByThread(bool value); |
| 155 | |
| 156 | #ifdef _DEBUG |
| 157 | // Get the thread associated with this list. |
| 158 | Thread* GetThread(); |
| 159 | |
| 160 | // Validate the consistency of the list. |
| 161 | // This function will assert if the list is in an inconsistent state. |
| 162 | bool EnsureConsistency(); |
| 163 | #endif // _DEBUG |
| 164 | }; |
| 165 | |
| 166 | #endif // FEATURE_PERFTRACING |
| 167 | |
| 168 | #endif // __EVENTPIPE_BUFFERMANAGER_H__ |
| 169 | |