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
6#include "common.h"
7#include "eventpipe.h"
8#include "eventpipeeventinstance.h"
9#include "eventpipebuffer.h"
10
11#ifdef FEATURE_PERFTRACING
12
13EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize)
14{
15 CONTRACTL
16 {
17 THROWS;
18 GC_NOTRIGGER;
19 MODE_ANY;
20 }
21 CONTRACTL_END;
22
23 m_pBuffer = new BYTE[bufferSize];
24 memset(m_pBuffer, 0, bufferSize);
25 m_pLimit = m_pBuffer + bufferSize;
26 m_pCurrent = GetNextAlignedAddress(m_pBuffer);
27
28 m_mostRecentTimeStamp.QuadPart = 0;
29 m_pLastPoppedEvent = NULL;
30 m_pPrevBuffer = NULL;
31 m_pNextBuffer = NULL;
32}
33
34EventPipeBuffer::~EventPipeBuffer()
35{
36 CONTRACTL
37 {
38 NOTHROW;
39 GC_NOTRIGGER;
40 MODE_ANY;
41 }
42 CONTRACTL_END;
43
44 if(m_pBuffer != NULL)
45 {
46 delete[] m_pBuffer;
47 }
48}
49
50bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack)
51{
52 CONTRACTL
53 {
54 NOTHROW;
55 GC_NOTRIGGER;
56 MODE_ANY;
57 PRECONDITION(pThread != NULL);
58 PRECONDITION(((size_t)m_pCurrent % AlignmentSize) == 0);
59 }
60 CONTRACTL_END;
61
62 // Calculate the size of the event.
63 unsigned int eventSize = sizeof(EventPipeEventInstance) + payload.GetSize();
64
65 // Make sure we have enough space to write the event.
66 if(m_pCurrent + eventSize >= m_pLimit)
67 {
68 return false;
69 }
70
71 // Calculate the location of the data payload.
72 BYTE *pDataDest = m_pCurrent + sizeof(EventPipeEventInstance);
73
74 bool success = true;
75 EX_TRY
76 {
77 // Placement-new the EventPipeEventInstance.
78 EventPipeEventInstance *pInstance = new (m_pCurrent) EventPipeEventInstance(
79 session,
80 event,
81 pThread->GetOSThreadId(),
82 pDataDest,
83 payload.GetSize(),
84 pActivityId,
85 pRelatedActivityId);
86
87 // Copy the stack if a separate stack trace was provided.
88 if(pStack != NULL)
89 {
90 StackContents *pInstanceStack = pInstance->GetStack();
91 pStack->CopyTo(pInstanceStack);
92 }
93
94 // Write the event payload data to the buffer.
95 if(payload.GetSize() > 0)
96 {
97 payload.CopyData(pDataDest);
98 }
99
100 // Save the most recent event timestamp.
101 m_mostRecentTimeStamp = *pInstance->GetTimeStamp();
102
103 }
104 EX_CATCH
105 {
106 // If a failure occurs, bail out and don't advance the pointer.
107 success = false;
108 }
109 EX_END_CATCH(SwallowAllExceptions);
110
111 if(success)
112 {
113 // Advance the current pointer past the event.
114 m_pCurrent = GetNextAlignedAddress(m_pCurrent + eventSize);
115 }
116
117 return success;
118}
119
120LARGE_INTEGER EventPipeBuffer::GetMostRecentTimeStamp() const
121{
122 LIMITED_METHOD_CONTRACT;
123
124 return m_mostRecentTimeStamp;
125}
126
127void EventPipeBuffer::Clear()
128{
129 CONTRACTL
130 {
131 NOTHROW;
132 GC_TRIGGERS;
133 MODE_ANY;
134 }
135 CONTRACTL_END;
136
137 memset(m_pBuffer, 0, (size_t)(m_pLimit - m_pBuffer));
138 m_pCurrent = GetNextAlignedAddress(m_pBuffer);
139 m_mostRecentTimeStamp.QuadPart = 0;
140 m_pLastPoppedEvent = NULL;
141}
142
143EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent, LARGE_INTEGER beforeTimeStamp)
144{
145 CONTRACTL
146 {
147 NOTHROW;
148 GC_NOTRIGGER;
149 MODE_ANY;
150 }
151 CONTRACTL_END;
152
153 EventPipeEventInstance *pNextInstance = NULL;
154 // If input is NULL, return the first event if there is one.
155 if(pEvent == NULL)
156 {
157 // If this buffer contains an event, select it.
158 BYTE *pFirstAlignedInstance = GetNextAlignedAddress(m_pBuffer);
159 if(m_pCurrent > pFirstAlignedInstance)
160 {
161 pNextInstance = (EventPipeEventInstance*)pFirstAlignedInstance;
162 }
163 else
164 {
165 return NULL;
166 }
167 }
168 else
169 {
170 // Confirm that pEvent is within the used range of the buffer.
171 if(((BYTE*)pEvent < m_pBuffer) || ((BYTE*)pEvent >= m_pCurrent))
172 {
173 _ASSERT(!"Input pointer is out of range.");
174 return NULL;
175 }
176
177 // We have a pointer within the bounds of the buffer.
178 // Find the next event by skipping the current event with it's data payload immediately after the instance.
179 pNextInstance = (EventPipeEventInstance *)GetNextAlignedAddress(const_cast<BYTE *>(pEvent->GetData() + pEvent->GetDataLength()));
180
181 // Check to see if we've reached the end of the written portion of the buffer.
182 if((BYTE*)pNextInstance >= m_pCurrent)
183 {
184 return NULL;
185 }
186 }
187
188 // Ensure that the timestamp is valid. The buffer is zero'd before use, so a zero timestamp is invalid.
189 LARGE_INTEGER nextTimeStamp = *pNextInstance->GetTimeStamp();
190 if(nextTimeStamp.QuadPart == 0)
191 {
192 return NULL;
193 }
194
195 // Ensure that the timestamp is earlier than the beforeTimeStamp.
196 if(nextTimeStamp.QuadPart >= beforeTimeStamp.QuadPart)
197 {
198 return NULL;
199 }
200
201 return pNextInstance;
202}
203
204EventPipeEventInstance* EventPipeBuffer::PeekNext(LARGE_INTEGER beforeTimeStamp)
205{
206 CONTRACTL
207 {
208 NOTHROW;
209 GC_NOTRIGGER;
210 MODE_ANY;
211 }
212 CONTRACTL_END;
213
214 // Get the next event using the last popped event as a marker.
215 return GetNext(m_pLastPoppedEvent, beforeTimeStamp);
216}
217
218EventPipeEventInstance* EventPipeBuffer::PopNext(LARGE_INTEGER beforeTimeStamp)
219{
220 CONTRACTL
221 {
222 NOTHROW;
223 GC_NOTRIGGER;
224 MODE_ANY;
225 }
226 CONTRACTL_END;
227
228 // Get the next event using the last popped event as a marker.
229 EventPipeEventInstance *pNext = PeekNext(beforeTimeStamp);
230 if(pNext != NULL)
231 {
232 m_pLastPoppedEvent = pNext;
233 }
234
235 return pNext;
236}
237
238#ifdef _DEBUG
239bool EventPipeBuffer::EnsureConsistency()
240{
241 CONTRACTL
242 {
243 NOTHROW;
244 GC_NOTRIGGER;
245 MODE_ANY;
246 }
247 CONTRACTL_END;
248
249 // Check to see if the buffer is empty.
250 if(GetNextAlignedAddress(m_pBuffer) == m_pCurrent)
251 {
252 // Make sure that the buffer size is greater than zero.
253 _ASSERTE(m_pBuffer != m_pLimit);
254 }
255
256 // Validate the contents of the filled portion of the buffer.
257 BYTE *ptr = GetNextAlignedAddress(m_pBuffer);
258 while(ptr < m_pCurrent)
259 {
260 // Validate the event.
261 EventPipeEventInstance *pInstance = (EventPipeEventInstance*)ptr;
262 _ASSERTE(pInstance->EnsureConsistency());
263
264 // Validate that payload and length match.
265 _ASSERTE((pInstance->GetData() != NULL && pInstance->GetDataLength() > 0) || (pInstance->GetData() == NULL && pInstance->GetDataLength() == 0));
266
267 // Skip the event.
268 ptr = GetNextAlignedAddress(ptr + sizeof(*pInstance) + pInstance->GetDataLength());
269 }
270
271 // When we're done walking the filled portion of the buffer,
272 // ptr should be the same as m_pCurrent.
273 _ASSERTE(ptr == m_pCurrent);
274
275 // Walk the rest of the buffer, making sure it is properly zeroed.
276 while(ptr < m_pLimit)
277 {
278 _ASSERTE(*ptr++ == 0);
279 }
280
281 return true;
282}
283#endif // _DEBUG
284
285#endif // FEATURE_PERFTRACING
286