Skip to content

Commit 30130c3

Browse files
sywhangcshung
authored andcommitted
Pulling in Noah's fix in event_pipe_lock_fix branch
1 parent 1144aca commit 30130c3

File tree

6 files changed

+544
-280
lines changed

6 files changed

+544
-280
lines changed

src/vm/eventpipe.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,9 @@ void EventPipe::DisableInternal(EventPipeSessionID id, EventPipeProviderCallback
389389
// Delete the flush timer.
390390
DeleteFlushTimerCallback();
391391

392-
// Flush all write buffers to make sure that all threads see the change.
393-
FlushProcessWriteBuffers();
392+
// Force all in-progress writes to either finish or cancel
393+
// This is required to ensure we can safely flush and delete the buffers
394+
s_pBufferManager->SuspendWriteEvent();
394395

395396
// Write to the file.
396397
if (s_pFile != nullptr)
@@ -401,7 +402,10 @@ void EventPipe::DisableInternal(EventPipeSessionID id, EventPipeProviderCallback
401402

402403
if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
403404
{
404-
// Before closing the file, do rundown.
405+
// Before closing the file, do rundown. We have to re-enable event writing for this.
406+
407+
s_pBufferManager->ResumeWriteEvent();
408+
405409
const EventPipeProviderConfiguration RundownProviders[] = {
406410
{W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL}, // Public provider.
407411
{W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL} // Rundown provider.
@@ -425,6 +429,9 @@ void EventPipe::DisableInternal(EventPipeSessionID id, EventPipeProviderCallback
425429
// Delete the rundown session.
426430
s_pConfig->DeleteSession(s_pSession);
427431
s_pSession = NULL;
432+
433+
// Suspend again after rundown session
434+
s_pBufferManager->SuspendWriteEvent();
428435
}
429436

430437
delete s_pFile;
@@ -437,6 +444,10 @@ void EventPipe::DisableInternal(EventPipeSessionID id, EventPipeProviderCallback
437444
// Delete deferred providers.
438445
// Providers can't be deleted during tracing because they may be needed when serializing the file.
439446
s_pConfig->DeleteDeferredProviders();
447+
448+
// ALlow WriteEvent to begin accepting work again so that sometime in the future
449+
// we can re-enable events and they will be recorded
450+
s_pBufferManager->ResumeWriteEvent();
440451
}
441452
}
442453

@@ -912,4 +923,17 @@ EventPipeEventInstance *EventPipe::GetNextEvent()
912923
EventPipeProvider::InvokeCallback(eventPipeProviderCallbackData);
913924
}
914925

926+
#ifdef DEBUG
927+
/* static */ bool EventPipe::IsLockOwnedByCurrentThread()
928+
{
929+
return GetLock()->OwnedByCurrentThread();
930+
}
931+
932+
/* static */ bool EventPipe::IsBufferManagerLockOwnedByCurrentThread()
933+
{
934+
return s_pBufferManager->IsLockOwnedByCurrentThread();
935+
}
936+
#endif
937+
938+
915939
#endif // FEATURE_PERFTRACING

src/vm/eventpipe.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,12 @@ class EventPipe
333333
// Get next event.
334334
static EventPipeEventInstance *GetNextEvent();
335335

336+
#ifdef DEBUG
337+
static bool IsLockOwnedByCurrentThread();
338+
static bool IsBufferManagerLockOwnedByCurrentThread();
339+
#endif
340+
341+
336342
template<class T>
337343
static void RunWithCallbackPostponed(T f)
338344
{

src/vm/eventpipebuffer.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
#include "eventpipe.h"
88
#include "eventpipeeventinstance.h"
99
#include "eventpipebuffer.h"
10+
#include "eventpipebuffermanager.h"
1011

1112
#ifdef FEATURE_PERFTRACING
1213

13-
EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize)
14+
EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize DEBUG_ARG(EventPipeThread* pWriterThread))
1415
{
1516
CONTRACTL
1617
{
@@ -19,13 +20,18 @@ EventPipeBuffer::EventPipeBuffer(unsigned int bufferSize)
1920
MODE_ANY;
2021
}
2122
CONTRACTL_END;
22-
23+
m_state = EventPipeBufferState::WRITABLE;
24+
#ifdef DEBUG
25+
m_pWriterThread = pWriterThread;
26+
#endif
2327
m_pBuffer = new BYTE[bufferSize];
2428
memset(m_pBuffer, 0, bufferSize);
2529
m_pLimit = m_pBuffer + bufferSize;
2630
m_pCurrent = GetNextAlignedAddress(m_pBuffer);
2731

28-
m_mostRecentTimeStamp.QuadPart = 0;
32+
33+
QueryPerformanceCounter(&m_creationTimeStamp);
34+
_ASSERTE(m_creationTimeStamp.QuadPart > 0);
2935
m_pLastPoppedEvent = NULL;
3036
m_pPrevBuffer = NULL;
3137
m_pNextBuffer = NULL;
@@ -41,6 +47,9 @@ EventPipeBuffer::~EventPipeBuffer()
4147
}
4248
CONTRACTL_END;
4349

50+
// We should never be deleting a buffer that a writer thread might still try to write to
51+
_ASSERTE(m_state == EventPipeBufferState::READ_ONLY);
52+
4453
if(m_pBuffer != NULL)
4554
{
4655
delete[] m_pBuffer;
@@ -58,6 +67,9 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, Eve
5867
}
5968
CONTRACTL_END;
6069

70+
// We should never try to write to a buffer that isn't expecting to be written to.
71+
_ASSERTE(m_state == EventPipeBufferState::WRITABLE);
72+
6173
// Calculate the size of the event.
6274
unsigned int eventSize = sizeof(EventPipeEventInstance) + payload.GetSize();
6375

@@ -106,9 +118,6 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, Eve
106118
payload.CopyData(pDataDest);
107119
}
108120

