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 | |