109-
// Save the most recent event timestamp.
110-
m_mostRecentTimeStamp = *pInstance->GetTimeStamp();
111-
112121
}
113122
EX_CATCH
114123
{
@@ -126,11 +135,11 @@ bool EventPipeBuffer::WriteEvent(Thread *pThread, EventPipeSession &session, Eve
126135
return success;
127136
}
128137

129-
LARGE_INTEGER EventPipeBuffer::GetMostRecentTimeStamp() const
138+
LARGE_INTEGER EventPipeBuffer::GetCreationTimeStamp() const
130139
{
131140
LIMITED_METHOD_CONTRACT;
132141

133-
return m_mostRecentTimeStamp;
142+
return m_creationTimeStamp;
134143
}
135144

136145
EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent, LARGE_INTEGER beforeTimeStamp)
@@ -143,6 +152,8 @@ EventPipeEventInstance* EventPipeBuffer::GetNext(EventPipeEventInstance *pEvent,
143152
}
144153
CONTRACTL_END;
145154

155+
_ASSERTE(m_state == EventPipeBufferState::READ_ONLY);
156+
146157
EventPipeEventInstance *pNextInstance = NULL;
147158
// If input is NULL, return the first event if there is one.
148159
if(pEvent == NULL)
@@ -212,6 +223,8 @@ EventPipeEventInstance* EventPipeBuffer::PeekNext(LARGE_INTEGER beforeTimeStamp)
212223
}
213224
CONTRACTL_END;
214225

226+
_ASSERTE(m_state == READ_ONLY);
227+
215228
// Get the next event using the last popped event as a marker.
216229
return GetNext(m_pLastPoppedEvent, beforeTimeStamp);
217230
}
@@ -226,6 +239,8 @@ EventPipeEventInstance* EventPipeBuffer::PopNext(LARGE_INTEGER beforeTimeStamp)
226239
}
227240
CONTRACTL_END;
228241

242+
_ASSERTE(m_state == READ_ONLY);
243+
229244
// Get the next event using the last popped event as a marker.
230245
EventPipeEventInstance *pNext = PeekNext(beforeTimeStamp);
231246
if(pNext != NULL)
@@ -236,6 +251,19 @@ EventPipeEventInstance* EventPipeBuffer::PopNext(LARGE_INTEGER beforeTimeStamp)
236251
return pNext;
237252
}
238253

254+
EventPipeBufferState EventPipeBuffer::GetVolatileState()
255+
{
256+
LIMITED_METHOD_CONTRACT;
257+
return m_state.Load();
258+
}
259+
260+
void EventPipeBuffer::ConvertToReadOnly()
261+
{
262+
LIMITED_METHOD_CONTRACT;
263+
_ASSERTE(m_pWriterThread->GetLock()->OwnedByCurrentThread());
264+
m_state.Store(EventPipeBufferState::READ_ONLY);
265+
}
266+
239267
#ifdef _DEBUG
240268
bool EventPipeBuffer::EnsureConsistency()
241269
{

src/vm/eventpipebuffer.h

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,34 @@
1212
#include "eventpipeeventinstance.h"
1313
#include "eventpipesession.h"
1414

15+
class EventPipeThread;
16+
17+
18+
// Synchronization
19+
//
20+
// EventPipeBuffer starts off writable and accumulates events in a buffer, then at some point converts to be readable and a second thread can
21+
// read back the events which have accumulated. The transition occurs when calling ConvertToReadOnly(). Write methods will assert if the buffer
22+
// isn't writable and read-related methods will assert if it isn't readable. Methods that have no asserts should have immutable results that
23+
// can be used at any point during the buffer's lifetime. The buffer has no internal locks so it is the caller's responsibility to synchronize
24+
// their usage.
25+
// Writing into the buffer and calling ConvertToReadOnly() is always done with EventPipeThread::m_lock held. The eventual reader thread can do
26+
// a few different things to ensure it sees a consistent state:
27+
// 1) Take the writer's EventPipeThread::m_lock at least once after the last time the writer writes events
28+
// 2) Use a memory barrier that prevents reader loads from being re-ordered earlier, such as the one that will occur implicitly by evaluating
29+
// EventPipeBuffer::GetVolatileState()
30+
31+
32+
enum EventPipeBufferState
33+
{
34+
// This buffer is currently assigned to a thread and pWriterThread may write events into it
35+
// at any time
36+
WRITABLE = 0,
37+
38+
// This buffer has been returned to the EventPipeBufferManager and pWriterThread is guaranteed
39+
// to never access it again.
40+
READ_ONLY = 1
41+
};
42+
1543
class EventPipeBuffer
1644
{
1745

@@ -24,6 +52,15 @@ class EventPipeBuffer
2452
// It is OK for the data payloads to be unaligned because they are opaque blobs that are copied via memcpy.
2553
const size_t AlignmentSize = 8;
2654

55+
// State transition WRITABLE -> READ_ONLY only occurs while holding the m_pWriterThread->m_lock;
56+
// It can be read at any time
57+
Volatile<EventPipeBufferState> m_state;
58+
59+
// Thread that is/was allowed to write into this buffer when m_state == WRITABLE
60+
#ifdef DEBUG
61+
EventPipeThread* m_pWriterThread;
62+
#endif
63+
2764
// A pointer to the actual buffer.
2865
BYTE *m_pBuffer;
2966

@@ -33,8 +70,10 @@ class EventPipeBuffer
3370
// The max write pointer (end of the buffer).
3471
BYTE *m_pLimit;
3572

36-
// The timestamp of the most recent event in the buffer.
37-
LARGE_INTEGER m_mostRecentTimeStamp;
73+
// The timestamp the buffer was created. If our clock source
74+
// is monotonic then all events in the buffer should have
75+
// timestamp >= this one. If not then all bets are off.
76+
LARGE_INTEGER m_creationTimeStamp;
3877

3978
// Used by PopNext as input to GetNext.
4079
// If NULL, no events have been popped.
@@ -89,7 +128,7 @@ class EventPipeBuffer
89128

90129
public:
91130

92-
EventPipeBuffer(unsigned int bufferSize);
131+
EventPipeBuffer(unsigned int bufferSize DEBUG_ARG(EventPipeThread* pWriterThread));
93132
~EventPipeBuffer();
94133

95134
// Write an event to the buffer.
@@ -100,8 +139,8 @@ class EventPipeBuffer
100139
// - false: The write failed. In this case, the buffer should be considered full.
101140
bool WriteEvent(Thread *pThread, EventPipeSession &session, EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId, LPCGUID pRelatedActivityId, StackContents *pStack = NULL);
102141

103-
// Get the timestamp of the most recent event in the buffer.
104-
LARGE_INTEGER GetMostRecentTimeStamp() const;
142+
// Get the timestamp the buffer was created.
143+
LARGE_INTEGER GetCreationTimeStamp() const;
105144

106145
// Get the next event from the buffer as long as it is before the specified timestamp.
107146
// Input of NULL gets the first event.
@@ -113,6 +152,12 @@ class EventPipeBuffer
113152
// Get the next event from the buffer and mark it as read.
114153
EventPipeEventInstance* PopNext(LARGE_INTEGER beforeTimeStamp);
115154

155+
// Check the state of the buffer
156+
EventPipeBufferState GetVolatileState();
157+
158+
// Convert the buffer writable to readable
159+
void ConvertToReadOnly();
160+
116161
#ifdef _DEBUG
117162
bool EnsureConsistency();
118163
#endif // _DEBUG

0 commit comments

Comments
 (0)